Push 4b: ji-adaptive-5limit resolves, and the blocker was never HarmonicContext
The last fail-closed entry in the built-in tuning catalog now resolves, leaving
only the compatibility-mapping registry open in Push 4b.
The recorded blocker was wrong. Five places in tuning.rs claimed adaptive
"needs HarmonicContext, which does not exist in Rust". But
req:tuning:adaptive-default-version makes version 1 a pure function of
(position, anchor pitch class) — it ignores concurrent, recent, hints,
parameters, and mode. And two of HarmonicContext's four specified fields are
UNIMPLEMENTABLE: key_context and hints are typed on KeyContext / ContextHint,
which core_spec.tex:4111 leaves undefined deliberately, "so defining them now
would freeze a type surface on a chapter with no consumer". The minimal
one-field shape was not a preference; it was the only implementable one. Each
remaining field arrives with the first function that consumes it.
The real blocker was structural and small: locate_voice returned
(RegionId, StaffId) while key_sequence lives on StaffInstance, so the resolver
threw away the only object that could answer "what key is prevailing here".
No new pitch math. ji_static_5limit_ratios already took a runtime anchor — the
three ji-static-5limit-{C,G,D} built-ins are that one function at anchors
0, 7, 2 — so adaptive v1 is that same call with a derived anchor. Verified as
an identity: adaptive at anchor 0 is bit-identical to ji-static-5limit-C and at
anchor 7 to ji-static-5limit-G, across all seven naturals.
A missing tonal centre is NOT an error. core_spec.tex:3452 mandates C
(chromatic position 0) when none is supplied, so that is a defined default, not
a fail-closed case; a test pins it against a future "fail closed" refactor.
Fail-closed applies to exactly two things: an unregistered
AdaptiveTuningFunctionId (hard error, no fallback) and a TimeAnchor that cannot
be unambiguously ordered against the onset (AnchorNotOrderable, naming the kind
that defeated it). If any KeySignatureChange in a sequence is unorderable the
whole derivation fails, rather than risk skipping the true prevailing signature.
Anchor arithmetic is (7 * fifths).rem_euclid(12), never %: fifths reaches -7 and
% yields a negative pitch class. ChromaticPitchClass is a checked 0..=11 newtype,
so the sign bug cannot degrade into a silently wrong anchor — under mutation it
panics outright. Verified by hand across the whole -7..=7 range, including the
enharmonic collisions (6 and -6 both F#/Gb = 6).
Nothing reaches the wire: TuningResolution is catalog-computed and appears in
neither codec.rs nor textvalue_graph.rs, so TuningResolution::Adaptive and
HarmonicContext are in-memory only and schema major 3 is untouched. Zero Codec
impls added; no vector or golden moved.
Gate: fmt clean, clippy 0, 1311 passed / 0 failed, doc 0, conformance 8/8,
requirement labels 6/6 at 212/282/282. Independently verified: the C default,
static systems ignoring context, statelessness across reordered resolution, and
the 0..=11 invariant. Two mutations killed — rem_euclid -> % and a silent
fallback for an unregistered id.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1dfa8b4f3a
commit
56950f8d7c
|
|
@ -34,10 +34,11 @@
|
|||
//! no `Codec` impl exists for anything here (Push 4b Ruling C).
|
||||
//! * `tuning` — the Chapter 4 tuning-resolution vocabulary
|
||||
//! ([`TuningSystem`], [`TuningResolution`], [`TuningOverride`],
|
||||
//! [`TuningScope`]), [`built_in_tuning_system`] (nine of the twenty
|
||||
//! catalog identifiers; the rest fail closed), and
|
||||
//! [`resolve_pitch_frequency`], the five-scope resolver from a pitch to a
|
||||
//! frequency in Hz. In-memory only, same discipline as `pitch_space`
|
||||
//! [`TuningScope`], [`HarmonicContext`]), [`built_in_tuning_system`] (all
|
||||
//! twenty catalog identifiers resolve), [`derive_tonal_centre`] (the
|
||||
//! score-graph-derived adaptive anchor), and [`resolve_pitch_frequency`],
|
||||
//! the five-scope resolver from a pitch to a frequency in Hz. In-memory
|
||||
//! only, same discipline as `pitch_space`
|
||||
//! (Push 4b Ruling C).
|
||||
//! * `accidental` — the Chapter 4 accidental/glyph/engraving vocabulary
|
||||
//! ([`AccidentalDefinition`], [`ScoreAccidentalExtensions`],
|
||||
|
|
@ -106,15 +107,16 @@ pub use time::{
|
|||
|
||||
pub use pitch::{
|
||||
canonical_pitch_bytes, derive_system_pitch_id, spell, AccidentalGroupId, AccidentalId,
|
||||
AccidentalRegistryId, AcousticPitch, AcousticRealization, CmnNominal, CustomGlyphId,
|
||||
DecompositionAlgorithmId, ForeignFormatId, IdentifiedPitch, IntervalAlgebraRegistryId,
|
||||
ModificationRegistryId, NominalRegistryId, Pitch, PitchRange, PitchSpaceId, PitchSpacePosition,
|
||||
PitchSpelling, PositionRegistryId, PositionStructureRegistryId, ReferencePitch, ScalePosition,
|
||||
SpellingAlgorithmId, SpellingAttachment, SpellingContext, SpellingDirective, SpellingNominal,
|
||||
SpellingPrecedence, SpellingRenderHints, SpellingRule, SpellingRuleSetId, SpellingScope,
|
||||
SpellingSource, SpellingSourceKind, StaffGroupKindRegistryId, TieClassRegistryId,
|
||||
TransposeRefusal, TranspositionInterval, TranspositionRegistryId, TuningFunctionId,
|
||||
TuningReference, TuningSystemId, VoiceSelector,
|
||||
AccidentalRegistryId, AcousticPitch, AcousticRealization, AdaptiveTuningFunctionId,
|
||||
ChromaticPitchClass, CmnNominal, CustomGlyphId, DecompositionAlgorithmId, ForeignFormatId,
|
||||
IdentifiedPitch, IntervalAlgebraRegistryId, ModificationRegistryId, NominalRegistryId, Pitch,
|
||||
PitchRange, PitchSpaceId, PitchSpacePosition, PitchSpelling, PositionRegistryId,
|
||||
PositionStructureRegistryId, ReferencePitch, ScalePosition, SpellingAlgorithmId,
|
||||
SpellingAttachment, SpellingContext, SpellingDirective, SpellingNominal, SpellingPrecedence,
|
||||
SpellingRenderHints, SpellingRule, SpellingRuleSetId, SpellingScope, SpellingSource,
|
||||
SpellingSourceKind, StaffGroupKindRegistryId, TieClassRegistryId, TransposeRefusal,
|
||||
TranspositionInterval, TranspositionRegistryId, TuningFunctionId, TuningReference,
|
||||
TuningSystemId, VoiceSelector,
|
||||
};
|
||||
|
||||
pub use pitch_space::{
|
||||
|
|
@ -167,9 +169,10 @@ pub use tempo::{
|
|||
};
|
||||
|
||||
pub use tuning::{
|
||||
built_in_tuning_system, frequency_for_position, resolve_pitch_frequency, resolve_tuning_scope,
|
||||
PositionRatio, ResolvedTuning, TuningCatalogEntry, TuningOverride, TuningParameters,
|
||||
TuningResolution, TuningResolutionError, TuningScope, TuningSystem,
|
||||
built_in_tuning_system, derive_tonal_centre, frequency_for_position, resolve_pitch_frequency,
|
||||
resolve_tuning_scope, HarmonicContext, PositionRatio, ResolvedTuning, TuningCatalogEntry,
|
||||
TuningOverride, TuningParameters, TuningResolution, TuningResolutionError, TuningScope,
|
||||
TuningSystem,
|
||||
};
|
||||
|
||||
pub use codec::{CanonicalValue, ScoreDecodeError};
|
||||
|
|
|
|||
|
|
@ -91,6 +91,18 @@ catalog_id!(
|
|||
/// registry for) and fails closed.
|
||||
TuningFunctionId
|
||||
);
|
||||
catalog_id!(
|
||||
/// Identifies a registered adaptive tuning function, consumed by
|
||||
/// [`crate::tuning::TuningResolution::Adaptive`] (Chapter 4 §"Adaptive
|
||||
/// Tuning", `req:tuning:adaptive-default-version`). The built-in catalog
|
||||
/// entry `ji-adaptive-5limit` is bound to `"default-v1"` — the version
|
||||
/// lives *inside* the identifier string, not as prose beside it, so a
|
||||
/// future version 2 would mint a new identifier rather than silently
|
||||
/// changing what `"default-v1"` means. An unregistered id (this tranche
|
||||
/// registers exactly the one) is a hard error with no silent fallback to
|
||||
/// a default version.
|
||||
AdaptiveTuningFunctionId
|
||||
);
|
||||
catalog_id!(
|
||||
/// Identifies an accidental registry (Chapter 4 §"Accidental Registries").
|
||||
AccidentalRegistryId
|
||||
|
|
@ -550,6 +562,35 @@ pub struct AcousticPitch {
|
|||
pub realization: AcousticRealization,
|
||||
}
|
||||
|
||||
/// A chromatic pitch class in `0..=11` — one of the twelve positions of the
|
||||
/// `cmn-12` chromatic layer, with no octave. Used where a value must name
|
||||
/// *which* pitch class rather than a full pitch, such as
|
||||
/// [`crate::tuning::HarmonicContext`]'s tonal centre
|
||||
/// (`req:tuning:adaptive-anchor-derivation`). [`Pitch::twelve_tet_class`],
|
||||
/// just below, already returns a `0..=11` `u8`; this newtype enforces that
|
||||
/// range in the type itself rather than merely documenting it, following
|
||||
/// [`crate::graph::KeySignature::new`]'s checked-constructor style — a raw
|
||||
/// `u8` field a caller could set to `200` is not a chromatic pitch class.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct ChromaticPitchClass(u8);
|
||||
|
||||
impl ChromaticPitchClass {
|
||||
/// Builds a chromatic pitch class, rejecting values outside `0..=11`.
|
||||
pub const fn new(value: u8) -> Option<Self> {
|
||||
if value <= 11 {
|
||||
Some(ChromaticPitchClass(value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The underlying `0..=11` value.
|
||||
#[inline]
|
||||
pub const fn get(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A pitch's intrinsic identity: scale position plus acoustic realization
|
||||
/// (Chapter 2 §"The Pitch Type"). Spellings are attached externally.
|
||||
///
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,230 @@
|
|||
# CONTRACT — Push 4b: `ji-adaptive-5limit`, the last fail-closed tuning system
|
||||
|
||||
**Status:** dispatch-ready. Closes the final deferred entry in the built-in
|
||||
tuning catalog (`req:tuning:builtin-tuning-catalog`), leaving only the
|
||||
compatibility-mapping registry open in Push 4b.
|
||||
|
||||
**Ratified by the user 2026-07-23:**
|
||||
1. **A minimal `HarmonicContext`** carrying only the tonal centre — not the full
|
||||
four-field spec listing, and not "no struct at all".
|
||||
2. **One resolver with an optional context parameter** — static systems ignore
|
||||
it, adaptive consumes it. This is the spec's own stated API shape.
|
||||
3. **Constrain and fail closed** on anchor ordering: order what is
|
||||
unambiguously determinable, return a distinct reported error otherwise.
|
||||
Never guess.
|
||||
|
||||
**Nothing in this tranche reaches the wire.** `TuningResolution` appears nowhere
|
||||
in `codec.rs` or `textvalue_graph.rs` — the score stores only *identifiers*
|
||||
(`default_tuning_system`, `overrides`). `TuningResolution::Adaptive` and
|
||||
`HarmonicContext` are in-memory only and fully reversible. Do **not** add a
|
||||
`Codec` for anything here, and do not touch schema major 3.
|
||||
|
||||
---
|
||||
|
||||
## Correct the recorded blocker first
|
||||
|
||||
`tuning.rs` says in three places that adaptive "needs `HarmonicContext`, which
|
||||
does not exist in Rust". That framing is wrong and should be fixed as you go:
|
||||
|
||||
- `req:tuning:adaptive-default-version` (`core_spec.tex:3438`) makes version 1
|
||||
**"a pure function of (position, anchor pitch class)"**. It explicitly
|
||||
*ignores* `concurrent`, `recent`, `hints`, `parameters`, and mode.
|
||||
- **Two of `HarmonicContext`'s four spec'd fields are unimplementable by
|
||||
construction**: `key_context: Option<KeyContext>` and `hints: Vec<ContextHint>`
|
||||
name types the specification deliberately leaves undefined
|
||||
(`core_spec.tex:4111`, Forward References: "defining them now would freeze a
|
||||
type surface on a chapter with no consumer").
|
||||
|
||||
So the minimal shape is not merely preferable — it is the only implementable
|
||||
one. What v1 needs is a single chromatic pitch class.
|
||||
|
||||
## What already exists — do not rebuild it
|
||||
|
||||
`ji_static_5limit_ratios(anchor_chromatic_degree: i32) -> Vec<PositionRatio>`
|
||||
(`tuning.rs:358`) **already takes a runtime anchor**. The three
|
||||
`ji-static-5limit-{C,G,D}` entries are that one function at anchors 0, 7, and 2.
|
||||
|
||||
`req:tuning:adaptive-default-version`'s first clause — "the position resolves
|
||||
through the construction of `req:tuning:ji-static-construction`, transposed so
|
||||
the anchor takes the role of 1/1" — **is exactly that call with a derived
|
||||
anchor**. Reuse it. Do not write a second lattice construction, and do not
|
||||
transcribe a cents table.
|
||||
|
||||
## The surface
|
||||
|
||||
### 1. `AdaptiveTuningFunctionId`
|
||||
|
||||
Mint via the existing `catalog_id!` macro in `pitch.rs` (beside
|
||||
`TuningFunctionId`, `pitch.rs:92`), with a doc comment stating that
|
||||
`ji-adaptive-5limit` is bound to `"default-v1"`, that the version lives *inside*
|
||||
the identifier string, and that an unregistered id is a hard error with no
|
||||
silent fallback (`req:tuning:adaptive-default-version`, final clause).
|
||||
|
||||
### 2. `TuningResolution::Adaptive`
|
||||
|
||||
Add the variant the specification names:
|
||||
|
||||
```rust
|
||||
Adaptive { function: AdaptiveTuningFunctionId },
|
||||
```
|
||||
|
||||
In-memory only. Follow the `Function` variant's doc style (`tuning.rs:119-134`):
|
||||
say what resolves it, and that an unregistered id fails closed.
|
||||
|
||||
### 3. `HarmonicContext` — minimal, and say why
|
||||
|
||||
```rust
|
||||
pub struct HarmonicContext {
|
||||
/// The active tonal centre as a chromatic pitch class (0..=11), if known.
|
||||
pub tonal_centre: Option<ChromaticPitchClass>,
|
||||
}
|
||||
```
|
||||
|
||||
The doc comment MUST record why this is not the four-field listing at
|
||||
`core_spec.tex:3411`: `key_context` and `hints` are typed on `KeyContext` /
|
||||
`ContextHint`, which the specification leaves undefined on purpose; `concurrent`
|
||||
and `recent` are ignored by version 1, so carrying them would mint an unconsumed
|
||||
type surface (the `NOTEHEAD_ANCHORS` failure the module doc at `tuning.rs:25`
|
||||
already cites). Each field arrives with the first function that consumes it.
|
||||
|
||||
For the pitch class, enforce the `0..=11` invariant in the type rather than
|
||||
merely documenting it — follow `KeySignature::new`'s checked-constructor style
|
||||
(`graph.rs:156`, returns `Option`). A raw `u8` field a caller can set to 200 is
|
||||
not acceptable. (`Pitch::chromatic_pitch_class`, `pitch.rs:585`, already returns
|
||||
a `0..=11` `u8`; a small checked newtype in `pitch.rs` beside it is the natural
|
||||
home, and lets that method's contract be stated in the type.)
|
||||
|
||||
### 4. The anchor derivation — `req:tuning:adaptive-anchor-derivation`
|
||||
|
||||
A public function deriving the tonal centre from the score graph. Per the
|
||||
requirement (`core_spec.tex:3474-3489`):
|
||||
|
||||
- `anchor_pc = (7 × fifths).rem_euclid(12)`, reading `fifths` as the **major
|
||||
tonic**. **Mode is not consulted** — a signature of 0 anchors at C whether the
|
||||
prevailing key is C major or A minor. Use `rem_euclid`, never `%`: `fifths` is
|
||||
negative for flat keys (`KeySignature::MIN_FIFTHS == -7`) and `%` would yield a
|
||||
negative pitch class.
|
||||
- The *prevailing* signature is the one **on the staff containing the pitch**,
|
||||
from that staff instance's `key_sequence` (`StaffInstance.key_sequence`,
|
||||
`graph.rs:607`), at the latest `KeySignatureChange` whose anchor is **at or
|
||||
before the pitch's onset**.
|
||||
- No applicable change ⇒ no tonal centre (the resolver then defaults to C; see
|
||||
§5).
|
||||
|
||||
**`locate_voice` (`tuning.rs:1081`) currently returns `(RegionId, StaffId)` and
|
||||
throws away the `StaffInstance` it already has in hand.** `key_sequence` lives on
|
||||
the *instance*, not on `Staff`. Widen it (or add a sibling) to return the
|
||||
instance. This is the single structural blocker that made adaptive resolution
|
||||
impossible before.
|
||||
|
||||
**Ordering — constrain and fail closed.** `KeySignatureChange.anchor` is a
|
||||
`TimeAnchor` (Event / Measure / Region / WallClock, `time.rs:592`) and an event's
|
||||
onset is an `EventPosition` (`Musical` | `WallClock`, `time.rs:628`). Order the
|
||||
cases that are unambiguously determinable, and return a **distinct, reported
|
||||
error** for the rest (cross-clock comparison, or an indirect anchor you cannot
|
||||
resolve) rather than guessing an ordering. Precedent for anchor resolution, which
|
||||
you should follow rather than reinvent: `tempo.rs:331/356/376` inject a
|
||||
`resolve: impl Fn(&TimeAnchor) -> Option<MusicalPosition>` closure, and
|
||||
`invariants.rs:407` walks anchors depth-bounded. Reusing `tempo.rs`'s injected
|
||||
shape is preferred — it keeps this module out of the time-model business.
|
||||
|
||||
Name the error for what it is (e.g. `AnchorNotOrderable { .. }`), document that
|
||||
it is a *deliberate deferral* like `IncompatiblePitchSpace`, and make the message
|
||||
say which anchor kind defeated it. A caller must be able to tell "this score
|
||||
uses a form I don't order yet" from "your key signature is wrong".
|
||||
|
||||
### 5. The resolver seam
|
||||
|
||||
```rust
|
||||
pub fn resolve_pitch_frequency(
|
||||
score: &Score,
|
||||
pitch: &Pitch,
|
||||
voice: VoiceId,
|
||||
context: Option<&HarmonicContext>,
|
||||
) -> Result<f64, TuningResolutionError>
|
||||
```
|
||||
|
||||
`resolve_pitch_frequency` has **no production callers** — only its own tests
|
||||
(verified across the workspace), so this signature change is nearly free. Update
|
||||
the five in-module test call sites and the `lib.rs:170` re-export docs.
|
||||
|
||||
Resolution of `TuningResolution::Adaptive`:
|
||||
1. If `function` is not the registered `"default-v1"` ⇒ **hard error**. No
|
||||
fallback (`req:tuning:adaptive-default-version`).
|
||||
2. Anchor = `context`'s `tonal_centre` when supplied; **otherwise C, chromatic
|
||||
position 0**. This is spec-mandated (`core_spec.tex:3452-3453`: "C (chromatic
|
||||
position 0) when no tonal centre is supplied") — it is a *defined default*,
|
||||
**not** a fail-closed case. Do not turn a missing context into an error.
|
||||
3. Resolve the position through `ji_static_5limit_ratios(anchor)`.
|
||||
|
||||
Static systems ignore `context` entirely — that is the spec's shape
|
||||
(`core_spec.tex:3426-3428`).
|
||||
|
||||
### 6. Catalog entry
|
||||
|
||||
`built_in_tuning_system` (`tuning.rs:326`) currently returns
|
||||
`TuningCatalogEntry::Deferred(DEFERRED_ADAPTIVE)` for `"ji-adaptive-5limit"`.
|
||||
It becomes `Resolved(..)` with `resolution: Adaptive { function: "default-v1" }`
|
||||
over pitch space `cmn-12`. Remove `DEFERRED_ADAPTIVE` and retire the stale
|
||||
"needs HarmonicContext" notes at `tuning.rs:27`, `:43`, `:223`, `:279`, `:1386`.
|
||||
|
||||
The test `deferred_ji_adaptive_fails_closed` (`tuning.rs:1382`) **inverts**: it
|
||||
must now prove the system resolves. Rename it accordingly. Do not delete it.
|
||||
|
||||
## Tests that would fail against the bugs this invites
|
||||
|
||||
- **Comma-drift-free by shape.** `req:tuning:adaptive-default-version`'s last
|
||||
clause: "No adjustment is ever carried forward from a previous resolution."
|
||||
Resolve the same position repeatedly, and in varying orders, and assert
|
||||
bit-identical results — the property that would catch a stateful implementation.
|
||||
- **Mode-blindness.** A signature of 0 anchors at C. Assert a relative-minor
|
||||
reading is *not* applied.
|
||||
- **Flat keys.** `fifths = -1` ⇒ `(7 × -1).rem_euclid(12) = 5` (F). A `%`
|
||||
implementation yields `-7` — assert the correct value so the sign bug cannot
|
||||
survive.
|
||||
- **The default.** No context, or a context with `tonal_centre: None`, resolves
|
||||
at anchor C — equal to `ji-static-5limit-C`'s result for the same position.
|
||||
This pins the spec's mandated default against a future "fail closed" refactor.
|
||||
- **Unregistered function id is a hard error**, with no fallback to C.
|
||||
- **Prevailing selection.** With two `KeySignatureChange`s on a staff, a pitch
|
||||
between them takes the earlier; a pitch after both takes the later.
|
||||
|
||||
## Do NOT
|
||||
|
||||
- Define `KeyContext`, `ContextHint`, or `AdaptiveTuningParameters` — the
|
||||
specification leaves all three undefined deliberately.
|
||||
- Add `concurrent` / `recent` to `HarmonicContext`.
|
||||
- Add any `Codec`, touch schema major 3, or alter `spec/vectors/`.
|
||||
- Build a general `TimeAnchor` resolution layer — constrain and fail closed.
|
||||
- Touch the editor track (`spec/PLAN_EDITOR_APP.md`,
|
||||
`spec/CONTRACT_EDITOR_T1A_GOLDENS.md`, `spec/CONTRACT_EDITOR_T2_SELECTION.md`,
|
||||
`crates/epiphany-editor-gui/goldens/`) or `.claude/worktrees/`.
|
||||
|
||||
## Spec
|
||||
|
||||
The two requirements are already ratified and need **no change**. If a
|
||||
`core_spec.tex` touch is genuinely needed (e.g. a note that the implementation's
|
||||
`HarmonicContext` carries only the tonal centre until a consumer needs more), add
|
||||
it as prose or a `rationale` — **no new `\label{req:...}`**. Requirement counts
|
||||
MUST stay **212 / 282 / 282**.
|
||||
|
||||
## The gate (report exact numbers)
|
||||
|
||||
- `cargo fmt --all --check`
|
||||
- `cargo clippy --workspace --all-targets` → 0 warnings
|
||||
- `cargo test --workspace` → 0 failed (3b-ii landed at 1283; report the count)
|
||||
- `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps` → 0
|
||||
- `cargo run -q -p epiphany-testkit --example conformance_suite` → 8/8
|
||||
- `cargo test -p epiphany-testkit --test requirement_labels` → 6/6, counts
|
||||
**212 / 282 / 282**
|
||||
|
||||
## What the reviewer will verify independently — build to survive it
|
||||
|
||||
- The anchor arithmetic by hand across the full `-7..=7` range, especially flat
|
||||
keys, against `(7 × fifths).rem_euclid(12)`.
|
||||
- That adaptive at anchor 0 equals `ji-static-5limit-C` position-for-position,
|
||||
and at anchor 7 equals `ji-static-5limit-G` — the transposition identity.
|
||||
- That the missing-context default is C and is **not** an error.
|
||||
- Mutation: break `rem_euclid` to `%` and confirm the flat-key test dies; make
|
||||
the unregistered-id path fall back to C and confirm the hard-error test dies.
|
||||
- That no `Codec` was added and canonical bytes are unmoved.
|
||||
Loading…
Reference in New Issue