manifest-before-floor; floor field LAST (hub_settings DB row overrides env, acts immediately — the 0.81/0.113 9-minute skew incident); MinAgent fleet gate (controller CHANGELOG header convention from ctrl v0.114.0); the controller capability gate as box-level backstop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2.9 KiB
Publish-train rules — coupled controller/agent releases
Standing gates for every artifact publish train (agent binary + controller golden + Day-0 manifest + floor). Each rule cites the incident that created it. The box-level backstop (rule 4) exists because rules 1–3 are operator discipline and discipline fails; the rules stay the primary control.
1. Manifest before floor (the GL-1 rule, restated)
Publish and vouch the artifacts in the Day-0 manifest before any floor movement. A floor that points at an unvouched (or unpublished) version bricks self-updates: boxes are told to move to a version they cannot verify. (GL-1 supply-chain arc; see the go-live package records.)
2. The manifest screen carries the LIVE DB floor — save the floor field LAST
The hub's Day-0 manifest UI also persists the global floor as a DB row
(hub_settings.min_controller_version — hub/internal/store/store.go,
GetGlobalMinControllerVersion). The DB row overrides the env floor and acts immediately on the
next report cycle — it is not staged by the manifest vouch.
Incident: publish train 0.81/0.113 (2026-07-11,
documentation/pilot/RUNBOOK-publish-0.81-0.113-2026-07-11.md): the operator saved the manifest
screen with the floor field filled; the floor acted at once and controller 0.113.0 reached Peti's
box ~9 minutes before agent 0.81.0 — the exact forbidden skew, benign only because the box had
zero NAS shares.
Rule: fill the floor field last, in a separate save, only after rule 3's fleet check passes.
3. MinAgent fleet gate
A controller release that depends on coupled agent behavior declares MinAgent: X.Y.Z in its
CHANGELOG entry header line (felhom-controller convention, since v0.114.0; retroactively, 0.113.0's
effective MinAgent was 0.81.0 for the NAS add). The floor may not rise past a controller version
whose MinAgent is not yet met fleet-wide: check the hub dashboard — every reporting box must show
agent ≥ MinAgent before the floor save. Manual at the current fleet size; hub-enforced per-box
gating is the deferred option-3.
4. Box-level backstop: the controller's capability gate
Since controller v0.114.0, a coupled feature entry point probes the agent's capability
(controller/internal/agentapi/features.go — route probe: 2xx ⇒ supported, 404 ⇒ older agent,
transport/5xx ⇒ indeterminate, never "too old") and refuses up front with an honest Hungarian
message instead of failing mid-pipeline. This turns a violated ordering into a graceful refusal —
it does not license sloppy trains: rules 1–3 remain the primary control.
Convention for new coupled features: add a row to the featureProbes table + a Supports gate
call at the feature's entry point, and declare MinAgent per rule 3. When the agent someday reports
an explicit version in its envelope, Supports should prefer the version comparison (roadmap).