Files
cc-ci/machine-docs/STATUS-settings.md
autonomic-bot dd6712c243
Some checks failed
continuous-integration/drone/push Build is failing
status(settings): ## DONE — M1+M2 fresh Adversary PASS (cd19c1b, 99d6bbc), no VETO
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:07:14 +00:00

151 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# STATUS — phase `settings`
**Phase:** server-level `settings.toml` + `SKIP_CANONICALS_FOR_UPGRADE` + release-tag-first no-canonical
fallback. Plan: `/srv/cc-ci/cc-ci-plan/plan-phase-settings-ci-server-config.md`.
## DONE
All Definition-of-Done items Adversary-verified with fresh PASSes (no standing VETO), 2026-06-17:
- **M1 PASS** — REVIEW-settings.md @17:00Z (claim fed2678 / code cd19c1b): settings loader (stdlib
tomllib, defaults, graceful absent/malformed, warn-and-ignore unknown, TypeError on wrong type) +
`SKIP_CANONICALS_FOR_UPGRADE` wired into `resolve_upgrade_base` + release-tag-first no-canonical
fallback reusing samever's helper; 32 + 315 unit pass; scope narrow; stdlib-only; no secrets.
- **M2 PASS** — REVIEW-settings.md @17:35Z (claim a9ff941 / deployed `/etc/cc-ci`@99d6bbc, byte-identical
runner to cd19c1b): live on cc-ci — keycloak (no canonical) → release tag `10.7.1+26.6.2` not main-tip;
gitea (canonical) unchanged `last-green` under default false; scratch `true` bypasses gitea's canonical
to the release-tag path; restored to false; harness file-pickup proven via the real `/etc/cc-ci/settings.toml`.
Server in steady state: `/etc/cc-ci/settings.toml` ABSENT (default false), checkout clean @99d6bbc.
## Gate: M1 PASS (Adversary @2026-06-17T17:00Z, REVIEW-settings.md) · M2 CLAIMED (see below)
**M1 commit:** `cd19c1b` (feat: settings loader + flag + fallback + unit tests) — Adversary cold-PASS,
no VETO. **M2 deployed:** `99d6bbc` on `/etc/cc-ci`. Tree clean, pushed to origin/main.
### WHAT is claimed (M1 — implemented + unit-tested)
1. **Settings loader** `runner/harness/settings.py` — stdlib `tomllib`, one `[upgrade]` table with
`skip_canonicals_for_upgrade` (bool, **default false**). `_SCHEMA` (table→{key:(type,default)}) is the
single source of defaults + validation. Graceful on absent/unreadable/malformed file (WARN +
all-defaults — never crashes); unknown table/key → warn-and-ignore; present known key of wrong type →
`TypeError`. Path = `$CCCI_SETTINGS` else `/etc/cc-ci/settings.toml`. Tracked `settings.toml.example`
documents the key (no secrets).
2. **`SKIP_CANONICALS_FOR_UPGRADE` wired into `resolve_upgrade_base`** (`runner/run_recipe_ci.py`): when
true, the canonical (`version`) branch is skipped entirely → no-canonical fallback. Guard:
`if rec and rec.get("version") and not skip_canonicals:`. Scope = upgrade base only (promotion /
`--quick` untouched).
3. **Release-tag-first no-canonical fallback** `_no_canonical_base` (`runner/run_recipe_ci.py`),
always-on: (1) newest release **tag** with version strictly older than `head_version` — reuses
`warm_reconcile.newest_older_version(warm_reconcile.recipe_tags(recipe), head_version)`; (2) raw
`main`-tip — only if no prior release tag; (3) skip. (Guarded: tag lookup skipped when
`head_version` is falsy → main-tip, preserving prevb behavior for that caller.)
### HOW to verify (re-runnable from a fresh clone)
All commands from the repo root of a clone at `cd19c1b` (or later). Unit tests need pytest from nixpkgs:
```
# 1. Full upgrade-base matrix + settings loader tests
nix shell nixpkgs#python311Packages.pytest -c pytest tests/unit/test_upgrade_base.py tests/unit/test_settings.py -v
# 2. Whole unit suite (regression — nothing else broke)
nix shell nixpkgs#python311Packages.pytest -c pytest tests/unit/ -q
# 3. Lint (my files) — ruff check + format
nix shell nixpkgs#ruff -c ruff check runner/ tests/unit/test_settings.py tests/unit/test_upgrade_base.py
nix shell nixpkgs#ruff -c ruff format --check runner/harness/settings.py runner/run_recipe_ci.py tests/unit/test_settings.py tests/unit/test_upgrade_base.py settings.toml.example
# 4. Default is false WITHOUT any file (this server unchanged):
nix shell nixpkgs#python311Packages.pytest -c python3 -c "import sys; sys.path.insert(0,'runner'); from harness import settings; print(settings.load('/no/such/file.toml').skip_canonicals_for_upgrade)"
# -> False
```
### EXPECTED outcome
- (1) → **32 passed**. Key cases proving the matrix:
- `test_flag_false_canonical_present_unchanged` — flag false + canonical (≠head) → canonical, `last-green`
reason, tags/main NOT consulted (byte-for-byte prevb).
- `test_no_canonical_prefers_release_tag_over_main_tip` — flag false + no canonical → version=`10.7.1+26.6.2`
(newest tag `< head 10.8.0+26.6.3`), main NOT consulted; reason contains `no-canonical fallback`/`release tag`.
- `test_no_canonical_no_older_tag_falls_back_to_main_tip` — no older tag → `ref`=main-tip.
- `test_no_canonical_no_tag_no_main_skips` — neither → `skip`.
- `test_flag_true_bypasses_canonical_into_release_tag_fallback` — flag true + canonical present → resolves to
release tag `10.7.1+26.6.2`, NOT the canonical `10.6.0+26.5.0`.
- `test_flag_true_canonical_present_no_older_tag_uses_main` — flag true routes through the FULL chain → main-tip.
- `test_canonical_equals_head_steps_back_to_newest_older` / `..._no_older_published_skips` — samever step-back
unchanged (older→version; none→skip, NOT main-tip).
- loader: `test_absent_file_yields_defaults`, `test_malformed_toml_degrades_to_defaults`,
`test_wrong_type_errors_clearly`, `test_int_not_accepted_for_bool`, `test_unknown_key/table_warns_and_ignored`,
`test_env_var_path_override`, `test_flag_true_read`/`_false_read`.
- (2) → **315 passed** (full suite, no regression).
- (3) → `All checks passed!` and `... already formatted`.
- (4) → `False` (default with no file → this server behaves as today).
### WHERE
- `runner/harness/settings.py` (loader, `DEFAULT_PATH = /etc/cc-ci/settings.toml`, `Settings`, `load()`, `get()`).
- `settings.toml.example` (repo root, tracked).
- `runner/run_recipe_ci.py`: `resolve_upgrade_base` (flag guard) + new `_no_canonical_base` helper; import
`settings as settings_mod`.
- `tests/unit/test_settings.py` (loader, 13 tests), `tests/unit/test_upgrade_base.py` (matrix, +8 new tests).
### NOTE (pre-existing, out of scope — see DECISIONS)
`scripts/lint.sh` (pinned ruff) flags `dashboard/dashboard.py` + `tests/unit/test_dashboard.py` as
needing reformat — confirmed present at HEAD f68f1c5, NOT in this phase's diff. Not fixed here (narrow
scope). My 5 phase files are ruff-clean + format-clean.
---
## Gate: M2 CLAIMED, awaiting Adversary @2026-06-17T17:25Z
M1 is Adversary-PASS (REVIEW-settings.md verdict @17:00Z, fed2678/cd19c1b). M2 = verified live on cc-ci.
**Deployed:** `/etc/cc-ci` at **99d6bbc** (pushed to origin/main; the deployed checkout the nightly
sweep runs from, and the absolute path the Drone recipe-CI runner reads). No nixos-rebuild needed — the
change is pure runner Python loaded at runtime from the checkout. Live settings file
`/etc/cc-ci/settings.toml` is **ABSENT** → default false (server steady state restored after the test).
### WHAT is claimed (M2)
- The live server harness reads the settings file from the host path `/etc/cc-ci/settings.toml`
(absent → default false), confirmed by the flag value flipping with the file's presence.
- **(a)** A recipe **without** a canonical (`keycloak`, no `canonical.json`) resolves its upgrade base
to the **newest release tag `< head`** (`10.7.1+26.6.2`), NOT the raw main-tip.
- **(b)** With `SKIP_CANONICALS_FOR_UPGRADE = true` (scratch file), a **canonical-bearing** recipe
(`gitea`, canonical `3.5.3+1.24.2-rootless`) resolves to the **release-tag base** (canonical
BYPASSED) — proven by the reason changing from `last-green (warm canonical, status=idle)` to
`no-canonical fallback: newest release tag older than head 3.6.0+1.24.2-rootless`. Scratch file then
removed → restored to false (reason back to `last-green (warm canonical)`).
- Default false ⇒ this server's canonical-bearing path is unchanged (gitea false → `last-green` base).
### HOW to verify (cold, on the server, from /etc/cc-ci or your own clone)
The probe runs the EXACT deployed `resolve_upgrade_base` against live settings + live canonical registry
(`/var/lib/ci-warm/<r>/canonical.json`) + live recipe tags (`~/.abra/recipes/<r>`). Faithful, no
deploy/teardown.
```
ssh cc-ci
cd /etc/cc-ci && git rev-parse --short HEAD # 99d6bbc (or later)
ls /etc/cc-ci/settings.toml # ABSENT -> default false
# CASE 1 — flag false (default, no file): (a) keycloak, plus gitea unchanged
HOME=/root cc-ci-run scripts/show-upgrade-base.py keycloak gitea
# CASE 2 — flag true (scratch), then RESTORE
printf '[upgrade]\nskip_canonicals_for_upgrade = true\n' > /etc/cc-ci/settings.toml
HOME=/root cc-ci-run scripts/show-upgrade-base.py gitea keycloak
rm -f /etc/cc-ci/settings.toml # restore default false
HOME=/root cc-ci-run scripts/show-upgrade-base.py gitea
```
### EXPECTED (verbatim BasePlan lines observed @17:2017:25Z)
- CASE 1 (false):
- `keycloak``BasePlan(kind='version', version='10.7.1+26.6.2', ref='', reason='no-canonical fallback: newest release tag older than head 10.8.0+26.6.3')` (canonical=None; newest_release_tag<head=10.7.1+26.6.2; NOT main-tip 12ac6db8…)
- `gitea` `BasePlan(kind='version', version='3.5.3+1.24.2-rootless', ref='', reason='last-green (warm canonical, status=idle)')` (canonical=3.5.3 present used)
- CASE 2 (true):
- `gitea` `BasePlan(kind='version', version='3.5.3+1.24.2-rootless', ref='', reason='no-canonical fallback: newest release tag older than head 3.6.0+1.24.2-rootless')` (canonical 3.5.3 present but BYPASSED reason is the release-tag path)
- `keycloak` same as CASE 1 (no canonical either way)
- RESTORE (file removed false):
- `gitea` reason back to `last-green (warm canonical, status=idle)`; flag reads `False`.
### WHERE
- Deployed code: `/etc/cc-ci` @ 99d6bbc (origin/main). Probe: `scripts/show-upgrade-base.py`.
- Live registry: `/var/lib/ci-warm/{keycloak (none),gitea}/canonical.json`. Recipe tags:
`~/.abra/recipes/{keycloak,gitea}`. Settings path: `/etc/cc-ci/settings.toml` (absent now).
Server left in steady state: `/etc/cc-ci/settings.toml` ABSENT (default false), checkout clean @99d6bbc.
On a fresh Adversary PASS of M2 (with M1 PASS standing), I will write `## DONE`.