# FIELD PLAN v2 — repaired candidate 53d625cd. SOURCE-ONLY. NOT GRANTED, NOT RUN.

todlando 2026-09-12. Revised against the registered record: releases#304 comments 5644042784
(A7 registration + Amendment 2), 5644076361 (Amendment 3), 5644088658 (Amendment 4), and the
preserved drivers .spt/preserved/a7-304/{cpubracket.ps1,armb.sh,census.ps1}. v1 is SUPERSEDED —
it was built from repository keyword searches and regressed the registered experiment.

## 0. WHAT v1 GOT WRONG (corrected by replacement, not annotation)

- v1 called a KILLED leg "a VOID arm, never a red". WRONG. killed = a measured 3 s deadline
  expiry AND the populated arm's FAILURE VERDICT (Amendment 3 A4.1). failed = a measured
  non-deadline error. The only VOID is a MISSING SUBJECT ROW, and it stops investigation
  before any further trial.
- v1 read "populated" as a large host rule store. WRONG. Populated = THE BOOTSTRAP-OWNED PAIR
  PRESENT vs ABSENT on the SAME host. A7 Arm A (absent) measured 2633 / 1784 / 1952 ms; Arm B
  (present) NEVER RAN because setup failed acceptance. That question is still open.
- v1 bracketed the SERIES. WRONG on both counts: rule census AND CPU bracket bracket EACH TRIAL.
- v1 had no CPU brackets for Arm A. A7 omitted exactly those; this plan does not repeat it.

## 1. SUBJECT AND BOUNDARY

Subject = the PRODUCT's own stderr line, not a hand-rendered script:

    bootstrap-firewall leg=verify-query program=powershell.exe wall_ms=<n> outcome=<completed|failed|killed>

Boundary: the wall is measured INSIDE the process around the child — the same boundary the 3000 ms
budget is enforced at (bootstrap_firewall.rs:136-188, Duration::from_secs(3)). It is NOT comparable
with a Measure-Command wall around powershell.exe; the two never go in one table. RETAINED from the
registration verbatim.

Port 29470 throughout, never 5470. Isolated SPT_HOME. Every capture REDIRECTED TO A FILE, never
piped — a pipeline's exit is the tail's.

## 2. PRE-CHECK — amended predicate (A2.1), RE-MEASURED AT THIS CANDIDATE

Scoped to the QUERY const body with comment lines stripped. Measured by me at 53d625cd:

    QUERY body 94 lines, 32 stripped, 62 remain
    Named-Rules       1  (expect 1)  OK
    PersistentStore   0  (expect 0)  OK
    Get-NetIPAddress  1  (expect 1)  OK
    ActiveStore       2  (expect 2)  OK

CONTROLS, both non-zero as required: the unstripped body reads PersistentStore 1 (the comment that
explains why the store is no longer read) and the stripper removed 32 lines. A dead stripper or a
dead counter would show as 0 on one of these.

Note vs the registration: 32 of 92 at 921aa68f, 32 of 94 here — two lines added, same 32 stripped.
The raw-string count predicate stays WITHDRAWN as an instrument (it reads Named-Rules 3 /
PersistentStore 1 over the composed script, because script() always concatenates OWNERSHIP, which
DEFINES Remove-Owned); retained as intent only.

## 3. BINARY PROVENANCE — separate from any source check (A2.2)

    git -C W rev-parse HEAD            must read 53d625cd0bd88a04815efdf6c8209a3096bf53e8
    git -C W status --porcelain        must be EMPTY (measured EMPTY at plan time)
    git -C W rev-parse HEAD:crates/spt-daemon/src/bootstrap_firewall/windows.rs
                                       reads c28874ef6823a7bb522168514ac81c16ddd427d5
    cargo run -p xtask -- pool-claim --pool W/target --label <lane>   from W itself
    cargo build -p spt --bins          W stated explicitly per command, never an inherited cwd
    then its OWN row: sha256, byte size, mtime, absolute path of the built spt.exe

The product's self-printed sha256 anchor is a CLAIM. The independently measured hash is retained
whether or not the anchor appears, and the two are COMPARED — neither substitutes for the other.
Each command's exit is read directly before the next runs.

## 4. ARMS — three scheduled attempts each, no replacement runs

ARM A  owned pair ABSENT, unelevated, x3 cold. report_lan_admission verifies BEFORE requesting
       elevation, so this is exactly one verify-query over an empty owned set.

ARM B  owned pair PRESENT (POPULATED), x3, after ONE elevated reconcile by the product itself —
       never a hand netsh, so the rules carry the shipped desired_specs shape. Teardown is the
       second elevated action (an unelevated --stop removes nothing). TWO elevated actions is the
       minimum, and they are the only thing asked of liam, who stays limited to otherwise-blocking
       elevated setup/cleanup.

All three scheduled attempts are kept in place. A non-completed row is REPORTED, never re-rolled to
manufacture three completions.

## 5. OUTCOME CLASSIFICATION (binding, Amendment 3 A4.1 as doyle re-stated it)

    completed   measurement; three required for a SUCCESSFUL arm
    failed      measurement OF failed (non-deadline Err); error text quoted in place; does not count
    killed      measurement OF killed; IS the populated arm's failure verdict; does not count
    row absent  NOT a measurement -> VOID -> investigate before any further arm runs

Absence is never a quiet success. armb.sh already encodes this: the row extraction prints
"ABSENT -- VOID, investigate before further arms" when the subject line is missing.

## 6. BRACKETS — per TRIAL, both kinds, both arms

Order per trial, from armb.sh, with the A7 Arm A omission repaired by applying it to BOTH arms:

    census pre -> CPU bracket pre -> START stamp -> PRODUCT INVOCATION (timed) -> stop -> END stamp
    -> CPU bracket post -> census post -> subject-row extraction

CPU bracket = cpubracket.ps1: a 3-second per-process CPU delta, tagged and UTC-stamped, reporting
total_cpus_3s, pct_16core, build_class count and the top 5. It runs OUTSIDE the timed invocation. It
describes ADJACENT LOAD ONLY — no threshold, no gate, no causal attribution: a busy bracket does not
discard a wall and a quiet one does not upgrade it.

Census = census.ps1, per-rule reads only, with TWO POSITIVE CONTROLS OR THE DUMP IS VOID:
ctl_5470 >= 1 AND ctl_sptexe >= 1, else valid=NO-VOID. Subject rows: group "spt-core bootstrap TCP"
and rows on TCP 29470. A bare port-filter enumeration read 5470 as ZERO unelevated; that is why the
controls exist.

## 7. THE REPRESENTATION QUESTION — what Arm B can decide, WITHOUT touching the subject

A7 never reached this: its Arm B setup log is the DEFECT ITSELF at the old binary. Preserved at
.spt/preserved/a7-304/a7b-setup.log — reconcile-write completed, verify-query completed, then
LAN_FIREWALL_UNVERIFIED naming the pair. The product refused rules it had just written.

And .spt/preserved/a7-304/matcher-fields-b-precondition.csv records what the host then held:

    Profile         "Any" / "Domain, Private"                           vs constants "Any" / "Private,Domain"
    RemoteAddress   100.64.0.0/255.192.0.0 and 192.168.1.0/255.255.255.0 vs /10 and /24
    Program         "Any"                                               vs no program filter

Those are the three axes the lane normalizes, observed on the host, from the store, at the moment of
the refusal the same run produced.

DISCRIMINATOR, available with NO subject change: decide() refuses with a DIFFERENT STRING at each
arm, and reconcile() surfaces it to the operator.

    spec match    mismatch_message(&want) — "the observed rules do not match the admission pair: ..."
    source store  "Bootstrap rule X is effective but its source store is Y, not Local: ..."
    no LAN scope  no_lan_scope_message()
    ENFORCEMENT   "Bootstrap rule X is configured but ActiveStore enforcement is [...], not Full"
    verified      Ok(())

PRE-REGISTERED OUTCOME TABLE (all five, so no reading is chosen after the fact):

    mismatch_message again  -> representation NOT accepted in the field; the lane's fix does not
                               reach this host's spelling. A real finding against my own change.
    enforcement string      -> representation ACCEPTED; the refusal has moved PAST the spec match to
                               an arm this lane never touched. Full is NOT weakened to obtain it.
    source-store string     -> representation accepted; persistence arm refused.
    no_lan_scope_message    -> host carries no LAN prefix; LAN half unverifiable this trial.
    Ok(())                  -> representation accepted AND enforcement reads Full.

CONDITIONAL PREDICTION, stated with its condition: the B-precondition CSV reads EnforcementStatus
"ProfileInactive NoLocalUser" and PrimaryStatus "Inactive". If the host's profiles are inactive at
trial time as they were then, the EXPECTED representation-accepted signal is the ENFORCEMENT string,
NOT Ok(()). If profiles are active, Ok(()) becomes reachable. Both are pre-registered above; neither
is a weakened predicate, and no reconcile is ever ASSUMED successful — a trial that did not observe a
written pair reports that.

## 8. D1 ONLY — out-of-band QUERY capture, per the ruling

53d625cd stays UNCHANGED. D2 (emitting the raw verify-query stdout) is NOT implemented.
Any out-of-band QUERY capture is a SEPARATE DIAGNOSTIC INVOCATION, never the product's failure-time
snapshot, and it is SERIALIZED OUTSIDE the timed trials — never concurrent with one.

The product's own snapshot remains unrecoverable at this sha, measured by reading: snapshot()
windows.rs:675-680 hands stdout straight to serde_json::from_str; powershell() :666-674 forwards to
super::run; super::run bootstrap_firewall.rs:99-109 logs only leg/program/wall_ms/outcome;
run_bounded :136-188 pipes, caps at 1 MiB, returns. No tee, no file, no hook.

## 9. PREREQUISITE — specified, not demonstrated

The listener serves only an APPLIED signed set, so the isolated home must carry one or
`serve lan --bootstrap` refuses by name. S1-S5 run BEFORE any timed trial and S5's provenance row
must exist and match the staged set's artifact_sha256. If the listener refuses by name, the arm is
VOID and THE PREREQUISITE IS THE FINDING, never a wall.

Runner/job census is refreshed AFTER the release build and BEFORE ARM A, two-axis: a jobs-level CI
read plus an ancestry-authenticated local process census (the parent chain, not a name match —
rust-analyzer rustup shims match a build-name filter and are not builds).

## 10. WHAT THIS PLAN DOES NOT ASK FOR

No build grant. No field grant. No instrumentation change. No CI launch. No checkout edit. Both pins
and the warm target preserved.
