# Session handoff — 2026-06-15 (70) Seventieth handover. A dedicated infrastructure session, run on a separate **`ci`** branch (in a worktree), that built the project's **entire CI/CD pipeline** on the self-hosted Gitea Actions runner — from nothing to a live gate plus a six-target cross-platform release. Net: the **CI** / `requirements.md` **TT5** item and **D1**/**D2** are now done; **D3** and a couple of TT5 tails remain. Decisions are recorded in a new ADR namespace, **`docs/ci/adr/`** (ADR-ci-001/002/003), kept separate from the main integer ADR sequence to avoid cross-branch number collisions (the same split the website branch uses — and it paid off: `main` independently took ADR-0049 this period, which would have collided). ## §1. State at handoff **Branch:** `ci` (worktree). **`main` has been merged into `ci`** (commit `138e766`, clean — `ci` and `main` touched disjoint files) so the gate runs against current `main` before CI lands there. Working tree clean except the in-progress doc updates from this handoff. Pushes/promotion are the user's step. **Gate verified locally on the merged code:** `clippy -D warnings` clean; **`cargo test` 2488 passing / 0 failing / 1 ignored** (the long-standing `friendly` doctest). main's features came in with their tests (2424 → 2488). **Pipeline (`.gitea/workflows/`):** - `build-ci-image.yaml` — builds + pushes the CI image (`node:22-bookworm-slim` + single-user nix + the flake's devShell pre-warmed) to the Gitea registry. Triggers only on image-input changes (Dockerfile / flake / toolchain). - `ci.yaml` — the gate: `clippy -D warnings` + `cargo test`, branch pushes + PRs (docs-only changes skipped). - `release.yaml` — on a `v*` tag: `test` → `build` matrix over the **four non-macOS** targets via `cargo-zigbuild`, upload to the Gitea release. - `release-macos.yaml` — **workflow_dispatch** (tag input) on the Tart Apple-Silicon runner (`runs-on: macos`): test → build both `*-apple-darwin` → de-nix `libiconv` + ad-hoc re-sign → upload. **Verified live this session:** the 4-target release published **8 assets** (binary + `.sha256` each) for tag `v.0.0.0-citest3`; the macOS build was proven portable (system-only deps) + signed + launches on the runner. ## §2. What was built (and the non-obvious bits) - **Nix flake** (ADR-ci-002, relocated from a would-be `main` ADR-0049): one pinned toolchain (`1.95.0`) for dev *and* CI; `cargo-zigbuild` + `zig` (Linux only) for the cross targets; `apple-sdk` on darwin. - **Runner facts** (ADR-ci-001): jobs run *inside* a container (`ci-public` → `catthehacker/ubuntu`), so host nix is unreachable — hence the baked image. The Mac runner is **host execution**; its label is `macos` (`:host` in the registration is the act_runner backend, not part of the label). - **Cross-compile** (ADR-ci-003): `cargo-zigbuild` for the 4 non-macOS targets. Windows needs an **empty `libsynchronization.a` stub** (`ci/winstub/`, wired via `.cargo/config.toml`) — std links `-lsynchronization`, absent from rust-overlay's toolchain + zig's mingw, but forwarded by `kernel32`. - **macOS** (ADR-ci-003 amendment): built on **real Apple hardware** (Tart), so the SDK is fully licensed — no osxcross grey area. The darwin stdenv bakes a `/nix/store` `libiconv` path into the binary; the build rewrites it to `/usr/lib/libiconv.2.dylib` (`install_name_tool`) and re-signs ad-hoc (`codesign -f -s -`; `install_name_tool` invalidates the signature, arm64 refuses unsigned). A guard fails the build on any remaining `/nix/store` dep. - **Cache hygiene (Mac):** the runner wipes the workspace each run, so cargo `target/` never accumulates; the persistent nix store is bounded by **generation** (record the devShell in a persistent profile, keep the 2 newest via `nix-env --delete-generations +2`, GC the rest). First sweep reclaimed a ~3.8 GB one-time backlog of build scaffolding (source + build-only deps, *not* re-installed toolchains). ## §3. Immediate next steps (user) 1. **Push `ci`** → the gate re-runs in CI (should be green; no image rebuild — the merge didn't touch the flake/Dockerfile). 2. **Promote:** `git checkout main && git merge ci` — a **fast-forward** (`ci` already contains `main`) — then push `main`. CI goes live; `release-macos` becomes dispatchable (workflow_dispatch needs the default branch). 3. **First real release:** tag `v0.1.0` (auto-builds the 4 Linux/Windows assets), then **dispatch `release-macos` for `v0.1.0`** with the Mac up (adds the 2 macOS assets) → a full 6-binary release. 4. **Cleanup:** delete the `v.0.0.0-citest*` test tags + their releases. 5. **Runner-side:** add `min-free`/`max-free` to the Mac's `/etc/nix/nix.conf` as a hands-off nix-store backstop. ## §4. Known gaps / follow-ups - **Versioning is not wired into the binary** (flagged by the user). The release **git tag is nowhere in the produced binary** — there is no `--version` flag, no `CARGO_PKG_VERSION` use anywhere in `src/`, and the release workflows use the tag only for the *release name* + *asset filenames* (`rdbms-playground--`). `Cargo.toml` is a static `version = "0.1.0"`, decoupled from the tag. So a `v0.5.0` tag yields a `…-v0.5.0-…` asset whose binary knows nothing of "0.5.0". To fix later: add a `--version` flag, and inject the tag at build time (e.g. a `build.rs` reading a CI-provided env, or bumping `Cargo.toml` as part of tagging) so the binary and the release agree. - **D3 packaging** — Homebrew / Scoop / winget / `cargo binstall` manifests (asset naming is already binstall-friendly). - **TT5 tails** — Windows is build-only (no execution runner); Tier-4 PTY (TT4) is unwired in CI. - **`fmt` gate** — deliberately off (tree isn't stock-`rustfmt`-clean); revisit on `main`. - **Website → Cloudflare** deploy — the separate, simpler workflow, still to do.