M5 Phase 3: the rule studio onto the fixture corpus - #65
Open
kjpatel wants to merge 1 commit into
Open
Conversation
test_rules_api goes from 29 failures under deletion to 0; the demo directory falls from 57 to 28. 969 passed, 7 skipped; 351 golden receipts replay byte-for-byte; impact reports 0 of 351 decisions flip. Two of the 29 were not failures. test_declared_cases_run_green looped over an empty glob so its body never ran, and the refusal test was parametrized over one, so pytest collected zero cases for it. Both passed under deletion. Both are named after claims nothing was checking. That is the founding rule of this phase in its sharpest form yet, and the two shapes are now named in CLAUDE.md: assert a glob is non-empty before using it, and loop inside the test rather than parametrizing over the filesystem. Three artifacts the fixtures lacked, each added because a test needed it: expected.yaml (declared outcomes — the studio runs them beside impact, so a corpus without them left half the surface untestable), fixtures/dmn/ (one table that compiles, one refused), and a nested abstentionPolicy.attributes, since two scalars cannot catch a re-emitter that flattens. Pack version 2026.2.0 -> 2026.3.0; receipts, digest vectors and the corpus aggregate re-pinned. Also fx-0006, a restricted widget scoring 60. Every existing case scored 12 or 80 against a threshold of 50, so a threshold edit flipped all three restricted cases or none — impact analysis over these fixtures could only ever answer "everything moved", which cannot demonstrate what impact analysis is for. It clears a new bar for this corpus: not a behaviour that was unreachable, but a distinction the corpus could not draw. reload_demo() hoisted into demotest_helpers after being copied into a second suite, one copy short of the version that gets edited wrong. The content-root assembly now performs three path rewrites, each of which fails silently if skipped by offering an empty list rather than raising. Found on the way, and worth more than the conversion: editing the derived threshold moves all three decisions while two of the three rules stay byte-identical on the page. The blast radius of an edit is not a syntactic property of the edit, which is the argument for the equivalence panel and is stronger than the one the docs were making. Sharpened into docs/neuro-symbolic-architecture.md. Three of the plan's claims about this task were wrong and are corrected: the suite is 29 failures not 31, test_api has 6 residual not 4 (verified against #62, so not a c-0 regression), and part 5's "needs a date attribute" is false — placeholder validation is syntactic. The progress log had also lost the PR links for #61-63 and never recorded #64. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Phase 3, part 4b(ii)c-1.
test_rules_apimoves off the teaching packs onto a content root assembled fromfixtures/.The measurement
test_rules_apifailures under deletiondemo/teststotal under deletionFull suite 969 passed / 7 skipped.
verifyreplays all 351 golden receipts byte-for-byte;impactreports 0 of 351 decisions flip. The fixture builder reproduces byte-identically across runs.Two of the 29 were not failures
test_declared_cases_run_green_against_every_committed_packlooped over a glob of packs — with the packs deleted, the loop body never ran.test_every_refusal_example_is_reported_not_raisedwas@pytest.mark.parametrized over a directory, and parametrize is evaluated at collection, so an empty directory produced zero test cases. pytest reports the count that remains and says nothing about what left.Both passed. Both are named after claims nothing was checking. CLAUDE.md already said "asserts over an empty glob" was a failure mode; these are the two shapes it actually takes, and both fixes are one line.
What the fixtures lacked
Each added because a test needed it, not for completeness:
fixtures/expected.yaml— declared outcomes. The studio runs them beside impact analysis, so a fixture corpus without them left half that surface untestable.fixtures/dmn/— one table that compiles (2 decisions, 4 rules), one that is refused. The import panel and compiler happy path were reachable only throughdmn/examples/, which an adopter deletes.abstentionPolicy.attributes— two scalars cannot catch a re-emitter that flattens.fx-0006, a restricted widget scoring 60.Pack
2026.2.0→2026.3.0; receipts, digest vectors and the corpus aggregate re-pinned. OnlyrulePack.versionand the resulting hashes moved — no decision changed.Why
fx-0006existsEvery existing case scored 12 or 80 against a threshold of 50, so any threshold edit flipped all three restricted cases at once or none. Impact analysis over these fixtures could only ever answer "everything moved" — which cannot demonstrate the thing it exists for: a pack whose meaning moved while every declared outcome stayed green. It clears a new bar for this corpus: not a behaviour that was unreachable, but a distinction the corpus could not draw.
Found on the way
Editing the derived threshold from 50 to 70 moves all three of the pack's decisions, while two of the three rules stay byte-identical on the page — they read the moved value as a
derivedbinding. The equivalence panel returns a witness naming a widget scoring exactly 50, the boundary where the packs part.The diff is complete and honest and it is still not where the effect lives. The blast radius of an edit is not a syntactic property of the edit — that is the argument for the panel, and it is stronger than the one the docs had been making. Sharpened into
docs/neuro-symbolic-architecture.md.Plan corrections
Three claims about this task were wrong:
test_apihas 6 residual failures, not 4 — verified against M5 Phase 3: test_api onto the fixture pack, and a docs pass that is not optional #62, so not a c-0 regressionPart 5's fixture DMN prerequisite is also delivered here. The progress log had lost the PR links for #61–63 and never recorded #64; both fixed.
Docs pass
Per CLAUDE.md's definition of done:
fixtures/README.md(three stale counts), the CLAUDE.md layout row and two gotchas, the architecture doc, CHANGELOG, and the plan. A stale docstring intest_receipts_api.pyclaiming it runs against the committed corpus — false since #59 — is corrected.🤖 Generated with Claude Code