Skip to content

feat: answer D3, add the R11 class-sweep gate and its record field - #425

Merged
TheAmericanMaker merged 2 commits into
mainfrom
docs/d3-answer-and-r11
Sep 21, 2026
Merged

TheAmericanMaker merged 2 commits into
mainfrom
docs/d3-answer-and-r11

Conversation

@TheAmericanMaker

Copy link
Copy Markdown
Member

Closes #422. Answers D3 on #418.

D3: answered NO, not left pending

The E12 investigation settled it. The SessionStart initialization-marker candidate recorded in the contract is withdrawn — that hook's payload carries session_id, transcript_path, cwd, hook_event_name, source and nothing about the sandbox, and hooks run outside it so their own writes into the protected path succeed and prove nothing.

A PreToolUse kernel probe does give genuine per-call boundary verification — fail-closed in both directions, kernel-sourced, not model-authored. It is not continuity: a session launched without the protected settings source runs no hooks and leaves no negative record at all. Its preconditions are also wider than E5's, since a shell function can shadow the grep the probe calls. And a consumer must key on event kind + exit status, never marker text — a model can print any marker, and appended command text can make the whole command a syntax error that exits non-zero without the probe ever running.

Only managed settings and auditd could ever make verified reachable, both as platform-administrator attestations rather than host self-reports. Neither tested.

Effect: verified is unreachable on this host by the host's design, not by an unfinished decision. E02 must not carry a placeholder for a continuity mechanism that does not exist.

R11: the gate the pilot exposed

Three consecutive review rounds produced the same pattern — fix closes the reported instance, CI green, author confident, sibling defect one identifier away:

Round Found Fixed Same class still open
E03 review 1 repository unvalidated shape-checked executable still coerced
E03 review 2 executable coerced non-booleans refused —
B01 review GIT_CONFIG_COUNT COUNT=0 GIT_CONFIG_PARAMETERS live

In the third the original failure reproduced byte-for-byte on the fixed branch.

ReviewObjection.class_sweep records the class, every instance of it in the changed surface, and for each either closure or a reason it is out of scope. Two evasions are refused at the schema, because both let the sweep be claimed without being done: an empty instances list, and an out-of-scope instance with no note.

The field is optional — existing records stay valid, and its absence is an evaluator finding rather than a malformed record. R11 does not mandate widening every fix: B01 leaving GIT_TEMPLATE_DIR out of scope and filing #423 satisfies it. What it forbids is never asking.

Verification

26/26 contract tests, 1008/1008 full suite, build exit 0, git diff --check clean. Four mutation checks bite (empty-instances, missing note, disposition values, unnamed class).

Two decisions the B01 pilot and the E12 investigation left ready to record.

D3 (storage-protection continuity) is answered NO for this host, not left
pending. The SessionStart initialization-marker candidate is withdrawn: that
hook's payload carries five fields, none of them sandbox state, and hooks run
outside the sandbox so their own writes prove nothing. A PreToolUse kernel
probe does give real per-call boundary verification, fail-closed in both
directions, but it cannot speak to initialization order or cross-session
continuity — a session launched without the protected settings source runs no
hooks and leaves no negative record at all. Its preconditions are also wider
than E5's, since a shell function can shadow the grep it calls. Consumers of
that probe must key on event kind and exit status, never marker text, because
a model can print any marker and appended text can make the command a syntax
error that never runs the probe. Only managed settings and auditd could ever
reach verified, both platform-administrator attestations, neither tested.

Practical effect: verified is unreachable here by the host's design rather
than by an unfinished decision, so E02 must not carry a placeholder for a
continuity mechanism that does not exist.

R11 (#422) adds the gate the pilot exposed. Three consecutive review rounds
closed the reported instance and left a sibling of the same class one
identifier away — E03 fixed repository and left executable coerced; B01 fixed
GIT_CONFIG_COUNT and left GIT_CONFIG_PARAMETERS live, where the original
failure then reproduced byte-for-byte on the fixed branch.

ReviewObjection.class_sweep records the class, its instances, and for each
either closure or a reason it is out of scope. Two evasions are refused at
the schema because both let the sweep be claimed without being done: an empty
instances list, and an out-of-scope instance with no note. The field is
optional, so existing records stay valid and its absence is an evaluator
finding rather than a malformed record. R11 does not mandate widening every
fix — B01 leaving GIT_TEMPLATE_DIR out of scope and filing #423 satisfies it;
what it forbids is never asking.

Verified: 26/26 contract tests, 1008/1008 full suite, build exit 0. Four
mutation checks bite.
Independent review found the validator I wrote for R11 was hand-rolled and
bypassed the module's own checkObject/isPlainObject path, so it was the one
object in the schema held to weaker rules than the rest.

The blocking case: a prototype-backed sweep validated ok:true and then
serialized to {}. The record was valid; the bytes that would be written were
not. A validator whose verdict does not survive the write is not validating
the thing that gets persisted.

Applying R11 to itself, the class is "hand-rolled Check that inspects an
object without checkObject" and the swept surface is validation.ts.
Instances: classSweep (closed) and isPlainObject's own guard at line 394
(out of scope - it IS the primitive). No third instance exists; every other
object field in the file already routes through objectOf.

Closed by the rewrite, each with a test and a mutation check that bites:

- non-plain objects and unknown keys, inherited from objectOf/checkObject.
  A typo'd "dispostion" no longer silently means nothing was checked.
- duplicate "locus" values, via the existing uniqueBy helper. Listing one
  place five times was "enumerating the surface" in exactly the way R11
  exists to refuse.
- a "note" on a "closed" instance, which is a contradiction: the field means
  why something was NOT done.
- SWEEP_DISPOSITIONS is now a named const beside OBJECTION_DISPOSITIONS
  rather than two inline string comparisons.

Proxy and array-subclass values still validate. That is schema-wide - a
Proxy over "reviewer" or a subclassed "objections" behaves identically - and
both serialize faithfully, so there is no validate/store asymmetry. Not
treated as a class_sweep defect.

Two documentation findings, both real:

- record-contract.md still said "D3 must name a location [beyond an agent
  with a shell tool], and until it does..." while D3 is now closed WITHOUT
  naming one. Rewritten: the boundary is real, the declarations' history has
  no protected home, which is exactly why protection never reaches
  continuous-since-initialization here.
- The contract now states plainly that NOTHING IN THE CODE ENFORCES R11. The
  field is optional by design, but remaining_blockers sits one row above and
  IS derived, which invited the opposite reading.

The rubric gains a definition of "the swept surface" - the file or module the
defect was found in, plus every call site of the function that carried it -
because a gate that makes an unanswered class blocking cannot rest on an
undefined phrase; ambiguity would push implementers toward the narrowest
defensible class, which is the behaviour R11 exists to stop. It also records
where a sweep goes when a class spans several objections, and notes the
review-with-no-objections case as a known limitation of the per-objection
shape.

Verified: 27/27 contract tests, 1009/1009 full suite, build exit 0. Five
mutation checks bite.
@TheAmericanMaker
TheAmericanMaker merged commit 18eb019 into main Sep 21, 2026
6 checks passed
@TheAmericanMaker
TheAmericanMaker deleted the docs/d3-answer-and-r11 branch September 21, 2026 04: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.

[E13] Acceptance checks do not ask what else is of the same class

1 participant