Skip to content

docs(adr): propose ADR-0032 — holdout filtering boundary over LoadedChunk - #166

Merged
PhysShell merged 2 commits into
mainfrom
claude/adr-holdout-boundary
Jul 29, 2026
Merged

docs(adr): propose ADR-0032 — holdout filtering boundary over LoadedChunk#166
PhysShell merged 2 commits into
mainfrom
claude/adr-holdout-boundary

Conversation

@PhysShell

@PhysShell PhysShell commented Jul 29, 2026

Copy link
Copy Markdown
Owner

What

Docs-only ADR-0032 (Proposed) fixing the executable holdout boundary between corpus loading and material construction, its authoritative input, and its ownership — the governance step the reachability Phase-0 audit requires before any implementation. The A→B→C→ADR-0031→D modernization arc stays frozen.

Diff: the new ADR + its docs/adr/README.md row. No decisions.log.md change (reserved for ADRs).

Why here

The generator loads Vec<LoadedChunk> — each carrying full ChunkMeta (sha256, song_id, bar_range, track_index) — and immediately folds it into CorpusMaterial via corpus_material (core/src/generation_input.rs:137), discarding provenance into anonymous rhythms/references/gesture. The CLI loader does exactly this with no decision between (cli/src/generation_input.rs:64,68) and never reads a CorpusManifest. So the only place a holdout decision can happen is over Vec<LoadedChunk>, before corpus_material. ADR-0031 gave us song_holdout_preflight, but nothing calls it in a generation path yet.

Decisions (Proposed)

  • Ownership & placementCorpusMode, TargetIdentity, HoldoutError, and the boundary orchestration live in the offline Reachability Lab (isolated lab-style instrument, ADR-0010 precedent). Production CLI/cockpit gain no holdout policy; core generation/rerank unchanged; ordinary generation bypasses the seam. No roadmap stage assigned (audit defers placement).
  • Authoritative input — the boundary consumes a bound LoadedCorpus { manifest, loaded, skipped }, so the complete CorpusManifest (+ optional songs map) reaches the preflight. Single-authority invariant: manifest.chunksLoadedChunk.meta matched by ChunkId; duplicate / missing / provenance-mismatched records typed-refuse (or the lab builds loaded directly from manifest.chunks). Closes the stale-manifest leak.
  • Absence-capable boundary:
    fn prepare_corpus_for_mode(
        corpus: LoadedCorpus, mode: CorpusMode, target: &TargetIdentity,
    ) -> Result<Option<CorpusMaterial>, HoldoutError>
    • NoCorpusOk(None) — genuinely corpus-free (matches existing material: None).
    • LeakyDiagnosticOk(Some(corpus_material(all_loaded, skipped))) — a corpus deliberately supplied unfiltered, named so it can't be mistaken for a holdout.
    • HoldoutTargetSong → preflight → filter → Ok(Some(...)).
  • HoldoutTargetSong — preflight the whole corpus.manifest → typed-refuse on any coverage/identity inconsistency → require a target song_id → exclude every LoadedChunk carrying it → only then compile the rest.
  • TargetIdentity carries per-mode provenance (source_sha256, song_id, bar_range, track_index, projection, eligibility).
  • Bound non-goals — no generation/scoring change, no curation or auto grouping, no post-hoc relabelling of a failed/absent holdout as valid; deterministic, non-mutating; no holdout policy in production CLI/cockpit.
  • First slice = song mode only; file/fragment modes (esp. bar_range == None = whole-source overlap) follow as separate slices.

After acceptance

RED→GREEN: synthetic fixtures prove that, once the slice lands, an uncurated corpus typed-refuses before material construction and a curated fixture excludes every representation of the target song with zero leakage into any channel; then the smallest pure boundary over LoadedCorpus.

🤖 Generated with Claude Code

https://claude.ai/code/session_012T7SRMiXmZe5v1imtrKMPi

…hunk

Docs-only. Fixes the executable boundary between corpus loading and material
construction, plus offline-lab ownership, before any implementation — as the
reachability Phase-0 audit recommends.

Decisions (Proposed):
- The offline Reachability Lab owns CorpusMode and holdout run artifacts; an
  isolated lab-style instrument (ADR-0010 precedent), no roadmap stage
  assigned (audit defers placement).
- Holdout filtering occurs over Vec<LoadedChunk>, before corpus_material and
  before any rhythm/reference/gesture compilation — the only point where full
  provenance (sha256, song_id, bar_range, track_index) survives
  (core/src/generation_input.rs:65,137; cli/src/generation_input.rs:64,68).
- HoldoutTargetSong: preflight the whole corpus (song_holdout_preflight),
  typed-refuse on any coverage/identity inconsistency, require a target
  song_id, exclude every LoadedChunk carrying it, then compile the rest.
- TargetIdentity carries per-mode provenance (source_sha256, song_id,
  bar_range, track_index, projection, eligibility), not a vague source string.
- Bound non-goals: no generation/scoring change, no curation or auto
  grouping, no post-hoc relabelling of a failed holdout as valid.
- First slice = song mode only; file/fragment modes follow separately.

No new roadmap stage number. Not appended to decisions.log.md (reserved for
ADRs). Gates the holdout-wiring implementation PR once accepted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012T7SRMiXmZe5v1imtrKMPi
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds ADR-0032 defining a fail-closed, provenance-preserving holdout filtering boundary before CorpusMaterial construction, with song-level filtering semantics, provenance requirements, scope, consequences, and follow-on sequencing. The ADR index now lists it as proposed.

Changes

Holdout filtering boundary

Layer / File(s) Summary
Boundary and filtering contract
docs/adr/0032-holdout-filtering-boundary.md
Defines the pre-materialization boundary, prepare_corpus_for_mode contract, ordered HoldoutTargetSong behavior, and target identity provenance fields.
Consequences and ADR index
docs/adr/0032-holdout-filtering-boundary.md, docs/adr/README.md
Documents consequences, exclusions, follow-on implementation sequencing, and adds ADR-0032 to the proposed ADR index.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • PhysShell/griff#157: Defines related song_id schema and coverage requirements for song-level holdout semantics.

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the ADR proposal and its main focus on the holdout filtering boundary over LoadedChunk.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/adr-holdout-boundary

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 864977044c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/adr/0032-holdout-filtering-boundary.md Outdated
Comment thread docs/adr/0032-holdout-filtering-boundary.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/adr/0032-holdout-filtering-boundary.md`:
- Around line 88-94: Update the ADR’s synthetic-fixture characterization and
boundary-function sections to describe refusal and end-to-end holdout execution
as expected future behavior after implementation, not current guarantees.
Preserve the distinctions among missing targets, uncurated or inconsistent
sources, unrelated songs, deterministic filtering, untouched metadata, NoCorpus,
and explicitly named leaky diagnostics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2158b5b9-4bb8-4098-bd8b-66c6c6b4f13f

📥 Commits

Reviewing files that changed from the base of the PR and between adf45bb and 8649770.

📒 Files selected for processing (2)
  • docs/adr/0032-holdout-filtering-boundary.md
  • docs/adr/README.md

Comment thread docs/adr/0032-holdout-filtering-boundary.md Outdated
…tense

Address the review's three architectural/documentation holes:

1. Absence-capable result. prepare_corpus_for_mode now returns
   Result<Option<CorpusMaterial>, HoldoutError>. NoCorpus -> Ok(None) (a
   genuinely corpus-free run, matching the existing material: None), distinct
   from a new LeakyDiagnostic -> Ok(Some(unfiltered)). NoCorpus is no longer
   mislabelled a "no-holdout" mode; they are different experiments.

2. Single source of truth. The boundary consumes a bound LoadedCorpus
   { manifest, loaded, skipped } so it can pass the complete manifest (and
   its optional songs map) to song_holdout_preflight. Manifest chunks and
   loaded records must describe the same corpus (match by ChunkId; duplicate
   / missing / mismatched -> typed refusal), closing the stale-manifest leak
   where preflight validates one dataset and filtering executes another.

3. Tense. Current-behavior claims rewritten as post-implementation
   consequences — nothing calls the preflight in a generation path today.

Also states placement explicitly: CorpusMode / TargetIdentity / HoldoutError
and the orchestration live in the isolated lab; production CLI/cockpit gain
no holdout policy; core generation/rerank unchanged; a loader addition only
supplies the lab one authoritative LoadedCorpus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012T7SRMiXmZe5v1imtrKMPi
@PhysShell

Copy link
Copy Markdown
Owner Author

REQUEST CHANGES addressed — ADR-0032 v2 (head c44a89c)

Docs-only revision; all three holes closed and both Codex P1 + the CodeRabbit thread resolved.

1. Absence-capable result / distinct modes. prepare_corpus_for_mode now returns Result<Option<CorpusMaterial>, HoldoutError>:

  • NoCorpusOk(None) — genuinely corpus-free (matches the existing material: None).
  • LeakyDiagnosticOk(Some(corpus_material(all_loaded, skipped))) — a corpus deliberately supplied unfiltered, named so it can't be mistaken for a holdout.
  • HoldoutTargetSong → preflight → filter → Ok(Some(...)).

NoCorpus is no longer a "no-holdout" alias; ordinary generation bypasses the seam.

2. Single source of truth. The boundary consumes a bound LoadedCorpus { manifest, loaded, skipped }, so the complete CorpusManifest (+ optional songs map) reaches song_holdout_preflight. The ADR binds manifest.chunksLoadedChunk.meta by ChunkId — duplicate/missing/mismatched records typed-refuse (or the lab builds loaded directly from manifest.chunks) — so preflight and filtering read the same song_id facts. Closes the stale-manifest leak.

3. Tense. Current-behavior claims rewritten as post-implementation consequences.

Placement stated explicitly: CorpusMode / TargetIdentity / HoldoutError and the orchestration live in the isolated lab; production CLI/cockpit gain no holdout policy; core generation/rerank unchanged; a loader addition only supplies the lab one authoritative LoadedCorpus.

Still docs-only (ADR + README row); no decisions.log.md change.

@PhysShell
PhysShell merged commit 6f04c21 into main Jul 29, 2026
15 checks passed
@PhysShell
PhysShell deleted the claude/adr-holdout-boundary branch July 29, 2026 05:57
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.

1 participant