Operator 2026-06-17: (1) no-canonical upgrade base should prefer the most recent release TAG < head (a real published predecessor, reusing samever's helper), with raw main-tip only as a last resort, then skip — always-on, improves this server too. (2) SKIP_CANONICALS_FOR_UPGRADE=true feeds that same release-tag-first fallback (so it swaps canonical->latest-release base without losing upgrade coverage). (3) model bumped sonnet->opus.
8.6 KiB
Phase settings — CI-server settings.toml + SKIP_CANONICALS_FOR_UPGRADE + release-tag-first fallback
Mission (operator-specified 2026-06-17): two cohesive resolve_upgrade_base changes for the cc-ci
server:
- A minimal, extensible server-level
settings.tomlwith its first valueSKIP_CANONICALS_FOR_UPGRADE(bool, defaultfalse,falseon this server). Whentrue, the upgrade tier resolves its base without canonicals — codifying that canonicals are an optional optimization (an operator switch). The file is structured to hold other CI-server configs later; ship it minimal. - An improved no-canonical fallback (always-on, not flag-gated): when no canonical is used — none
exists, its promote failed, or the flag is
true— the base should be the most recent release TAG onmainolder than the PR head (a real published predecessor), with the rawmain-tip only as a further fallback if the recipe has no prior release, then skip. Today the no-canonical path jumps straight tomain-tip, which may be an untagged WIP commit.
These compose: SKIP_CANONICALS_FOR_UPGRADE=true always takes the improved release-tag-first fallback.
State files: STATUS-settings.md, BACKLOG-settings.md, REVIEW-settings.md, JOURNAL-settings.md. DECISIONS.md shared.
1. Background
Canonicals are an optimization + robustness aid for the upgrade base, not a requirement: the upgrade tier already falls back to main-tip when no canonical exists (and the install/backup/restore/custom tiers never use canonicals). This flag makes that explicit and operator-controllable: a server can run the upgrade tier purely off main-tip (real predecessor), ignoring the warm-canonical layer entirely — useful for a deployment that doesn't run the canonical sweep, for debugging, or for a simpler setup.
This server keeps false (canonicals on — the optimized/robust path); the flag is the documented escape
hatch.
2. Design
A. A minimal server settings file + loader.
- A server-level TOML (today cc-ci server config is scattered env vars like
MAX_TESTS,CCCI_RUNS_DIR). Add a small settings layer that reads a TOML once. Suggested home: a host path the harness reads (e.g./srv/cc-ci/settings.toml), with a trackedsettings.toml.exampledocumenting the keys + defaults — OR extend any existing cc-ci config module if one fits. Builder: check for an existing cc-ci config mechanism first and extend it rather than spawn a parallel one. - Defaults baked into the loader → an absent file, or an absent key, yields the default (so this server
needs no file to behave as today). Stdlib only (
tomllib). Validate: unknown keys warn-and-ignore; wrong type errors clearly. Per-server: the file is a host override, not committed config (a tracked.exampleis fine; the live file is operator-managed and must carry no secrets — secrets stay in sops). - Keep it minimal + extensible: one
[ci]/[upgrade]table with the single key now, shaped so future CI-server configs slot in without a redesign.
B. SKIP_CANONICALS_FOR_UPGRADE (bool, default false).
- Wire into
resolve_upgrade_base(run_recipe_ci.py): guard the canonical (version) branch — when the flag istrue, skip the canonical lookup entirely and fall through to the improved no-canonical fallback (§2.C). Effectively: behave as if no canonical exists. - Scope it narrowly to the upgrade BASE (its name says so). Do NOT change canonical promotion or the
--quickwarm-reattach with this flag — those are separate optimizations (a futureSKIP_CANONICAL_SWEEP/SKIP_QUICKcould gate them; out of scope here — note in DECISIONS).
C. Improved no-canonical fallback — release tag before main-tip (always-on). Replace the current
"jump straight to main-tip" no-canonical path with:
- most recent release TAG with version strictly older than the PR head — reuse
samever's existing helper (newest published version< head, fromrecipe_tags, version-ordered); deploy that tag (a clean published predecessor) as the base. - raw
main-tip (the target-branch tip) — only if the recipe has no prior release tag at all. - skip — if neither (no predecessor / head == main-tip with no older tag).
This applies whenever no canonical is used (none exists, promote failed, or the flag is
true), so it improves this server too (false flag, but un-promoted recipes get a real release base instead of a WIP commit). The unified chain becomes:canonical (unless flag/none/==head) → newest release tag < head → main-tip → skip, withsamever's step-back and this fallback sharing the same release-tag helper.
- Sweep interaction (now better): with the flag
true, a cold-on-latest sweep run resolves base = newest release tag< latest= the previous release → the upgrade tier still runs a realprevious-release → latestupgrade (not a skip, not a no-op). SoSKIP_CANONICALS_FOR_UPGRADEswaps the base from last-green canonical to latest published predecessor without losing upgrade coverage. PR runs likewise get a real release base. This server runsfalse, so the canonical path is unchanged.
3. Gates
M1 — implemented + unit-tested. Settings loader (TOML, stdlib, defaults, validation, graceful on
absent/malformed file); SKIP_CANONICALS_FOR_UPGRADE wired into resolve_upgrade_base; the release-tag-first
no-canonical fallback (§2.C) implemented by reusing samever's newest-release-tag-<-head helper. Unit
tests for the full resolution matrix: flag false + canonical present → canonical (unchanged); flag
false + no canonical → newest release tag < head (NOT main-tip); no canonical AND no older release
tag → main-tip; none → skip; flag true → canonical skipped → same release-tag-first fallback; absent
file / absent key → default false; malformed file handled. Adversary cold-verifies: default is false
(this server byte-for-byte unchanged); the no-canonical fallback returns a real older release tag
(main-tip only as last resort); true genuinely bypasses the canonical; the loader can't crash the harness
on a bad/absent file; scope is the upgrade base only (promote/--quick untouched); the fallback reuses the
samever helper (no divergent version-ordering).
M2 — verified on the server. The live server reads the settings (file present with
SKIP_CANONICALS_FOR_UPGRADE = false, or absent → default false) and the upgrade path still uses
canonicals — unchanged. Demonstrate, with evidence: (a) a recipe without a canonical resolves its
base to the newest release tag < head (a real published predecessor), not the raw main-tip; (b)
flipping SKIP_CANONICALS_FOR_UPGRADE = true (test/scratch, not permanent) makes a canonical-bearing
recipe ALSO resolve to that release-tag base (canonical bypassed), then restore false. Confirm the
harness picks up the file on the server. Fresh Adversary PASS on both milestones → ## DONE.
4. Guardrails
- Default
false; this server staysfalse— the change must be a no-op for current behavior. A regression to the default upgrade-base path fails the gate. - Minimal + extensible — one setting now, structured for more; do not over-build a config framework.
- Stdlib only for the loader; no secrets in settings.toml (config only; secrets stay in sops).
- Narrow scope — the flag affects only the upgrade-base resolver, not promotion or
--quick. - Never weaken a test. Commit author
autonomic-bot <autonomic-bot@noreply.git.autonomic.zone>; push every commit. If the settings file lives at a host path requiring a deploy, treat it as a host change (loops may deploy if clean + verify health, else file for the orchestrator).
5. Definition of Done
A minimal, extensible CI-server settings.toml (+ tracked .example, defaults in the loader) is read by
the harness; SKIP_CANONICALS_FOR_UPGRADE (default false, false on this server) is wired into the
upgrade-base resolver; AND the no-canonical fallback now prefers the most recent release tag < head
(reusing samever's helper) with raw main-tip only as a last resort. false/absent leaves current
behavior byte-for-byte unchanged; true bypasses the canonical into the same release-tag-first fallback.
Unit-tested across the full resolution matrix; verified live on the server (false = unchanged; no-canonical
recipe → release-tag base; true = canonical bypassed, then restored). Scope limited to the upgrade base
(promote/--quick separate). M1 + M2 fresh Adversary PASSes in REVIEW-settings.md.