Repository navigation
feat: answer D3, add the R11 class-sweep gate and its record field - #425
Merged
Merged
Conversation
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
force-pushed
the
docs/d3-answer-and-r11
branch
from
September 21, 2026 04:27
4e656ca to
7c9ebd0
Compare
This was referenced Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #422. Answers D3 on #418.
D3: answered NO, not left pending
The E12 investigation settled it. The
SessionStartinitialization-marker candidate recorded in the contract is withdrawn — that hook's payload carriessession_id,transcript_path,cwd,hook_event_name,sourceand nothing about the sandbox, and hooks run outside it so their own writes into the protected path succeed and prove nothing.A
PreToolUsekernel 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 thegrepthe 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
verifiedreachable, both as platform-administrator attestations rather than host self-reports. Neither tested.Effect:
verifiedis 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:
repositoryunvalidatedexecutablestill coercedexecutablecoercedGIT_CONFIG_COUNTCOUNT=0GIT_CONFIG_PARAMETERSliveIn the third the original failure reproduced byte-for-byte on the fixed branch.
ReviewObjection.class_sweeprecords 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 emptyinstanceslist, and anout-of-scopeinstance 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_DIRout 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 --checkclean. Four mutation checks bite (empty-instances, missing note, disposition values, unnamed class).