Skip to content

srvN quality sweep: enrollment wall, swap-backing axis, read-back independence criterion - #7235

Merged
briansrls merged 26 commits into
mainfrom
session/vivid-wolf-453
Jul 25, 2026
Merged

briansrls merged 26 commits into
mainfrom
session/vivid-wolf-453

Conversation

@briansrls

@briansrls briansrls commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor

Item 3 of the operator's three-item direction: the quality sweep over the "modeled" middle of the host-standup spine. Three pieces, each with a discriminating RED control run by execution.

1. Enrollment construction wall — gunbc.runner_host_deploy

Runner-host commands were built from a bare RunnerHostDeploy, so nothing structurally prevented producing installer/enable argv for a host that is not a fleet_intent row. Now no function takes a bare deploy and yields an ArgvCommand: both builders consume a RunnerHostAdmission and return RunnerCommandOrRefusal whose refusal arm carries no command — the bad state is unwritable rather than checked after the fact (DESIGN §5 construction-over-validation).

Honest residual, named in-code: .dag has no visibility modifiers, so RunnerHostEnrolled can still be hand-forged. That is an explicit lie rather than an accident, and it closes when node/subtree visibility grants land.

2. Swap backing as a modeled axis — gunbc.host_swap_backing

The finding this piece surfaced is the reason it is worth having: the corpus already converges a swap cap over a resource whose existence nothing models. runner_slot_allocation declares memory_swap_max = 34359738368 (32 GiB) per slot, host_converge drives it as MemorySwapMax, runner_unit_live_read reads it back and reports Converged on a match. Every step is correct in isolation and the composition proves nothing, because MemorySwapMax is a ceiling: systemd stores, reports, and matches it byte-for-byte on a host with zero swap devices. Corpus-wide there are zero mkswap/swapon/fallocate//proc/swaps occurrences — swap exists only as prose in fleet_acceptance_criteria and as the floor-timeout mechanism in ci_budget_tree.

  • Requirement derived, never authored. No fleet reach from a session container (the A1 gap), so a per-host swap table here would be invention. Declaring a nonzero MemorySwapMax is declaring that backing must exist, so the requirement falls out of gunbc_runner_slot_desired().memory_swap_max and moves when it moves. Declared limit: min_total is the per-slot cap, not slots × cap — the aggregate question is unmeasured and multiplying would assert a worst case as fact.
  • Four separable states, each naming different work: no producer → UnknownRefused (build one), zero → Absent (provision), undersized → Drifted (resize), sufficient → Converged. No "assume the distro default gave us swap" arm: srv1..srv4 were installed across at least two paths, so its error rate would be unmeasurable by construction.
  • Cap qualified by backing, asymmetrically — a presupposition check, and a witness pins the asymmetry against a later refactor symmetrizing it into a lattice meet.
  • Frontier counted, not silent. 4 of 4 enrolled hosts derive UnknownRefused today; the count is pinned by witness, so a fabricated fixture reds rather than greening the matrix.

Effect realization (fallocate/mkswap/swapon/fstab, /proc/swaps read) is blocked on the argv materializer's hardcoded five-parameter vocabulary and is declared, not stubbed — declaring unreachable operations would add census rows no consumer can reach.

3. Read-back independence as a standing criterion — gunbc.readback_independence

#7213 Finding 2 was recorded as a note beside one verdict fold. Generalized: a read-back is evidence about a subject only if performing the read could not have established the subject. The class is not rare — a client that auto-starts its daemon, a mount check that triggers an automount, a token read that refreshes the token, an ensure-shaped operation used as a query. The tell is about the transport, never the value: could running this have made the answer true?

  • Asymmetric correction — tainted negatives survive (the probe could have created the subject and still could not report it); only Converged is rewritten, and to UnknownRefused rather than Absent or Conflict, so the deficit stays countable.
  • Unclassified ⇒ tainted — the opposite default converts every un-audited transport into evidence.
  • Non-vacuous both ways — systemctl show/is-active and a /proc/swaps read are classified inert and keep their Converged; a criterion that refused every probe would be the absorbing fallback wearing a safety badge.

Live consumer: host_swap_backing routes every host verdict through it. The routing is a no-op today, so the witness proves it load-bearing by substitution — the same verdict carried by the sccache probe is refused.

Verification

Green 7 swap conjuncts, 7 read-back conjuncts, 4 wall conjuncts — all PASS by execution
RED (swap) swap_cap_verdict_qualified_by_backing → backing-blind ⇒ FAIL host_swap_backing_witnesses
RED (read-back) readback_evidence_verdict → trust the probe ⇒ FAIL readback_independence_witnesses
RED (wall) admit_runner_host → admit everything ⇒ 3 of 4 wall conjuncts red, enrolled_host_still_admits correctly stays green (non-vacuity control)

Plan doc gains §5.5 (criterion) and §5.6 (swap presupposition). No merged module's behaviour changes.

Brian Searls and others added 24 commits July 25, 2026 06:23
…server was a no-op)

Follow-up to #7170. Live execution on srv3 caught what the string witnesses
missed (§5 specification-without-execution): the modeled unit was
  Type=simple
  ExecStart=/usr/local/bin/sccache --start-server   # daemonizes, exits 0
so systemd saw the ExecStart exit cleanly and marked the unit `inactive (dead)`
the instant it started; the backgrounded server idle-timed-out unsupervised and
Restart=on-failure never fired (success exit). The unit was a no-op — sccache
survived only via its own per-invocation auto-start. The string witness
"ExecStart equals the install path" was green the whole time.

Fix (grounded by execution on srv3 first): foreground the server, matching the
production ctrl-sccache.service invocation verified running month+ on srv1/srv2:
  Type=simple
  Environment=SCCACHE_START_SERVER=1
  Environment=SCCACHE_NO_DAEMON=1
  ExecStart=/usr/local/bin/sccache
This reduces the §3 fork between the modeled unit and production rather than
minting a third shape.

Closes the execution gap two ways: build_cache_unit_is_foreground_supervised is
a construction check that makes the defective combo (Type=simple paired with a
backgrounding --start-server) UNWRITABLE (carries the CI grain, since systemd
--user is not runnable in the hermetic container), and the live srv3
re-provision carries the runtime is-active grain. Census note accounts the
+1-line delta as a correctness swap toward the cited production authority.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ependence criterion

Three quality items over the "modeled" middle of the host-standup spine, each
landing with a discriminating RED control run by execution.

1. Enrollment construction wall (runner_host_deploy). No function takes a bare
   RunnerHostDeploy and yields an ArgvCommand any more: both command builders
   consume a RunnerHostAdmission and return RunnerCommandOrRefusal whose refusal
   arm carries no command, so a host absent from fleet_intent cannot produce
   installer or enable argv at all. Honest residual named in-code: .dag has no
   visibility modifiers, so RunnerHostEnrolled can be hand-forged — an explicit
   lie rather than an accident; closes when node/subtree visibility grants land.

2. Swap backing as a modeled axis (host_swap_backing). The corpus already
   converges MemorySwapMax = 32 GiB per slot over a resource whose existence
   nothing models — systemd stores and reports a ceiling byte-for-byte on a host
   with zero swap devices, so the knob greens over nothing. The requirement is
   DERIVED from the declared cap rather than authored per host (no fleet reach
   from a session container, so a per-host swap table would be invention);
   backing states stay separable (no producer / zero / undersized / sufficient,
   four different pieces of work); and a cap verdict is qualified by its backing,
   asymmetrically — a presupposition check, pinned by witness against becoming a
   lattice meet. The observation frontier is a counted number (4 of 4 hosts
   UnknownRefused today), so a fabricated fixture reds the witness instead of
   greening the matrix. Effect realization is blocked on the argv materializer's
   hardcoded five-parameter vocabulary and is declared, not stubbed.

3. Read-back independence as a standing criterion (readback_independence).
   #7213 Finding 2 generalized off one host: a read-back is evidence only if
   performing the read could not have established the subject. Asymmetric
   correction (tainted negatives survive; only Converged is rewritten, to
   UnknownRefused so the deficit stays countable), unclassified probes treated
   as tainted, and inert probes keep their evidence so the criterion is
   non-vacuous in both directions. Live consumer: host_swap_backing routes every
   host verdict through it; the witness proves the routing is load-bearing by
   substitution, since /proc/swaps is inert today.

Witnesses: 7 swap conjuncts, 7 read-back conjuncts, 4 wall conjuncts — all PASS.
RED controls: perturbing swap_cap_verdict_qualified_by_backing to be
backing-blind and readback_evidence_verdict to trust the probe FAILs both
witnesses; perturbing admit_runner_host to admit everything reds 3 of 4 wall
conjuncts with enrolled_host_still_admits correctly staying green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title srvN subsumption srvN quality sweep: enrollment wall, swap-backing axis, read-back independence criterion Jul 25, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review July 25, 2026 19:28
@briansrls
briansrls merged commit baf5bc8 into main Jul 25, 2026
10 checks passed
@briansrls
briansrls deleted the session/vivid-wolf-453 branch July 25, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant