Skip to content

feat: ADR-0033 Slice 2 — transactional Apply implementation (review candidate) - #191

Open
PhysShell wants to merge 18 commits into
mainfrom
claude/adr-0033-slice-2-apply-impl
Open

feat: ADR-0033 Slice 2 — transactional Apply implementation (review candidate)#191
PhysShell wants to merge 18 commits into
mainfrom
claude/adr-0033-slice-2-apply-impl

Conversation

@PhysShell

@PhysShell PhysShell commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Implementation review candidate for ADR-0033 Slice 2 (transactional Apply). The contract was independently accepted at the exact reviewed artifact 47e734cfbf1a6bd90c1bd2a035cdc68692378e96 (acceptance act: docs/decisions.log.md @ bad7b44); this PR is the implementation, which needs its own independent acceptance under the contract's §16. Nothing here is self-accepted or marked closed.

Slice 3, the controlled pilot, and any real-/production-/full-corpus labeling were not started and remain BLOCKED. No real corpus file or label was read or modified — every test runs over synthetic fixtures in process-unique temp directories.

Base / head

  • Base: main @ 249b585 · Head: 54639fc (after review repair rounds 1–2)
  • Scope: song-curation/ (isolated non-workspace crate; posture unchanged) + one evidence doc in docs/audit/. No production crate, no dependency, no unsafe, no Swang/stage/ADR changes.

Commit sequence (unsquashed — the history is the RED→GREEN proof)

Original nine commits (implementation):

# SHA Kind Content
1 dfa7e06 RED 43 preregistered §14 cases (A1–A8, C1–C10, L1–L9, K1–K9, K12, R1–R3, R5, F3, F5) + fixture/builder module. Evidence in message: both test binaries fail E0432: unresolved import griff_song_curation::apply.
2 4d311f4 TEST-FIX A8 drift fixtures clear the corpus tree before rewriting (stale files tripped the honest step-3 refusal first).
3 f8c9f1c GREEN Apply core: §5 strict wire contracts, §6 ordering, §7 laws (chain equations, already-applied precedence, supersession consistency, replacement authority via the single shared replay carrying acting-event attribution — the one §9-permitted internal Slice-1 accommodation), §8 happy path, §10 preservation + round-trip guard, §11 single preflight. Adversarial hardening deliberately left RED.
4 6ce1a69 FIXTURE Deterministic fault-injection registry (apply::fault): thread-local one-shot hooks, inert in production; lets concurrency cases run the second applier inline on the same thread.
5 bd2a339 RED 20 adversarial cases; message declares the split: 12 genuinely FAILED (F1, F2, F6, F10–F13, C13, C15, K10, K11, R4), 8 passing characterizations (F4, F7–F9, C11, C12, C14, C16).
6 5cc34b3 TEST-FIX K10 split (finding 1): serde's derived parse already refuses duplicated known fields at step 3, so the distinct §10.3 pass is proven on its actual residual — duplicates inside unknown members.
7 c9489e1 GREEN Full §8 protocol: exact step-1 order; prefix-closed lock-marker classification; under-lock temp inspection; create_new no-clobber temp; hardlink refusal; reserved .apply-staging namespace; coordination-path collisions; staged tree-agreement re-run; duplicate-key pass.
8 5bb78fb WITNESS Phase-8 falsification pass: 14 mutation targets probed; 4 survivors got dedicated killing witnesses (index schema check; null-prev vs non-empty head; record/report wire strictness).
9 bbc7928 DOCS Implementation evidence (docs/audit/2026-08-slice2-apply-implementation.md) + truthful crate-README update.

Repair round 1 (review of bbc7928: 4 blockers — reserved-area shape; non-regular lock occupant; swallowed release warning; C16 evidence):

# SHA Kind Content
10 8de362a RED Witnesses for blockers 1–2 (0 passed / 3 failed evidence in message).
11 9d505cd RED Witness for blocker 3 (double-fault release warning; 0/1).
12 987d0a3 GREEN Directory-entry-level reserved shape; stat-before-read lock-occupant classification; acquisition split so every post-create exit releases via the §8.2 warning channel.
13 92c97d6 TEST-FIX C16(a) made exact: live non-empty strict marker prefix. 63/63 holds as of this commit (before it the claim was overstated, as the review found).
14 5aca93a DOCS Repair-round-1 evidence section.

Repair round 2 (re-review of 5aca93a: 1 blocker — reserved subtree traversed before the shape law):

# SHA Kind Content
15 94e5c38 FIXTURE walk:descend trace point (inert in production) making "this subtree was never traversed" observable.
16 60fc228 RED Two armed witnesses: foreign subtree song-curation/extra/deep/…; reserved root symlinked to an external directory. Both FAILED with ApplyIoError { path: ".../song-curation", op: "walk" } — the walk descended / followed the symlink.
17 96ca755 GREEN The walk skips the reserved root by relative name before any type check or descent (is_dir would follow a symlink merely to answer); check_reserved_shape stays the single no-follow classifier. Both witnesses flip to CorpusTreeDisagreement with the hook never reached.
18 54639fc DOCS Repair-round-2 evidence section + this body refresh.

§14 matrix: 63/63 (as of 92c97d6)

Every preregistered case has a same-named test; the full case → test → RED-commit → result table is in docs/audit/2026-08-slice2-apply-implementation.md. Coverage: 23/24 new typed refusals exercised (+ CuratedManifestPathNotDistinct implemented as the hard guard §12 itself declares structurally unreachable — flagged as finding 3, not silently inherited); 11/11 Apply-reachable Slice-1 refusals exercised through the literally reused verify_plan; the 3 ledger-side members proven intentionally unreachable. The six review-repair witnesses (rounds 1–2) are additional to the preregistered 63.

Validation matrix (current head)

Check Result
isolated crate suite 118 passed / 0 failed (45 frozen Slice-1 + 73 Slice-2)
frozen Slice-1 suite 45/45, public API and semantics untouched
cargo test --workspace 1535 / 0 (workspace untouched by the repair rounds)
cargo clippy --workspace --all-targets -- -D warnings clean
crate clippy (deny(all)) clean
cargo fmt --all -- --check clean
cargo doc --no-deps (workspace + crate) only two pre-existing warnings (swang ambiguous link; a frozen Slice-1 doc comment), untouched
MSRV cargo +1.92 check (crate, all targets) clean

Findings (no contract law changed; none required a STOP)

  1. Duplicate known fields refuse even earlier than §10.3: serde's derive rejects them at the step-3 parse (CorpusTreeDisagreement); the distinct duplicate-rejecting pass guards its real residual — duplicates inside unknown members (K10 split proves both branches). Fail-closed both ways.
  2. Unresolvable non-index inputs (missing corpus dir / output parent) map to ApplyIoError { op: canonicalize… } — the single typed I/O boundary; no new refusal invented.
  3. §14's coverage sentence vs the structurally unreachable CuratedManifestPathNotDistinct guard: read together with §12, the guard belongs to the documented-unreachable set; recorded for the reviewer to confirm that reading explicitly.

Stop conditions

None triggered across the implementation and both repair rounds: no §14 case required changing a law; no new refusal, schema change, ordering change, frozen-Slice-1 observable change, new dependency, or unsafe; no contradiction found inside 47e734c.

Do not merge; do not self-accept. This PR stops as the implementation review candidate, pending independent re-review at exact head 54639fc.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn


Generated by Claude Code

claude added 9 commits August 20, 2026 14:28
Tests-only commit — no implementation. Preregisters 43 of the accepted
contract's 63 §14 cases against the not-yet-existing apply module:

- plan/integrity A1–A8 (incl. the A8 sweep of the five Apply-reachable
  Slice-1 inventory/replay refusals at the artifact boundary);
- chain/index C1–C10 (initial and chained application over the real
  published output tree, already-applied precedence over the chain law,
  the exact §7.2 relation refusals, fingerprint-neutral batches,
  index-internal validation, independent lineages);
- labels L1–L9 (four-way authority case table, supersession-evidence
  consistency, drift excluded by fingerprint);
- corpus completeness K1–K9 + K12 (every-chunk-together, raw byte copy of
  untouched files, laundering guard, no-root-songs law, tree agreement,
  curated projection, protected curated path, partial vs holdout-ready,
  recursive reserved-area shape law);
- report/index R1, R2, R3, R5 (digest law, record/report binding,
  publish-neither on pre-publication refusal, curated digest law);
- pure filesystem laws F3 (no-trace refusals) and F5 (byte determinism).

Plus the shared fixture/builder module (tests/common): corpus trees,
serialized plan artifacts, index files, byte-walk comparators, and the
contract's staging/lock/temp name derivations — no production behaviour.

RED evidence (cargo test --manifest-path song-curation/Cargo.toml):
  error[E0432]: unresolved import 'griff_song_curation::apply'
    (both test binaries: apply_core, apply_outputs)
  error: could not compile 'griff-song-curation' (test "apply_core")
  error: could not compile 'griff-song-curation' (test "apply_outputs")
The tests compile exactly as far as the absent API allows and fail because
Slice 2 is not implemented. The frozen Slice-1 lib target still compiles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
Fixture-only correction, no law change: the A8(a)/A8(b) drift scenarios
rewrote the corpus with a different chunk set but left the previous chunk
files on disk, so the honest step-3 tree-agreement refusal fired before the
step-5 refusal the case actually preregisters. The fixtures now clear the
corpus directory before writing the replacement snapshot. Also applies
cargo fmt to the test tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
GREEN for the 43 preregistered RED-A cases (A1–A8, C1–C10, L1–L9, K1–K9,
K12, R1–R3, R5, F3, F5): the new public apply module executes the accepted
contract's pipeline over the serialized artifact boundary —

- §5 wire contracts: strict (deny_unknown_fields) application-index v1 and
  apply-report v1 with the four-way partition, total-order holdout-refusal
  records, no wall-clock timestamp, and report_digest over the Slice-1
  shared canonical encoding (canonical_json made pub(crate), not copied);
- §6 order for the implemented checks: index existence resolved before the
  lock; lock create_new + marker; strict plan/index parsing before any
  mutation; snapshot load with recursive sorted walk, reserved-area
  exclusion + recursive shape law, multiset tree agreement, and the
  no-root-songs law; index schema/uniqueness/internal-chain validation;
  verify_plan reused literally; already-applied before the chain equations;
  the three exact §7.2 relations (and the §7.1 null relation); supersession
  -evidence consistency; replacement authority via the single shared replay
  primitive carrying acting-event attribution (§9 — the one permitted
  internal Slice-1 accommodation; observable Slice-1 behaviour unchanged,
  frozen suite green);
- §8 happy-path protocol: fixed-name staging via create_dir, preservation
  law (§10) with raw byte copy for untouched files and the round-trip
  laundering guard for touched ones, curated manifest at the protected
  path, step-10 re-read-from-staged-bytes self-check + the single real
  song_holdout_preflight over the curated view, report written last into
  staging, one publication rename, temp+sync+rename commit point, lock
  released on every exit with the §8.2 release-warning result shape.

Deliberately NOT yet implemented (their §14 cases stay RED for the
adversarial phase): hardlink refusal, coordination-path collisions,
reserved output namespace, output/staging pre-existence refusals,
containment laws, lock-content classification, under-lock temp inspection,
no-clobber temp creation, duplicate-key rejection, staged tree-agreement
re-run.

Evidence: song-curation suite 45 (frozen Slice 1) + 27 + 16 green; clippy
--all-targets clean under the crate's deny(all) lints; fmt --check clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
Fixture/harness commit — no production behaviour. Adds the deterministic
fault-injection registry (apply::fault): thread-local, one-shot hooks that
a test registers at named points; a production run registers none, so
every point is an inert no-op. One-shot consumption makes a nested Apply
inside a hook safe (a point cannot re-fire recursively), which is what
lets the concurrency cases (C14, C16 live window) run a second applier
inline on the same thread instead of relying on scheduler timing.

Named points wired into the protocol as inert pass-throughs:
  lock:after_create      — between lock create_new and marker publication
  lock:release           — best-effort release failure (F9)
  stage:write            — staged-write failure (F4)
  stage:before_selfcheck — staged-corruption window before step 10 (K11)
  publish:rename         — failure of the step-11 publication rename (F4)
  commit:before_temp     — late temp-occupant window (F12)
  commit:temp_write      — temp-write failure inside step 12 (F4/R4)
  commit:rename          — commit-rename failure after the temp exists
                           (F4/R4; also the C14 live step-12 temp window)

Full suite stays green (45 + 27 + 16); clippy/fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
Tests-only commit — no production changes. Preregisters the remaining 20
§14 cases: F1, F2, F4, F6–F13, C11–C16, K10, K11, R4. Concurrency cases
run the second applier inline inside a one-shot fault hook on the same
thread (C14 inside the live step-12 temp window; C16(a) inside the
create_new→marker window), so nothing depends on scheduler timing.

RED evidence (cargo test --test apply_adversarial): 8 passed; 12 FAILED.

Genuinely RED — the §8/§6 adversarial hardening does not exist yet:
  f1  (containment + symlink aliases → OutputWouldModifyInput)
  f2  (pre-existing output/staging → OutputAlreadyExists)
  f6  (index inside tree → ApplicationIndexInsideTree)
  f10 (output vs lock/tmp/index collisions → OutputCollidesWithIndexArtifacts)
  f11 (hardlinked index → ApplicationIndexHardLinked)
  f12 (late temp occupant → pre-commit ApplyIoError, occupant untouched)
  f13 (reserved staging namespace → OutputNameReserved)
  c13 (real second index at the temp name → ApplicationIndexTempExists,
       never unlinked; today it is silently truncated)
  c15 (real second index at the lock name → ApplicationIndexLockPathOccupied)
  k10 (duplicate JSON keys → NonCanonicalCorpusFile via a distinct
       duplicate-rejecting pass; Value comparison cannot see them)
  k11 (staged tree corruption → OutputPreflightInconsistent via the staged
       tree-agreement re-run)
  r4  (orphan output after a commit failure must make a retry refuse
       OutputAlreadyExists, not fail as a late I/O error)

Already green — characterization of behaviour the GREEN-A core plus the
fault fixtures already provide (no new public API; passing before commit
per the repo's characterization rule): f4, f7, f8, f9, c11, c12, c14, c16.
They are committed here to pin those §14 laws against regression while the
RED cases above are closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…ot see

Fixture-only correction discovered while closing RED-B: serde's derived
ChunkMeta deserializer already rejects a duplicated KNOWN struct field at
parse time, so that variant refuses even earlier than §10.3 — at step-3
tree agreement, as CorpusTreeDisagreement, before the rewrite path exists.
The distinct duplicate-rejecting pass therefore has exactly one residual
to guard: a duplicate inside an UNKNOWN member, which the tolerant derive
skips wholesale and Value comparison cannot see (last wins).

K10 is split accordingly: (i) a duplicate inside an unknown member must
refuse NonCanonicalCorpusFile with the duplicate named — proving the
distinct pass runs and runs before the round-trip guard; (ii) a duplicated
known field is pinned as the even-earlier step-3 refusal. Fail-closed both
ways; no duplicate is ever silently laundered. No contract law changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
GREEN for the 12 RED-B cases (plus the 8 pinned characterizations), and
exactly the accepted §6 step-1 order and §8 semantics:

Step 1, in order: index resolves to an existing regular file (before
canonicalization and the lock); nlink == 1 or ApplicationIndexHardLinked;
coordination names derived from the canonical index only; output/staging
resolved as canonical parent + final component; collisions with the
canonical index / lock / temp paths refused before lock acquisition
(OutputCollidesWithIndexArtifacts); the reserved .<name>.apply-staging
namespace refused for outputs (OutputNameReserved); output/staging
pre-existence (OutputAlreadyExists); containment against the resolved
corpus root both ways (OutputWouldModifyInput); canonical index equals-or-
inside any tree root (ApplicationIndexInsideTree); the distinct curated-
manifest hard guard; then lock acquisition; then — only under the held
lock — the temp inspection (ApplicationIndexTempExists), so a live
writer's step-12 temp is unreachable for a non-holder.

Lock: create_new + ownership marker; contention classification is
prefix-closed (empty, partial, or complete marker → ApplicationIndexLocked;
anything else → ApplicationIndexLockPathOccupied) so live ownership is
never misclassified and a real second index named .foo.lock is never
deletable by recovery.

Commit: the temp write is an atomic no-clobber create_new — the step-1
absence check is a fail-fast courtesy, not the safety argument; a late
occupant yields a pre-commit ApplyIoError and is left untouched.

Step 10: the §4.2 tree-agreement law is re-run over the staged tree
(staged root manifest ↔ staged chunk files) before fingerprinting, the
single preflight, and the report, so a write that missed one affected
chunk file can never publish (K11).

§10.3: duplicate-key rejection as a distinct native serde visitor pass
(NoDupKeys) over touched files, run before the round-trip guard — Value
comparison cannot prove duplicates absent, and the derive parse already
refuses duplicated known fields at step 3.

Publication stays plain rename (honestly non-no-clobber, §8.1): compliant
overlap is impossible via the reserved namespace + atomic staging
create_dir; the external empty-directory residual remains exactly as the
contract scopes it.

Evidence: full crate suite green — 45 (frozen Slice 1) + 27 + 16 + 20;
clippy --all-targets clean under deny(all); fmt --check clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
Phase-8 falsification pass over the implementation. Fourteen mutation
targets were probed against the 63-case matrix; four survived with no
killing test, and each gets a dedicated witness here (characterization —
green before commit, no new public API):

1. removing the §5.1 index schema-identity check
   → witness_unsupported_index_schema_refuses;
2. letting prev == null pass §7.2 relation (1) against a non-empty head
   → witness_null_prev_digest_against_nonempty_head_refuses;
3. dropping deny_unknown_fields from the index RECORD type
   → witness_index_record_foreign_field_refuses;
4. dropping deny_unknown_fields from the report type
   → witness_published_report_is_strict_on_the_wire.

Probed-and-already-killed (killer in parentheses): already-applied
precedence over the chain (C3); temp inspection under the lock (C14);
prefix classification both ways (C15/C16); no auto-deletion of stale locks
(C11/C12); create_new temp no-clobber (F12); hardlink refusal (F11);
reserved output namespace (F13); coordination collisions (F10);
commit-point result semantics (R4, F4c/d, F9a); raw-copy preservation
(K2); duplicate-key guard (K10); staged tree re-check (K11); report/index
digest binding (R1/R2).

Survived-by-unreachability, documented rather than tested: §7.2 relation
(3) (entailed by relation (2) plus the step-5 fingerprint proof — the
contract itself notes it is stated for attribution); the
CuratedManifestPathNotDistinct hard guard (structurally unreachable under
the fixed v1 path, per §12); corpus-inside-output containment (an output
that contains an existing corpus necessarily exists itself and refuses
OutputAlreadyExists first, per the §6 order).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
Docs-only closure candidate for the Slice-2 implementation:

- docs/audit/2026-08-slice2-apply-implementation.md — the implementation
  evidence: implemented API; the full unsquashed RED→GREEN commit
  sequence with kinds; the 63/63 §14 matrix (case → test → RED commit →
  result, characterizations marked); refusal coverage (23/24 new typed
  refusals exercised + the structurally unreachable hard guard documented,
  11/11 Apply-reachable Slice-1 refusals exercised, 3 ledger-side members
  proven intentionally unreachable); adversarial/fault-injection results;
  the three implementation-time findings; and the validation matrix
  (isolated crate 112/0, workspace 1535/0, clippy -D warnings, fmt, doc,
  MSRV 1.92).
- song-curation/README.md — truthful lifecycle update: title covers
  Slices 1–2, a Slice-2 section describes the apply module, and the
  out-of-scope list now names what actually remains gated (Slice 3, the
  pilot, real-corpus labeling). Slice-1 sections untouched.

Explicitly preserved distinctions: contract acceptance (recorded at
bad7b44 against 47e734c) is NOT implementation acceptance — this branch is
a review candidate and marks nothing ACCEPTED/CLOSED/FROZEN; Slice 3, the
controlled pilot, and any real-/full-corpus labeling remain BLOCKED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e3b14a6e-7af4-4428-a203-032905cb7633


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Owner Author

Hostile implementation review on exact head bbc79288f66e8633a3f3887eeface6632e04ac55: REVIEW FAILED — implementation acceptance blocked.

I reviewed the implementation against the independently accepted contract artifact 47e734cfbf1a6bd90c1bd2a035cdc68692378e96, not against the evidence summary. The commit graph is intact (9 commits ahead of base, unsquashed), scope is confined to the isolated song-curation/ crate plus its evidence doc, and the current GitHub Actions run is green. The core ordering also survives review: lock before temp inspection and parsing, literal verify_plan, already-applied before chain, and the step-11/12 commit point are in the accepted order.

However, I found three implementation blockers and one acceptance-evidence blocker. None requires reopening the accepted contract; all are implementation/test defects relative to it.

1. BLOCKER — reserved-area shape law does not actually reject every foreign entry

Accepted §4.2 / §6 step 3 is stronger than the current implementation: when song-curation/ exists, the only permitted entries are the two tool-owned proof artifacts as regular files directly at the reserved root; any subdirectory or nested entry is foreign.

Current walk() recursively descends directories but never records directory entries themselves, and check_reserved_shape() checks only the relative names that walk() returned. Consequences:

  • an empty <corpus>/song-curation/extra/ is completely invisible and passes;
  • the allowed names are not checked to be regular files, so a symlink / other non-regular entry at song-curation/manifest.json or song-curation/apply-report.json can satisfy the name-only test.

K12 does not kill the first defect: its nested-directory half creates extra/x.json, so the file is what gets detected; it never tests an empty foreign subdirectory. This violates the contract's recursive shape law and makes the evidence claim for K12 too weak.

Required repair: add failing witnesses for empty subdirectory and allowed-name non-regular entry, then make the shape check inspect directory entries/file types rather than infer shape from the file-only corpus walk. No refusal or spec change is needed: both are CorpusTreeDisagreement exactly as accepted.

2. BLOCKER — pre-existing non-regular lock occupant is not classified at the lock boundary

On create_new(lock)AlreadyExists, acquire_lock() immediately does fs::read(lock_path) and only then performs prefix classification. That assumes every pre-existing occupant is a readable regular file.

The accepted step-1 law says pre-existence is classified at the lock boundary: marker-prefix content → ApplicationIndexLocked; anything else/unproven → ApplicationIndexLockPathOccupied. §12 explicitly reserves ApplyIoError for lock-acquisition I/O causes other than pre-existence.

Concrete counterexamples:

  • a directory at .foo.lock produces AlreadyExists, then the read fails, so the run returns ApplyIoError instead of ApplicationIndexLockPathOccupied;
  • a FIFO/special file is worse: reading it can block, contradicting the fail-closed/no-wait coordination protocol.

C15 only covers a regular second-index file and therefore misses this class.

Required repair: classify file type without following/reading unsafe non-regular occupants; only read content when the occupant is a regular file for which content classification is meaningful. Non-regular/unproven occupants must fail closed as ApplicationIndexLockPathOccupied and remain untouched. Add a deterministic witness at least for a directory occupant; do not add a new refusal.

3. BLOCKER — marker-publication failure can swallow a failed lock release

After create_new(lock) succeeds, the lock has been acquired. If lock:after_create or the marker write_all fails, acquire_lock() does best-effort remove_file(lock_path) via let _ = ... and returns the I/O refusal directly. apply() treats every acquire_lock error as pre-acquisition and returns it with lock_release_warning: None.

If that cleanup/remove also fails, the stale lock remains but the release failure is silently discarded. Accepted §8.2 explicitly fixes the observable shape for this state: refusal stays the primary outcome and failed release is attached as the orthogonal lock-release warning. Release failure must never disappear merely because the primary failure happened during marker publication.

Required repair: preserve the distinction between “lock was never acquired” and “lock acquired, then marker publication failed”; route the latter through the same release-warning semantics as every other post-acquisition exit. Add a deterministic fault witness for primary marker-write failure + release/cleanup failure. No taxonomy change is needed.

4. ACCEPTANCE-EVIDENCE BLOCKER — C16 is not the preregistered live-partial-marker case

The evidence doc claims §14 63/63, but current C16(a) observes an empty marker: the hook runs immediately after create_new, nested Apply observes zero bytes, and the assertion explicitly says “an empty prefix is a Griff lock”. C16(b) covers a non-empty partial marker only as stale/crash debris.

The accepted C16 specifically requires a non-empty partial marker in both manifestations: (a) observed live by a concurrent applier while marker publication is incomplete; (b) left permanently by a crash. That distinction was the final r6→r7 contract blocker and is load-bearing evidence, not decorative wording.

The implementation's prefix check appears capable of classifying a live partial marker correctly, but the required witness is absent, so 63/63 is presently overstated. The existing lock:after_create hook is sufficient to make the live half exact: write a non-empty strict prefix at the held lock path inside the hook before invoking the nested second Apply, then allow the first writer to continue. Update the evidence only after the exact case is green.

What held

I did not find a reason to reopen 47e734c. The following survived this pass: the 12-step ordering around parse/index/verify/already-applied/chain/authority; literal Slice-1 verify_plan reuse; the single replay attribution accommodation; hardlink refusal; under-lock temp inspection; create_new temp commit; raw-copy/touched-file preservation split; duplicate-key pass; staged tree re-read; the single real song_holdout_preflight; commit-point primary-outcome semantics in the normal step-12 paths. I also did not promote an artificial curated-manifest-corruption scenario to a blocker: the accepted §5.3 equality is established by construction plus the real preflight, and the production construction satisfies that law.

The public apply::fault::{set, clear} harness is a minor surface/documentation smell because the evidence calls it “test harness, not contract surface”, but I am not blocking acceptance on that alone.

Required history discipline

Please repair this as evidence, not as a flattened patch:

  1. tests-only RED witnesses for the reserved-area and non-regular-lock defects;
  2. fixture-only support if needed for the marker-write + release-failure double fault;
  3. tests-only RED for that result-shape defect;
  4. GREEN implementation repair;
  5. exact C16 live non-empty-partial witness / test correction;
  6. docs/evidence update with the corrected matrix statement and new SHAs.

Keep the existing 9 commits intact; do not squash/rewrite the prior RED→GREEN record.

State at exact bbc7928: IMPLEMENTATION REVIEW FAILED / DO NOT MERGE / DO NOT RECORD ACCEPTANCE. Contract acceptance at 47e734c remains valid; Slice 3, controlled pilot, and real/full-corpus labeling remain blocked.

claude added 5 commits August 20, 2026 16:05
Tests-only. Preregisters the first two defects from the hostile
implementation review of bbc7928 (PR #191), both fixable inside the
accepted law — the expected refusals are exactly the ones §12 already
assigns:

- review1_empty_foreign_reserved_subdir_refuses: an EMPTY foreign
  subdirectory in the reserved area is invisible to the file-only walk and
  currently passes; §4.2 admits only the two tool-owned proof artifacts as
  regular files at the reserved root, so a directory entry is foreign even
  when empty → CorpusTreeDisagreement naming the entry.
- review1_reserved_allowed_name_must_be_a_regular_file: an allowed NAME is
  not enough — a symlink at song-curation/manifest.json currently passes
  the name-only test → CorpusTreeDisagreement naming the entry.
- review2_directory_lock_occupant_classifies_occupied: a pre-existing
  NON-REGULAR lock-path occupant (a directory) currently falls through
  fs::read into ApplyIoError; pre-existence must be classified at the lock
  boundary → ApplicationIndexLockPathOccupied, occupant untouched, and no
  read of unproven non-regular occupants (a FIFO could block the no-wait
  protocol).

RED evidence: 0 passed; 3 failed (review1 ×2 currently APPLY SUCCEEDS
where the law refuses; review2 returns ApplyIoError).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…ng survives marker failure

Tests-only. After create_new succeeds the lock IS acquired, so a marker-
publication failure is a post-acquisition exit and §8.2's observable
result shape applies in full: the I/O refusal stays the primary outcome
and a failed release attaches as the orthogonal LockReleaseWarning —
never silently discarded by a best-effort remove. The witness drives both
manifestations with the existing fault points (no new fixture needed):

- double fault (lock:after_create + lock:release both fail): primary
  ApplyIoError, warning Some, stale lock remains for §8.2 recovery;
- single fault (marker fails, release succeeds): warning None, no stale
  lock.

RED evidence: 0 passed; 1 failed — current code returns warning: None on
the double fault and removes the lock through an untracked cleanup path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…ted law

GREEN for the four RED review witnesses. No refusal added, no ordering
changed, no contract law touched.

1. Reserved-area shape (§4.2 / §6 step 3) is now inspected at the
   directory-entry level: check_reserved_shape reads the reserved root
   itself, so an EMPTY foreign subdirectory is foreign (a file-only walk
   could not see it), and the two allowed names must be REGULAR FILES —
   a symlink or any other non-regular type at song-curation/manifest.json
   or apply-report.json refuses. Both are CorpusTreeDisagreement exactly
   as the accepted taxonomy assigns.

2. Lock-boundary classification no longer assumes a readable regular
   file: classify_lock_occupant stats without following first; a
   non-regular occupant (directory; FIFO — which a read could block on,
   breaking the no-wait protocol), an unstatable or unreadable occupant,
   or non-prefix content all fail closed as
   ApplicationIndexLockPathOccupied and are never read or touched.
   Prefix classification is reached only for regular readable files;
   ApplyIoError stays reserved for non-pre-existence causes, per §12.

3. Lock acquisition is split at the true acquisition point: create_lock
   (create_new + occupant classification — a failure here means the lock
   was never acquired, warning channel n/a) and publish_marker (the
   marker write into the already-acquired lock). Every post-create exit —
   marker-publication failure included — now releases through the one
   release_lock channel, so a failed release always surfaces as the
   orthogonal §8.2 LockReleaseWarning instead of vanishing in a
   best-effort remove; the primary refusal is preserved unchanged.

Evidence: crate suite 116/0 (45 frozen Slice-1 + 71 Slice-2, including
the four review witnesses now green); clippy deny(all) clean; fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…arker case

Evidence correction (review blocker 4): the accepted C16 requires a
NON-EMPTY partial marker in BOTH manifestations, and the live half
previously observed only the empty state right after create_new. The
lock:after_create hook now materializes a non-empty strict prefix of the
marker at the held lock path, asserts the observed bytes really are that
prefix, and only then runs the second applier inline — which must classify
ApplicationIndexLocked, never LockPathOccupied. The first writer then
completes its marker publication over the same handle and commits. The
crash half (permanent non-empty partial + provenance-gated recovery) was
already exact and is unchanged.

Green on the repaired implementation; the 63/63 claim becomes true only
as of this commit and is restated in the evidence update that follows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…on evidence

Appends the repair-round section: the four review blockers, the RED→GREEN
repair commits (8de362a, 9d505cd, 987d0a3, 92c97d6), the corrected matrix
statement (63/63 holds only as of 92c97d6 — the prior claim was
overstated, as the review found), updated totals (crate 116/0, clippy,
fmt, MSRV re-verified), and the unchanged acceptance state: implementation
acceptance pending independent re-review; contract acceptance at 47e734c
unaffected; Slice 3 / pilot / corpus labeling still BLOCKED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn

Copy link
Copy Markdown
Owner Author

Repair round 1 (5aca93ad6f7b8b9137d278d5ce95caa7786f306c) — all four review blockers addressed under the mandated history discipline. The original nine commits are untouched (no squash/rewrite); the repairs are five new commits in the required RED-witness → GREEN → evidence order. No contract law changed; every expected refusal is the one §12 already assigns.

Blocker RED witness GREEN / correction
1 — reserved-area shape law incomplete (empty foreign subdir invisible to the file-only walk; allowed names checked by name only) 8de362a: review1_empty_foreign_reserved_subdir_refuses, review1_reserved_allowed_name_must_be_a_regular_file — RED evidence: both wrongly applied 987d0a3: check_reserved_shape inspects the reserved root at the directory-entry level — an empty subdirectory is foreign, and manifest.json / apply-report.json must be regular files (symlink_metadata, no following); both refuse CorpusTreeDisagreement naming the entry
2 — non-regular lock occupant fell through fs::read into ApplyIoError 8de362a: review2_directory_lock_occupant_classifies_occupied — RED evidence: returned ApplyIoError 987d0a3: classify_lock_occupant stats before ever reading; a non-regular (directory/FIFO — never read, so nothing can block the no-wait protocol), unstatable, or unreadable occupant fails closed as ApplicationIndexLockPathOccupied and stays untouched; prefix classification is reached only for regular readable files; ApplyIoError stays reserved for non-pre-existence causes
3 — marker-publication failure swallowed a failed release 9d505cd: review3_marker_failure_with_release_failure_keeps_the_warning — double fault (marker + release) and single fault; RED evidence: warning was None on the double fault 987d0a3: acquisition split at the true acquisition point — create_lock (never-acquired failures, no warning channel) vs publish_marker (post-acquisition); every post-create exit now releases through the single release_lock §8.2 channel, so a failed release always attaches as the orthogonal warning with the primary refusal preserved
4 — C16(a) observed an empty, not a non-empty, live partial marker 92c97d6: the lock:after_create hook materializes a non-empty strict prefix at the held lock path, asserts the observed bytes are exactly that prefix, then runs the inline second applier (must classify ApplicationIndexLocked); the first writer completes marker publication and commits. The crash half was already exact and is unchanged

Evidence update: 5aca93a appends the repair-round section to docs/audit/2026-08-slice2-apply-implementation.md, including the corrected matrix statement — 63/63 holds as of 92c97d6; before it the claim was overstated, exactly as the review found. New totals: isolated crate 116 passed / 0 failed (45 frozen Slice-1 + 71 Slice-2, including the four review witnesses); crate clippy deny(all) clean; fmt clean; cargo +1.92 check --all-targets re-verified clean. The repairs touch only the isolated crate; workspace results are unaffected.

State: implementation acceptance pending independent re-review at exact head 5aca93a. Not merged, not self-accepted; contract acceptance at 47e734c unaffected; Slice 3 / controlled pilot / real-corpus labeling remain BLOCKED.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Independent implementation re-review on exact head 5aca93ad6f7b8b9137d278d5ce95caa7786f306c: REVIEW FAILED — one remaining implementation blocker.

The four findings from the previous review are genuinely repaired:

  1. reserved-area empty subdirectories and allowed-name non-regular entries now refuse as CorpusTreeDisagreement;
  2. pre-existing non-regular .lock occupants are classified without reading them, so directory/FIFO-style occupants cannot fall into ApplyIoError or block the no-wait lock protocol;
  3. marker-publication failure is now correctly post-acquisition and every such exit flows through the single release_lock warning channel;
  4. C16(a) now exactly witnesses a live non-empty strict marker prefix, so the preregistered 63/63 claim is no longer relying on the prior empty-prefix approximation.

The repair history is also valid evidence: bbc7928 -> 5aca93a is exactly five appended commits, the two RED commits are tests-only and reproduce the old failures, 987d0a3 is the GREEN repair, 92c97d6 is the exact C16 evidence correction, and 5aca93a is docs-only. Current GitHub Actions run is green.

Remaining BLOCKER — the reserved area is shape-checked only after the recursive corpus walk, so it is not actually excluded from enumeration

Accepted §4.2 / §6 step 3 says the reserved song-curation/ subtree is excluded from corpus-content enumeration and independently shape-checked: only the two proof artifacts as regular files directly at its root are allowed; any subdirectory/nested/non-regular entry is foreign.

Current load_snapshot does the opposite order:

  1. walk(corpus, corpus, &mut files) recursively traverses the whole tree;
  2. only after that does it call check_reserved_shape(&reserved_root);
  3. reserved paths are filtered out only after both operations.

walk() uses Path::is_dir(), which follows directory symlinks. Therefore a foreign reserved-root directory/symlink is traversed before the shape law gets a chance to refuse it. This is more than wasted work:

  • song-curation -> <external directory> causes Apply to enumerate outside the corpus even though the contract says that subtree is excluded;
  • song-curation -> <corpus root> (or a loop beneath the reserved area) can recurse through the alias repeatedly until a path-length / recursion failure, surfacing ApplyIoError or nontermination-like behaviour instead of the required immediate CorpusTreeDisagreement for the foreign reserved entry;
  • an arbitrarily large foreign tree under song-curation/extra/ is recursively walked even though the immediate extra entry is already sufficient to refuse under the accepted shape law.

The new witnesses cover an empty real subdirectory and a symlink file at an allowed name, but they do not cover a reserved-root / nested directory symlink that Path::is_dir() follows before check_reserved_shape.

Required repair is still entirely inside accepted 47e734c: make the corpus walk skip the reserved root before descending into it, and shape-check that root independently at directory-entry level. Add a witness that proves a reserved directory symlink/foreign subtree is refused as CorpusTreeDisagreement without being traversed. A safe deterministic fixture is preferable to an actual self-loop; a fault/trace hook proving “walk never enters reserved root” is fine if needed. No new refusal, schema, or contract change is required.

Non-blocking cleanup

The PR's actual head is 5aca93a, but its body still says Head: bbc7928 and still shows the pre-repair 112/0 validation summary / original 9-commit sequence. The evidence document is current, so I am not treating this as a contract blocker, but update the PR body after the final repair so the review surface does not point at a stale candidate.

State at exact 5aca93a: IMPLEMENTATION REVIEW FAILED / DO NOT MERGE / DO NOT RECORD ACCEPTANCE. Contract acceptance at 47e734c remains valid; Slice 3, controlled pilot, and real/full-corpus labeling remain blocked.

claude added 4 commits August 21, 2026 02:06
…tnesses

Re-review of repair round 1 found one remaining blocker: the reserved
subtree is excluded from enumeration only AFTER the recursive walk has
already traversed it (Path::is_dir() even follows a directory symlink at
the reserved root). To witness that defect deterministically, the walk
gains a trace point that fires before every descent below the corpus
root. Pure test harness, same posture as the fault registry itself:
production registers no hook, so the point is an inert no-op.

Fixture-only: no behavioural change; isolated crate suite remains
116 passed / 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…hout traversal

Re-review blocker on 5aca93a: load_snapshot runs the full recursive walk
BEFORE check_reserved_shape, and the walk's Path::is_dir() follows
directory symlinks, so a foreign reserved subtree (or a reserved root
symlinked to an external directory) is traversed before the §4.2 / §6
step-3 shape law can refuse it. The accepted contract excludes the
reserved subtree from corpus-content enumeration; the immediate
reserved-root entry is already sufficient to refuse.

Two witnesses, both armed with the walk:descend trace hook so traversal
is observable (the fixture corpus has no legitimate subdirectory):

- rereview_foreign_reserved_subtree_refused_without_traversal:
  song-curation/extra/deep/foreign.json must refuse as
  CorpusTreeDisagreement naming the immediate entry, never descending.
- rereview_reserved_root_symlink_refused_without_traversal:
  song-curation -> <external dir> must be classified no-follow as 'not
  a directory', never entering the external target.

RED evidence (tests-only; cargo test --test apply_review_repairs):
both FAILED with the identical defect signature —
  ApplyIoError { path: ".../corpus/song-curation", op: "walk",
                 detail: "injected fault" }
i.e. the walk descended into / followed the symlinked reserved root.
Expected instead: the shape law's own CorpusTreeDisagreement with the
trace hook never reached. 2 failed / 0 of the new passing; the prior
116 tests unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…y descent

GREEN for the round-2 re-review blocker. The recursive corpus walk now
computes each entry's relative name FIRST and skips the reserved root
before any type check — Path::is_dir() follows directory symlinks, so
even asking the type would traverse a symlinked reserved root. The
reserved subtree is therefore excluded from corpus-content enumeration
exactly as §4.2 / §6 step 3 require, and check_reserved_shape stays the
single, no-follow, directory-entry-level classifier of that root:

- a foreign subtree under song-curation/ refuses on its immediate
  root entry (CorpusTreeDisagreement) without one descent into it;
- song-curation -> <external dir> refuses as 'not a directory' via
  symlink_metadata without ever entering the external target — no
  enumeration outside the corpus, no alias loops, no path-length
  degradation into ApplyIoError;
- the post-walk is_reserved filter remains only as a redundant
  invariant.

No refusal, schema, ordering, or contract change; both round-2
witnesses flip to the shape law's own refusal with the walk:descend
trace hook proving zero descents (the fixture corpora have no other
subdirectory).

Isolated crate suite: 118 passed / 0 failed (45 frozen Slice-1 +
73 Slice-2). Crate clippy deny(all) clean; fmt clean;
cargo +1.92 check --all-targets clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn
…on evidence

Appends the round-2 section: the re-review confirmed the four round-1
repairs and failed on one remaining blocker (reserved subtree traversed
before the shape law); commits 15-17 repair it as evidence (FIXTURE
trace point, tests-only RED, GREEN walk exclusion). 63/63 unchanged;
crate suite 118/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kc6r6PhKQCNqArwkijUnzn

Copy link
Copy Markdown
Owner Author

Repair round 2 (54639fc) — the remaining re-review blocker addressed under the mandated history discipline. The prior 14 commits are untouched; the repair is four appended commits in FIXTURE → RED → GREEN → DOCS order. No contract law changed; the refusals involved are the ones the accepted taxonomy already assigns.

Blocker Repair
Reserved subtree shape-checked only after the recursive walk — load_snapshot ran walk() over the whole tree before check_reserved_shape, and Path::is_dir() follows directory symlinks, so a foreign reserved subtree was traversed (a symlinked reserved root even enumerated outside the corpus; alias loops could degrade into ApplyIoError/path-length failures) before the §4.2 / §6 step-3 law could refuse 94e5c38 FIXTURE: walk:descend trace point, firing before every descent below the corpus root — inert in production, it makes "this subtree was never traversed" observable. 60fc228 RED: two armed witnesses over corpora with no legitimate subdirectory — a foreign subtree song-curation/extra/deep/… and song-curation -> <external dir>; RED evidence in the message: both FAILED with the identical defect signature ApplyIoError { path: ".../corpus/song-curation", op: "walk", detail: "injected fault" }, i.e. the walk descended into / followed the symlinked reserved root. 96ca755 GREEN: the walk computes each entry's relative name first and skips the reserved root before any type check or descent (is_dir would follow a symlink merely to answer); check_reserved_shape stays the single, no-follow, directory-entry-level classifier of that root. Both witnesses flip to the shape law's own CorpusTreeDisagreement — the foreign subtree refused on its immediate root entry, the symlinked root refused as "not a directory" via symlink_metadata — with the trace hook never reached. The post-walk is_reserved filter remains only as a redundant invariant.

The chosen witness mechanism is the trace-hook option the review explicitly allowed ("a fault/trace hook proving 'walk never enters reserved root' is fine"): the fixture corpora contain no subdirectory other than the planted reserved subtree, so an armed always-fail walk:descend hook means any descent at all degrades the run — a clean CorpusTreeDisagreement is possible only if the reserved root was never entered. This avoids an actual self-loop fixture entirely.

Non-blocking cleanup done as requested: the PR body now describes the real head (54639fc), the full 18-commit sequence across both repair rounds, and the current validation matrix.

Evidence: 54639fc appends the round-2 section to docs/audit/2026-08-slice2-apply-implementation.md. §14 matrix unchanged — 63/63 continues to hold (round-2 witnesses are additional, like round 1's). New totals: isolated crate 118 passed / 0 failed (45 frozen Slice-1 + 73 Slice-2); crate clippy deny(all) clean; fmt clean; cargo +1.92 check --all-targets clean. Only the isolated crate is touched; workspace results unaffected.

State: implementation acceptance pending independent re-review at exact head 54639fc. Not merged, not self-accepted; contract acceptance at 47e734c unaffected; Slice 3 / controlled pilot / real-corpus labeling remain BLOCKED.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Independent implementation re-review on exact head 54639fc8bb2c993211ca8ca13e8e7fee4765f6e1: IMPLEMENTATION REVIEW PASS — no remaining contract-level or implementation blockers found.

This re-review was against the independently accepted contract artifact 47e734cfbf1a6bd90c1bd2a035cdc68692378e96 and includes the full repair history, not only the final diff.

Round-2 blocker closure

The remaining §4.2 / §6 step-3 defect is closed correctly. walk() now computes the relative entry name before any type query and skips the exact reserved root song-curation before Path::is_dir() or descent. The reserved root is then classified independently by check_reserved_shape using no-follow metadata and directory-entry inspection. Therefore:

  • a foreign subtree below song-curation/ is never corpus-enumerated before refusal;
  • a reserved-root symlink to an external directory is never followed;
  • an alias loop at the reserved root cannot degrade into recursive walk / path-length ApplyIoError;
  • the accepted refusal remains CorpusTreeDisagreement, with no taxonomy or ordering change.

The evidence is load-bearing and valid: 94e5c38 is fixture-only; 60fc228 is tests-only RED and demonstrates both old failures via the walk:descend trace hook; 96ca755 is the GREEN repair; 54639fc is docs-only. The two new witnesses prove non-traversal, not merely the eventual refusal.

Previous repair round

I rechecked the four round-1 findings while reviewing the new flow. They remain closed: recursive reserved-area shape including empty subdirectories/non-regular allowed names; non-regular lock occupants are classified without unsafe reads; marker-publication failure preserves the §8.2 release-warning channel after true acquisition; C16(a) is the exact live non-empty partial-marker case.

Head-wide evidence

  • PR feat: ADR-0033 Slice 2 — transactional Apply implementation (review candidate) #191 remains open, unmerged, mergeable, exact head 54639fc8bb2c993211ca8ca13e8e7fee4765f6e1.
  • Branch is exactly 18 commits ahead of base 249b585; prior history was not rewritten.
  • Scope remains the isolated song-curation/ crate plus its audit document; no production/workspace/ADR/Swang scope creep appears in the diff.
  • The preregistered §14 matrix is 63/63 as of 92c97d6; six review-repair witnesses are additional.
  • The documented CuratedManifestPathNotDistinct non-exercise is acceptable: the accepted contract itself marks that v1 guard structurally unreachable, so it is not an untested reachable refusal.
  • Current GitHub Actions run on this exact head is completed successfully, 14/14 jobs, including check and msrv.
  • The public apply::fault harness remains the previously noted minor surface/documentation smell; it is not a blocker and does not alter production runs unless explicitly armed.

I found no reason to reopen 47e734c, no missing refusal, no schema/order deviation, and no remaining witness mismatch.

Substantive decision: implementation at exact SHA 54639fc8bb2c993211ca8ca13e8e7fee4765f6e1 is independently ACCEPTED on review merits. Per the repository's established governance pattern, merge itself is not the acceptance act; record the implementation acceptance separately in docs/decisions.log.md against this exact reviewed SHA after merging the PR without changing the reviewed head/history.

That acceptance opens no later scope: Slice 3, the controlled pilot, and any real/full-corpus labeling remain BLOCKED behind their own gates.

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