# Enforcement-representation validation package (#297 / #304 W2)

hertz, for doyle. The committed Rust half, the revised boundary check, the synthetic controls,
the single admitted live boundary run, and the targeted gate. Read `README-WRONG-MODEL.md` first
if you have not — the Python pair beside these files is frozen evidence of a wrong model and is
not part of this package.

## 1. The committed half

| | |
|---|---|
| commit | `85f84d738fa702f35c83910f314aae17849d125c` |
| branch | `test/304-w2-enforcement-codes` (worktree `.worktrees/repr-fix-53d625cd`) |
| base | `96080953088f311047720bca6ad05e753e384ae2` (todlando's successor) |
| diff | one file, `crates/spt-daemon/src/bootstrap_firewall/windows.rs`, +91 −14 |
| blob | `848a23fe18e5ca819774f9881ef13504221992f3` |
| on-disk sha256 | `ee5db634b03a84338fa73d14ab9505f757f3015249ecc59c7aacb6280b5573e8` |
| `traceable-reqs check` | exit 0 (0.4.1); `REQ-BOOTSTRAP-FIREWALL-ENFORCEMENT-CODES` `+impl +unit` |
| cargo | **RUN, section 5** — compiled and 27/27 passed under the targeted gate. The commit message still says NOT COMPILED because it was true when written; section 5 supersedes it and this row says which. |

**The two pins are not interchangeable.** This tree checks out with `core.autocrlf=true`, so the
same commit is CRLF on this host and LF on a Linux one. The blob hash is the revision; the
sha256 is this host's bytes. `extraction-boundary.ps1` pins on the sha256 and says so in its
refusal, because that is what it can hash — re-pin on whichever host runs it.

Cases in the migrated acceptance table, as literals: `[0] [2] [5] [20] [1,1] [1,5] [5,20] []`,
every one a refusal. The `[1,1]` and `[1,5]` cells are the ones that fail the moment whole-slice
equality is softened into membership.

## 2. The revised boundary check — your six gaps

`extraction-boundary.ps1`, plus `boundary-lib.ps1` which now holds the extraction, parsing and
classification so this check and the synthetic control run **one** parser rather than two.

1. **Dead parse.** `Read-ProbeArm` requires exactly one COUNT record per arm, and TYPE and VALUE
   rows must each form a complete `0..n-1` run matching it. Missing, duplicated, gapped,
   non-numeric, unknown-key, ERROR-beside-results and silent arms are all MALFORMED, which
   classifies VOID and can never reach a PASS. The old behaviour — `PROBE_COUNT=3` with no rows
   reading as INTEGRAL — is case 2 of the synthetic control.
2. **Hardcoded value.** The probe reads the rule's own raw CIM values into a `REF` arm from the
   same object, and `Compare-ArmToReference` compares the subject element-for-element against it.
   INTEGRAL alone no longer passes. Its limit is written into the function: a constant that
   happens to *equal* the rule's value is not distinguishable in a single-rule run.
3. **One process, exactly one object.** A single `powershell.exe` imports NetSecurity once, calls
   `Get-NetFirewallRule` once, and hands that one `$rule` to the reference read and to both arms,
   each evaluated in its own scope so their identically-named helpers cannot collide. **Exactly
   one rule is now enforced** (rev 2): `-Name` is a filter, not a key, so the result is captured
   as an array, `RULE_COUNT` is emitted, anything but one element exits 3, and the returned
   `Name` is compared back ordinally or it exits 4 — the outer script VOIDs on either, naming the
   count. Default `-RuleName` is the existing `{FF36EE51-2837-46D8-9AB8-2441D9070633}`, not an
   absent bootstrap rule.
4. **Complete extraction, by existing syntax.** Verified against both sources (below): subject
   lifts the **whole** `RawValue` helper and the **whole** multiline block — the raw read, its
   null guard and the element loop, lines 179–187; control lifts `Value` and its single-line
   assignment, line 126. Each construct must match exactly once or the run VOIDs. No markers are
   asked of production.
5. **Deadline, streams, exit.** `System.Diagnostics.Process` with `-NoLogo -NoProfile
   -NonInteractive -EncodedCommand`, stdout and stderr redirected separately and read
   asynchronously before the wait (a full pipe cannot deadlock the wait), a 60 s external
   deadline, and the real exit code checked.
   **A deadline is not a cleanup** (rev 2): the kill attempt's outcome, the child's *confirmed*
   exit under a bounded cleanup wait, and whatever the readers collected are recorded
   separately; a child that survives is reported **by pid as still running**, never described as
   killed. `Get-DeadlineReport` writes that sentence. Collected output is preserved and printed
   into the VOID rather than discarded.
   **Persisted streams are decoded text, not verbatim bytes** (rev 2): the files are
   `probe.stdout.decoded.txt` / `probe.stderr.decoded.txt` under `-OutDir` (renamed from
   `-RawOutDir`, which claimed more than it delivered), and the counts are reported as
   `PROBE_STDOUT_CHARS` / `PROBE_STDERR_CHARS` because `String.Length` counts UTF-16 code units —
   neither the child's bytes nor its glyphs.
6. **The false "touches nothing" claim is gone.** The header now states that running this
   launches a PowerShell process, imports NetSecurity and queries the host firewall; that the
   query is read-only; and that `-Admitted` is an accidental-run guard, not authorization.

### A defect in my own extractor, found before it ran

The first revision of `Get-BalancedBlock` read the running depth *after* applying a whole line's
delta, so a construct that opens and closes on one line never set "we have opened something" and
the extractor ran on past it. The pre-repair assignment is exactly that shape: it extracted 22
lines of unrelated production instead of its own one line. Fixed (opens are counted per line),
and `parser-control.ps1` now carries three synthetic line-array cases for it.

## 3. Synthetic results — run, with no live query

Both of these read no rule, import no module and touch no host state, which is why they could run
without a window. Raw captures are beside them.

### `parser-control.ps1` → `parser-control.out.txt` — **PARSER_CONTROL_PASS, 30/30, exit 0**

23 parse/classification cases, 4 element-for-element comparisons, 3 extraction cases. A positive
control parses first, so the rejections are not a parser that rejects everything. The harness
carries its own negative control, and I also proved it red on purpose: flipping the dead-parse
case's expectation to `OK/INTEGRAL` in a scratch copy gave `FAILED=1`, the case named, exit 1.

### `enumeration-fidelity.ps1` → `enumeration-fidelity.out.txt` — exit 0

Your "measure, do not assume" item. The production block is lifted verbatim and driven with
synthetic values, accessor stubbed — the stub returns its value as a function's last statement,
which is exactly where PowerShell unrolls, so it reproduces the mechanism rather than papering
over it. Three findings:

- **`ENUMERATION_COLLAPSED`.** An empty array and `$null` are **indistinguishable** after the
  accessor: `@()` unrolls to nothing on the way out of the function, so `$enforcementRaw` is
  `$null` and the property-level guard fires. **Consequence for the Rust suite: the query can
  never emit `enforcement: []`.** The empty-array cell in the acceptance table is a defensive arm
  over a shape this query cannot produce, not coverage of a reachable path. It stays — a later
  transport change could make it reachable — but it should not be read as covering the field.
- **The single-null-element arm is unreachable for the same reason.** `@($null)` also unrolls to
  `$null` and reports the *property*-level fault. The *element*-level null guard is reached only
  by a multi-element array carrying a null, which the last row confirms.
- **The transported element type is `System.Int32`, not `System.UInt16`.** The block validates as
  UInt16 and then converts with `[int]`, by design. So the boundary check's SUBJECT arm classifies
  the post-conversion type while the REF arm carries the raw CIM type; Int32 is in the integral
  set, the comparison is on values, and neither is a false VOID. Worth knowing before reading the
  output.

Refusals measured as expected for a display string, a boolean, an `Int32` element, and a null
beside a code — the strict type check does reject each, loudly and by name.

### `timeout-control.ps1` → `timeout-control.out.txt` — **TIMEOUT_CONTROL_PASS, 19/19, exit 0**

The cleanup path exercised with **harmless children** — `powershell.exe` processes that print a
line and sleep — never NetSecurity. A positive control runs first (a child inside its deadline
keeps its real exit code and complete output, and no kill is attempted). The two deadline cases
then assert the **post-state by pid via `Get-Process`**, not the kill's return value: killed,
confirmed exited, gone, and the line printed before the deadline preserved. The survivor sentence
is exercised on a synthetic record rather than by making an unkillable process on this box, and
the harness carries a negative control that a survivor must not read as a confirmed exit. Proved
red on purpose: flipping "THE CHILD IS ACTUALLY GONE" to `False` in a scratch copy gave
`FAILED=1`, exit 1.

One measured detail worth carrying: **a killed child reports `exit=-1`**, so a bare "exit ≠ 0"
test would read a kill as an ordinary failure. The boundary script tests `timedOut` first, then a
null exit, before it looks at the code.

## 4. The live boundary run — **BOUNDARY_PASS**, one authorized invocation

Authorized by doyle for a single read-only invocation; run once, no retries. Capture:
`boundary-run.out.txt`, decoded streams under `boundary-run/`.

Availability checked immediately before launch: no `cargo`/`rustc`/`link`/`msbuild`/
`Runner.Worker` on the box, `Runner.Listener` 22712 idle with no worker, C: free 120.4 GB, and
`gh run list` on `spt-bs-core` showing nothing queued or in progress (newest `34676423540`,
success, 16 h earlier). Script and library hashes re-verified against the pins, both worktrees
clean at `85f84d73` and `53d625cd`.

| | |
|---|---|
| rule | `{FF36EE51-2837-46D8-9AB8-2441D9070633}`, ActiveStore — `RULE_COUNT=1`, name matched ordinally |
| reference (raw CIM) | `System.UInt16`, value `1`, `CIM_TYPE=UInt16Array` |
| control (pre-repair `Value`) | `System.String`, value `Enforced` → `CONTROL_CLASS=STRING` |
| subject (post-repair `RawValue`) | `System.Int32`, value `1` → `SUBJECT_CLASS=INTEGRAL` |
| subject vs reference | `MATCH`, element for element |
| child | pid 41316, not timed out, exited, no kill attempted, exit 0 |
| verdict | `BOUNDARY_PASS`, exit 0 |

**This is the first time both expressions were measured against ONE rule object in ONE process.**
The earlier capture put accessors side by side; this puts the pre- and post-repair expressions on
the same object in the same pass, so the control is not a different read of a possibly different
state. The negative control was live: the unchanged assertion rejected the old expression's
`Enforced` on the very rule where the new one carried `1`.

**The 492 stderr characters are not an error.** The decoded stderr is a single CLIXML `progress`
record — PowerShell serializing the progress stream of `Get-NetFirewallRule` — with `Completed`
and `1/1 completed`. No error record was written. It is preserved verbatim rather than filtered,
because a stream that is silently swallowed cannot be checked later.

**Scope, as ruled: this validates EXTRACTION ONLY** — that the production expression transports
the rule's own numeric code rather than adapted text. It is not a test of the complete product
query, and it says nothing about network admission.

## 5. The targeted gate — **27 selected, 27 passed**

Granted by doyle after the boundary run; performed by me in `.worktrees/304-w2-repr` after todlando
detached it at `85f84d73` with both branch refs preserved. Outputs under `targeted-gate/`,
selected names in `targeted-gate/SELECTED-INVENTORY.txt`.

| # | command (all `CARGO_BUILD_JOBS=2`, all bounded by `timeout 600`) | exit | wall |
|---|---|---|---|
| 1 | `cargo run -p xtask -- pool-claim --pool target --label hertz-304-w2-enforcement-codes` | 0 | 20 s |
| 2 | `cargo nextest list -p spt-daemon --lib -E 'test(/bootstrap_firewall::windows::tests::/)'` | 0 | 23 s |
| 3 | `cargo nextest run -p spt-daemon --lib -E '…same filter…' --test-threads 2` | 0 | 2 s |
| 4 | `traceable-reqs check` (0.4.1) | 0 | — |
| 5 | `cargo run -p xtask -- pool-release --pool target` | 0 | 1 s |

Result: **27 tests run, 27 passed, 987 skipped, 0.427 s**. Traceability 917/917 complete, 0
findings; `REQ-BOOTSTRAP-FIREWALL-ENFORCEMENT-CODES` `+impl +unit`.

**Step 2 is what established compilation**, not step 1. The claim's build compiled the spt-daemon
*lib* as an xtask dependency, which says nothing about a `#[cfg(test)]` module; `nextest list`
produced `Finished \`test\` profile`. The suite had not compiled since the `Vec<String>` →
`Vec<u16>` repair.

**Filter control**, because a count alone does not prove the filter aimed where it was meant: of
the 27 listed lines, **0** fall outside `bootstrap_firewall::windows::tests::`. The run's own
arithmetic corroborates — 27 run against 987 skipped, so the binary's 1014 cells narrowed visibly
rather than only in the filter string. 27 was treated as **expected inventory, not an oracle**; a
mismatch would have stopped for classification.

Clean-pin verification *after* the run: HEAD `85f84d73` still detached, 0 porcelain lines,
`windows.rs` `ee5db634` / blob `848a23fe`, both refs unmoved, and `target\release\spt.exe` still
hashing `edd3d8e0…` identical to the preserved copy — the gate neither moved nor rebuilt it.
Free space 120.35 → 120.08 GiB (~0.27 GiB growth), the 96 GiB floor cleared before every
producer and the 64 / 32 GiB stops never approached. Stray scan after: no `spt_daemon-*`, `cargo`
or `rustc` process **observed** — an observation about the process table, not proof that no handle
exists on `deps\`.

**Measured warm duration, scope stated so it is not over-borrowed:** warm pool, targeted filter,
this box — **46 s of producer wall** across claim + list + run, ~39 s of it compilation and
0.427 s of it test execution. It does not describe a cold pool, the full spt-daemon lib, or CI.

## 6. Scope of what is now demonstrated

Extraction (`BOUNDARY_PASS`) and the Rust-side verdict (27/27) are both demonstrated. **Not**
demonstrated and not claimed: the complete product query, bootstrap enforcement on the real pair,
and network admission.

The boundary check's invocation, for the record:

```
powershell.exe -NoLogo -NoProfile -NonInteractive -File extraction-boundary.ps1 `
  -SubjectSource <subject windows.rs> -SubjectSha256 <re-pinned on that host> `
  -ControlSource <53d625cd windows.rs> -OutDir <dir> -Admitted
```

`-RuleName` defaults to the existing `{FF36EE51-2837-46D8-9AB8-2441D9070633}`, so it need not be
passed.

## 7. Revision note

Rev 2 corrects only the instrument. **The Rust pin is frozen at `85f84d73`** — no source file in
the commit was touched for any of the above. All three synthetic exercises were re-run against
the revised library, and every capture header now names the **child** interpreter
(`powershell.exe 5.1.26100.8875`, the product's own invocation mode) rather than the shell that
launched it.
