Skip to content

RFC-015 P1-S2: consolidation protection parity, marker inheritance, playbook advisory#625

Merged
lantiscooperdev merged 3 commits into
mainfrom
feat/rfc-015-p1-s2
Jul 27, 2026
Merged

RFC-015 P1-S2: consolidation protection parity, marker inheritance, playbook advisory#625
lantiscooperdev merged 3 commits into
mainfrom
feat/rfc-015-p1-s2

Conversation

@lantisprime

@lantisprime lantisprime commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Implements RFC-015 Phase 1 slice 2 by mechanical transcription of the frozen, review-ACCEPTed plan docs/plans/rfc-015-p1-s2.md.

What lands

  • Protection parity (R4b). Every consolidation path — legacy apply, legacy dry-run, clerk apply, --fold, --all-projects — now derives protected ids through the single shared resolveProtectionOrAbort helper instead of per-path ad-hoc logic. The clerk path's bare catch {} around computeProtectedIds is replaced with a real failure path that aborts and releases the lock.
  • Marker inheritance (R4a). A digest inherits playbook: true when any member carries it, on both the legacy and clerk paths, through the frontmatter serializer, the index row builder, and the clerk merge builder.
  • Marker-preferring canonical (REQ-8b). Dedupe selects a marker-bearing member as canonical over build order, deterministically.
  • Playbook advisory (REQ-9). New exported buildConsolidationAdvisory() in scripts/lib/playbook-registration.mjs, wired into all three envelopes: dry-run, legacy apply, and clerk apply. It names the causing declaration and the recovery path when a cluster is skipped by class-e protection, and flags marker inheritance when a marked-but-undeclared episode is folded.
  • CI registration. The new suite runs in the substrate job.

Verification

CI green across all three workflows (Tests, Plugin + schema validation, RFC registry validation) on d660624.

Suite Result
tests/test-rfc-015-p1-s2-consolidation.mjs 29/29 pass, 1 pending
tests/test-em-consolidate.mjs 8/8
tests/test-store-write-concurrency.mjs 46/46
tests/test-rfc-009-p4-apply.mjs 30/30
tests/test-fold-superseded.mjs 11/11
tests/test-fold-all-projects.mjs 8/8
tests/test-ci-suite-registration.mjs 16/16

Workflow YAML re-parsed after the CI edit (yaml.safe_load resolves all six jobs), since a YAML-invalid workflow edit is how #613 broke.

Two things CI caught after this PR was opened

1. --include-pinned regression (fixed in 9ed38da). Centralizing protection added gating to the legacy dry-run and apply paths, where main had none. Class-0 pinned protection then re-skipped rows the pre-lock row filter had already admitted, so --include-pinned silently became a no-op and a pinned-during-lock-wait race stopped folding. Two suites that pass on main went red: test-em-consolidate.mjs (7/8) and test-store-write-concurrency.mjs test 46 (45/46). Verified against a detached worktree at main to confirm the regression was ours.

liftPinnedGate() strips only the pinned reason, only from the two protectedIds maps those newly-gated paths feed to partitionProtectionSkips. It is a no-op in the default case because the row filter already excludes pinned rows before clustering. protection.mjs, the clerk path and the fold paths are untouched, and every other protection class including playbook-referenced passes through unchanged.

2. t4b_abort_message_discriminates marked pending (d660624). It exposes a real product gap rather than a bad assertion: scripts/em-consolidate.mjs:808 loads the index at module-init with no guard, so the EISDIR fixture throws inside relevance.mjs:100 before resolveProtectionOrAbort runs and the process dies with a raw stack trace and empty stdout. Filed as #626. Closing it is a production change outside this frozen plan, so the leg's fixture and all four assertions are kept verbatim behind a single T4B_PENDING_626 constant; the runner prints a distinct PEND line naming the issue and tallies honestly as 29/29 pass, 1 pending rather than claiming 30/30. Re-enabling is flipping that constant to false.

Review

Adversarial review by an independent GLM-5.2 seat, two rounds, every finding backed by a runtime probe rather than code reading.

Round 1 returned ACCEPT-WITH-MOD with one P2: the clerk apply envelope emitted neither playbook_advisory nor protection_skips, leaving REQ-9 ("both apply paths") half-met. Round 1 also independently proved by probe that the clerk marker chain is correct despite its suite leg passing vacuously, that canonical selection is stable across insertion order, that the abort path releases the lock with no double-release, and that buildConsolidationAdvisory never throws on malformed input.

The P2 was fixed by implementing Listing L11's clerk-envelope prose, and t7b_clerk_marker_inherit was de-vacuumed with a state-D fixture that actually reaches the clerk merge path. Round 2 returned ACCEPT with no findings above NIT, confirming content parity with the legacy envelope, correct negative polarity (both keys omitted, not null), no canonical-superseded leak into protection_skips, and that the rewritten t7b would fail against the pre-fix envelope.

Note that the review rounds predate the two CI-caught items above, which were found by CI rather than by review.

Filed alongside

🤖 Generated with Claude Code

…laybook advisory

Routes every consolidation path through the shared protection resolver, inherits the
playbook marker into digests on both the legacy and clerk paths, prefers a marked member
as dedupe canonical, and emits a playbook_advisory on the dry-run, legacy apply and clerk
apply envelopes (REQ-9).

Suite tests/test-rfc-015-p1-s2-consolidation.mjs is 29/30 and registered in the substrate
CI job. t4b_abort_message_discriminates is knowingly red: its EISDIR fixture throws at
em-consolidate.mjs:808 (module-init loadIndex) before the resolver runs, so the process
dies with a raw stack trace instead of the structured abort envelope. Fixing that needs a
production change this frozen plan does not authorize; filed separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
lantisprime and others added 2 commits July 26, 2026 23:33
Routing the legacy dry-run and apply paths through resolveProtectionOrAbort made
class-0 `pinned` protection re-skip rows the pre-lock row filter had already
admitted. main had no pinned gating on those paths at all, so two suites that
pass on main went red:

  test-em-consolidate.mjs        --include-pinned became a no-op (7/8)
  test-store-write-concurrency   S3c test 46, a row pinned during lock-wait (45/46)

liftPinnedGate() strips only the `pinned` reason, only from the two protectedIds
maps those paths feed to partitionProtectionSkips. It is a no-op in the default
case because the row filter already excludes pinned rows before clustering.
protection.mjs, the clerk path and the fold paths are untouched, and every other
protection class including playbook-referenced passes through unchanged.

test-em-consolidate 8/8, test-store-write-concurrency 46/46,
test-rfc-015-p1-s2-consolidation 29/30 (t4b unchanged), test-rfc-009-p4-apply
30/30, test-fold-superseded 11/11, test-fold-all-projects 8/8.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
t4b_abort_message_discriminates exposes a real product gap, not a bad test:
em-consolidate.mjs:808 loads the index at module-init with no guard, so the
EISDIR fixture throws in relevance.mjs:100 before resolveProtectionOrAbort runs
and the process dies with a raw stack trace and empty stdout. Filed as #626.
Closing it is a production change outside this slice's frozen plan.

The leg's fixture and all four assertions are kept verbatim behind a single
T4B_PENDING_626 constant. The runner prints a distinct PEND line naming the
issue and tallies honestly as "29/29 pass, 1 pending" rather than claiming
30/30. Re-enabling once #626 lands is flipping that constant to false.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lantiscooperdev
lantiscooperdev merged commit ee2997b into main Jul 27, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants