diff --git a/docs/audit/2026-05-stage-label-reconciliation.md b/docs/audit/2026-05-stage-label-reconciliation.md index 7d8f67e4..b464af10 100644 --- a/docs/audit/2026-05-stage-label-reconciliation.md +++ b/docs/audit/2026-05-stage-label-reconciliation.md @@ -19,7 +19,12 @@ forward. transport refactor · `S3` Guitar Pro import · `S4` phrase boundary detection · `S5` corpus + schema · `S6` rule generator v0 · `S7` graph layer · `S8` preview app · `S9` feedback layer · `S10` CLAP MVP · `S11` region -regeneration · `S12` neural assistance. +regeneration · `S12` neural assistance · `S13` complementary part generation · +`S14` structure controls and metrics · `S15` tonal context and harmonic control. + +S13 and later stages are append-only additions. Their logical dependency order +may place them beside or before an earlier-numbered future stage; existing stage +numbers are never repurposed or renumbered to make the list look prettier. ## Mislabel mapping @@ -43,14 +48,17 @@ baseline**. It is the input to: ## Rules from here -1. New commits use canonical `S0…S12` numbering per the glossary only. +1. New commits use canonical stage numbers per the glossary and this roadmap. 2. A stage is "closed" only when its acceptance criterion (its stage doc) is met, tested, and documented. 3. Earlier mislabeled work is **not** re-closed under its old number; relevant pieces are re-credited to their true stage in the stage docs' "See also". +4. New cross-cutting stages take the next free number. Existing S-numbers are + never reassigned, even when the logical execution order differs. ## See also - [`../glossary.md`](../glossary.md) §0, §17 - [`../SPEC.md`](../SPEC.md) - [`../stages/S0-baseline-and-tests.md`](../stages/S0-baseline-and-tests.md) +- [`../stages/S15-tonal-context-and-harmonic-control.md`](../stages/S15-tonal-context-and-harmonic-control.md) diff --git a/docs/audit/2026-07-symbolic-harmony-and-evolution-research.md b/docs/audit/2026-07-symbolic-harmony-and-evolution-research.md new file mode 100644 index 00000000..fad8ff38 --- /dev/null +++ b/docs/audit/2026-07-symbolic-harmony-and-evolution-research.md @@ -0,0 +1,238 @@ +# Research: symbolic harmony, global paths, and interactive evolution (2026-07) + +Status: accepted as roadmap input; no dependency adoption +Decision: map the useful ideas onto S7/S8/S9/S12/S15 rather than create a +repository-per-stage roadmap + +## Executive verdict + +The strongest common pattern is: + +```text +enumerate feasible local possibilities +→ assign explainable local / transition costs +→ optimise globally +→ return diverse top-k paths +→ let the human choose +→ retain feedback and lineage +``` + +The responsibilities are intentionally split: + +- **S15** defines tonal/harmonic meaning and uncertainty; +- **S7** optimises multi-step paths; +- **S8** displays alternatives and provenance; +- **S9** captures selection and experiments with structural evolution; +- **S12** remains the owner of future neural assistance. + +No reviewed repository is adopted as a production dependency by this decision. + +## Source assessment + +| Source | Value now | Later | What to take | Owner | +|---|---:|---:|---|---| +| [`ekzhang/harmony`](https://github.com/ekzhang/harmony) | 8/10 | 8/10 | layered DP, transition costs, path reconstruction | S7 | +| [`napulen/romanyh`](https://github.com/napulen/romanyh) | 8/10 | 9/10 | alternative harmonisations, k-best global paths, RomanText fixtures | S7 + S15 | +| [`napulen/AugmentedNet`](https://github.com/napulen/AugmentedNet) | 4/10 | 9/10 | decomposed harmonic targets, uncertainty, synthetic labelled examples | S15 + S12 radar | +| [`perfect-shuffle-music`](https://github.com/kroger66/perfect-shuffle-music) | 6/10 concept | 7/10 | human-in-the-loop generations, crossover concept, lineage | S9, surfaced by S8 | +| [`napulen/harmalysis`](https://github.com/napulen/harmalysis) | 4/10 | 7/10 | external harmonic fixture DSL inspiration | S15 | +| [`ekzhang/composing.studio`](https://github.com/ekzhang/composing.studio) | 3/10 | 6/10 | textual playground, live preview/playback, inspectable edits | S8 | +| [`ekzhang/crepe`](https://github.com/ekzhang/crepe) | 5/10 | 6/10 | possible declarative rule engine when rule closure/explanations justify it | S7/S15 radar | + +The ratings describe architectural relevance to `griff`, not general project +quality. + +## 1. `ekzhang/harmony`: global path shape + +The useful lesson is not classical four-part harmony itself. It is the layered +optimisation shape: + +1. enumerate valid states for each musical position; +2. calculate local and adjacent-state costs; +3. use dynamic programming to select a globally coherent path; +4. reconstruct the selected path with an inspectable total cost. + +This is directly relevant to S7's multi-bar candidate chain. Potential Griff +transition terms include: + +- phrase / contour continuity; +- register continuity; +- rhythm continuity or complement; +- repeated-technique penalties; +- playability / fret travel; +- harmonic fit supplied by S15; +- repetition and mud penalties. + +Do **not** copy SATB-specific prohibitions or doubling/resolution rules as +swancore policy. Transfer the algorithmic form; make costs Griff-specific and +explainable. + +The earlier idea of making a new generic `RegisterPlanner` the first client is +superseded: the register track is already accepted and closed after generator +semantics were repaired. Reopen register planning only when a new measured +multi-bar problem justifies it. + +## 2. `napulen/romanyh`: k-best paths + +The important extension is returning several ranked global alternatives rather +than one optimum. For Griff this supports: + +- globally coherent alternatives without seed-only noise; +- several complementary-guitar trajectories; +- controlled register/harmonic alternatives; +- human selection feeding S9; +- explicit diversity constraints and fixed tie-breaking. + +S7 should first extract a concrete layered-path contract from a real multi-bar +client, then add deterministic k-best enumeration. Avoid a universal +`MusicDPGodObject`; specialised state/cost modules share only the small engine. + +## 3. Nápoles ecosystem: harmonic decomposition and fixtures + +### `AugmentedNet` + +Do not introduce its Python/TensorFlow/MusicXML runtime into Griff. Useful ideas: + +- decompose harmonic analysis into tonal centre, mode, chord root, quality, + inversion, and related targets; +- expose distributions/alternatives rather than one unquestioned label; +- create labelled synthetic examples and vary their texture. + +S15 should grow incrementally from its accepted tonic/mode estimate. Full Roman +numeral analysis is not the next task. + +### `harmalysis` and RomanText + +Use a small text language only as an external fixture/debugging surface: + +```text +C: I | vi | IV | V +C: I | V/V | V | I +a: i | VI | III | VII +``` + +Core representation stays typed. The fixture pipeline can generate +transpositions, omissions, inversions, passing tones, pedal textures, and +modulations with known labels for calibration tests. + +### Audio key detection + +Audio chroma/HMM approaches are deferred. Griff currently receives symbolic +MIDI/Guitar Pro evidence; rendering it to audio/chroma and guessing the lost +symbolic information back would be an avoidable information-loss loop. Revisit +only with a real audio-input stage. + +## 4. `perfect-shuffle-music`: concept, not code + +The implementation is too note-array-centric for Griff. Do not port its genome +or crossover code. + +The useful experiment is structural evolution over Griff objects: + +```text +candidate set +→ human selects parents +→ bar/motif/parameter crossover +→ mutation +→ normalisation and validators +→ existing rerank as a safety/quality guard +→ next generation +``` + +Candidate operators for an S9 experiment: + +```rust +pub enum EvolutionOperator { + AlternateBars, + AlternateMotifs, + RhythmFromAContourFromB, + PrefixSuffixCrossover, + ParameterBlend, +} +``` + +Lineage must record parents, operator, mutations, generation, and session. Track +population collapse, strategy diversity, operator survival, and repeated-parent +dominance. This remains an **Evolution Lab** experiment under S9; create a new +stage only if it later becomes a standalone persistent workflow. + +## 5. `composing.studio`: S8 playground direction + +The transferable UI idea is editable text plus immediate visual/audio feedback. +A Griff playground should expose: + +- textual request / constraints / future harmonic fixture input; +- piano roll / tab / playback; +- candidate list and score axes; +- register, novelty, playability, and tonal diagnostics; +- path and transition explanations; +- like/dislike/favourite controls; +- parent/child lineage for the Evolution Lab. + +S8 supplies the surface; it does not own tonal inference, path optimisation, or +preference semantics. + +## 6. `crepe`: parked implementation option + +A Datalog-like rule engine may become useful when Griff has all of: + +- many independently maintained rules; +- recursive graph closure; +- a need to explain derivations; +- frequent rule additions that make imperative orchestration brittle. + +Until then, typed Rust remains simpler. A pleasant macro is not evidence that the +generator needs an expert-system runtime. + +## Roadmap mapping + +### S7 — graph layer + +- concrete layered-path contract from the first multi-bar client; +- global candidate-chain DP/Viterbi; +- deterministic k-best diverse paths; +- later harmonic/complement/cadence clients using S15 states. + +### S8 — preview app / cockpit + +- textual playground; +- candidate, tonal, path, and lineage inspectors; +- UI for S9 feedback/evolution experiments. + +### S9 — human feedback + +- feedback capture and preference reranking first; +- Evolution Lab second; +- diversity/collapse controls before productisation. + +### S12 — neural assistance + +- AugmentedNet/MiniBach remain decomposition and synthetic-data research inputs; +- no runtime model before the existing corpus, baseline, and feedback gates. + +### S15 — tonal context and harmonic control + +- Phase 0 evidence audit: accepted/closed; +- Phase 1 shared tonal core: accepted/closed; +- Phase 2 explicit scoped context: next; +- later calibration, fixture DSL, soft harmonic generation, local context and + cadence. + +## Immediate actions + +1. Maintain S15 as the owner of tonal meaning and confidence. +2. Implement S15 Phase 2 without changing generated output. +3. Write the S7 layered-path design against a concrete multi-bar candidate-chain + client before extracting a generic engine. +4. Add harmonic synthetic-fixture planning to S15 Phase 3/4. +5. Add Evolution Lab to the S9 backlog, not the production API. +6. Add the textual/candidate/tonal/lineage playground to the S8 backlog. + +## Explicit non-decisions + +- no automatic highest-margin track selection; +- no hard inferred-scale pitch whitelist; +- no generic register planner without new evidence; +- no classical harmony rule transplant; +- no neural or Datalog dependency; +- no new Evolution stage yet. diff --git a/docs/stages/S12-neural-assistance.md b/docs/stages/S12-neural-assistance.md index 6b705934..83532c24 100644 --- a/docs/stages/S12-neural-assistance.md +++ b/docs/stages/S12-neural-assistance.md @@ -22,17 +22,38 @@ from scratch". - Models suited to fixed-region control / infilling (e.g. anticipatory / masked-LM style on MIDI tokens). - Inference in Rust via `candle` or ONNX (`ort`); training offline. +- Harmonic-analysis research such as AugmentedNet is useful for **task + decomposition** (tonal centre, mode, chord root, quality, inversion) and + synthetic labelled examples, not as a Phase-1 runtime dependency. +- Symbolic generation projects such as MiniBach remain research references only; + they do not replace the S6/S7 deterministic baseline or S15 calibrated tonal + context. ## Preconditions (hard gate) - Corpus ≥ ~100 phrases; working S6 baseline; S9 feedback integration. - Not started before all three hold (glossary §17.5). +- Any harmonic neural proposal must also compare against the accepted S15 + symbolic estimator/fixtures and preserve uncertainty/abstention rather than + emit one unquestioned label. ## Acceptance criteria - Neural suggestions beat S6/S7 on the quality score in a blind comparison. - Deterministic given a fixed seed and model checkpoint. +- Harmonic assistance, when attempted, beats or complements the S15 symbolic + baseline on a labelled test set and reports calibrated uncertainty. + +## Non-goals + +- No Python/TensorFlow/MusicXML service in the core runtime merely because a + research implementation uses that stack. +- No rendering symbolic input to audio/chroma and guessing its lost semantics + back while native symbolic evidence is available. ## See also +- [`../audit/2026-07-symbolic-harmony-and-evolution-research.md`](../audit/2026-07-symbolic-harmony-and-evolution-research.md) +- [`S9-feedback-layer.md`](S9-feedback-layer.md) +- [`S15-tonal-context-and-harmonic-control.md`](S15-tonal-context-and-harmonic-control.md) - [`../glossary.md`](../glossary.md) §8, §17.5 diff --git a/docs/stages/S13-complementary-part-generation.md b/docs/stages/S13-complementary-part-generation.md index a1fdadc3..2fda7157 100644 --- a/docs/stages/S13-complementary-part-generation.md +++ b/docs/stages/S13-complementary-part-generation.md @@ -22,6 +22,13 @@ ADRs: ADR-0012, ADR-0011 > `ConstrainedRandomWalk` line over a request derived from A; typed > `NonUniformTimeline` on mid-score meter changes). `ModeNotImplemented` is > gone — every `RelationMode` arranges. +> +> Ownership update (2026-07): the original private winner-only key estimate was +> generalised into the accepted shared `core::tonal` layer. S13 keeps the +> backwards-compatible `PartProfile::harmony` projection and complement-specific +> use of it; **S15** owns scoped tonal evidence, uncertainty, calibration, +> harmonic fixtures, and future soft harmonic/cadence policy. **S7** owns any +> later multi-bar global optimisation or k-best complementary trajectories. ## Remaining work (follow-up increments) @@ -42,7 +49,8 @@ ADRs: ADR-0012, ADR-0011 `PartProfile::harmony` (tonic, major/natural minor, `scale_fit` as a fact); B's substitution material becomes A's literal pitch classes *plus* the inferred key's scale, so a sparse part no longer collapses - B onto one pitch per band (2026-06-11). + B onto one pitch per band (2026-06-11). The estimator now delegates to + S15's shared `core::tonal` implementation. > Roadmap note: appended as the next free stage number (append-only, per the > stage-label audit). Logically it sits between the single-part generator (S6) @@ -103,7 +111,7 @@ Generative-first: B is derived from A by rule; no corpus pair mining. `ChunkMeta ## Open questions - Harmonic-compatibility thresholds (allowed coincident intervals) — calibrate - on the corpus. + on the corpus and S15 labelled fixtures. - Default per-axis ratios (e.g. support-layer density factor) before S9 feedback exists to tune them. - Where the part profile lives: extend the feature layer vs a dedicated @@ -111,6 +119,8 @@ Generative-first: B is derived from A by rule; no corpus pair mining. `ChunkMeta ## See also +- [`S15-tonal-context-and-harmonic-control.md`](S15-tonal-context-and-harmonic-control.md) +- [`../audit/2026-07-symbolic-harmony-and-evolution-research.md`](../audit/2026-07-symbolic-harmony-and-evolution-research.md) - [`../glossary.md`](../glossary.md) §8 (Complementary part, Complement relation, Relation mode), §9 (Complement hyperedge), §10 (Relation preference) - [`../adr/0012-complementary-part-generation.md`](../adr/0012-complementary-part-generation.md) diff --git a/docs/stages/S15-tonal-context-and-harmonic-control.md b/docs/stages/S15-tonal-context-and-harmonic-control.md new file mode 100644 index 00000000..4f32abde --- /dev/null +++ b/docs/stages/S15-tonal-context-and-harmonic-control.md @@ -0,0 +1,214 @@ +# S15: Tonal context and harmonic control + +Status: in progress — Phase 0 (evidence audit) and Phase 1 (shared tonal core) +accepted and closed on 2026-07-12; Phase 2 is next +Depends on: S1 (canonical score), S5 (corpus), S6 (rule generator) +Builds on: S13 harmonic-context analysis +Feeds: S6 generation, S7 graph costs, S11 regeneration, S13 complement + +## Goal + +Make tonal and harmonic context explicit, uncertain, scoped, and reusable before +it is allowed to influence generation. Separate observed evidence from inferred +musical meaning; preserve an honest abstention path; never turn one best guess +into a hard seven-note whitelist. + +S15 owns the meaning of tonal/harmonic states. S7 owns global path optimisation +over such states. S8 displays the alternatives and provenance. S9 learns from +human choices among them. + +## Guardrails + +- A real-song key result is a tonal hypothesis for one `EvidenceScope`, not + verified ground truth. +- Confidence thresholds and automatic scope selection require calibration; a + larger margin alone does not prove that one track is the correct reference. +- The observed `PitchClassSet` remains distinct from an inferred scale or tonal + hierarchy. Chromatic passing tones, borrowed notes, and tensions are not + automatically errors. +- `None` / ambiguous context is a valid result. No silent fallback to C major, + the lowest pitch, or the highest-margin track. +- Cadence and generation integration remain frozen until their phase-specific + acceptance gates are met. + +## Phase 0 — evidence audit and diagnostics ✅ + +Accepted and closed. + +- Audited the existing generation input and established that + `PitchMaterial.root` was an anchor derived from the minimum input pitch, not a + tonic. +- Measured `WholeScore`, `Track`, and `Voice` evidence on real and synthetic + inputs. +- Established raw evidence vocabulary: onset counts, duration mass, note count, + and observed pitch range. +- Demonstrated that scope can change the winning tonal hypothesis (including the + Wolf & Bear whole-score vs track conflict). +- Rejected confidence cut-offs inferred from the small diagnostic fixture set. + +Primary record: +[`../audit/2026-07-tonal-context-phase0.md`](../audit/2026-07-tonal-context-phase0.md). + +## Phase 1 — shared evidence/inference core ✅ + +Accepted and closed. + +`core/src/tonal.rs` now provides: + +- `EvidenceScope::{WholeScore, Track, Voice}`; +- `PitchEvidence::measure` with raw integer `onset_counts`, `duration_mass`, + `note_count`, and observed `feature::PitchRange`; +- `estimate_key` returning all 24 major/natural-minor `TonalCandidate`s, + best-first, with per-candidate correlation and `scale_fit`; +- `TonalEstimate::confidence_margin` as winner minus runner-up; +- duration-only KS v1, with onset-count fallback only when total duration mass is + zero. + +`complement::estimate_harmony` delegates to the shared inference core and keeps +its public winner-only projection. Focused validation proved: + +- `HarmonicContext`: 16/16 exact, 0 changed; +- structure consumer: 7/7 byte-identical; +- evidence mapping: 39/39, 0 mismatches; +- histogram additivity: pass; +- 24 finite candidates per non-empty scope: pass; +- generation smoke: 30/30 byte-identical. + +Cloud implementation: `6f9114d` (red), `184b586` (green), `e2c9c7f` (docs), +`af26206` (accepted/closed). Local validation: `bd2c7c8`; archival: +`3993bb0`. + +## Phase 2 — explicit scoped context contract (next) + +Allow generation-facing requests and provenance to carry an optional, explicit +scoped tonal estimate **without changing note selection yet**. + +The exact type is a design output, not pre-decided, but it must preserve: + +- the chosen `EvidenceScope`; +- the ranked estimate or an intentionally compact immutable projection; +- absence / ambiguity; +- deterministic serialisation and replay; +- provenance identifying how the estimate was measured. + +Acceptance: + +- requests without tonal context remain byte-identical to the Phase-1 baseline; +- context is optional and scope is explicit; +- no automatic whole-score/track/voice selection; +- no pitch restriction, reranker-weight change, cadence, or production behaviour + change; +- round-trip and deterministic replay tests cover the new contract. + +## Phase 3 — scope policy and confidence calibration + +### Phase 3A — scope-selection experiments + +Compare explicit policies rather than silently choosing the largest margin: + +- selected/reference track; +- whole score; +- guitar-only subsets; +- weighted combinations of tracks; +- multiple competing scope estimates carried together. + +A repetitive bass pedal or ostinato may yield a strong margin while describing +only one layer, so `argmax(margin)` is not an approved policy. + +### Phase 3B — confidence calibration and synthetic controls + +Build labelled, programmatic controls covering: + +- exactly flat chromatic material; +- diatonic and pentatonic material; +- pedal tones and omitted tones; +- borrowed notes and secondary dominants; +- modal ambiguity; +- modulations / tonicisations; +- transpositions and alternative textures. + +Report error and abstention behaviour by scope/material class. Only then may a +stable confidence vocabulary or threshold be proposed. + +## Phase 4 — harmonic fixture DSL + +Introduce a small external fixture language inspired by RomanText / harmonic +analysis DSLs, for example: + +```text +C: I | vi | IV | V +a: i | VI | III | VII +C: I | V/V | V | I +``` + +The text format is for fixtures, debugging, and synthetic corpus generation. +Core uses typed structures (`Degree`, `HarmonicFunction`, chord quality, +inversion, modulation/tonicisation); parser strings are not the domain model. + +Acceptance: + +- scripts transpose deterministically; +- scripts generate labelled symbolic fixtures with multiple textures; +- parser failures are typed and localised; +- no runtime dependency on Python, TensorFlow, MusicXML, or an external analysis + service. + +## Phase 5 — soft harmonic generation policy + +Allow calibrated tonal/harmonic information to influence candidate generation or +ranking as a **soft preference**: + +- observed pitch classes stay legal unless an explicit user constraint says + otherwise; +- inferred hierarchy may reward chord/scale tones and controlled resolutions; +- ambiguous estimates abstain; +- borrowed/chromatic colour tones remain representable; +- A/B evaluation covers harmonic fit, closure, novelty, rhythm, register, and + playability together. + +No hard `inferred scale == allowed notes` shortcut. + +## Phase 6 — local context and cadence + +Move the remaining S6 cadence-aware-ending backlog here. Cadence requires local +section context, phrase boundaries, calibrated confidence, and an abstention +path; a global winner plus `last_note = tonic` is not a cadence model. + +Candidate ending states and explainable resolution costs may be optimised through +the S7 layered-path engine once both stages provide stable contracts. + +Acceptance: + +- section-local context beats global-only context on defined fixtures; +- ambiguous/modulating regions may decline to force a cadence; +- endings are returned as ranked alternatives with explanations; +- context-free generation remains unchanged. + +## Research inputs + +See +[`../audit/2026-07-symbolic-harmony-and-evolution-research.md`](../audit/2026-07-symbolic-harmony-and-evolution-research.md). +The main inputs are: + +- `ekzhang/harmony` and `napulen/romanyh`: layered DP, transition costs, and + k-best global alternatives (algorithmic shape shared with S7); +- `napulen/AugmentedNet`: decomposed harmonic targets and synthetic labelled + examples, not a runtime dependency; +- `napulen/harmalysis` / RomanText: fixture-language inspiration. + +## Non-goals + +- No classical SATB rules copied wholesale into swancore guitar generation. +- No neural runtime dependency in S15; neural assistance remains S12. +- No audio chroma/HMM round-trip while the source is already symbolic. +- No generic `MusicDPGodObject`; S7 owns a small path engine with separate + state/cost clients. + +## See also + +- [`S6-rule-generator-v0.md`](S6-rule-generator-v0.md) +- [`S7-graph-layer.md`](S7-graph-layer.md) +- [`S8-preview-app.md`](S8-preview-app.md) +- [`S9-feedback-layer.md`](S9-feedback-layer.md) +- [`S13-complementary-part-generation.md`](S13-complementary-part-generation.md) +- [`../audit/2026-07-tonal-context-phase0.md`](../audit/2026-07-tonal-context-phase0.md) diff --git a/docs/stages/S6-rule-generator-v0.md b/docs/stages/S6-rule-generator-v0.md index 842598b1..82a60f3e 100644 --- a/docs/stages/S6-rule-generator-v0.md +++ b/docs/stages/S6-rule-generator-v0.md @@ -10,9 +10,21 @@ ADRs: ADR-0005, ADR-0010 > policy (ADR-0017; melodic-closure note §7.2/§7.3). `griff generate` uses it > by default, and `--corpus