feat: ADR-0033 Slice 2 — transactional Apply implementation (review candidate) - #191
feat: ADR-0033 Slice 2 — transactional Apply implementation (review candidate)#191PhysShell wants to merge 18 commits into
Conversation
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
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 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. Comment |
|
Hostile implementation review on exact head I reviewed the implementation against the independently accepted contract artifact 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 entryAccepted §4.2 / §6 step 3 is stronger than the current implementation: when Current
K12 does not kill the first defect: its nested-directory half creates 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 2. BLOCKER — pre-existing non-regular lock occupant is not classified at the lock boundaryOn The accepted step-1 law says pre-existence is classified at the lock boundary: marker-prefix content → Concrete counterexamples:
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 3. BLOCKER — marker-publication failure can swallow a failed lock releaseAfter 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 caseThe evidence doc claims §14 63/63, but current C16(a) observes an empty marker: the hook runs immediately after 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 What heldI did not find a reason to reopen The public Required history disciplinePlease repair this as evidence, not as a flattened patch:
Keep the existing 9 commits intact; do not squash/rewrite the prior RED→GREEN record. State at exact |
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
|
Repair round 1 (
Evidence update: State: implementation acceptance pending independent re-review at exact head Generated by Claude Code |
|
Independent implementation re-review on exact head The four findings from the previous review are genuinely repaired:
The repair history is also valid evidence: Remaining BLOCKER — the reserved area is shape-checked only after the recursive corpus walk, so it is not actually excluded from enumerationAccepted §4.2 / §6 step 3 says the reserved Current
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 Required repair is still entirely inside accepted Non-blocking cleanupThe PR's actual head is State at exact |
…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
|
Repair round 2 (
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 Non-blocking cleanup done as requested: the PR body now describes the real head ( Evidence: State: implementation acceptance pending independent re-review at exact head Generated by Claude Code |
|
Independent implementation re-review on exact head This re-review was against the independently accepted contract artifact Round-2 blocker closureThe remaining §4.2 / §6 step-3 defect is closed correctly.
The evidence is load-bearing and valid: Previous repair roundI 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
I found no reason to reopen Substantive decision: implementation at exact SHA That acceptance opens no later scope: Slice 3, the controlled pilot, and any real/full-corpus labeling remain BLOCKED behind their own gates. |
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
main@249b585· Head:54639fc(after review repair rounds 1–2)song-curation/(isolated non-workspace crate; posture unchanged) + one evidence doc indocs/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):
dfa7e06E0432: unresolved import griff_song_curation::apply.4d311f4f8c9f1c6ce1a69apply::fault): thread-local one-shot hooks, inert in production; lets concurrency cases run the second applier inline on the same thread.bd2a3395cc34b3c9489e1create_newno-clobber temp; hardlink refusal; reserved.apply-stagingnamespace; coordination-path collisions; staged tree-agreement re-run; duplicate-key pass.5bb78fbbbc7928docs/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):8de362a9d505cd987d0a392c97d65aca93aRepair round 2 (re-review of
5aca93a: 1 blocker — reserved subtree traversed before the shape law):94e5c38walk:descendtrace point (inert in production) making "this subtree was never traversed" observable.60fc228song-curation/extra/deep/…; reserved root symlinked to an external directory. Both FAILED withApplyIoError { path: ".../song-curation", op: "walk" }— the walk descended / followed the symlink.96ca755is_dirwould follow a symlink merely to answer);check_reserved_shapestays the single no-follow classifier. Both witnesses flip toCorpusTreeDisagreementwith the hook never reached.54639fc§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 (+CuratedManifestPathNotDistinctimplemented 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 reusedverify_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)
cargo test --workspacecargo clippy --workspace --all-targets -- -D warningsdeny(all))cargo fmt --all -- --checkcargo doc --no-deps(workspace + crate)cargo +1.92 check(crate, all targets)Findings (no contract law changed; none required a STOP)
CorpusTreeDisagreement); the distinct duplicate-rejecting pass guards its real residual — duplicates inside unknown members (K10 split proves both branches). Fail-closed both ways.ApplyIoError { op: canonicalize… }— the single typed I/O boundary; no new refusal invented.CuratedManifestPathNotDistinctguard: 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