!testme gitea PR #10 (drone build 35, 2026-10-05) rendered a summary card whose
app screenshot was custom-html-tiny's — a fossil from a May-31 run dir with the
same number. Root cause chain:
- /var/lib/cc-ci-runs/ keeps old-era run dirs numbered up to 1377; drone's
build counter restarted when its DB was re-created (2026-09-27), so new
builds collide with old dirs (35 was one).
- the run reused the collided dir without cleaning it: fresh results.json/
summary.* landed NEXT TO the old screenshot.png.
- screenshot.capture()'s SCREENSHOT-hook branch skipped the actual snap when
out_path already existed ('the hook may have saved it' — no hook does), so
capture() 'succeeded' without writing and the card embedded the fossil.
Fix, cosmetics-only (R7 — verdict logic untouched):
- results.fresh_artifact_dir(): at run start, remove ONLY the files/dirs this
run owns (results.json, summary.*, badge.svg, lint.txt, screenshot.png,
junit/) from its artifact dir; anything else is left alone; never raises.
- run_recipe_ci calls it right after computing run_artifact_dir.
- screenshot.capture(): the hook branch ALWAYS snaps (same settle/blank-retry
path as the default branch) — a pre-existing file is never trusted.
Unit tests: fresh_artifact_dir (owned-only removal, dir creation, odd entries)
+ capture() fake-playwright regression proving a fossil out_path is overwritten.
The live /root/.ssh/authorized_keys on cc-ci had drifted from this
declarative list: several keys were added manually over time and would
have been wiped by the next nixos-rebuild switch.
- sync the declared list to the live file (10 keys, verified blob-for-blob)
- add the new notplants-orchestrator and nptest keys (added live first)
- drop claude-sandbox keys at operator request (incl. one unnamed live
key identified as a sandbox key, fingerprint SHA256:Wlhj5g4IjCId1wvR
HnxEiwVVu2N+aYa4gRsY1XfOkts) and the never-live claude@claude-vm key
- live file updated in the same change, so state is converged now
- acme-dns.nix: ONE dual-zone SAN cert (ci+*.ci.autonomic.zone AND ci+*.ci.commoninternet.net)
via the same acmedns account — storage re-keyed by cc-ci-acme-storage-seed.service; handoff
reads the new cert dir. Single secret pair => zero changes to the traefik reconciler.
- dashboard/bridge/reports: dual Host rules during the bake window (bridge gets explicit
parentheses so && does not shadow the dashboard on the new host).
- drone abra app renamed to drone.ci.autonomic.zone (fresh DB supported: DRONE_USER_CREATE
re-injects the sops bridge token); runner RPC + bootstrap-drone-oauth.sh follow.
- harness: app_domain() issues *.ci.autonomic.zone run domains; RUN_APP_RE / stack-name
regexes / docker-prune accept BOTH zones during the bake. Warm stacks deliberately stay
on the legacy zone (data-warm volumes; post-bake migration).
- URLs in bridge/dashboard defaults + recipe-report.py follow the new names.
The operator enabled China-hosted models on the workspace, so deepseek-v4-flash
is available on the Go tier again (it was the model the weekly run used before
the host moved off ZEN, and it is the cheap/fast one for the per-recipe
subagents). Verified on the host: `opencode run --model
opencode-go/deepseek-v4-flash` answers.
The weekly run's MAIN agent moves to opencode-go/glm-5.3-flash separately, in
upgrader.env on the host.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
The host's opencode credential is an OpenCode Go subscription key, so ZEN
models (`opencode/…`) fail there with "Insufficient balance". The weekly
run's recipe subagents read this file, so they move to the Go provider.
glm-5.2 rather than deepseek-v4-flash because the latter is China-hosted on
Go and needs a per-workspace opt-in.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
cc-ci-secrets now encrypts to the new host (195.201.88.249) via its own
ssh-host-key-derived age identity, like the canonical cc-ci did, so the
off-box master recovery key no longer has to live on that box —
/var/lib/sops-nix/key.txt there holds the host-derived identity instead.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
statix flagged the repeated `systemd.` keys (tmpfiles marker, acme-dns
daemon, traefik handoff oneshot); they are now one nested attrset. Purely
structural: `#cc-ci` still evaluates. With #33 this makes the push
self-test's lint stage pass again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
`scripts/lint.sh --fix` from the pinned lint devshell: 90 Python files
reformatted (ruff format, mechanical) and one C420 (dict comprehension →
dict.fromkeys) in tests/unit/test_f211_sso_skip.py. The push self-test had
been failing at the lint stage since build 1313 (2026-08-31) on exactly
these files; nothing else changed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
The whole server (every service module, the harness tooling, sops wiring,
acme-dns) becomes one reusable module, nix/modules/default.nix, so another
flake can run cc-ci on a host it defines. First consumer: the
cc-ci-orchestrator repo's `#cc-ci` host, which runs the CI server and the
orchestrator together on one Hetzner machine.
Two things the modules hard-coded become options (nix/modules/options.nix):
- cc-ci.publicIPv4 — acme-dns's listen address and ns-acme glue record.
- cc-ci.sopsFile — the secrets.yaml path; defaults to the secrets/ submodule,
but a consumer that imports cc-ci as a plain input (no private submodule)
points it at the deployed --recursive checkout and sops-nix reads it at
activation (validateSopsFiles off for that case).
The standalone host (nix/hosts/cc-ci-hetzner) now only carries hardware,
networking and identity and imports the module via the flake. Verified: the
`#cc-ci` system derivation is byte-identical before and after
(/nix/store/ckp1244bz86fz3qbx81n5kx60c1lak3m-…531670d.drv on both).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz