12 KiB
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.texspec/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 —
pythagoreanin 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-constructiontransposed so the anchor is1/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, asreq:pitch:spelling-algorithmdoes 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_COUNT209 → 212SUITE_REQUIREMENT_COUNT279 → 282SUITE_LABEL_COUNT279 → 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:
cargo test -p epiphany-testkit --test requirement_labels→ 6 passed.- Rebuild with
latexmk -xelatextwice. Grep the log for^!,Undefined control sequence, andReference .* undefined, and report the actual counts. All must be zero. - 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
Codecimpl, or any Rust type. Part 3 is spec-only. - Edit
binary_format.tex,operation_catalog.tex, or any other companion. - Run
cargo fmt --all.