Repository navigation
docs(windows): spec the LTC decoder contract (#728 M3 slice 3) - #861
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Spec for #728 M3 slice 3 — pinning
LTCDecoderasgolden/ltc-decode-v1.jsonwith aLtcDecodermirror inOnlyCue.Core. Docs only; no code.Slices 1 (#854) and 2 (#859) pinned what OnlyCue emits — the wire format, then the timing plan. This slice pins what it believes when it reads, which is the harder half: a decoder that is subtly wrong still returns plausible timecode on a clean signal and only diverges at the edges.
Motivation
The decoder is the largest pure algorithm left unpinned in the timecode subsystem (178 lines of comparator → histogram → demodulator → framing), and every stage has a threshold, a rounding, or an advance-by-N that a reasonable C# port would get nearly right. Once this lands, the whole LTC path is pinned in both directions on both platforms: a Windows build can stripe a file and read one back and be provably byte-compatible with macOS.
What Changed
docs/superpowers/specs/2026-09-16-windows-m3-ltc-decoder.md— the spec.Scope decisions worth reviewing
Nine named hazards, each of which gets at least one case whose expectation would change if the port got it wrong. The two most likely to be ported "nearly right":
extractFramesadvances 80 on a sync match, not on a valid frame. A frame with a good sync word but bad parity is not merely dropped — it consumes its 80 bits and the search resumes after it. A port that movesend += 80inside theisWellFormedcheck keeps searching bit-by-bit through the corrupted frame and can lock onto a spurious window. Invisible on clean signals, so it gets a dedicated case.statestarts at 0 and the first latch sets it without appending an index. A port that appends it shifts everystartSampleand adds a leading interval to the histogram.Plus the float-width ones:
rmsaccumulates inDoubleand narrows toFloatexactly once, and the threshold multiply happens inFloat.Noise cases are deliberately excluded. Reproducing added noise / DC drift cross-platform needs a seeded PRNG whose output becomes part of the contract — that pins the PRNG, not the decoder. Structural corruption (flipped parity bit, broken sync word, out-of-range BCD, truncation, silence, amplitude below the floor) is deterministic, expressible in the vector, and is where ports actually break. One exception is pinned as a non-decode: a DC-offset buffer must fail, because the comparator is symmetric about zero and has no DC blocker — pinning the failure stops a port from "helpfully" adding one.
Signals are recipes, not sample dumps. A case names generator parameters plus an ordered list of mutations; both sides build the identical
float[]. Keeps the file readable and still pins the signal.Five ops instead of one.
decode/transitions/halfBit/framesPerSecond/bits. A singledecodeop would make every hazard fail the same way and the mutation step could not tell them apart.Two things the spec flags about itself
InternalsVisibleTois not configured today. The spec initially claimed it was; I checkedOnlyCue.Core.csprojand it has onlyTargetFramework/ImplicitUsings/Nullable, with no reference to the attribute anywhere in the tree. Adding it is therefore part of the slice and is called out rather than slipped in — the intermediate seams should beinternal(mirroring Swift'sprivate) rather thanpublic, because pinning an intermediate is not a reason to widen the shipping surface.framesPerSecond) gets a further instruction: if no case can make it go red, the honest conclusion may be that the matrix is incomplete rather than that the mutant is harmless.How to Verify
swiftlint lint --strictunaffected.Out of Scope
No implementation. PLL jitter tracking, half-speed and reverse playback are out (the type does not do them; pinning them would pin fiction).
LTCAudioReaderis AVFoundation-facing and gets its own Windows front end.LTCBiphaseEncoderstays outside the contract for the reason recorded in slice 1.OnlyCue verification (required — doc)
Spec link:
docs/superpowers/specs/2026-09-16-windows-m3-ltc-decoder.mdCloses: — (spec only; the implementation issue is filed once this is approved, and updates epic #728)