# W7 gate notes (doyle, in progress)

Lane: `feat/331-w7-auto-apply`, todlando on `.worktrees/w7-updater`.
Gate rig: `.worktrees/gate-w7`, pool claimed `gate-w7-doyle`, warmed green
(`cargo build --workspace --tests` exit 0) at the pre-product head `3c23dcd1`.

## Negative control — measured by me, with a hole

At `3c23dcd1` (REQ activation + hertz's 3 rig cherry-picks, NO product code):

    cargo test -p spt --test auto_apply_e2e
    -> b_applies_a_peer_staged_brain_only_set_with_no_operator_verb ... FAILED
    -> exit 101, 146.15s, 2026-09-24 ~18:47-18:49Z

So the rig is red on the lane head under MY hand, not only on hertz's 2/2 on main.

**The hole: I do not know WHICH wait failed.** I piped the run through `tail -40`
and the panic text fell outside it — the two daemon children inherit stderr and
keep writing `CONN_LIFECYCLE` lines past the panic, so the tail is child spam, not
the failure. 146s is *consistent* with a timeout at the apply wait (RIG_WAIT 120s
+ ~26s setup) rather than at setup or at the `ARTIFACT_WAIT` positive control, but
consistent is not measured and this is recorded as unproven.

**DISCHARGED 2026-09-24, by arithmetic over measured bounds — NOT by reading a label.**
The bounds are `ARTIFACT_WAIT` 600s (staging) and `RIG_WAIT` 120s (`peer_rig/wait.rs:28`,
the bare `wait_for`). todlando's GREEN run at `21f83701` measures staging 14.03s, apply
7.47s, adapters leg 5.15s. So: my red ran 146.15s; a failure at the staging wait would
have burned 600s+ and did not, so staging PASSED; the next bare `wait_for` is the apply
wait at 120s, and setup ~12s + staging ~14s + 120s = ~146s is the only bound that fits;
it cannot be the adapters leg, since reaching that requires the apply wait to pass, which
`3c23dcd1` cannot do. **Failed at the apply wait** — the assertion the gate cares about,
so the control is valid.

Recorded as an inference, not an observation. The re-run is no longer needed; had the
arithmetic been ambiguous it would have been.

## Standing rig caveat (hertz's comment, `auto_apply_e2e.rs:452`)

The apply wait was `RIG_WAIT` (120s), a legacy default, NOT a measured bound.

**MEASURED and RULED 2026-09-24:** the apply wait is **7.47s against the 120s bound** on
hfenduleam (Windows debug, `21f83701`, todlando, full capture). **KEEP 120s** — 16x headroom
here, and kitsubito's ~460 MB Linux debug artifact is STILL unmeasured, so shrinking would buy
nothing and risk a flake on the box nobody has measured. What changes is the COMMENT, riding
hertz's rig commit: the measured number replaces the prediction, the Linux arm stays named as
unmeasured, and the "a red HERE may be this wait" caveat is dropped for Windows only.

## Gate plan on handback

1. Move `.worktrees/gate-w7` HEAD to the handback sha, re-claim the pool from that
   worktree (same tree, same pool — not a foreign claim).
2. Compile gate.
3. `auto_apply_e2e` GREEN, full capture, failure-point discharged above.
4. Ordering-shaped tests re-run BY NAME (a pass count is not evidence the test exists).
   Enumerated at `3c23dcd1` — W7 makes the BROKER the swap initiator and hangs a new adapters
   leg off promotion, so the promotion state machine and the swap-adjacent e2es are the blast
   radius. My first enumeration was an incompetent predicate (grepped the whole workspace for
   the WORD "order" and returned string-ordering tests in spt-proto/spt-term); this list is
   scoped to the update domain instead.

   `crates/spt-daemon/src/brainproc.rs` — the promotion state machine the adapters leg waits on
   (it must fire on Applied for exactly the staged version, never during the trial, never after
   a rollback):
     trial_promotes_on_ready_then_supervises_the_accepted_binary
     ready_but_old_gen_never_drains_does_not_promote_rolls_back
     ready_candidate_promotes_once_old_gen_drains
     ready_but_wrong_bytes_rolls_back_never_promotes
     ready_with_matching_bytes_promotes
     trial_rolls_back_after_budget_of_pre_ready_exits
     trial_kills_alive_never_ready_candidate_before_rollback
     stale_generation_minus_one_ready_never_promotes

   `crates/spt-daemon/src/release.rs`:
     non_monotonic_version_is_rollback
     update_set_channel_and_rollback_gates_hold

   Swap-adjacent e2es (KH 6.11 neighborhood — canonical exe captured at broker start):
     brain_respawn_rename::brain_respawns_onto_applied_bytes_after_in_place_rename
     brain_survive::pty_and_quic_survive_brain_process_restart_onto_swapped_binary
     live_adapt_translation_swap_e2e::adapter_apply_swaps_locked_translation_binary_without_restarting_endpoint
     bundled_adapters_e2e::a_bundled_set_installs_its_members_and_never_downgrades_one
     adapter_swap_e2e::an_update_prunes_retired_strings_and_keeps_dropped_binaries
     adapter_swap_e2e::a_declared_entry_arriving_without_its_exec_bit_is_forced_loud_then_runs
5. `traceable-reqs check` exit 0 on the FINAL head (activation-time exit 1 at
   `4d91f5d1` is correct and binds nothing).
6. Land by ff push: `git push origin <sha>:refs/heads/main`, `ls-remote`, then assert
   `mergeCommit.oid == headRefOid`. No `gh pr merge` verb.

## ORDER's int evidence — ruled 2026-09-24

todlando found that arm 1's bundle member is a FRESH INSTALL, so no apply record is written
(`cli.rs:23784` returns None on an empty `old`) and the rig could not carry
`[int->REQ-UPDATE-AUTO-APPLY-ORDER]`. I verified that line myself; hertz reached it
independently from main.

Ruled: hertz authors his accepted plan (preinstall the member at 0.9.0 so the bundle's 1.0.0
is an upgrade) WITH his A1 (assert no `adapter:` row before B starts), A2 (`applied_by.counter`
== B's `applied_version` in the SAME `BState`, both 11), A3 (typed read via
`ReleaseCache::subject_applies()`). Assert on COUNTER, never version — same build plus a tag,
so version cannot discriminate old core from new.

I WITHDREW my own amendment (a second, un-preinstalled member to keep fresh-install coverage)
after checking: `bundled_adapters_e2e` arm 1 covers fresh-node install of both members, and
`cli.rs:34175` already unit-pins `adapter_apply_record(.., "", ..) == None`. Redundant rig.

Scope: W7 orders MOVES, not installs. Widening `SubjectApply` would change W6's shipped divulge
contract (#337, in acceptance) and render an empty-`old` row as a move from nothing. The gap is
real and filed — **releases#344**, backlog/change — not dropped. REQ text must cite it.

**A3's consequence:** the new rig head will NOT compile on main, so the red-on-main arm is
historical (hertz 2/2 @ `eb0b9a2f`) plus my own red at `3c23dcd1` above. The LIVE control for
the new assertion is todlando's pre-promotion revert of the adapters leg — full capture, failure
LABEL recorded, run id kept beside the green.

Sequencing: todlando pushes the `applied_by` field and names branch + sha -> hertz rebases
`hertz/w7-336-rig` onto it and hands back ONE commit -> todlando takes it. Nobody else edits
that rig.

## Impl head read — `21f83701`, 2026-09-24 (pre-gate, box-free)

`feat/331-w7-auto-apply` @ `21f83701`, one commit, 19 files, +1645/-128. Trailer carries the
space spelling `Co-authored by: todlando`. Read at source, not from the summary:

**The owed variant answer: a SPLIT** — new `applyhost::apply_staged_in_broker`. Verified it
shares the one gate (`prepare_apply`) and the one swap (`swap_and_record`) with both CLI paths
and replaces only the restart transport, raising `brainproc::BrainRestart` in-process. His
rejections are recorded in the fn doc with mechanisms: `apply_staged` would self-dial the
broker's own socket (a seat on a brain-role client of itself, a 300-try connect loop against
its own accept path, a restart verb answered by the process that asked);
`apply_staged_daemonless` restarts nothing, leaving the old brain on old code under an open
trial. That is the right call and the right reason.

`swap_and_record` grew a `prior_product_version` parameter: CLI paths pass their own build
constant (they ARE that image), the broker passes the image the supervised brain announced —
correct, since after a brain-only update the broker still runs older bytes.

**Edge I chased and cleared:** the broker's fallback when no brain has announced is
`.unwrap_or_else(|| env!("CARGO_PKG_VERSION"))` (`autoapply.rs`). `coordinator_image()` reuses
the PRE-EXISTING `recorded_coordinator_image` behind the same generation gate `daemon status`
reads, and that helper's unit already covers all four arms including `None`
(`broker.rs:10394`-`10410`). The only uncovered line is glue that degrades to old behavior.
Not a finding — checked rather than raised.

Unit `apply_staged_in_broker_swaps_records_and_raises_the_signal_in_process` pins the split's
properties: new bytes at the canonical path, old image stepped aside, signal raised, prior
version taken from the caller.

## Owed by todlando with the head

Which `apply_staged` variant the broker calls — its signature takes a broker socket
name, so a broker calling it self-dials. Deliberately not ruled.
