Five corrections against CONTRACT_P13S16_PROJECTION.md, all against the
document rather than the code -- aee4ff9 is accepted and unchanged. Filed as
one amendment on the owner's decision because the findings are not
independent: two pairs share a root cause, and separate rounds would have
recorded symptoms while hiding the causes.
Root A -- the contract reasoned about MECHANISM where the question was what
the test ASSERTS. Findings 1 and 3. Both cells are right about the production
code and wrong about the observation, because a test that verifies its own
fixture is meaningful fails for a second reason no mechanism argument reaches.
Root B -- enumeration where completeness was required. Findings 2 and 4. Both
lists were built by reading one file. The contract already carries derive-do-
not-enumerate under pin 10a and 6 item 1, and broke it twice in its own
apparatus.
1. Invariant 21 does not detect the undo residue (0.6, pin 5a, pin 6b). It
abstains on dangling members by design; M5 shows invariant 10 firing
instead. The hole was already covered before this rung. u5's direct
members assertion is what signs pin 5 -- the fallback pin 6b itself names
-- so nothing is unsigned.
2. 3's M6a row names six failing tests; seven occur. The fifth all()
consumer is in tests/score_graph.rs, outside the four-in-generators.rs
list.
3. 3's t8d-under-M2 survivor cell is falsified -- it fails on non-vacuity,
not idempotence. The assertion must not be weakened: under M2 t8d is
genuinely vacuous. The other falsifiable cell, t9 under M1, was CONFIRMED.
4. Pin 10 cites four of the nine sites its instruction covers, and gate 6
cannot see core_spec.tex's "exactly 20 invariants" sentence.
5. cargo test --workspace truncates the failure set at the first failing
suite -- exactly the condition every mutation creates. 3's uniform rule
and gate 1 now specify --no-fail-fast.
Finding 6 is WITHDRAWN as false and retained as record. Pin 8's line numbers
were exact on the ratified tree 34232dc -- all four verified -- and only
stopped matching because this rung inserted ~1,500 lines above them. It had
been reported without the cited locations ever being measured. Writing the
amendment is what forced the check, which is an argument for consolidating
rather than filing six rounds.
Touch row 12 added, so spec/EVIDENCE_P13S16_EXECUTION.md is committed rather
than left untracked: the findings are conclusions, and the annex is the
evidence they were derived from, including the runs that falsified two of this
contract's own predictions. Its trailing whitespace is stripped so gate 4
passes, and it says so.
CLAUDE.md, the handoff and the ledger row are corrected in step -- all three
said "six findings", which is now the count before review rather than after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Ratification round 12 returned zero findings against 25b4925 -- the live
mutation-site locators, M7's field anchors, M6's structural mapping,
touch-table coverage, the gate and report consumers, and the expected-outcome
table all rechecked, with the revised m40 citations and the remaining named
mutation sites resolving against the tree. Ratified on the authority of the
repository owner.
Contract status block: RATIFIED, DISPATCHABLE, PINS FROZEN -- executed, not
edited; a defect found during execution is reported as its own amendment with
its own review round. The block's earlier promise to say RATIFIED when the
decision was taken is discharged, and it still states no round count, per
round 2's correction.
NOT YET DISPATCHED. Ratification and dispatch are separate acts, no execution
instruction has been given, and nothing is implemented -- so the ledger row
does NOT move to RESOLVED. The contract now distinguishes four states:
unblocked, ratified, dispatchable, resolved. This rung is the first three.
What ratification does not settle, recorded at the top of the contract:
- Two cells of §3's expected-outcome table -- t8d under M2, t9 under M1 -- are
PREDICTIONS from pins not yet executed, stated so they can be falsified. A
mismatch is a finding in the contract or the implementation.
- Every gate, test and mutation is specified and none has been run. Twelve
rounds went into the claim that they can be run and that their results would
be evidential; execution is what tests that claim.
- §4a's landing obligation is outstanding by construction: CLAUDE.md and the
handoff carry statements pin 12's bump falsifies, and must not be staged
during execution.
- The execution report is subject to independent review before completion is
accepted, as S27's was -- which turned a clean paper record into seven
post-execution amendments.
Round 11 is also recorded: pin 6's m40 locators had drifted to invariants.rs:6045,
the enclosing module's doc comment rather than m40's own. Fixed by the owner at
25b4925 to :6060-:6135 and gate 6 to :6063; verified against the tree.
The defect record, so ratification is not read as vindication: 32 findings
closed before it -- 19 in draft amendment 1 and revisions A-J, 13 across rounds
1-11. None was in the pins' substance. The maintenance rule, the refusal,
invariant 21, the undo strip and the authority bump have been stable since
draft amendment 1, and every single finding was in the evidence apparatus:
what observes a requirement, what channel carries an observation, who owns a
claim, and whether a locator resolves. That is where this contract was weak and
where execution should be read hardest.
Governing docs updated to the now-true state: CLAUDE.md's track head, the
handoff's POST-S27 chain row, and the S16 ledger cell, which supersedes its own
"has not been through adversarial review" sentence per the append-only
convention.
Documentation only: no .rs or .toml touched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of a5f3013 returned one blocking finding.
§6 item 1 declared that no count is stated there and then stated one: "M7's
split into M7a and M7b makes ten executions." §3's table now has eleven
mutation rows -- M1 through M5, M6a/M6b, M7a/M7b, M8, M9 -- because M6 split
too. This is a live report requirement, not dated history, so an execution
report could reconcile against the wrong set.
The paragraph indicted itself: its closing sentence warned that "a count here
goes stale the next time a mutation splits", and then M6 split and the count
went stale exactly as predicted, with the warning still attached to it.
Item 1 now reads: no count is stated here, §3's table is the single origin,
read its rows. The parenthetical records both stale values -- the original
"nine mutations (M1-M9)" and the replacement tally -- without restating a
current one.
This is revision J's rule, applied to item 1 after J applied it to item 2: an
explanation of a removed count must not restate a count. Items 1 and 2 are
adjacent lines, and J swept one of them. The neighbour-not-swept failure, in
the very pair of items that produced the rule.
Swept for other live mutation tallies: three remain, all inside dated revision
records or quoting superseded text (revision B's finding cell, round 7's
record, and item 1's own parenthetical). The live requirement is count-free.
Round 9's field-declaration anchors are confirmed sound by the reviewer: both
declarations are unique in graph.rs, and a B-wording revert leaves each
declaration available, so the needle assertion rather than the locator produces
the required output. That closes round 9's second finding on evidence rather
than on derivation.
Committed one round late: the shell tooling failed mid-round after the edit
landed, so this change sat uncommitted and was reported as such rather than
claimed as done.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of 942f261 returned one blocking finding. A sweep of it
escalated a second.
1. The expected-outcome table recombined M7a and M7b as "its own grep guard;
the other guard." That recreates the exact ambiguity M7's split existed to
remove: a report can say "own guard failed" without establishing which guard
failed or which survived -- and round 8's full-suite rule reports tests by
name, so a row naming none cannot be checked against it.
Split into two rows with the exact names. M7a fails
t14_staff_group_field_doc_comment_states_sole_authority (graph.rs:2136)
while t14_staff_group_members_field_doc_comment_states_non_authoritative_projection
(:2160) must pass; M7b the reverse. Each quotes its needle-miss message and
the doc_block that message dumps.
M7 was divided in draft amendment 1 precisely because one mutation covering
two guards could pass with the other still weak. The table re-merged them
for brevity. A summary row is a restatement, and a restatement of a
distinction can drop it.
2. Sweep: M7b's required output was unobtainable. Both guards locate their doc
block by searching for a phrase from the doc text, and :2163's anchor is the
disposition-B claim pin 10 rewrites. Once the anchor is updated to the A
wording, M7b's revert to B makes .find() return None and
.expect("StaffGroup.members's doc comment is present") panics -- naming no
needle and dumping no block. The required observation would be an anchor
panic pointing at absent text.
Pin 10 now requires the slice to be located from the FIELD DECLARATION --
pub group: Option<StaffGroupId>, and pub members: Vec<StaffId>,, which no
wording changes -- extending backwards over the contiguous /// lines. The
block then exists under either disposition and the only way to fail is the
needle assertion, with message and block intact.
This is the absence-grep lesson one level down. Pin 10 hardened the NEEDLES
against wording both dispositions satisfy and left the ANCHORS matching
wording only one disposition has. A guard whose locator depends on the text
it inspects fails before it can report, and failing early looks like failing
correctly: the test is red, the mutation "worked", and the evidence never
existed.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of 2a445d1 returned one blocking finding: the table named
required survivors without requiring them to be run.
§6 consumed mutation output generally and no cell specifically -- t8c, t8d, u5,
pin 8's four, the structural gates -- and only M6 required its sibling's
verdict. "Report any mismatch" cannot detect a survivor that was never run: an
unrun test yields no mismatch and no evidence, so every survivor cell was an
unobserved claim, and the table read as evidential while being advisory.
One uniform rule now governs every mutation:
1. full cargo test --workspace, with the COMPLETE observed failure set
reported;
2. the named failing tests' output, per the channel rules -- behaviour, not
the fact of failure;
3. each named required survivor's PASS VERDICT, by name;
4. each named structural gate's output -- gate 8 under M1 and M8, gate 12
under M6a/M6b, pin 10's guards under M7a/M7b.
An omitted named artifact is itself a finding, on the same footing as a wrong
outcome. §6 item 1 consumes the rule. M6's per-half list is now marked as the
general rule applied rather than a special case.
This is revision D's class -- a requirement nothing can fail -- reproduced by
the very table written to end the discovery model. Round 7 moved authorship of
the expected outcome from execution to the contract, which was right, and left
the outcome unobserved. A claim the contract owns is not thereby a claim
anything checks.
The finding also forced a distinction left implicit. The survivors column said
"notable", quietly meaning not exhaustive, so a uniform "run every named
survivor" rule had to say what IS exhaustive. Now stated: the MUST-fail column
is exhaustive, verified by requirement 1's full-suite run; the survivors column
is illustrative, naming the ones a reader would doubt, and everything outside
the failing column must survive whether named or not. Without the full-suite
run the failing column's completeness was unverified as well, so the fix closes
a second hole the finding did not name.
Three rounds running have the same shape: round 6 wrote a rule and exempted
three mutations, round 7 wrote a table and left it unrun, round 8 makes the
table evidential. Each fix was correct about what to specify and incomplete
about who observes it.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of 80be47c returned one blocking finding: round 6's
blast-radius rule contradicted itself.
It required every mutation to state every expected failing test, then left M1,
M2 and M5 for execution to derive -- the discover-the-radius model it claimed
to eliminate, with the expected set depending on the executor's reading. And
execution cannot both author the table and certify its own result against it.
The cited surfaces were also insufficient, not merely incomplete. Round 6
listed touch row 8, pin 1a and pin 8. None owns t8c, which pin 3a defines four
sections away asserting g.members == [s] -- exactly the value pin 2 maintains,
so M2 breaks it. Any derivation from the stated surfaces would have missed it.
The instruction was unfollowable as well as misassigned.
§3 now carries the contract's own per-mutation expected-outcome table: failing
tests, notable required survivors with the reason each survives, and structural
gate outcomes, for all ten mutation halves. Dependency surfaces are cited and
now include pins 3a, 4a, 5a, 6a and 7 alongside pin 1a, pin 8, touch row 8, and
check_invariant = check_invariants(...).filter(...).
Execution re-derives the table against the staged tree and reports any
mismatch, in either direction, as a finding against the contract or the
implementation. No cell is a licence to ignore an outcome.
Notable entries the table now owns rather than leaving open: M1 fails gate 8,
since the refusal is what that gate reads for; M8 fails gate 8 as its only
signature and must break nothing behavioural; M6a and M6b both fail gate 12
because a call site is gone; t8c, t8d, u5 and pin 8's four all survive M1
because they carry members: [] so the refusal never fires for them.
Cells carrying a reason -- t8d under M2, t9 under M1 -- are derivations from
pins not yet executed, stated so they can be falsified. That is the division of
labour the finding asks for: the contract predicts, execution measures, and a
mismatch is a finding against whichever is wrong. The table has no totals, so
it avoids the stale-count class entirely.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of 85429d6 returned one blocking finding: M6a's blast
radius was understated.
check_invariant is check_invariants(...).filter(...) (invariants.rs:313-:318),
so deleting the S->G dispatcher call silences that violation for every
consumer, not only the two tests M6a named. The full radius is six, each now
tabled with where it fails and why:
m41...staff_names_absent_group its own assert_eq! cardinality check
invariant_21_negative_generator_... same
every_invariant_has_a_negative_generator own assert! at generators.rs:994
negative_generators_are_reasonably_targeted own assert! at :1046
every_invariant_shrinks_to_a_small_witness INSIDE shrink, entry assert :933
shrink_is_idempotent INSIDE shrink, same assertion
All six share one cause and all six are expected, not findings. The last two
panic with "shrink starting point must violate the target invariant", raised in
a function the mutation never edited, so they are to be reported as
shrink-entry failures rather than misread as a defect in shrink.
M6b's single expected failure -- m41b alone -- is now recorded as positive
evidence: the four all() consumers and the generator test all stay green
because every invariant-21 fixture is S->G, which confirms touch row 8's pinned
direction more directly than any assertion about the generator does.
Round 5 added the blast-radius rule and then enumerated the radius from memory.
Touch row 8 already lists those four consumers with these exact line numbers --
draft amendment 1 put them there as the two-crate root cause -- and M6a's
outcome was written without consulting the row that owns them. The document
knew; the mutation did not ask.
That is a new variety of the propagation failure. Revisions A-J recorded
corrections failing to reach a consumer; round 1 a declared peer; round 4 a
gate written later. This one is a fact recorded in one section not reaching a
requirement written in another -- nothing stale, nothing contradicted, and no
sweep for restated text would find it. It needed the question "what does this
section already know that this one should be using?"
So the fix is an obligation rather than a list: §3 now requires every mutation
to state its complete expected-failure set, derived from the recorded consumer
surfaces (touch row 8's four, pin 1a's three, pin 8's four), with any unlisted
failure treated as a finding in the contract or the implementation. M1's, M2's
and M5's radii are deliberately not enumerated here, since enumerating from
memory is what produced this finding; execution derives each and reports the
derivation.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of e48bb8d returned one blocking finding: M6 was not
covered by round 3's own channel rule.
Pin 6a requires an exact violation set without saying how, so a conforming
assert!(violations.len() == 1 && ...) satisfies it and prints nothing on
failure -- and that assertion is precisely what M6 trips. Gate 6 and §6 item 2e
use the passing-test model, source assertions plus ok. So M6 reverted to "the
named test failed", the signature this contract rejects.
Pin 6a gains a harness for m41, m41b and the generator test: bind
check_invariants to a local before any assertion; make the cardinality check an
assert_eq! on that local with the full violations vector in its diagnostic; and
make it the first assertion after the bindings, since it is the one M6 trips.
Gate 6 now confirms the macro. M6a/M6b quote the failing output plus the
sibling's pass verdict.
Round 3 wrote the channel rule and applied it to M1, M2 and M5 -- the
mutations named in that round's finding -- and not to M6, which has the
identical need. Round 3's text even carved M6 out, listing it among mutations
that "already met the standard": true of its behavioural observation, which
names the state, and false of its channel, which had none.
The macro is part of the channel, not a style choice. assert_eq! prints
left/right; assert! prints only its message. Under M6a the S->G arm is gone,
check_invariants returns empty for m41's fixture, and assert_eq! prints 0
against 1 with an empty vector -- that is "the violation went unreported",
quoted rather than inferred. With assert! the same requirement is met with no
evidence. Where a mutation's observation IS the compared value, the pin must
name the macro; the rule is now at the head of §3 rather than left to each
mutation.
A second gap closed with it. M6a also breaks the generator test, since that
fixture is S->G. Unstated, an executor either reports a defect that is not one
or ignores a failure it cannot classify. M6b leaving the generator green is now
recorded as positive evidence that touch row 8's pinned direction holds. A
mutation must state every test it is expected to break, or its blast radius is
discovered instead of specified.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of 3e0a4d3 returned one blocking finding in round 3's own
harness.
Pin 7a collapsed two fixtures into one binding. t8b runs TWO authoring orders
(§0.5) -- two reductions over two different groups -- but pin 7a bound "the
resulting StaffGroup.members" and one filtered invariant result. A single
members local satisfies the harness as written while leaving either mutation's
required observation absent, depending on which order it came from; and the
invariant result filtered from the wrong reduction is the wrong verdict, not a
missing one.
Pin 7a now binds four, before any assertion:
1. spurious-order OperationEffect (M1: becomes an applied effect)
2. spurious-order members (M1: the spurious membership in the graph)
3. missing-order members (M2: stays empty)
4. missing-order invariant-21 violations (M2: the disagreement left behind)
Every assertion formats all four, so whichever assertion a mutation trips
carries both of that mutation's observations. Gate 14 must now establish WHICH
ORDER each binding came from rather than counting bindings. M1 and M2 name
their own order at every mention.
Round 3 fixed the channel and left the fixtures conflated. It correctly
established that a failing test has one output channel and that state must be
bound before the assertion a mutation trips, then described the state as though
t8b had one reduction. The ordering rule was right and the inventory was wrong,
which is why the harness looked complete: three plausible bindings, none wrong
on its face, and nothing in the pin revealing that two of them are per-order.
The generalisable form: a definite article asserts uniqueness exactly as
silently as a number asserts a total. "The resulting value" reads as precise
and hides the question of which one -- the singular-noun cousin of the count
defects this contract has been removing since revision A.
And gate 14 inherited gate 12's defect before gate 12's fix could reach it.
Gate 12 was rewritten in revision H because a four-line aggregate proved a
population and not a pairing; gate 14 was written in round 3 requiring "three
bindings" -- the same shape, one section over, three rounds later. Ratification
round 1 recorded a fix failing to reach a declared peer; this is a fix failing
to reach a gate written after it.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact review of e43cd34 returned one blocking finding: three
mutations required runtime state that none of their prescribed artifacts is
required to emit.
M1 wants the applied OperationEffect and minted members; M2 the still-empty
members and invariant-21 verdict; M5 the post-undo members plus the
invariant witness. But pin 7 says only that t8b inverts, and gate 13 and §6
item 2f prescribe source assertions plus a normal pass verdict -- a
passing-test evidence model. A mutated test stops at its first failed
assertion, so state carried anywhere else is never printed, and M5's witness
assertion may not execute at all.
Fixed by pinning observation harnesses:
- Pin 7a: t8b binds the OperationEffect, the resulting StaffGroup.members and
the invariant-21 verdict to locals BEFORE its first assertion, and formats
all three into EVERY assertion's failure message.
- Pin 5a: u5 binds the post-undo g.members and the check_invariants
violations with witness ids before assertion 2, and formats both into
assertions 2 and 3.
- Gates 13 and 14 quote those diagnostics from source. M1, M2 and M5 quote the
resulting failure output verbatim.
The finding is revision E's, one case over. Revision E chose "quote the source
assertion plus the pass verdict," which is correct for a PASSING test, since
that is all a passing test emits. Mutations need the failing case, and the
failing case has exactly one channel: the diagnostic of the assertion that
fired. Settling the first did not settle the second.
The ordering half would have survived a careless fix. Putting each observation
in "its own" assertion reads as tidy and is precisely wrong: M1 trips t8b's
spurious-order assertion and M2 the missing-order one, so a message carrying
only its own value disarms whichever mutation trips the other. In u5 it is
worse -- M5 trips assertion 2, making assertion 3's witness unreachable by
construction. The state a mutation owes must be bound before the assertion
that mutation trips.
Rather than pin this three times, §3 now heads with the general rule, which
also covers the mutations the review did not cite: where the observation is a
single value under assert_eq!, the default left/right diagnostic carries it,
and M3, M4 and M9 qualify provided they compare the verdict rather than
assert!(matches!(...)), which prints nothing -- the report must state which
form each uses. Where the observation is composite, or behind an assertion the
mutation makes unreachable, a harness is required.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Whole-artifact ratification review of 5ec2ce0 returned one blocking finding.
The live status block still said this contract "has not been through
adversarial review rounds." That was true when written and false from the
first independent pass onward, and it sat in the "what remains before
dispatch" summary -- so a top-down reader received a false review-state signal
before reaching any record that contradicts it.
Replaced with a non-rotating pointer: the review records below are the review
state; the block states no count and no ordinal, and will say RATIFIED when
that decision is taken. Also sharpened the neighbouring sentence to name three
distinct states -- unblocked, ratified, dispatchable -- since conflating the
first two is the specific failure it cites from S27.
This is the status-block defect S27 spent amendments 4 through 7 removing,
reproduced here in its purest form. S27 established that a status line
describing the review must not describe the review: it must point at the
record that owns it, because every such line goes stale exactly when the
review advances, which is the one moment nobody is reading the status block.
What makes this worse than a stale count: a stale count reads as an error,
while "has not been through adversarial review rounds" reads as a verdict on
the artifact's maturity. A reader deciding whether to trust this draft would
have taken it as the answer, with ten revisions of independent review recorded
immediately below it.
And it is the whole-artifact question again. Rounds against edits ask "is this
change right?"; only a whole-artifact read asks "is anything the document says
about itself still true?" Round 1 found a missing permanent test that way;
round 2 found a false status claim. Both were invisible to every incremental
pass, and neither was in the content those passes were correcting.
Swept for other live review-state claims: none. The only remaining
"NOT RATIFIED" is the status line itself, which is the fact that changes at
ratification, and the false sentence survives only inside its own correction.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
First independent round against the draft as a whole rather than against the
previous revision's edits. One blocking finding, and it is one that ten
revisions of amendment review did not surface.
Pin 5's staff-undo repair was signed by M5 alone. No named test performed
CreateStaffGroup(g, []) -> CreateStaff(s, group: Some(g)) -> undo the staff ->
assert the still-live g.members no longer holds s.
Verified that nothing existing supplies it:
- Pin 8's four tests are group-undo guards. u2tomb_a (reduce.rs:17125) does
undo a staff, but its assertions are that the undo tombstones the
referencer, that T1's undo then proceeds, and that "the group leaves
Score.staff_groups" (:17187) -- it undoes the group too, so no live group's
members is ever inspected.
- Gate 6's m41/m41b build materialized fixtures and never run the reducer's
undo path.
Pin 5a adds u5_undoing_a_staff_strips_it_from_the_live_groups_members with
three assertions: the group is still live (without which the test can pass
vacuously on a score where the group was undone too -- exactly how u2tomb_a
misses this path), g.members lacks s by id, and check_invariants reports no
StaffGroupMembershipAgreement violation. Gate 13 runs it. §6 item 2f reports
it. M5 now breaks it by name and reports the changed state -- the post-undo
g.members still containing s, with the invariant-21 witness -- rather than
stating a condition.
The finding is revision C's, on the sibling pin. Revision C established pin
3a's rule that a mutation demonstrates the hazard once and only a test keeps
it demonstrated, and applied it to pin 6 and M6. Pin 5 and M5 have the
identical shape and were left alone, two paragraphs from the sentence "pin 6
is coupled to pin 5 and they split only together."
So the lesson is not "check mutations for permanent tests" -- that was already
learned. It is that a coupling stated in prose does not propagate a fix. Every
fix-propagation failure in revisions A-J was a correction reaching a CONSUMER
one hop late; this one failed to reach a declared PEER.
Why ten revisions missed it: each reviewed the previous revision's edits, so
the question asked was always "is this change right?", never "is anything else
the same shape?" A first whole-artifact round asks the second question.
Swept the other mutations for the same shape: M1/M2 break t8b, M3/M4 break
t8c/t8d via pins 3a/4a and gate 9, M6 breaks m41/m41b, M7a/M7b break the two
grep guards, M9 uses t7, and M8's structural gate 8 is explicitly the only
signature available for a deletion no behaviour observes. M5 was the only gap.
Also fixed before commit: gate 13 was first inserted before gate 12.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 9e43994 returned one blocking finding in three live
consumers: adding gate 12 in revision G restored the tally defect this
contract had already removed twice.
Three sites still said §4 has "eleven gates, 1-11":
- revision C's disposition for its finding 3;
- gate 4's scope note;
- §6 item 2, immediately after the words "No count is stated here -- §4 is the
single origin."
All three removed, not updated. The revision-C record now says only that the
4a. scope note was demoted. Gate 4's note identifies §4a without counting
gates. §6 item 2 keeps the pointer to §4 and adds only that lettered subchecks
report under their gate.
§6 item 2 is the one that indicts the method: it declared §4 the single origin
and restated a count in the same breath. Revision B removed "the nine gate
results" from that very item; revision C's correction then wrote the
then-current number into the explanation; revision G's new gate made it false
again.
The rule this makes explicit: a correction that EXPLAINS a removed count must
not restate the corrected value. Say what changed, not what the number now is
-- otherwise the record becomes a new instance of the defect it records, and
the next addition to the set falsifies the explanation instead of the
original. Every count this contract has removed was re-created by the prose
written to remove it.
The sweep confirms the rule is not "no numbers anywhere". A count at its
origin, immediately above the table enumerating the set -- pin 1a's three
revised tests, pin 8's four G3a tests -- is read off rather than restated, and
a change to the set edits the table and the adjacent word together. The defect
is a count living away from the set it counts.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 5d2db93 returned one blocking finding: gate 8's new
method named a boundary that made the gate impossible to pass or honestly
fail.
Revision H said to quote create_staff_group's production body "to the
#[cfg(test)] boundary." But create_staff_group begins at reduce.rs:4458,
create_part_definition begins at :4515 and carries its own
PreconditionFailureReason::TargetMissing at :4554, and the next #[cfg(test)]
is at :9576. So the literal read spans roughly 5,000 lines and always contains
the very path the gate says must be absent, while any shorter read violates
the stated boundary.
Gate 8 now quotes exactly pin 1's slice: create_staff_group's body,
brace-matched from its fn line to its closing brace, production source only.
Pin 1 had the boundary right the whole time -- "slice the create_staff_group
body (brace-matched from its fn line to its closing brace, production source
only)". Revision H invented a second, looser boundary instead of citing the
pin. That is revision A's failure in a new place: importing a
plausible-sounding rule rather than re-deriving from the source that owns it.
There it was an S27 conclusion; here a boundary borrowed from a different kind
of check. The #[cfg(test)] boundary is the right instrument for "is this call
site production or test", which is what §0.4 used it for; it is the wrong
instrument for "where does this function end."
The rule: where a pin already defines the artifact, the gate CITES the pin, it
does not redescribe it. A redescription is a second definition, and two
definitions of one artifact are a contradiction waiting for someone to read
the looser one.
Worth noting the failure kind. This was not a gate that passes when it should
fail -- revision H's usual shape -- but a gate with no passing state at all.
It would have surfaced at execution, with an agent forced to choose between
obeying the boundary and obeying the requirement, and whichever it chose would
have been reported as a pass.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 09d8439 returned one blocking finding. A sweep found a
second of the same family.
1. Gate 12's four-line aggregate could pass with one surface missing. Both
definitions can exist (2 lines) while check_invariants calls
check_staff_names_absent_group TWICE and check_group_lists_unowned_staff
NEVER (2 lines). Four lines total, the stated "fewer than four fails" rule
reports success, and M6b has no call site to delete.
Replaced by four independent grep -c checks, each required to be exactly 1
-- so a count ABOVE 1 now fails too, catching the doubled dispatch
directly. Plus quoted context, because a count is not a mapping: each
definition with its enclosing impl GraphIndex<'_> header, proving it is a
method on the type check_invariants builds; each dispatch with the pub fn
check_invariants header, proving the call is in the dispatcher M6 edits and
not in a test or a second dispatcher.
This is the contract's oldest defect class in new clothes. Revisions A-C
removed counts that had gone stale; this one was never right -- an
aggregate can be satisfied by the wrong distribution of the same total.
Where a gate must establish a mapping, it cannot count. It has to check
each element on its own, which is the structural sibling of the rule this
document already carries: where a claim requires completeness, do not
enumerate, derive.
2. Sweep: gate 8 asserted an absence with no method. "contains the
empty-members refusal and no member-liveness/TargetMissing path" named no
command, and a TargetMissing path can be spelled without either literal, so
any grep would prove only that a chosen string is gone. Method pinned:
quote create_staff_group's production body in full to the #[cfg(test)]
boundary and read it, explicitly not a grep -- S27's gate-6a lesson and the
reason its gate 6c quotes a definition rather than searching for it. M8
signs exactly this gate, so a vacuous gate 8 leaves M8's deletion
unobserved.
Both findings are gates that report success without observing what they
claim. One counted instead of pairing; the other asserted an absence with
nothing able to establish it. A structural gate needs a method, and the method
must distinguish the passing case from every failing one -- not merely from
the most obvious failing one.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 2818ced returned one blocking finding: M6 was
unexecutable against a conforming implementation.
M6 assumed two independently removable arms, but pin 6 and pin 6a specify
behaviour only. A single shared comparison -- one walk emitting a violation
whichever way Staff.group and StaffGroup.members disagree -- satisfies m41,
m41b, the generator test and gate 6, and leaves nothing for M6 to delete one
at a time. Deleting the shared check disables both directions, so M6's
required "one test fails while the sibling passes" observation cannot be
produced at all. M6 was executable only against one implementation style,
which nothing required.
Pin 6b added: the mutation surface is pinned as two GraphIndex methods
following the crate's existing idiom --
fn check_staff_names_absent_group(&self, out: &mut Vec<InvariantViolation>)
fn check_group_lists_unowned_staff(&self, out: &mut Vec<InvariantViolation>)
both emitting StaffGroupMembershipAgreement violations, both dispatched from
check_invariants. The names are pinned because gate 12 greps for them and M6
deletes them by name -- the same reason S27 had to pin synthetic_for_fixture
after finding its gate searched for a name offered only as an example.
M6 is now M6a and M6b, each deleting one named call site. Gate 12 added:
structural, proving both definitions and both call sites exist -- four quoted
lines -- BEFORE M6 is attempted, so the surface's absence is a finding rather
than a mid-run discovery. Fewer than four lines is a pin 6b violation.
This is precedent, not invention: check_invariants (invariants.rs:257-:282)
already dispatches 23 check_* methods for 20 invariants, so more than one
method per invariant is the crate's existing shape. A shared helper both
methods call is explicitly permitted -- the deletable call site is what M6
needs, not a duplicated walk.
The class is one S27 hit twice, its M5 and M6 both rewritten after review
found no runnable observation behind them. The tell is identical: a mutation
phrased as an edit to a structure the pins never required. Behaviour pins
constrain outcomes; a mutation deletes code. Where a mutation is the
signature, the structure it deletes must itself be pinned -- otherwise the
contract is satisfiable in a shape that makes its own evidence unobtainable.
Also fixed before commit: gate 12 was first inserted before gate 11, leaving
the sequence 10, 12, 11. Moved, and a stray double blank line collapsed.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of abe2c35 returned one blocking finding, and it is both
prior revisions' lessons colliding.
The shrink leg had no observable direction or exactness guarantee. Row 8
requires the S->G fixture to survive shrinking, but:
- the named generator test asserted only on the raw violating_score(...);
- the shrunk score was checked solely by
every_invariant_shrinks_to_a_small_witness (generators.rs:1003), whose
!check_invariant(&small, inv).is_empty() is membership in ONE
GraphInvariant variant -- and both directions of invariant 21 are the same
variant, so a shrunk witness that flipped to G->S-only passes it. Because it
calls check_invariant (singular) rather than check_invariants, a shrunk
witness that gained an unrelated second defect passes too;
- §6 item 2d still said "quote the shrunk witness," which a passing test
cannot emit.
So a shrunk witness that changed direction, or acquired a second defect while
retaining invariant 21, satisfied every assertion and every gate.
Fixed: the named test now asserts the same three properties TWICE -- on the
raw fixture and again on shrink(&that, StaffGroupMembershipAgreement): exactly
one violation and it is StaffGroupMembershipAgreement, the witness naming the
S->G ids, and G->S asserted satisfied. Gate 6 requires both legs. Item 2d
rewritten to revision E's source-assertion-plus-pass-verdict model. Item 2e
extended to both legs. Row 8's description of the existing shrink tests now
says plainly that they establish only that SOMETHING still fires, and that
direction and exactness after shrinking come solely from the named test's
shrunk leg.
Two lessons collided here. Revision E established that a gate must name
evidence its artifact produces and fixed gate 6 -- stopping one hop short of
item 2d, which is the fix-propagation failure revisions A-D kept recording.
And the underlying gap is revision D's: a requirement, "survives shrinking,"
with nothing able to fail it.
The generalisable rule now recorded: shrink is a TRANSFORMATION, and a
transformation's output needs the same guarantees asserted of its input.
Requiring a fixture to "survive" a transformation establishes only that
something survived. Every property the input was pinned for must be
re-asserted on the output, or the transformation is free to change what the
fixture proves.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of cae1d32 returned two blocking findings and one stale
rationale. Both blocking findings are in requirements revision D itself wrote,
and both are revision D's own closing lesson turned back on it.
1. Row 8's new generator test had no name, so nothing consumed it. Gate 6
named only m41 and m41b; §6 item 2d asks for shrink evidence. Omitting the
test entirely would still compile, satisfy all four all() loops, and pass
every named gate.
Named invariant_21_negative_generator_breaks_staff_to_group_only, with its
three assertions spelled out: exactly one violation and it is
StaffGroupMembershipAgreement, the witness naming the S->G ids, and the
G->S direction asserted satisfied. Added to gate 6 and to new §6 item 2e.
This is revision D's own lesson unapplied to itself: it closed by
distinguishing a rule with no consumer from a rule with no observer, then
wrote a requirement with neither. An unnamed artifact cannot be gated,
because every gate here names what it checks.
2. Gate 6 demanded runtime evidence the prescribed tests cannot emit. It said
to quote check_invariants' full return and witness ids, but these are
assert!-style tests in m40's shape and cargo test prints ok, not local
values. Obeying it literally would need unpinned --nocapture
instrumentation added purely to produce a report, or source inference
presented as observation.
Evidence model now chosen explicitly: quote the SOURCE assertions plus the
pass verdict. A passing exact-set assertion IS the observation -- the
assertion text says what was checked, the verdict says it held. This
follows S27's gate 6c, which quotes a struct definition rather than
grepping for it: a quoted source construct is read, not inferred. It also
adds no code to epiphany-core written solely for a report.
The requirement was right and the mechanism was borrowed from gates that
run commands and read stdout. A gate must name evidence the prescribed
artifact actually produces; otherwise execution improvises, and improvised
instrumentation is unpinned scope arriving through the report.
3. Gate 4's rationale still said row 11 is "conditional," which revision D
changed to decided-unused. A fourth site under pin 10a said "carrying it
conditionally costs nothing" -- found by sweep. Both updated. The subset
rule itself is unaffected; only its rationale needed the current term.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 25473a1 returned two blocking findings. A sweep
escalated one of them into a third. Both reported findings are the same
failure in different clothes: a requirement stated with nothing able to fail
it.
1. Pin 10a still deferred the label decision to execution -- twice reworded,
never decided. The facts were readable in the .tex source the whole time:
core_spec.tex:6529-:6648 is ONE requirement box carrying the single label
req:graph:score-graph-invariants, with exactly 20 \items inside it.
Invariant 21 is a 21st \item within that box, so pin 6 mints nothing. Pin
10 rewrites prose plus a Revision History row and version bump, so it mints
nothing either.
DECIDED: neither document mints a label; touch row 11 is UNUSED and must
not be staged; no counter moves. If execution finds otherwise that is a
finding against this contract, not a keyboard decision. The counter table
is retained for that case and for the next rung. Row 11 is kept rather than
deleted because CLAUDE.md names the file as a recurring escapee -- a row
reading "deliberately unused, and why" survives review; an absent row looks
like an oversight.
"Decide and report" reads like rigour and is its opposite: it makes the
staged set and counter expectations depend on a choice made at the
keyboard, so the touch table can be wrong in either direction and the
report will agree with whatever happened.
2. Pin 6a required each fixture to violate its own direction only, and nothing
could observe that. The prescribed model, m40, asserts only
check_invariants(&s).iter().any(...) -- any() cannot see a second unrelated
defect -- and gate 6 checked the target verdict and the opposite direction
but never the absence of invariants 1-20. So a fixture carrying an
unrelated second defect satisfied every stated gate and mutation outcome.
Each m41/m41b must now assert the EXACT violation set: exactly one
violation, StaffGroupMembershipAgreement, witness naming that direction's
staff and group ids, opposite direction asserted satisfied. Gate 6 reports
check_invariants' full return for both.
Borrowing a test's shape imports its blind spots along with its virtue. m40
was cited for its dispatch property, which is real and still applies;
nothing about invariant 20 ever turned on exactness.
3. Sweep: the same blind spot covers touch row 8's generator, and worse.
negative_generators_are_reasonably_targeted bounds kinds:
BTreeSet<GraphInvariant> at <= 3, but both directions of invariant 21 are
the SAME variant -- they collapse to one element, so no existing test can
observe direction at all; the other three all() loops assert only
!is_empty(). Row 8 now requires a dedicated permanent test that the
generator violates S->G and not G->S.
Also fixed: revision D's block was first inserted inside revision C's,
orphaning C's closing paragraphs after it. Reordered, and the cross-revision
pattern paragraph moved to the end and extended.
That paragraph now carries rev D's converse: the fix-every-site rule asks who
READS a corrected rule; rev D asks what OBSERVES each requirement. Findings 2
and 3 were invisible to every sweep that looks for restated text, because
nothing was restated. A rule with no consumer goes stale; a rule with no
observer was never enforced at all.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 0a5b936 returned two blocking findings and one factual
error. The first two are one issue: invariant 21 mandates two directions, and
only one -- unspecified -- had durable evidence.
1. Neither direction had permanent named coverage. Pin 6 asked for "a score
violating only invariant 21" (singular), gate 6 asked for one score, the
generator carries one, and M6 observed both directions only while mutated.
A mutation is reverted, so the restored suite could ship with one branch
untested.
Pin 6a added: two permanent, direction-isolated tests --
m41_check_invariants_dispatches_invariant_21_staff_names_absent_group (S->G)
and m41b_check_invariants_dispatches_invariant_21_group_lists_unowned_staff
(G->S) -- each required to SATISFY the direction it does not break. Gate 6
requires both verdicts. M6 now breaks those exact tests, one each, and
requires the sibling to still pass; the surviving test passing is what
proves the arms independent rather than one arm catching everything.
This contract already stated the rule and did not apply it to itself: pin
3a says "a mutation demonstrates the hazard once; only a test keeps it
demonstrated." M6 was carrying both directions on mutation alone, three
sections below that sentence.
2. "One named direction" delegated a design decision to execution. Either
choice changes the generated witness and the shrink evidence, so reporting
it afterward is not specifying it. Pinned to S->G in touch row 8, with the
reason: smallest corruption of valid_score (drop the staff id from
group.members, leave staff.group intact), matching every other arm's
doctrine, and the exact shape pin 2's append failing produces -- which is
what M2 observes.
Three fixtures now have three distinct purposes: row 8's generator (S->G,
survives shrink), pin 6a's two direction-isolated tests, and M6 as their
signature. None stands in for another.
3. §6's revision-B history said §4 has "twelve entries -- 1-11 plus 4a," a
false identity. The item numbered 4a. was a scope note with no command and
no output -- it could not be "a gate result" -- and it collided with §4a,
the landing-obligation section. Demoted out of the gate numbering into gate
4's body. §4 now has eleven gates, 1-11, and §4a is unambiguous.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of 3096c54 returned three blocking findings, all in
revision A's text, and each is a rule revision A had just corrected surviving
one step downstream of where it was fixed.
1. §6 item 2c still said "all three counters and their new values" -- the rule
pin 10a corrected in the same revision. Third site of one false claim: the
pin, then touch row 11 (caught by rev A's sweep), then the report item that
READS the pin. Now points at pin 10a's table and requires naming which
document minted the label.
2. §6 still demanded "the nine gate results" while §4 carries twelve entries
(1-11 plus 4a). Revision A removed the identical tally from item 1 for
mutations and left its neighbour on the next line standing. Count removed,
§4 named as origin, 11a-e identified as subchecks of one gate rather than
five separate results.
3. Touch row 8 still required violating_score's fixture to violate "both
directions" while M6 requires direction-isolated fixtures -- incompatible
evidence models in one contract. A both-direction generator remains
reported after either M6 arm is deleted, so it cannot sign that arm's
absence. Row 8 now specifies one named direction plus shrink survival; pin
6/M6 own two separate isolated fixtures. violating_score returns one Score
per variant and could not have carried both regardless.
Found by sweeping and fixed with them: gate 7 and §6 item 4 both said "the
four pin-8 tests." Correct today, but the same construction -- a count
restated away from its origin, and inconsistent with the two tallies just
removed. Pin 8's table named instead.
The pattern across revisions A and B is sharper than any single finding: a
correction propagates one hop and stops. Rev A fixed pin 10a and left touch
row 11; the sweep caught row 11 and stopped before §6's consumer. Rev A
removed item 1's mutation tally and left item 2's gate tally on the next line.
The fix-every-site rule is not satisfied by fixing the site and its obvious
neighbour -- it requires asking who READS the corrected rule.
Noted, not changed: §6's items run 2, 2b, 2c, 2d, 2a because 2a predates the
new items. Cosmetic, and renumbering would break pin 12's and gate 10's
references to item 2b.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Independent review of d06e2f7 returned six findings, five blocking, all in
draft amendment 1's own text. A sweep found a seventh: the same false claim at
a second site.
1. Report item 2d asked execution to decide a static fact the draft could
read. shrink (generators.rs:932) does NOT match GraphInvariant -- it calls
check_invariant(score, inv). The unverified claim was made twice, in item
2d and in touch row 8. Both corrected. Item 2d replaced with the real
obligation: invariant 21's fixture must survive shrinking (:1025), since
shrink asserts on entry that its input still violates the target.
2. Pin 10a's "all three counters move if either document mints a label" is
false. CORE_REQUIREMENT_COUNT is asserted only against core_spec.tex
(requirement_labels.rs:259); a label in operation_catalog.tex moves the two
suite counters only. Replaced with a per-document table.
Sweep finding: touch row 11 carried the same false sentence and was left
standing while pin 10a was fixed -- the fix-one-site defect, committed
inside the edit that fixed the other site. Row 11 now points at pin 10a
rather than restating it.
3. Gate 11 permitted the exact tautology it exists to prevent. "Updated, not
silenced" does not forbid replacing the literals with
CURRENT_REDUCTION_ALGORITHM_VERSION -- the tidiest-looking update, after
which both operands move together and M5a/M5b are vacuous. Rewritten as
11a-e requiring independent literal 1 values, never the constant, each
quoted. S27 round 3 caught this substitution and roundtrip.rs:882 forbids
it by name.
4. Gate 11 omitted roundtrip.rs:947, test 10b's mutation-only Err arm. Left at
0, M5b aborts on the base comparison before reaching the two-field panic
that is its required observation -- failing at the wrong assertion while
observing nothing. Added as 11d, with 11e for the literal-preservation
comments whose reasoning is what stops the next rung making substitution 3.
5. §6 demanded "the nine mutations (M1-M9)" while M7's split makes ten
executions. Count removed; §3 is the single origin.
6. Pin 12 said no gate catches a missed bump except the tripwires, written in
the same amendment that added gate 10, which compares the value against
HEAD directly. Split: gate 10 guards this bump, 11a-e guard the wiring,
only the general future case stays undetectable.
Tightening folded in with finding 1: M6's two fixtures must each violate ONE
direction only. A fixture disagreeing in both is still reported after either
arm is deleted, so the mutation appears to fail correctly while signing
nothing. The same trap applies to touch row 8's generator, whose all()-driven
consumers only ask whether 21 is reported.
Findings 1, 2, 3 and 6 share one root cause: an S27 conclusion applied without
re-derivation. S27's "name all three counters," its "no mechanism can detect a
semantics change," and its literal-independence rule are true statements about
S27; two are false or incomplete here and one was dropped where it was needed.
A ratified contract is reusable as a source of questions, not of answers.
Still DRAFT, still unratified. No implementation work has begun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Fourteen findings, thirteen blocking, against the draft BEFORE its first
ratification round. Nine from the recon pass, five from a second sweep of the
same defect classes. The contract is a DRAFT, so these are edits to an
unratified plan, not amendments to frozen pins.
Root cause, and the one that changes the rung's shape: invariant 21 is a
TWO-crate change. violating_score (epiphany-core/src/generators.rs:498)
matches GraphInvariant exhaustively, so the new variant does not compile
without an arm, and four all()-driven tests (:991, :1004, :1025, :1042) then
require a real generator rather than a stub. The contract had treated the enum
extension as local to invariants.rs. Nothing in its §0 inspection would have
surfaced this -- it appears at compile time, after execution begins.
Touch table gained five rows:
- epiphany-ops/src/lib.rs -- the bump and its Bumps entry. The rung's defining
act had no pin, no touch row, no gate and no report item; it existed only as
a note inside pin 0's discharge. Now pin 12, touch row 7, gate 10, item 2b.
- epiphany-core/src/generators.rs -- the root cause above.
- epiphany-testkit/src/roundtrip.rs and epiphany-textproj/src/serialize.rs --
S27's two tripwires. Test 10b panics by design once the authority moves;
test 10a asserts the literal 0 and its own doc says it is expected to fail
when S16 bumps. Both fail gate 1 with no row to fix them in -- the same
shape as S27's own gminor.rs failure. Gate 11 requires they be updated, not
silenced: a tripwire accepting both values rebuilds the tautology S27
existed to remove.
- requirement_labels.rs -- conditional, the escapee CLAUDE.md names by name.
Pin 10a now forces an explicit decision on whether pin 6 or pin 10 mints a
label; this rung touches both counted documents, and all three counters move
if either does.
Gates: 2 and 3 now pin +1.95.0 (CI gates on it; this machine defaults to
1.97.1). Gate 3 formatted two crates of four and would have reported clean
over the two the new rows added. Gate 1 gained the 1577/0/0 baseline, three
delta buckets, and a 0-ignored requirement. Gate 4's "exactly §2" was the
formulation S27's round 17 found unsatisfiable with a conditional row.
Mutations: M1, M2 and M4 accepted "the named test fails" as their whole
signature; each now names the behaviour the mutated build produces -- the
applied spurious mint, the still-empty members, the re-carry misverdict. M3,
M5, M6 and M9 already met the standard. M7 covered two independently guarded
doc blocks while reverting one, so it could pass with the other guard still
weak; split into M7a and M7b, each quoting its own needle's non-match.
Locators: pin 8's four tests were identified by INTERIOR line numbers, a dozen
lines into each body, anchored to nothing searchable -- now named. t6/t7/t9
re-derived to :16158/:16231/:16461, with the trap recorded that reduce.rs has
two t6/t7 families and grepping "fn t6" lands on the wrong one.
New §4a, landing obligation: the bump falsifies live statements in
CLAUDE.md:106 and the handoff's POST-S27 block. Both are explicitly NOT staged
during execution -- staging them would assert the rung had landed while it
awaited acceptance -- and are required as post-acceptance reconciliation the
report must list as outstanding. Pin 11's ledger row is different and is
staged: a ledger records what a rung did; those two state what is true now.
Report item 2d requires execution to determine whether shrink() also matches
GraphInvariant exhaustively. Deliberately not guessed -- guessing about an
exhaustive match is what produced the root-cause finding.
Findings 5-8 are all defects S27 had already found and fixed in its own gate
set; this contract predates those corrections and inherited none of them. The
next contract drafted here should start from S27's §5.
No implementation work has begun. Still DRAFT, still unratified.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
The supersession declared pin 0's "no mechanism to detect a
reduction-semantics change" false because version mismatches are now
rejected. That conflates two different claims, and it contradicted this same
contract's requirement 3 thirty lines below, which says no mechanism can
detect a missed bump. CLAUDE.md, the handoff, and epiphany-ops's own
authority doc all state the correct version.
Only one of the two claims moved:
- Declared-version mismatch -- a base whose recorded
reduction_algorithm_version differs from the running authority -- is now
ENFORCED, refused with CanonicalBaseRequiresRebuild on read and write.
- A semantics change is STILL UNDETECTABLE. Nothing compares the semantics
the code implements against the number it declares, and nothing can.
So S27's enforcement is conditional on the bump discipline, not a substitute
for it. If this rung changes CreateStaffGroup's verdict and the bump is
missed, every base it produces declares 0, matches an authority still reading
0, and passes every check S27 installed -- the enforcement fires correctly on
a number that is itself wrong. That is exactly why pin 0's requirement 3
inverts into a mandatory bump rather than dissolving: S16's bump to 1 is the
human-enforced half of the guarantee, and the only half that applies to
itself.
Changes:
- The falsified-claims table splits the row in two: the accepted-stale-base
consequence is false now; the no-detection claim is marked STILL TRUE.
- New "Enforcement is not detection" section states the split as a table so
the two cannot collapse into each other again.
- The discharge marker before pin 0 no longer lists no-detection among the
falsified claims and warns not to read the discharge as closing that gap.
- Pin 11's retained blockquote had the same defect: it was annotated "every
claim in this quoted block is now false," but "no mechanism comparing
either against the semantics it actually implements" is still true -- S27
compares a declared number against a declared number. Annotation now
separates what is false from what stands.
Documentation only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Follow-up review found the previous reconciliation half-done in two places.
Documentation only; the accepted S27 code is untouched.
spec/HANDOFF_2026-08-07.md -- it declared CLAUDE.md the sole origin for the
green baseline and then stated the number twice anyway, once in its own update
table and once in §3.5. Both replaced by pointers; the figure now exists in
exactly one file. The §1.2 marker also said everything following it was
pre-S27 while two post-S27 notes sat inside that section. The notes are
deleted and their substance folded up into the top block's table rows -- the
permanent acceleration_snapshots warning onto the conformance row, the
"may not degrade to read-only" carry-over onto the errors row -- so §1.2 is
now purely a dated record. The marker is also scoped to §1.2 rather than
"everything after," which would have wrongly claimed §1.4 onward.
spec/CONTRACT_P13S16_PROJECTION.md -- pin 0 still carried live pre-S27
instructions: no authority exists, no mechanism detects a stale base,
ids.rs:288's catalog claim is false, this rung cannot execute, the ledger
stays blocked on P13-S27, and file S27. A status-and-ledger update did not
reach any of them.
- Pin 0 now carries a discharge marker BEFORE the pin, not only after it, so a
top-down reader meets the correction before the false claims.
- The discharge answers each falsified claim individually in a table, and
replaces all three numbered requirements. Pin 0's deliberate narrowing --
that no history audit was done, so the stronger "never detectable" claim
must not be written -- survives and still binds.
- Requirement 3 inverts rather than disappears: stale bases are now rejected
and S27 owns those tests, so this rung must not add a second detection path;
what it must do instead is bump CURRENT_REDUCTION_ALGORITHM_VERSION to 1.
- Pin 11's live "File P13-S27 in the same edit" instruction is struck inline,
not only in its preamble, and the retained reasoning is properly blockquoted
with every claim in it marked false.
- §6 report requirement 2a required confirmation that nothing was added
detecting stale bases -- the opposite of what is now correct. Rewritten to
require the version bump with its Bumps-list entry, no second detection
path, and the unchanged operation_catalog.tex rebuild note.
- "The P13-S16 row is marked blocked on P13-S27" corrected; "does NOT move to
RESOLVED" is retained, since it is still true and is the distinction that
matters -- unblocked, dispatchable and resolved are three different states.
Line-number citations throughout the S16 contract predate S27's 795-line
bundle.rs change and are NOT re-derived here; that is recorded as part of
ratification rather than silently patched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
P13-S27 landed at 4df8e25. Four documents carried live statements that the
landing invalidated, and they had to move together: updating the ledger alone
would have left the active S16 contract contradicting it.
CLAUDE.md
- Track head: P13-S27 LANDED; P13-S16 unblocked, contract still DRAFT.
- The T1b/S27 collision is resolved. T1b is NOT thereby free -- it stays
blocked on Ruling B blocker (ii), versioned decode. A future
epiphany-bundle rung re-creates the collision on its own terms.
- Green baseline 1570 -> 1577, and marked as the single origin for the count.
- "One live constraint" rewritten: the blanket no-canonical-base prohibition
is lifted, replaced by the authority check (accepted when
reduction_algorithm_version equals CURRENT_REDUCTION_ALGORITHM_VERSION,
currently 0; CanonicalBaseRequiresRebuild on both read and write paths;
legacy epoch still refuses outright), plus the bump discipline and the
synthetic_for_fixture / production_caps split.
spec/HANDOFF_2026-08-07.md -- a dated snapshot, so it keeps its text and gains
a POST-S27 UPDATE block at the top that is the single place the new state is
given. Each invalidated site now points there instead of restating:
§1.2 constraint (dated record), the suspended conformance wiring (restored),
ReductionAuthorityUnavailable (deleted, replaced), §1.4 chain state, §2.6 and
§4.3 collision, §4.3 items 7 and 9. The three 1570 repetitions are replaced by
a pointer to CLAUDE.md -- a figure kept in four places goes stale in three.
spec/CONTRACT_P13S16_PROJECTION.md
- Status: DRAFT, UNBLOCKED 2026-08-09, NOT RATIFIED and therefore NOT
dispatchable. Pin 0's blocker is discharged; core_spec.tex:11614 is met.
Original status retained verbatim.
- Pin 11 amended: it mandated a ledger state of "blocked on P13-S27," which is
now false -- a pin requiring a false ledger state would put the contract in
contradiction with the ledger it governs. Its instruction to file S27 in the
same edit is discharged.
spec/PASS13_CANDIDATES.md -- appended to both cells, per the append-only
convention.
- S16: unblocked, with the "additionally needs pin-2a's disposition" sentence
explicitly superseded (settled from outside S27 by the format rung's pin 8);
unblocked is not dispatchable; first act is bumping the authority to 1.
- S27: accepted and landed, with the gate figures, and a correction to pin
10's own wording -- it said S16 becomes "dispatchable," but by this repo's
definition S16 is unblocked, not dispatchable. Same unblocked/dispatchable
conflation S27's round 1 committed.
Documentation only: no .rs or .toml touched, so the gates re-run against the
landed tree stand unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x
Scoping disposition A found two things that move the rung's size in
opposite directions.
Cheaper: the refusal needs no new PreconditionFailureReason and no
schema-minor epoch. reduce.rs:1236's container_not_empty() helper
already covers "a create carrying children" by its own doc, and three
creates already call it for exactly this shape. create_staff_group is
the sole outlier.
More expensive: this is a canonical reduction-semantics change, not
merely a behaviour change. The same operation set now reduces to a
different Score, so core_spec.tex:11614 applies — canonical bases
materialized beforehand cannot be reused without rebuilding. That
requirement is currently unenforceable, so the contract is complete and
ratifiable as a plan but explicitly not dispatchable.
S27 is why. The version machinery is self-referential:
reduction_version_for sources a new superblock's value from the
canonical base's own self-report, and open compares it only against the
superblock that value seeded. The check is not vacuous — it catches a
corrupt base disagreeing with its superblock — but it necessarily
passes for a conformingly propagated stale base, which is the case the
requirement exists to prevent.
An earlier draft of pin 0 claimed no writer path existed at all. That
was false, and the way it was false is recorded in both the contract and
the S27 row: the search behind it looked for constructor calls, which
cannot find a path that propagates an existing value without
constructing one. The instrument could not observe the thing it was used
to rule out.
Docs only. No code, no spec sources, no implementation authorized.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV