Validation report: S7-02 — Fixtures batch 2 (monorepo-pnpm + stale-scip full materialization)¶
Validated: 2026-05-18 Verdict: HARDENED Validator version: phase-story-validator v1 (consolidated single-pass critics + S7-01 precedent template)
Summary¶
S7-02 plants the remaining two of the five Phase-2 portfolio fixtures and extracts the shared _shape_test_kernel.py deferred from S7-01 (6 consumers total → conclusively past Rule of Three). The story's intent and scope are sound and trace cleanly to phase-arch-design.md §"Fixture portfolio engineering" + High-level-impl.md §Step 7 + S7-01's Notes-for-implementer "Patterns DELIBERATELY deferred".
Five block-tier and ten harden-tier issues found. The block-tier set has the same root pattern as S7-01: the story prescribes mechanisms that contradict either ADR-0001 (pnpm not allowlisted) or the actual S4-02 stub mechanism (the story names last-indexed-commit.txt, but the existing stub uses a seed-template + gitignored-.git/ + gitignored-.codegenie/ pattern). All five are mechanically fixable in place; none is a goal/scope rewrite.
Story edited in place. Verdict: HARDENED.
Stage 1 — Context Brief¶
What the story promises. Two fixture trees:
1. tests/fixtures/portfolio/monorepo-pnpm/ — pnpm workspace with three packages (lib-a, lib-b depending on lib-a, app depending on both); root + per-package manifests; root pnpm-lock.yaml; Dockerfile; .github/workflows/ci.yml; per-package tsconfig.json with root references.
2. tests/fixtures/portfolio/stale-scip/ — "full materialization" of the S4-02 stub: real TypeScript source tree, real (binary) SCIP blob built from a prior commit, committed history with HEAD ahead by ≥ 1.
Plus the deferred shared tests/fixtures/portfolio/_shape_test_kernel.py consumed by all five S7-01/S7-02 fixtures + Phase 1's node_typescript_helm/ shape test (sixth consumer; kernel earns its keep).
Arch + ADR constraints (load-bearing).
phase-arch-design.md §"Fixture portfolio engineering"— fixtures ≤ 200 files (stale-scipstory permits ≤ 50.ts);regenerate.shreviewed-as-code;.codegenie/cache/NOT committed.phase-arch-design.md §"Edge cases"row 11 —stale-scipis the load-bearing CI-gating fixture; build FAILS ifIndexHealthProberegresses.- ADR-0001 closed binary set (verified at
src/codegenie/exec/__init__.py:96-111):{git, node, semgrep, syft, grype, gitleaks, scip-typescript, ast-grep, ripgrep, tree-sitter, docker, strace}.pnpmis NOT in the set;npmis NOT;node-gypis NOT. Same Phase-2 constraint S7-01 was hardened against. - ADR-0006 —
IndexFreshness = Fresh | Stale(reason: StaleReason);CommitsBehindis the structural assertion the adversarial reads. - ADR-0007 — no plugin loader; no fixture seeds
plugins/. - ADR-0009 — pytest-xdist veto; portfolio CI lane runs serial.
Existing S4-02 stub — source-of-truth (read at validation time). Critical: the story's prescription for stale-scip mechanism diverges from what S4-02 actually shipped. The actual stub at tests/fixtures/portfolio/stale-scip/:
- Has a gitignored
.git/directory (its own micro-repo, regenerated byregenerate.sh). - Has a gitignored
.codegenie/directory (regenerated byregenerate.sh). - Committed seed bytes:
_seed/scip-slice.template.json(JSON template withPARENT_COMMITplaceholder substituted at regen time) +_seed/scip-index.scip.placeholder(empty placeholder — S7-02's job is to replace it with a real binary) +package.json+main.ts+regenerate.sh+README.md+.gitignore+.gitattributes. - No
last-indexed-commit.txt. The fixture'slast_indexed_commitlives only inside the materializedscip.json(substituted byregenerate.shfrom_seed/scip-slice.template.json). - The current
regenerate.shalready implements the "refuse-against-current-HEAD" guard:LAST_INDEXED="${LAST_INDEXED:-$(git rev-parse HEAD~1)}"defaults to HEAD~1; theif [[ "$LAST_INDEXED" == "$(git rev-parse HEAD)" ]]; then exit 1; fiblock exits non-zero if forced. - The adversarial test at
tests/adv/phase02/test_stale_scip_fixture.pyreads.codegenie/context/raw/scip.json(the materialized template), NOT the binaryscip-index.scip. The binary blob is for S4-03's futureScipIndexProbe, not S4-01'sIndexHealthProbe. - The adversarial's outer-key invariant has widened since S4-02: it now asserts
set(index_health.keys()) == {"scip", "runtime_trace"}(S5-05 widened it). Future S6-08 registrations may widen it further.
S7-01 precedent (HARDENED 2026-05-17). Two block-tier and seven harden-tier issues hardened. Most relevant patterns for this story:
- pnpm/npm/node-gyp not allowlisted → hand-author lockfile bytes, regenerate.sh does NOT run any install command.
- Shell can't call run_allowlisted (it's a Python function) → bash invokes allowlisted binaries directly; the AC-31 static check is the structural guarantee.
- Closed-set enumeration via git ls-files <fixture-path> (not rglob) so gitignored artifacts don't dirty the test.
- Shared _fixture_regen_allowlist.py lives at tests/unit/_fixture_regen_allowlist.py (Rule-of-Three carve-out for load-bearing-policy ownership; reusable unchanged by S7-02).
- _ProbeName Literal uses subset semantics in AC-37 (not equality) so Phase-3+ probes added later don't retroactively break Phase-2 fixtures.
Ambiguities surfaced (resolved by edits).
- AC-18 says "S4-02's stub already chose one path, this story honors it" — but AC-19/AC-20 then concretely prescribe
last-indexed-commit.txt, which S4-02 did NOT choose. The actual stub uses_seed/scip-slice.template.jsonwithPARENT_COMMITsubstitution. Resolved: rewrite to honor the actual mechanism. - AC-15 says "the S4-02 stub directory + minimal SCIP blob is replaced wholesale by the full fixture" — but wholesale replacement destroys the working seed-template mechanism the adversarial relies on. Resolved: replacement is restricted to the
_seed/scip-index.scip.placeholderbinary (substituted with a realscip-typescript-built blob) plus an expandedsrc/tree. - AC-17 says
.codegenie/context/raw/scip-index.scipis committed with a.gitignorecarve-out — conflicts with the stub's gitignored-.codegenie/invariant and the central no-committed-.codegenie/cache/guard from S7-01. Resolved: the real binary lives in_seed/scip-index.scip(replacing_seed/scip-index.scip.placeholder);regenerate.shcopies it into.codegenie/context/raw/scip-index.scipat runtime;.codegenie/stays gitignored. - AC-10 says
regenerate.shispnpm install --frozen-lockfile—pnpmnot allowlisted (ADR-0001). Resolved by mirroring S7-01'snative-modulesprecedent — hand-author thepnpm-lock.yaml; no install at regen. - AC-21 step (b) says regen runs
scip-typescript"(viarun_allowlisted)" — bash can't call Python. Resolved by mirroring S7-01 AC-22 — bash callsscip-typescriptdirectly; AC-31's static check is the structural guarantee.
Stage 2 — Critic reports (consolidated)¶
Coverage critic — Verdict: HARDEN¶
- [block] C1 — AC-10 + Implementation Outline §1 prescribe
pnpm install --frozen-lockfileinregenerate.sh;pnpm∉ALLOWED_BINARIES(verified atsrc/codegenie/exec/__init__.py:96-111). Story's AC-31 ("regenerate.shinvokes only allowlisted binaries per fixture") would fail. Fix applied — AC-10 rewritten to require hand-authored bytes; Implementation Outline §1 rewritten; regen script is nowmkdir/touch+ invariant-assertions only (no install). - [block] C2 — Story prescribes
last-indexed-commit.txt(AC-19, AC-20, content predicates_last_indexed_not_equal_to_current_head,_regen_refuses_current_head); actual S4-02 stub uses_seed/scip-slice.template.jsonwithPARENT_COMMITplaceholder substitution. AC-18's "honor what S4-02 chose" makes the contradiction internal to the story. Fix applied — AC-18 rewritten to name the actual mechanism; AC-19 rewritten to describe the seed-template; AC-20 rewritten to point at the existingregenerate.shguard (LAST_INDEXED="${LAST_INDEXED:-$(git rev-parse HEAD~1)}"+if [[ "$LAST_INDEXED" == "$(git rev-parse HEAD)" ]]; then exit 1; fi). - [block] C3 — AC-17 commits
.codegenie/context/raw/scip-index.scipwith a.gitignorecarve-out; conflicts with the stub's gitignored-.codegenie/pattern AND with S7-01's central no-committed-cache guard. Fix applied — AC-17 rewritten: the real binary lives in_seed/scip-index.scip(committed; replaces_seed/scip-index.scip.placeholder);.codegenie/stays gitignored;regenerate.shcopies_seed/scip-index.scipto.codegenie/context/raw/scip-index.scipat runtime. - [block] C4 — AC-15 says "the S4-02 stub directory + minimal SCIP blob is replaced wholesale by the full fixture" — wholesale replacement destroys the seed-template mechanism the adversarial relies on. Fix applied — AC-15 rewritten as "the placeholder
.scipblob in_seed/is replaced with a realscip-typescript-built blob; the seed-template + regenerate-script mechanism is preserved; the source tree expands to ≤ 50.tsfiles." - [block] C5 — AC-21 step (b) prescribes "runs
scip-typescript(viarun_allowlisted)" insideregenerate.sh.run_allowlistedis a Python function (src/codegenie/exec/__init__.py); bash cannot call it. Same architectural mismatch S7-01 AC-22 had. Fix applied — AC-21 split into "seed-build ritual" (one-time, contributor's local box, invokesscip-typescriptdirectly; produces_seed/scip-index.scip) and "regenerate.sh runtime" (deterministic; noscip-typescriptinvocation at regen time; just copies the committed seed blob). - [harden] C6 — AC-32/AC-33 framing implies "full materialization makes the adversarial assertion non-trivially true" — but the adversarial reads
.codegenie/context/raw/scip.json(the materialized template), not the binary.scipblob. The assertion is ALREADY non-trivially true against the stub (the templatedscip.jsoncarriesPARENT_COMMIT, not HEAD). S7-02's actual contribution is producing a real binary SCIP for S4-03's futureScipIndexProbe, not changing what S4-02's adversarial currently passes/fails. Fix applied — AC-32/AC-33 reworded; the adversarial-still-passes claim is preserved; the binary-SCIP-for-S4-03 framing is made explicit. - [harden] C7 — Adversarial outer-key set has widened to
{"scip", "runtime_trace"}(S5-05) since S4-02 landed; story still says the adversarial asserts onlyisinstance(slice.freshness, Stale)etc. — accurate but incomplete. Fix applied — AC-32 footnote acknowledges the wider outer-key set as a tripwire so a future contributor doesn't think their materialization PR broke an unrelated invariant. - [harden] C8 — No AC for the kernel module's own structural-export test. AC-23 requires mypy --strict but a silent removal of
enumerate_trackedor one of themake_*factories would still pass mypy if the consumers are concurrently updated. Fix applied — AC-23 amended: kernel exposes a documented__all__set; a test attests/unit/test_shape_test_kernel.pyasserts the export set matches the documented contract.
Test-Quality critic — Verdict: HARDEN¶
- [harden] T1 — TDD-plan stale-scip
_FILE_SPECSlistslast-indexed-commit.txtand.codegenie/context/raw/scip-index.scip. Neither matches the actual stub mechanism. Fix applied —_FILE_SPECSrewritten to list_seed/scip-slice.template.json,_seed/scip-index.scip(committed real binary),regenerate.sh,README.md, the source tree (package.json,tsconfig.json,src/*.ts). - [harden] T2 — Content predicates need rewrite to match the corrected mechanism:
_last_indexed_not_equal_to_current_headoriginally readlast-indexed-commit.txt+ runsgit rev-parse HEAD. Rewrite: predicate readsregenerate.shand asserts the lineLAST_INDEXED="${LAST_INDEXED:-$(git rev-parse HEAD~1)}"is present (the structural guarantee thatlast_indexeddefaults to HEAD~1, never HEAD)._regen_refuses_current_headoriginally grepped for anifblock. Rewrite: predicate grepsregenerate.shfor[[ "$LAST_INDEXED" == "$(git rev-parse HEAD)" ]](or equivalent) + theexit 1branch._scip_blob_metadata_records_prior_commitoriginally read.codegenie/context/raw/scip-index.scipand parsed SCIP wire-format. Rewrite: predicate reads_seed/scip-index.scip(committed seed); asserts non-empty AND the file size is ≥ a documented minimum (e.g., ≥ 200 bytes, since a realscip-typescriptoutput for ≥ 5.tsfiles exceeds this; the placeholder is 0 bytes — so any non-empty seed blob passes the placeholder-replaced check)._readme_documents_structural_assertion— preserves; checks forCommitsBehind.n >= 1ANDlast_indexed != current_HEADphrases.- [harden] T3 — AC-26 says the kernel exposes
_ProbeNameas the closed Phase-1 + Phase-2 set with a test that asserts runtime-equality; S7-01's hardened AC-37 uses subset semantics so Phase-3+ additions don't retroactively break Phase-2 fixtures. Inconsistency between the story and its sibling. Fix applied — AC-26 rewritten to use subset semantics (set(registered_phase_2_names) ⊆ set(get_args(_ProbeName))), matching S7-01. - [harden] T4 — Story has no AC for the
_fixture_regen_allowlist.pyreuse pattern. The shared module lives attests/unit/_fixture_regen_allowlist.pyper S7-01; the two new fixtures need corresponding allowlist tests (tests/unit/test_fixture_monorepo_pnpm_regenerate_allowlist.py+tests/unit/test_fixture_stale_scip_regenerate_allowlist.py). Fix applied — AC-31 amended to reference the shared module + add two test files to "Files to touch". - [harden] T5 — TDD-plan stale-scip
_FILE_SPECSsays the source tree hassrc/{a,b,c,d,e}.ts(5 trivial chain-import files), but the_seed/scip-slice.template.jsoncontent currently records"files_indexed": 1, "files_in_repo": 1. If the source tree grows to 5+ files, B2 will surfaceCoverageGap, notCommitsBehind. Fix applied — TDD plan amended: the seed template'sfiles_indexed/files_in_repocounts must match the seeded source tree footprint (or the seeded SCIP must actually index all files). Added a content predicate_seed_template_counters_match_source_treethat assertsscip-slice.template.json's counts equal the count of*.tsfiles undersrc/. - [harden] T6 — Mutation table predicates
_regen_refuses_current_headand_last_indexed_not_equal_to_current_headpredicate intent statements need updating to match the corrected mechanism. Fix applied — mutation-resistance witness table rewritten to point at the corrected predicates and the corrected files they read. - [harden] T7 — AC-21 step list contains "seed-build ritual" steps and "regen-runtime" steps blended together. A reader would conclude
regenerate.shrunsscip-typescript(block C5 above). Fix applied — AC-21 split into AC-21a (seed-build ritual; contributor-owned; one-time per tool-version bump) and AC-21b (regen-runtime; deterministic; no scip-typescript). Documented inREADME.mdper AC-22.
Consistency critic — Verdict: HARDEN¶
- [block] Cn1 — Same as C1 (
pnpm∉ALLOWED_BINARIES, ADR-0001). Source-of-truth (ADR-0001) wins; AC-10 + Implementation Outline §1 rewritten. - [block] Cn2 — Same as C2/C4/C5 — story's prescribed
stale-scipmechanism contradicts the existing stub (which IS the source of truth per AC-18). All four contradictions resolved. - [harden] Cn3 — AC-26's runtime-equality test contradicts S7-01's HARDENED AC-37 (subset semantics). Fix applied — subset semantics (T3 above).
- [harden] Cn4 — Kernel location. AC-23 says
tests/fixtures/portfolio/_shape_test_kernel.py. AC-25 says Phase 1'stest_fixture_node_typescript_helm_shape.pymigrates to consume the kernel; Phase 1's fixture lives attests/fixtures/node_typescript_helm/(NOT underportfolio/). The Phase 1 test would import across theportfolio/-namespace boundary, which is awkward. Fix applied — kernel relocated totests/fixtures/_shape_test_kernel.py(above theportfolio/subdirectory) so all six consumers can import without crossing fixture-namespace boundaries. - [harden] Cn5 — Implementation Outline §4 says the central no-cache-committed test (
tests/unit/test_no_committed_codegenie_cache_under_portfolio_fixtures.py) needs to be updated to allowliststale-scip/.codegenie/context/raw/scip-index.scip. With the corrected design (C3 above —.codegenie/stays gitignored; the real binary lives in_seed/), no carve-out is needed; the existing S7-01 guard passes unchanged. Fix applied — AC-29 amended; Implementation Outline §4 rewritten.
Design-Patterns critic — Verdict: HARDEN¶
- [harden] DP1 — Kernel timing is correct (6 consumers; conclusively past Rule of Three). Kernel structure is sound. Endorsed.
- [harden] DP2 —
make_*_testfactory pattern returns pytest test functions for module-level assignment. This is awkward for pytest's natural module-level@pytest.mark.parametrizediscovery; a flatter pattern is to export pure helper functions (assert_file_exists(fixture, spec),assert_file_parses(fixture, spec), etc.) and let each consumer module write minimal@pytest.mark.parametrizetest bodies. Recommended as Notes-for-implementer (not promoted to AC — pattern advice is contextual per the validator skill prescription). Fix applied — added to Notes-for-implementer. - [harden] DP3 — Kernel cohesion question: should
_fixture_regen_allowlist.py(S7-01's shared module attests/unit/) be subsumed into the kernel? Different responsibilities (closed-set discovery vs. allowlist policy) argue for keeping them separate; module sizes are small; consumers import both today. Endorse keeping them separate; documented in Notes-for-implementer. - [harden] DP4 —
_FileSpecimmutability. S2-03's precedent isNamedTuple(immutable by construction). The kernel should preserve this. Endorse; added to Notes-for-implementer. - [harden] DP5 — Kernel's
enumerate_trackedshould be the only call site forgit ls-files <fixture-path>(port-and-adapter discipline — the subprocess port is encapsulated; consumers receive atuple[str, ...]of relpaths). Already implicit in AC-23; made explicit in Notes-for-implementer. - [nit] DP6 —
_SHELL_COREUTILS_ALLOWLISTlives in_fixture_regen_allowlist.py(S7-01). The kernel does NOT take ownership; consumers of the regen-allowlist test import from_fixture_regen_allowlist, consumers of the shape test import from_shape_test_kernel. Two flat modules; no cross-coupling. Endorsed. - [harden] DP7 —
_ProbeNameLiteral location. Currently each shape test declares its own_ProbeName; with the kernel,_ProbeNamelives there (single source of truth). Story's AC-23 already prescribes this — good. Note for implementer:_ProbeNameis exported from the kernel's__all__so all six consumers reference it viafrom tests.fixtures._shape_test_kernel import _ProbeName. AC-26 amended to make this explicit.
Stage 3 — Research¶
Skipped — no critic finding tagged NEEDS RESEARCH. All fixes have direct precedents:
- pnpm/install-discipline pattern: S7-01 native-modules HARDENED precedent (hand-authored pnpm-lock.yaml).
- scip-typescript bash-direct-invocation: S7-01 AC-22 HARDENED precedent (bash invokes docker directly).
- Closed-set via git ls-files: S7-01 AC-26 HARDENED precedent.
- Subset semantics for _ProbeName: S7-01 AC-37 HARDENED precedent.
- Seed-template + _seed/ mechanism: existing S4-02 stub at tests/fixtures/portfolio/stale-scip/_seed/.
- Shared regen-allowlist module: S7-01 _fixture_regen_allowlist.py HARDENED precedent.
Stage 4 — Synthesizer + conflict resolution¶
No critic conflicts. All four lenses agree on the directional fixes. The block-tier set is mechanically identical to S7-01's pattern; the harden-tier set tightens cross-story consistency, kernel cohesion, and the mechanism-vs-stub alignment.
Edits applied to the story (in order):
Status:Ready → HARDENED (validated 2026-05-18).- Inserted
## Validation notes (2026-05-18)block under header documenting every change. Depends on:annotated with the S7-01 HARDENED precedents (regen-allowlist module + lockfile-hand-author pattern).monorepo-pnpmblock:AC-10rewritten — hand-authoredpnpm-lock.yamlbytes;regenerate.shdoes NOT invokepnpm install(consistent with S7-01native-modules).stale-scipblock:AC-15rewritten — placeholder-replacement, not wholesale stub replacement; the seed-template + regenerate-script mechanism is preserved.AC-17rewritten — real binary SCIP lives in_seed/scip-index.scip(replaces_seed/scip-index.scip.placeholder);.codegenie/stays gitignored; no.gitignorecarve-out.AC-18rewritten — explicitly names the actual stub mechanism (seed-template + gitignored.git/+ gitignored.codegenie/); drops the "implementer picks" hedge.AC-19rewritten —last_indexedmechanism is_seed/scip-slice.template.json'slast_indexed_commitfield, substituted at regen time; nolast-indexed-commit.txtfile.AC-20rewritten — the existingregenerate.shguard already implements refusal; story preserves + documents the guard rather than prescribing a new mechanism.AC-21split into AC-21a (seed-build ritual; contributor-owned; one-time per tool-version bump) and AC-21b (regen-runtime; deterministic; no scip-typescript invocation at regen time).AC-22amended — README sections preserve the existing stub's prose; story is additive (Phase-3 entry-gate handoff note, structural-assertion section, seed-build ritual section).AC-23amended — kernel relocated totests/fixtures/_shape_test_kernel.py(aboveportfolio/); kernel exposes documented__all__set; test attests/unit/test_shape_test_kernel.pyasserts export set matches contract.AC-26rewritten — subset semantics (matching S7-01 AC-37); explicit phrasingset(registered_phase_2_names) ⊆ set(get_args(_ProbeName)).AC-28rewritten — closed-set complement test forstale-scipenumerates viagit ls-filesfrom the parent repo (gitignored.git/and.codegenie/don't appear); noinclude_pathscarve-out.AC-29amended — the existing S7-01 central no-cache-committed guard passes unchanged; no allowlist edit needed.AC-31amended — referencestests/unit/_fixture_regen_allowlist.py(S7-01's shared module); adds two new regen-allowlist test files (test_fixture_monorepo_pnpm_regenerate_allowlist.py,test_fixture_stale_scip_regenerate_allowlist.py).AC-32reworded — adversarial assertion has been passing since S4-02 against the seed-templatedscip.json; S7-02's contribution is the real binary SCIP for S4-03's future consumer, not changing what S4-02's adversarial asserts. Footnote acknowledges the widened outer-key set ({"scip", "runtime_trace"}since S5-05).AC-33reworded — preserves the structural assertion framing; ties to S4-02's existing test code rather than implying this story introduces the inequality.- TDD plan —
_FILE_SPECSfor stale-scip rewritten to list the actual files (_seed/scip-slice.template.json,_seed/scip-index.scip, etc.). - TDD plan — content predicates rewritten for the corrected mechanism (
_last_indexed_not_equal_to_current_headreadsregenerate.sh;_regen_refuses_current_headgreps the guard;_scip_blob_metadata_records_prior_commitreads_seed/scip-index.scipnon-emptiness; new predicate_seed_template_counters_match_source_tree). - Mutation-resistance witness table updated — predicates and files corrected.
- Implementation Outline §1 rewritten — no
pnpm installinvocation; hand-authored lockfile. - Implementation Outline §2 rewritten — explicitly preserves the existing stub mechanism; the seed-build ritual is OUT-OF-BAND (contributor's local box; not in
regenerate.sh). - Implementation Outline §3 amended — kernel location at
tests/fixtures/_shape_test_kernel.py; consumers' import paths spelled out. - Implementation Outline §4 rewritten — no central no-cache-committed test edit needed (the existing guard passes unchanged).
- Files-to-touch table — added
tests/unit/_fixture_regen_allowlist.pyconsumers (the two new allowlist tests); addedtests/unit/test_shape_test_kernel.py; kernel path updated;last-indexed-commit.txtremoved;.codegenie/context/raw/scip-index.scip.gitignore` carve-out removed. - Notes for the implementer — added paragraphs on:
- Kernel factory pattern alternatives (flatter helper-function style vs. test-factory style — recommended flatter style for pytest discovery naturalness, but not mandated).
- Why
_fixture_regen_allowlist.pyand_shape_test_kernel.pyare separate modules. enumerate_trackedas the kernel's port forgit ls-files._FileSpecis a frozen NamedTuple.- Why the seed-template mechanism is preserved (S4-02's adversarial reads
scip.json, not the binary blob; the binary lives in_seed/for S4-03's future consumer).
ACs touched: 12 modified (AC-10, AC-15, AC-17, AC-18, AC-19, AC-20, AC-22, AC-23, AC-26, AC-28, AC-29, AC-31, AC-32, AC-33). AC-21 split into AC-21a + AC-21b. Total ACs net: 36 → 37.
Final verdict¶
HARDENED. The story now:
- Conforms structurally to ADR-0001 (no silent
pnpm/npm/node-gypexpansion); themonorepo-pnpmregen script ismkdir/coreutils-only. - Removes the bash-calls-Python architectural mismatch (the
scip-typescriptinvocation moves to the contributor-owned seed-build ritual, OUT-OF-BAND). - Aligns with the actual S4-02 stub mechanism (seed-template + gitignored
.git/+ gitignored.codegenie/); no contradictorylast-indexed-commit.txtprescription. - Preserves the seed-template mechanism the adversarial test relies on; clarifies the binary-SCIP contribution as S4-03-forward-looking, not S4-02-assertion-changing.
- Adopts subset semantics for the kernel's
_ProbeNameLiteral, matching S7-01 (Phase-3+ probes won't retroactively break Phase-2 fixtures). - Relocates the kernel to
tests/fixtures/_shape_test_kernel.pyso all six consumers (incl. Phase 1'snode_typescript_helm/) import cleanly across the fixture-namespace boundary. - Specifies the kernel's
__all__contract as a runtime check (silent export removal becomes a build error). - Defers the factory-vs-helper kernel pattern choice to the implementer with a Notes-for-implementer recommendation (flatter helper-function style is more pytest-natural).
- Preserves S7-01's shared
_fixture_regen_allowlist.pypolicy ownership unchanged; adds two new consumer tests formonorepo-pnpm+stale-scip.
Ready for phase-story-executor.