252 lines
12 KiB
Markdown
252 lines
12 KiB
Markdown
# Contract: P13-S6 promotion — the tuning constructions become normative
|
||
|
||
Repo root `/home/jeans/Repos/active/epiphany`. Read this file in full before
|
||
editing anything. The rulings below are **granted**; they are law for this task,
|
||
not proposals.
|
||
|
||
You are promoting reviewed, ratified content into the specification. This is a
|
||
transcription task with three small additions. The hard part is not deciding
|
||
anything — it is not silently changing anything.
|
||
|
||
## Blast radius
|
||
|
||
You edit exactly two files, plus one rebuilt artifact:
|
||
|
||
* `spec/core_spec.tex`
|
||
* `spec/core_spec.pdf` (rebuilt, not hand-edited)
|
||
* `crates/epiphany-testkit/tests/requirement_labels.rs` — **three integer
|
||
constants only**, at lines 12–14. Nothing else in that file.
|
||
|
||
You write no other Rust. You touch no other document, no other companion
|
||
`.tex`, and no other agent's file.
|
||
|
||
## Source of truth, and the clause that matters most
|
||
|
||
`spec/DRAFT_P13S6_TEMPERAMENTS.md` is the reviewed draft. Its head carries a
|
||
block titled **"RATIFIED 2026-07-22"**. Read that block first; it supersedes
|
||
the draft's own §11–13 wherever they disagree.
|
||
|
||
**The draft's arithmetic is settled. Every closure sum, wolf size, and ratio in
|
||
Section A has been independently recomputed by a reviewer and confirmed.**
|
||
|
||
If you believe you have found an error in it: **report it, do not fix it.**
|
||
Stop and say so in your report. A silent "correction" to a verified
|
||
construction is the single most damaging thing you can do here, because it
|
||
would arrive wearing the same clothes as the verification. The draft already
|
||
survived one round in which three properly-cited entries turned out to be
|
||
arithmetically impossible; the arithmetic is what caught them, and the
|
||
arithmetic has now been run. Your disagreement is a finding, not an edit.
|
||
|
||
## Part 1 — the ten pinned constructions
|
||
|
||
`\subsection{Built-in Tuning Systems}` (`core_spec.tex:3593`) holds a
|
||
`longtable` of 20 identifiers with one-line descriptions, followed by
|
||
`req:tuning:builtin-tuning-catalog`, which already says *"The semantics
|
||
specified above are normative."* Ten of the fourteen non-`tet-*` rows are bare
|
||
names; Section A of the draft specifies them.
|
||
|
||
Add a new `\subsection` — **between** `req:tuning:builtin-tuning-catalog` and
|
||
`\subsection{Default Score Configuration}` (`core_spec.tex:3633`) — carrying the
|
||
normative construction for each of: `pythagorean`; `meantone-1/4-comma`,
|
||
`meantone-1/5-comma`, `meantone-1/6-comma`; `werckmeister-iii`,
|
||
`werckmeister-iv`; `vallotti`; `kirnberger-ii`, `kirnberger-iii`; `young-ii`.
|
||
|
||
For each, carry over from the draft:
|
||
|
||
* the generative rule — which fifths are tempered, by what fraction of **which**
|
||
comma (the draft names syntonic vs Pythagorean explicitly for every entry;
|
||
preserve that distinction exactly, it is the classic error);
|
||
* the wolf or chain placement where the construction does not force it —
|
||
`pythagorean` in particular states its cut as a *choice*, and it must read as
|
||
a choice in the spec too;
|
||
* the exact ratios where the construction yields rationals, and the derived
|
||
cents alongside, marked as derived;
|
||
* the closure statement — the twelve-fifths sum for the circulating
|
||
temperaments, and the wolf residue for the non-circulating ones.
|
||
|
||
Keep the closure sums. They are not decoration: they let a reader check the
|
||
construction without leaving the page, and they are what makes this section
|
||
self-verifying.
|
||
|
||
**Do not carry over:** the `verified`/`recalled` confidence tags, the URLs, the
|
||
"corrected this round" revision commentary, or any bibliography. The
|
||
temperament's *name* is its attribution, and the closure sum is its warrant.
|
||
This is deliberate — transcribing a citation you did not check is how a
|
||
fabricated source enters a normative document, and there is no reason to run
|
||
that risk when the arithmetic stands on its own.
|
||
|
||
**Do not add a `\begin{requirement}` block for any of the ten.** The existing
|
||
`req:tuning:builtin-tuning-catalog` already makes this text normative.
|
||
|
||
## Part 2 — the 5-limit construction (one new requirement)
|
||
|
||
Add `req:tuning:ji-static-construction`. It states:
|
||
|
||
The static 5-limit just-intonation systems are constructed from the contiguous
|
||
lattice block $\{3^a 5^b \mid a \in [-1,2],\ b \in [-1,1]\}$ — twelve cells,
|
||
octave-reduced, assigned in ascending order to the twelve chromatic positions
|
||
of `cmn-12` starting from the anchor, which takes the role of $1/1$. The block
|
||
is generated by its bounds; nothing is selected and nothing is discarded.
|
||
|
||
The twelve, anchor-relative (position 0 is the anchor):
|
||
|
||
| step | cell | ratio | cents (derived) |
|
||
|---|---|---|---|
|
||
| 0 | $3^{0}5^{0}$ | 1/1 | 0.000 |
|
||
| 1 | $3^{-1}5^{-1}$ | 16/15 | 111.731 |
|
||
| 2 | $3^{2}5^{0}$ | 9/8 | 203.910 |
|
||
| 3 | $3^{1}5^{-1}$ | 6/5 | 315.641 |
|
||
| 4 | $3^{0}5^{1}$ | 5/4 | 386.314 |
|
||
| 5 | $3^{-1}5^{0}$ | 4/3 | 498.045 |
|
||
| 6 | $3^{2}5^{1}$ | 45/32 | 590.224 |
|
||
| 7 | $3^{1}5^{0}$ | 3/2 | 701.955 |
|
||
| 8 | $3^{0}5^{-1}$ | 8/5 | 813.686 |
|
||
| 9 | $3^{-1}5^{1}$ | 5/3 | 884.359 |
|
||
| 10 | $3^{2}5^{-1}$ | 9/5 | 1017.596 |
|
||
| 11 | $3^{1}5^{1}$ | 15/8 | 1088.269 |
|
||
|
||
`ji-static-5limit-C`, `ji-static-5limit-G`, and `ji-static-5limit-D` are this
|
||
one construction at three anchors. The catalog keeps exactly those three;
|
||
additional anchors are an implementation extension and **MUST NOT** be read as
|
||
a conformance obligation.
|
||
|
||
Normalize the three catalog descriptions (`core_spec.tex:3617-3619`) to the same
|
||
form — *"anchored to X tonic"* — since today only the C row says "tonic".
|
||
|
||
**Banned vocabulary, throughout.** Never write "asymmetric scale", "symmetric
|
||
scale 1", or "symmetric scale 2". Those names come from a framing this ruling
|
||
rejects (a 5×3 grid with three cells discarded), and the word "symmetric" in
|
||
that framing describes the shape of the *discarded cells*, not any property of
|
||
the resulting scale. Do not claim any of these scales is inversionally
|
||
symmetric: none is, because $45/32$ is present and $64/45$ is not.
|
||
|
||
## Part 3 — adaptive tuning (two new requirements, plus two amendments)
|
||
|
||
### 3a. `req:tuning:adaptive-default-version`
|
||
|
||
`ji-adaptive-5limit` resolves to `TuningResolution::Adaptive` with function
|
||
identity **`"default-v1"`** — the version is part of the machine-visible
|
||
identifier string, not prose beside it.
|
||
|
||
Version 1 is a pure function of (position, anchor pitch class):
|
||
|
||
* resolve the position through `req:tuning:ji-static-construction` transposed so
|
||
the anchor is $1/1$;
|
||
* the anchor is the tonal centre supplied by the harmonic context; **C (chromatic
|
||
position 0) when none is supplied**;
|
||
* `concurrent`, `recent`, `hints`, `parameters`, and mode are **ignored** in
|
||
version 1 — state this explicitly, as `req:pitch:spelling-algorithm` does for
|
||
its own version 1, so that consuming them later is a new version rather than a
|
||
silent behaviour change;
|
||
* every result derives from the anchor and the reference pitch. No adjustment is
|
||
ever carried forward from a previous resolution, so the construction is
|
||
comma-drift-free by shape rather than by a correction step.
|
||
|
||
An unregistered or unknown `AdaptiveTuningFunctionId` **MUST** be a hard error.
|
||
There is no silent fallback. `AdaptiveTuningFunctionId` remains an extension
|
||
point and other functions may be registered — but the built-in
|
||
`ji-adaptive-5limit` is bound to `"default-v1"` and that binding cannot be
|
||
overridden.
|
||
|
||
### 3b. `req:tuning:adaptive-anchor-derivation`
|
||
|
||
Where a harmonic context is constructed from a score graph, the anchor is
|
||
derived from the prevailing key signature:
|
||
|
||
```
|
||
anchor_pc = (7 × fifths) mod 12
|
||
```
|
||
|
||
reading `fifths` as the major tonic — mode is not consulted, so a signature of
|
||
0 anchors at C whether the music is C major or A minor. `KeySignature` is
|
||
already defined (`core_spec.tex:4892`) with `fifths` validated to −7..=+7.
|
||
|
||
State the two things that rule leaves open, because `key_sequence`
|
||
(`core_spec.tex:4845`) is **per-staff and time-anchored**: the signature in
|
||
effect is the one on the staff containing the pitch, at the latest
|
||
`KeySignatureChange` whose anchor is at or before that pitch's onset. Without
|
||
both, a polytonal or modulating score resolves differently in two conforming
|
||
implementations, which `req:tuning:tuning-resolution-determinism` forbids.
|
||
|
||
### 3c. Amend the Adaptive Tuning prose
|
||
|
||
`core_spec.tex:3364-3368` promises that "the frequency of a given E depends on
|
||
whether it is the major third of a C chord, the perfect fifth of an A chord, or
|
||
a passing tone". That describes what an adaptive function **may** do. It is not
|
||
what the built-in does at version 1, and left as-is it reads as a promise the
|
||
normative text does not keep.
|
||
|
||
Rewrite it so per-sonority interpretation is described as within the scope of
|
||
registered adaptive functions generally, and note that `ji-adaptive-5limit`
|
||
version 1 consumes only the tonal centre. Do **not** weaken
|
||
`req:tuning:adaptive-tuning-purity` — it already permits the narrower algorithm,
|
||
since requiring a pure function of position and context never required consuming
|
||
every context field.
|
||
|
||
### 3d. Correct the forward reference
|
||
|
||
`core_spec.tex:3649-3653` says `KeyContext` is *"partially defined in
|
||
Chapter~\ref{ch:graph} and completed in the audio engine specification (out of
|
||
scope for this document)"*. `KeyContext` is not defined anywhere in this
|
||
specification, and no type of that name exists in the implementation.
|
||
|
||
Keep it out of scope — but replace the vague claim with the interface obligation
|
||
that Part 3 actually depends on: whatever completes `KeyContext` **MUST** expose
|
||
a tonal-centre chromatic pitch class consistent with
|
||
`req:tuning:adaptive-anchor-derivation`. Chapter 10 defines `KeySignature`, not
|
||
`KeyContext`; say that accurately.
|
||
|
||
`ContextHint` and `AdaptiveTuningParameters` stay undefined. Say so explicitly
|
||
rather than leaving the silence ambiguous. Version 1 ignores both, and inventing
|
||
them now would freeze a type surface on a chapter that has no consumer.
|
||
|
||
## Counts and verification
|
||
|
||
Three new requirements, so three constants move in
|
||
`crates/epiphany-testkit/tests/requirement_labels.rs`:
|
||
|
||
* `CORE_REQUIREMENT_COUNT` 209 → **212**
|
||
* `SUITE_REQUIREMENT_COUNT` 279 → **282**
|
||
* `SUITE_LABEL_COUNT` 279 → **282**
|
||
|
||
If your build produces different numbers, **stop and report** — do not tune a
|
||
constant until a test passes. That inverts what the constant is for.
|
||
|
||
Requirement numbering is automatic: the box steps a plain counter via
|
||
`code={\refstepcounter{requirement}}` (`core_spec.tex:245`), so inserting
|
||
requirements mid-document renumbers later ones in the rendered PDF while every
|
||
`\label` keeps resolving correctly. You do not maintain numbers by hand, and
|
||
you **MUST NOT** rename or move any existing `\label` — they are cited by code,
|
||
tests, and decision records.
|
||
|
||
Do not add code citations for the three new requirements. Nothing implements
|
||
them yet; that lands in a later Push 4b step.
|
||
|
||
Then:
|
||
|
||
1. `cargo test -p epiphany-testkit --test requirement_labels` → 6 passed.
|
||
2. Rebuild with `latexmk -xelatex` **twice**. Grep the log for `^! `,
|
||
`Undefined control sequence`, and `Reference .* undefined`, and report the
|
||
actual counts. All must be zero.
|
||
3. Full workspace gate: `cargo fmt --all --check`; `cargo clippy --workspace
|
||
--all-targets` → 0 warnings; `cargo test --workspace`;
|
||
`cargo run -q -p epiphany-testkit --example conformance_suite` → 8/8. Zero
|
||
golden churn is expected — report it if not.
|
||
|
||
Report the actual commands and their actual output. On this project an agent
|
||
once reported "verification passes" while errors pointed into its own file, and
|
||
another silently deleted a struct field while rewriting the comment above it —
|
||
so **re-read every listing and table you edit, in full, after editing it**, and
|
||
confirm nothing adjacent vanished.
|
||
|
||
## Do not
|
||
|
||
* Decide anything. All four ratifications are made; if you find a fifth
|
||
question, report it unanswered.
|
||
* Edit any construction in Section A of the draft, or "improve" its arithmetic.
|
||
* Carry a URL, a confidence tag, or a bibliography into `core_spec.tex`.
|
||
* Use the words "asymmetric scale", "symmetric scale 1", or "symmetric scale 2".
|
||
* Add a registry field, a `Codec` impl, or any Rust type. Part 3 is spec-only.
|
||
* Edit `binary_format.tex`, `operation_catalog.tex`, or any other companion.
|
||
* Run `cargo fmt --all`.
|