Files
cc-ci/machine-docs/REVIEW-canon.md

11 KiB

REVIEW-canon — Adversary verdicts for the canon (canonical-sweep) phase

SSOT for what is being verified: /srv/cc-ci/cc-ci-plan/plan-phase-canon-canonical-sweep.md. Gates: M1 (machinery works locally, each piece proven) and M2 (proven end-to-end in real CI), plus the operator-required samever-orthogonality proof. ## DONE only after fresh PASS on both.


Orientation @ 2026-06-17T06:18Z — Adversary online for canon phase; no gate claimed yet

Prior phase samever is DONE + Adversary-verified (M1 1310a95, M2 199f5b6, no VETO). The canon phase has not been bootstrapped by the Builder yet: no STATUS-canon.md / BACKLOG-canon.md, no claim(/status(canon commits, no inbox. I am idling per liveness protocol and will verify promptly when M1 is CLAIMED (watchdog will ping on the claim).

Independent COLD baseline of the claimed starting state (§1) — captured before any canon work

Verified from my own clone + a cold ssh cc-ci, NOT from the Builder:

  • Enrollment: exactly one recipe sets WARM_CANONICAL = Truecustom-html. (grep -rl 'WARM_CANONICAL *= *True' tests/*/recipe_meta.py → 1 hit.) Matches §1 "only custom-html enrolled".
  • canonical.json records on cc-ci: exactly one, for custom-html: /var/lib/ci-warm/custom-html/canonical.json = {recipe: custom-html, version: 1.13.0+1.31.1, commit: 2b82ebabde74a9d9b1fd4cb49722a7037b18a176, status: idle, ts: 20260617T050314Z}, retained volume warm-custom-html_..._content present.
    • NOTE — plan §1 is now slightly stale. The plan (authored 04:43Z) says "ZERO canonical.json records exist." That was true at authoring, but the just-completed samever M2 e2e (custom-html two-run) wrote this record at 05:03:14Z. So there is now exactly one canonical, produced by samever's promote path. This is favorable evidence for canon M1(A) — the promote path already demonstrably writes a real, reusable record + retains the volume for custom-html — but the Builder must NOT cite custom-html's pre-existing canonical as proof of canon's new work (tagged-gate, trigger, all-enrolled, mirror-sync). I will require fresh, canon-attributable evidence for each M1/M2 sub-claim.
  • Timer: nightly-sweep.timer enabled+active, daily OnCalendar (NEXT 2026-06-18 03:00:24 UTC), last fired 2026-06-17 03:09:20 UTC exit 0. So the timer plumbing works; the job was a near-no-op (only custom-html enrolled). Phase must (F) move this to weekly and (M2) prove a real fire advances canonicals, not exit-0 on an empty set.

What I will adversarially probe when claimed (from the plan, not the Builder's narrative)

  • M1(A): a canon-attributable green cold run writes canonical.json AND --quick warm-reattach reuses it; promote now ALSO requires a release tag — feed an UNTAGGED state, confirm NO promote.
  • M1(C): mirror-sync is faithful upstream sync only — never pushes our changes to mirror main, never disturbs unrelated PRs. Will diff before/after on a mirror.
  • M1(D): trigger keyed on latest release tag vs canonical version, NOT commit — new untagged commits on main with same tag ⇒ SKIP; newer tag ⇒ run cold on that tag.
  • M1(B): all ~21 recipes enrolled; warm-volume disk budget recorded (not silently dropped).
  • M2: full sweep promotes greens / leaves reds intact / skips unchanged; run-twice ⇒ skip-all determinism; real (non-hollow) timer fire; tagged-promote proof (untagged green ⇒ no promote).
  • samever orthogonality: (a) no-new-tag ⇒ SKIPPED; (b) new-tag ⇒ canonical(older)→new, real delta, promote; step-back NEVER fires in the sweep. Construct scenarios if the live set doesn't cover both.
  • §2.G: if plausible's canonical lands at 3.0.1, UPGRADE_BASE_VERSION retired cleanly (key + resolver branch + docs + tests) AND plausible still resolves base 3.0.1 dynamically + passes — else kept with a recorded DECISIONS reason. Will re-derive, not trust.
  • Guardrail: NO AI at runtime (pure script + timer).

Pre-claim code read @ 2026-06-17T06:41Z — M1 still IN PROGRESS (M1.2 not yet committed)

Builder has landed 4 of 5 M1 items (27e0628 M1.1, 136100f M1.3, f8c0e53 M1.4+M1.5). M1.2 (the release-tag trigger sweep_decision + mirror-sync wiring into nightly_sweep.sweep()) is not yet committed — M1 is correctly not-yet-claimed. Read the landed code (NOT JOURNAL); points to scrutinize when claimed:

  • M1.1 (27e0628): should_promote_canonical gained tagged param; caller computes tagged = warm_reconcile.is_released_version(recipe, head_version). ⚠️ PROBE: the gate checks head_version (code under test) but promote_canonical records latest_version(recipe_tags(recipe)) (newest tag). Confirm these can't diverge — e.g. a manual latest run where main sits on a tagged commit OLDER than latest tag would gate on the older tag yet promote the newer. In the sweep path (D) the tag is checked out so head==tag; verify the manual/RECIPE=<r> path too.
  • M1.4 (f8c0e53): root cause = sweep service ran the nix-STORE runner copy (no tests/) so TESTS_DIR missing → enrolled_recipes()=[]. Fix sets CCCI_REPO=/etc/cc-ci + cd + execs $CCCI_REPO/runner/nightly_sweep.py. ⚠️ PROBE at M2: confirm /etc/cc-ci actually exists on cc-ci, has runner/ AND tests/, and is git-pulled before nixos-rebuild (else still hollow). The fix also means sweep-logic ships via checkout pull, NOT a store rebuild — verify deploy procedure pulls it.
  • M1.5 (f8c0e53): OnCalendar daily → Sun *-*-* 03:00:00, Persistent kept. Trivial; verify the deployed timer shows the weekly schedule after M2.1 nixos-rebuild.
  • M1.3 (136100f): enroll all 21 — verify the count is exactly the used-recipes.md set and that fixtures (custom-html-*-bad, concurrency, regression) were NOT enrolled.
  • Still owed for M1 claim: M1.2 sweep_decision(recipe, latest_tag, canon_version) → run|skip:no-new-version|skip:never-released keyed on version_key NOT commit; mirror-sync via open-recipe-pr.sh --reconcile-only (faithful, vendored); cold-run ON THE TAG. Unit tests for all.

M1: PASS @ 2026-06-17T07:12Z — machinery cold-verified (claim 626badd, code @ d4cc9e4)

Verified from a COLD start: my own clone for code/pure-logic, a fresh independent clone on cc-ci (/tmp/adv-canon @ 626badd) for the unit suite, and a cold ssh cc-ci for live state. I did NOT read JOURNAL-canon.md before forming this verdict. Every M1 sub-claim re-derived against the plan, not the Builder's narrative.

M1.1 tagged-promote gate (§2.A) — PASS.

  • Code: should_promote_canonical returns is_enrolled and overall==0 and not quick and not ref and tagged; caller computes tagged = is_released_version(recipe, head_version); promote_canonical now records the TESTED head_version (commit d4cc9e4), not a re-derived latest_version. My prior PROBE (head_version-vs-latest_version divergence on a manual RECIPE=<r> run) is CLOSED by d4cc9e4 — read the diff, it promotes exactly the tested version.
  • Unit: ran tests/unit/test_promote.py myself in the fresh cc-ci clone — all 6 pass, each gate clause individually exercised (test_no_promote_when_untagged asserts tagged=False → False; all-conditions asserts tagged=True → True). Not hollow.
  • Live PROMOTE: re-derived git rev-list -n1 1.13.0+1.31.1 = df2e27339f983a25da548fc8b8d56e9af8645f83 and /var/lib/ci-warm/custom-html/canonical.json records EXACTLY that commit + version 1.13.0+1.31.1, status idle, retained volume warm-custom-html_..._content present. So the promote recorded the tag's own commit (correcting samever's earlier 2b82eba merge-commit record) — the divergence fix is live-proven, not just unit-tested.
  • Live UNTAGGED → NO PROMOTE: independently confirmed 1.13.1+1.31.1 is NOT-A-TAG in the custom-html clone → is_released_version returns False → gate blocks. canonical.json is unchanged (still df2e273). The full live tagged-vs-untagged e2e is M2.4; at M1 the code + unit + live-not-a-tag + unchanged-canonical chain is sufficient.

M1.2 release-tag trigger + faithful mirror-sync (§2.C/§2.D) — PASS.

  • sweep_decision re-derived directly (no pytest) — truth table exactly right and VERSION-keyed, not commit-keyed: new>canon→run; equal→skip no-new-version; older→skip; no tag→skip never-released; no canon→run(seed). The function takes only (latest_tag, canon_version) — it CANNOT see commits, so new untagged commits on main can never trigger a run. That IS the operator's refinement.
  • scripts/recipe-mirror-sync.sh read in full: pins an explicit coopcloud upstream remote, force- syncs mirror main := upstream/main + all tags, pushes NOTHING of our own. PR close is gated on git merge-tree --write-tree NEW_MAIN_SHA <pr-head> == upstream MAIN_TREE (i.e. the PR's merge is a no-op because it's already in upstream) → close; otherwise "left as-is". Faithful, never merges, never disturbs unrelated PRs.
  • nightly_sweep.sweep() wiring read: per enrolled recipe mirror_sync → fetch_recipe → sweep_decision → run_on_tag (checkout the release tag + CCCI_SKIP_FETCH=1 so head IS the tag → tagged-gate passes; REF popped → cold → promote allowed). Pure script.

M1.3 all recipes enrolled (§2.B) — PASS. My grep -rl 'WARM_CANONICAL = True' set is EXACTLY the 21 used-recipes.md rows (incl. uptime-kuma, the lone external row — correctly enrolled for CI/canonical even though excluded from weekly upgrade). Fixtures (custom-html-*-bad, concurrency, regression) NOT enrolled.

M1.4 hollow-sweep fix — PASS (code; live is M2.1). nix/modules/nightly-sweep.nix exports CCCI_REPO=/etc/cc-ci, cds there, and execs $CCCI_REPO/runner/nightly_sweep.py — the checkout WITH tests/, replacing the store copy whose missing tests/ caused enrolled_recipes()=[]. Root cause correctly addressed in code. ⚠️ CARRIED TO M2: /etc/cc-ci is currently STALE — git -C /etc/cc-ci HEAD is e60415d (Phase-3 era), canon code NOT yet there. M2.1 deploy MUST git -C /etc/cc-ci pull before nixos-rebuild, else the deployed timer stays hollow. I will verify the pull + a real fire at M2.5.

M1.5 weekly timer (§2.F) — PASS (code). OnCalendar = "Sun *-*-* 03:00:00", Persistent = true. Deployed-timer schedule verified at M2.

Guardrail NO-AI-at-runtime — PASS. grep of nightly_sweep.py / warm_reconcile.py / recipe-mirror-sync.sh for anthropic|claude|openai|llm|gpt|ai_ → only one code COMMENT match, zero calls. Pure script + systemd timer.

Full unit suite — PASS. Ran cc-ci-run -m pytest tests/unit/ in the fresh independent cc-ci clone @ 626badd295 passed in 5.60s, matching the claim. Enrolling 21 recipes broke nothing.

Minor narrative note (not a defect): the claim cites proof-A ts 065027Z but live canonical ts is 065532Z; promoting the same tag again yields the same version+commit (only ts moves), so this is a benign re-run, not a divergence — the recorded version/commit are correct either way.

Verdict: M1 PASS. No VETO. All M1 DoD items cold-verified; the deployed-state items (M1.4 live, M1.5 timer schedule) are honestly scoped by the Builder to M2 and I will hold them there. (Consulted JOURNAL-canon.md only AFTER writing this verdict: no surprises — confirms the proof-A/C sequence.)