docs: land the registers for schema v2 and the audio signature
Status and verification rows for the six requirements landed in this branch, plus UT-029 … UT-052, and the generated traceability matrix that `docs/traceability.md` holds in the other two components but was missing here. `traces-report.json` is gitignored to match the server: the matrix is committed, the JSON report is not, since nothing reads it back. Two corrections rather than additions: `AudioSignatureTests` was tagged UT-029 … UT-035, IDs the register had already assigned to the schema tests. Renumbered to UT-038 … UT-044, matching the register, which was right — duplicate IDs defeat the point of IDs being permanent. `ReadCappedAsync` implements JR-028's response cap and carried no tag, so the requirement read as uncovered. The gate reports 0 orphan tags. TRACES: JR-002, JR-003, JR-028, JR-041, JR-042, JR-043, JR-044, JR-045 | SR-003
This commit is contained in:
@@ -55,9 +55,14 @@ The truth file is **not** the Jmanifest. It carries installation-local fields
|
||||
identity block the exchange adds. See §6.
|
||||
|
||||
**Current:** [`Models/TruthFile.cs`](Jellyfin.Plugin.JRay/Models/TruthFile.cs),
|
||||
`schema_version: 1`. **Gap:** the schema below is not implemented, and the other
|
||||
two specs describe the pending bump in more detail than this one does — the
|
||||
ownership is stated but not yet exercised.
|
||||
`schema_version: 2` — the schema below is implemented, with
|
||||
[`Models/TruthScene.cs`](Jellyfin.Plugin.JRay/Models/TruthScene.cs),
|
||||
[`Models/TruthExtraction.cs`](Jellyfin.Plugin.JRay/Models/TruthExtraction.cs) and
|
||||
[`Models/TruthCut.cs`](Jellyfin.Plugin.JRay/Models/TruthCut.cs).
|
||||
|
||||
**Gap:** the ownership claim is still not *enforced* — nothing checks that the
|
||||
other two repos reference this section rather than restating the schema. JR-001's
|
||||
verification tier is `static` for that reason, and no such check exists yet.
|
||||
|
||||
### JR-002 — `schema_version: 2`
|
||||
|
||||
@@ -118,9 +123,12 @@ same upload must agree on its `content_id`, and belief is a producer-side
|
||||
estimate that may legitimately differ between pipeline versions for identical
|
||||
timings. It replicates the way `audio_signature` does — see the server spec §9a.
|
||||
|
||||
**Gap:** entire requirement. The changes are all breaking and ship as **one**
|
||||
bump (SR-003), together with extraction `IR-002` and the server's acceptance of
|
||||
the new shape.
|
||||
**Current:** implemented. `scenes` are `TruthScene` objects carrying `belief` and
|
||||
`route`; `extraction.*` and `cut.*` are their own types; `anneal_sec` and the
|
||||
top-level `sample_fps` are **deleted, not zeroed**. `ManifestConverter` carries
|
||||
belief, route and both blocks through from a fetched manifest rather than
|
||||
flattening them. **Gap:** none — this shipped with extraction `IR-002` and the
|
||||
server's `jmanifest_version: 2` as the one coordinated SR-003 bump.
|
||||
|
||||
### JR-003 — Unknown `schema_version` is refused, never guessed
|
||||
|
||||
@@ -140,9 +148,20 @@ version found, rather than silently reporting no coverage — an item that looks
|
||||
un-extracted when it was merely stale is the failure mode that wastes a user's
|
||||
compute.
|
||||
|
||||
**Current:** `PUT` rejects `schema_version != 1` with `400`; sidecar reads do not
|
||||
check the version at all. **Gap:** the version check must move into the shared
|
||||
read path so all three sources are covered, and the target becomes `2`.
|
||||
**Current:** implemented.
|
||||
[`Services/TruthSchema.cs`](Jellyfin.Plugin.JRay/Services/TruthSchema.cs) holds
|
||||
the single `SupportedVersion = 2` and is consulted on **all four** paths — sidecar
|
||||
read, managed store load, managed `PUT`, and the converter that writes a fetched
|
||||
manifest. Rejections name the file and the version found, per the paragraph above.
|
||||
|
||||
Managed truth is checked on load as well as on `PUT`, because data written by an
|
||||
earlier plugin build is already on disk and never passed today's `PUT`.
|
||||
|
||||
One consequence is worth stating: a v1 file usually fails to *deserialise* before
|
||||
its version can be read, since `scenes` was a float pair and is now an object. So
|
||||
both the parse-failure path and the version-gate path must name the file — a
|
||||
rejection the user cannot attribute to a stale sidecar is the failure mode JR-003
|
||||
exists to prevent. **Gap:** none.
|
||||
|
||||
### JR-004 — A window is a scene-membership claim
|
||||
|
||||
@@ -765,8 +784,42 @@ frames whose peak bin matches — is a **consumer** concern and belongs to this
|
||||
plugin. Offsets are applied client-side per JR-030; manifests are never
|
||||
rewritten.
|
||||
|
||||
**Current:** `ComputeAudioSignatures` exists as a configuration switch. **Gap:**
|
||||
entire requirement, both computation and matching.
|
||||
**Current:** JR-042 and JR-043 hold. `AudioSignature` implements the construction
|
||||
above — band table, periodic Hann, radix-2 FFT, band-mean peak, energy class,
|
||||
packing — and `AudioSignatureService` decodes the centre window by running the
|
||||
FFmpeg binary `IMediaEncoder.EncoderPath` names, so the plugin gained no
|
||||
dependency. `fixtures/audio/` holds the extraction repo's three golden-vector
|
||||
files byte-identically, and the computed signature equals the recorded vector
|
||||
exactly (UT-038 … UT-044). The binding check regenerates the fixture PCM from
|
||||
`make_fixture.py`'s arithmetic and verifies it against the recorded decode
|
||||
checksums, so it runs on a CI host with no codec at all; the two tests that
|
||||
exercise the real FFmpeg decode self-skip without a binary.
|
||||
|
||||
JR-044 and JR-045 hold as well, both halves. `AudioSignatureMatcher` is the
|
||||
reader they were waiting on: it implements the ±600-frame slide above, scoring
|
||||
the fraction of overlapping frames whose peak band agrees, and returns the tier
|
||||
and the offset. `TryParseFrames` refuses any prefix but `v1:`, so a future
|
||||
producer's `v2:` drops the item to the runtime tier rather than being scored as
|
||||
if it were understood; and a runtime under 120 s yields no match and therefore no
|
||||
offset, read off the runtime rather than inferred from a missing string, because
|
||||
the runtime is what both producers test (UT-045 … UT-052).
|
||||
|
||||
The offset has **two terms**, which is easy to miss: the recovered slide, and the
|
||||
difference between where the two windows are anchored. Both windows are centred
|
||||
on their own file's midpoint, so unequal runtimes start them at different
|
||||
absolute times. A release carrying 40 s of extra head material recovers 20 s from
|
||||
each term.
|
||||
|
||||
One parameter is **not** from the specification: an alignment must overlap by at
|
||||
least 64 frames (~6 s) before its score counts. Without a floor the extreme
|
||||
offsets compare a handful of frames, where a chance agreement scores 1.0 and
|
||||
beats the true alignment. It never binds on the real case — two full-length
|
||||
signatures still overlap by 688 frames at the widest offset.
|
||||
|
||||
**Gap:** the signature is computed and can now be read, but nothing yet *calls*
|
||||
either: `ComputeAudioSignatures` still gates nothing, no fetch path attaches a
|
||||
signature or consults the matcher, and no contribution carries one. That wiring
|
||||
belongs to the fetch and contribute paths (JR-031, JR-034), not here.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user