Files
jRay/docs/traceability.md
T
dtourolle 1e247c4c7d feat(audio): align a fetched manifest to the local file before storing
The signature had a producer and a reader but no consumer, so nothing
ever fingerprinted anything. `ManifestAligner` runs on the fetch path,
before the windows are stored.

A local alignment supersedes the server's offset. The server has never
seen this file — its offset is a runtime-difference inference at best,
while the local comparison is against the media the windows will actually
be drawn over. It also needs no round trip, so no signature leaves the
instance. This is what jRay's spec already meant by matching being a
consumer concern: the server never rewrites a manifest, so one stored
manifest serves every trim of the same cut.

The offset has two terms and only one is in the server's pseudocode. Both
windows are centred on their own file's midpoint, so unequal runtimes
start them at different absolute times; a release with 40 s of extra head
material recovers 20 s from the slide and 20 s from the anchor
difference. Using the slide alone is wrong by half the runtime difference
on every shifted release.

Degradation, never failure. Signatures off, no manifest signature, media
under the window, a `v2:` producer, a missing binary, a decode error —
each applies the server's offset rather than refusing, because a
signature is an enhancement to cut matching and must never break a fetch.

"Un-comparable" and "does not match" are kept distinct, which a test
caught: `Compare` returns null for both, and conflating them would report
a 90-second extra as content disagreeing with its own manifest. A genuine
disagreement is stored anyway — the audio may legitimately differ, a
different language track being the obvious case — and surfaced as a
caveat that outranks the tier's, since it is the stronger statement.

The applied offset, score, slide and the local file's own signature are
written beside the truth file: the offset is otherwise unrecoverable once
the windows are shifted, and the stored signature lets a later fetch
align without decoding again. Provenance is never injected into the truth
file, so the bytes served back stay the producer's (JR-004).

`docs/audio-alignment.md` documents the mechanism end to end.

TRACES: JR-047 | SR-003
2026-07-31 16:52:21 +02:00

46 KiB

Requirements traceability matrix

Generated: 2026-07-31T14:50:36+00:00

Denominators are read from requirements.md at run time, never hardcoded. Coverage counts a requirement only when it is tagged in source and has a verification tier this repo's CI host can execute (T1, T2, static).

Summary

Metric Value
Source files scanned 78
TRACES tags found 48
EXCEPTION tags found 0
Requirements defined 47
Requirements covered 38
Coverage 80.9% (38/47)
Coverage of CI-executable scope 84.4% (38/45)
Tagged but unexecuted in CI 1
Orphan tags 0

By type

Type Covered Tagged but unexecuted Defined
JR 38 1 47
  • UT tags present (separate taxonomy, not counted in coverage): UT-001, UT-002, UT-003, UT-004, UT-005, UT-007, UT-008, UT-009, UT-010, UT-011, UT-012, UT-013, UT-014, UT-015, UT-016, UT-017, UT-018, UT-019, UT-020, UT-021, UT-022, UT-023, UT-024, UT-025, UT-026, UT-027, UT-028, UT-029, UT-030, UT-031, UT-032, UT-033, UT-034, UT-035, UT-036, UT-037, UT-038, UT-039, UT-040, UT-041, UT-042, UT-043, UT-044, UT-045, UT-046, UT-047, UT-048, UT-049, UT-050, UT-051, UT-052, UT-053, UT-054, UT-055, UT-056, UT-057
  • PR tags present (separate taxonomy, not counted in coverage): PR-001, PR-003, PR-004, PR-005, PR-006
  • SR tags present (separate taxonomy, not counted in coverage): SR-001, SR-002, SR-003, SR-004, SR-005

Not executable in CI

These requirements have no verification tier this repo's CI host can run, so a tag on them is evidence of intent, not of verification. They are never counted as covered.

ID Tiers Tagged in source Requirement
JR-020 T4 yes Pause overlay: injected client script queries jray?t= and renders t…
JR-040 T4 no The config page states plainly that **each configured server multipli…

Tagged but unexecuted: JR-020 — a test exists and is tagged, but this CI host cannot run it. Report those runs separately.

Orphan tags

A tag naming an ID requirements.md does not define. This is what renumbering produces, and what a typo produces.

None.

Requirements tracing up to nothing

A register row whose Traces to cell names no parent. Work serving no stated goal is how scope creeps in, and it is invisible unless something looks.

None.

Recorded exceptions

Deliberate, documented departures from an invariant (EXCEPTION: XX-nnn <reason>). Reported separately and never counted as coverage — an exception is a decision to be reviewed, not evidence a requirement is met.

None.

Register

ID Status Tier Traces to Trace state Tagged in Requirement
JR-001 In Progress static SR-003 covered Jellyfin.Plugin.JRay/Models/TruthFile.cs The truth-file format is normatively defined here; other repos refere…
JR-002 Done (UT-029…03… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/TruthSchemaTests.cs, Jellyfin.Plugin.JRay/Models/TruthCut.cs, Jellyfin.Plugin.JRay/Models/TruthExtraction.cs, Jellyfin.Plugin.JRay/Models/TruthFile.cs, Jellyfin.Plugin.JRay/Models/TruthScene.cs schema_version: 2 shape — extraction.* provenance block, cut.*
JR-003 Done (UT-033…03… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/TruthSchemaTests.cs, Jellyfin.Plugin.JRay/Controllers/TruthController.cs, Jellyfin.Plugin.JRay/Services/TruthSchema.cs Reject an unknown schema_version, never guess. *Flag day: v2 only
JR-004 Done (UT-021, U… T1 SR-002 covered Jellyfin.Plugin.JRay.Tests/PresenceLookupTests.cs, Jellyfin.Plugin.JRay/Controllers/ActorsController.cs, Jellyfin.Plugin.JRay/Models/TruthActor.cs, Jellyfin.Plugin.JRay/Models/TruthScene.cs, Jellyfin.Plugin.JRay/Services/PresenceLookup.cs A window is a scene-membership claim, not a recognition event — n…
JR-005 Done (UT-016…01… T1 SR-002 covered Jellyfin.Plugin.JRay.Tests/PresenceLookupTests.cs, Jellyfin.Plugin.JRay/Controllers/ActorsController.cs, Jellyfin.Plugin.JRay/Models/ActorInScene.cs, Jellyfin.Plugin.JRay/Services/PresenceLookup.cs, Jellyfin.Plugin.JRay/Web/jray-overlay.js Query semantics: actor present at t if any window contains t; pre…
JR-006 Done (UT-020, U… T1 SR-002 covered Jellyfin.Plugin.JRay.Tests/PresenceLookupTests.cs, Jellyfin.Plugin.JRay/Services/PresenceLookup.cs Read path holds up under numerous windows — no assumption of a ha…
JR-007 Done T1 SR-001 covered Jellyfin.Plugin.JRay/Models/TruthActor.cs Identity is public identifiers: prefer jellyfin_id locally, else re…
JR-008 Done T1 PR-001 covered Jellyfin.Plugin.JRay/Services/TruthDataService.cs Discover a sidecar truth file beside the media, by configurable suffix
JR-009 Done T2 PR-004 covered Jellyfin.Plugin.JRay/Controllers/TruthController.cs, Jellyfin.Plugin.JRay/Services/ManagedTruthStore.cs Accept truth data pushed by a remote worker (PUT/DELETE), admin k…
JR-010 Done (UT-024…02… T1 PR-001 covered Jellyfin.Plugin.JRay.Tests/ManagedTruthStoreTests.cs, Jellyfin.Plugin.JRay/Controllers/ActorsController.cs, Jellyfin.Plugin.JRay/Models/TruthProvenance.cs, Jellyfin.Plugin.JRay/Services/ManagedTruthStore.cs, Jellyfin.Plugin.JRay/Services/TruthDataService.cs Precedence: managed truth (pushed or fetched) overrides a sidecar…
JR-011 Done T1 PR-001 covered Jellyfin.Plugin.JRay/Services/TruthDataService.cs Loaded truth is cached; any write invalidates the item's cache entry …
JR-012 Done T2 PR-001 covered Jellyfin.Plugin.JRay/Controllers/ActorsController.cs GET .../Timeline returns the full truth file for an item
JR-013 Done T2 PR-001 covered Jellyfin.Plugin.JRay/Controllers/ActorsController.cs GET .../jray?t= returns an extensible context envelope; consume…
JR-014 Done T2 PR-004 covered Jellyfin.Plugin.JRay/Controllers/ActorsController.cs, Jellyfin.Plugin.JRay/Controllers/PolicyController.cs, Jellyfin.Plugin.JRay/Controllers/TruthController.cs, Jellyfin.Plugin.JRay/Controllers/WebController.cs Authorisation: reads need an authenticated user, admin routes need th…
JR-015 Done T2 PR-003 covered Jellyfin.Plugin.JRay/Controllers/TasksController.cs Tasks/Pending serves a random sample of items with no truth data, s…
JR-016 Done (UT-007…01… T1 PR-003 covered Jellyfin.Plugin.JRay.Tests/PolicyResolverTests.cs, Jellyfin.Plugin.JRay/Controllers/PolicyController.cs, Jellyfin.Plugin.JRay/Services/MediaPolicyStore.cs, Jellyfin.Plugin.JRay/Services/PolicyResolver.cs Prioritise/ignore rules scoped Genre / Series / Item; **most sp…
JR-017 Done T1 PR-003 covered Jellyfin.Plugin.JRay/Controllers/TasksController.cs Rules steer work discovery only — never the overlay or the read e…
JR-018 Done T1 PR-003 covered Jellyfin.Plugin.JRay/Controllers/CoverageController.cs Coverage report by media type and genre; ignored items leave the perc…
JR-019 Done T2 PR-003 covered Jellyfin.Plugin.JRay/Controllers/CoverageController.cs Picker endpoints (genres, series, item search) populate the rule edit…
JR-020 Done T4 PR-001 tagged, unexecuted Jellyfin.Plugin.JRay/Controllers/WebController.cs, Jellyfin.Plugin.JRay/Services/FileTransformationRegistration.cs, Jellyfin.Plugin.JRay/Web/jray-overlay.js Pause overlay: injected client script queries jray?t= and renders t…
JR-021 Done static PR-004 covered Jellyfin.Plugin.JRay/Services/WebClientPatchService.cs, scripts/checks/no-index-injection.sh jRay never injects into index.html on disk. File Transformation…
JR-022 Done (UT-001…00… T1 PR-004 covered Jellyfin.Plugin.JRay.Tests/WebClientPatchServiceTests.cs, Jellyfin.Plugin.JRay/Services/WebClientPatchService.cs Migration: remove any on-disk patch left by an earlier jRay, identifi…
JR-023 Done (UT-012…01… T1, T4 PR-004 covered Jellyfin.Plugin.JRay.Tests/FileTransformationRegistrationTests.cs, Jellyfin.Plugin.JRay/Controllers/StatusController.cs, Jellyfin.Plugin.JRay/Services/FileTransformationRegistration.cs Absent the dependency, disable only the overlay and say so in the…
JR-024 Done T1 SR-004 covered Jellyfin.Plugin.JRay/Web/jray-overlay.js Actor names and all server-supplied strings render as **text, never m…
JR-025 Done T1 PR-006 covered Jellyfin.Plugin.JRay/Configuration/ManifestServer.cs, Jellyfin.Plugin.JRay/Controllers/ManifestController.cs, Jellyfin.Plugin.JRay/Models/Jmanifest.cs, Jellyfin.Plugin.JRay/Services/Interfaces/IManifestExchangeClient.cs, Jellyfin.Plugin.JRay/Services/ManifestExchangeClient.cs Query an ordered list of servers; first result clearing the confi…
JR-026 Planned T1 PR-006 untagged - For a series, first-match applies per episode — later servers are…
JR-027 Done T1 SR-004 covered Jellyfin.Plugin.JRay/Models/Jmanifest.cs, Jellyfin.Plugin.JRay/Services/ManifestValidator.cs Treat every server as untrusted, including the default: re-valida…
JR-028 Done T1 SR-004 covered Jellyfin.Plugin.JRay/Services/ManifestExchangeClient.cs Enforce response size caps while streaming — 2 MiB single, 25 MiB…
JR-029 Done T1 SR-004 covered Jellyfin.Plugin.JRay/Services/ManifestExchangeClient.cs HTTPS required for non-loopback servers; certificate validation must …
JR-030 Done T1 SR-003 covered Jellyfin.Plugin.JRay/Services/ManifestConverter.cs, Jellyfin.Plugin.JRay/Services/ManifestExchangeClient.cs Apply an audio-tier offset to every window before storing — s…
JR-031 Planned T2 PR-006 covered Jellyfin.Plugin.JRay/Controllers/ManifestController.cs Fetch endpoints: item fetch, series bundle fetch, per-server status, …
JR-032 Planned T1 PR-006 untagged - Identify is never automatic — storing a candidate is a separate c…
JR-033 Planned T1 PR-006 untagged - Scheduled sweep over items lacking truth data, using the batch `e…
JR-034 Planned T1 PR-005 untagged - Contribution strips movie and jellyfin_id, attaches identity from…
JR-035 Planned T1 PR-006 untagged - Uploads set Expect: 100-continue, so a rejection lands before a bun…
JR-036 In Progress T1 PR-006 covered Jellyfin.Plugin.JRay/Configuration/PluginConfiguration.cs, Jellyfin.Plugin.JRay/Models/TruthProvenance.cs Minimum accepted match tier is configurable; a loose match surfaces…
JR-037 Done T1 PR-006 covered Jellyfin.Plugin.JRay/Services/ManifestExchangeClient.cs A server that is unreachable or failing is skipped on a short timeout…
JR-038 Done T1 PR-005 covered Jellyfin.Plugin.JRay/Configuration/PluginConfiguration.cs Every exchange feature is opt-in and off by default, including th…
JR-039 Planned T1 PR-005 untagged - No library-wide inventory in one request: batch exists capped at 10…
JR-040 Planned T4 PR-005 untagged - The config page states plainly that **each configured server multipli…
JR-041 Done static SR-005 covered scripts/checks/no-gallery-data.sh The plugin never fetches, stores, or transmits gallery data — referen…
JR-042 Done (UT-039…04… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/AudioSignatureTests.cs, Jellyfin.Plugin.JRay/Services/AudioSignature.cs, Jellyfin.Plugin.JRay/Services/AudioSignatureService.cs Compute the signature exactly per server spec §3, using the FFmpe…
JR-043 Done (UT-038, U… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/AudioSignatureTests.cs Golden-vector fixture shared with the extraction repo, proving th…
JR-044 Done (UT-045, U… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/AudioSignatureMatcherTests.cs, Jellyfin.Plugin.JRay/Services/AudioSignatureMatcher.cs Media shorter than 120 s: emit no signature and apply no sync offset …
JR-045 Done (UT-047…04… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/AudioSignatureMatcherTests.cs, Jellyfin.Plugin.JRay/Services/AudioSignatureMatcher.cs Emit and honour the signature's own v1: prefix, so a DSP change is …
JR-046 TBD T2, T4 [system §4](../scripts/vendor/jray-proj… untagged - Review UI for unidentified track clusters: show context crops, pick f…
JR-047 Done (UT-053…05… T1 SR-003 covered Jellyfin.Plugin.JRay.Tests/ManifestAlignerTests.cs, Jellyfin.Plugin.JRay/Models/TruthAlignment.cs, Jellyfin.Plugin.JRay/Services/ManifestAligner.cs **A fetched manifest is aligned against the local file before its win…

Detailed mapping

JR-001

Locations: 1

JR-002

Locations: 5

JR-003

Locations: 3

JR-004

Locations: 5

JR-005

Locations: 5

JR-006

Locations: 2

JR-007

Locations: 1

JR-008

Locations: 1

JR-009

Locations: 2

JR-010

Locations: 5

JR-011

Locations: 1

JR-012

Locations: 1

JR-013

Locations: 1

JR-014

Locations: 4

JR-015

Locations: 1

JR-016

Locations: 4

JR-017

Locations: 1

JR-018

Locations: 1

JR-019

Locations: 1

JR-020

Locations: 3

JR-021

Locations: 2

JR-022

Locations: 2

JR-023

Locations: 3

JR-024

Locations: 1

JR-025

Locations: 5

JR-027

Locations: 2

JR-028

Locations: 1

JR-029

Locations: 1

JR-030

Locations: 2

JR-031

Locations: 1

JR-036

Locations: 2

JR-037

Locations: 1

JR-038

Locations: 1

JR-041

Locations: 1

JR-042

Locations: 3

JR-043

Locations: 1

JR-044

Locations: 2

JR-045

Locations: 2

JR-047

Locations: 3

PR-001

Locations: 3

PR-003

Locations: 5

PR-004

Locations: 5

PR-005

Locations: 3

PR-006

Locations: 4

SR-001

Locations: 1

SR-002

Locations: 7

SR-003

Locations: 13

SR-004

Locations: 3

SR-005

Locations: 1

UT-001

Locations: 1

UT-002

Locations: 1

UT-003

Locations: 1

UT-004

Locations: 1

UT-005

Locations: 1

UT-007

Locations: 1

UT-008

Locations: 1

UT-009

Locations: 1

UT-010

Locations: 1

UT-011

Locations: 1

UT-012

Locations: 1

UT-013

Locations: 1

UT-014

Locations: 1

UT-015

Locations: 1

UT-016

Locations: 1

UT-017

Locations: 1

UT-018

Locations: 1

UT-019

Locations: 1

UT-020

Locations: 1

UT-021

Locations: 1

UT-022

Locations: 1

UT-023

Locations: 1

UT-024

Locations: 1

UT-025

Locations: 1

UT-026

Locations: 1

UT-027

Locations: 1

UT-028

Locations: 1

UT-029

Locations: 1

UT-030

Locations: 1

UT-031

Locations: 1

UT-032

Locations: 1

UT-033

Locations: 1

UT-034

Locations: 1

UT-035

Locations: 1

UT-036

Locations: 1

UT-037

Locations: 1

UT-038

Locations: 1

UT-039

Locations: 1

UT-040

Locations: 1

UT-041

Locations: 1

UT-042

Locations: 1

UT-043

Locations: 1

UT-044

Locations: 1

UT-045

Locations: 1

UT-046

Locations: 1

UT-047

Locations: 1

UT-048

Locations: 1

UT-049

Locations: 1

UT-050

Locations: 1

UT-051

Locations: 1

UT-052

Locations: 1

UT-053

Locations: 1

UT-054

Locations: 1

UT-055

Locations: 1

UT-056

Locations: 1

UT-057

Locations: 1