Commit Graph
11 Commits
Author SHA1 Message Date
dtourolle e8ce779ad3 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
2026-07-31 16:24:40 +02:00
dtourolleandClaude Opus 5 5152f6f129 JR-010: record how truth data was obtained
🏗️ Build Plugin / build (push) Successful in 31s
Latest Release / latest-release (push) Successful in 43s
🧪 Test Plugin / test (push) Successful in 27s
Three routes now deliver truth data -- a sidecar, a worker push, and a fetched
manifest -- and once stored they were indistinguishable. The truth file records
nothing about how it arrived, so a locally computed sidecar and a loose-tier
manifest from a third-party server looked identical to every reader, despite
making claims of very different strength about the same item.

TruthProvenance records source, server, match tier, applied offset and caveat.
GET /Items/{itemId}/Provenance serves it, and JR-036's loose-tier caveat now
has somewhere to come from.

Two decisions carried in the code rather than assumed:

Provenance is stored BESIDE the truth file, never inside it. Injecting fields
would mean the bytes served back are not the bytes the producer wrote, which is
the property JR-004 turns on. UT-026 pins it by asserting the stored truth JSON
contains no provenance keys.

The applied offset is recorded because it is otherwise unrecoverable. Once
JR-030 shifts every window the timings look native, and nothing else would say
they had been shifted -- which matters when diagnosing an overlay that is
consistently a few seconds out.

A sidecar's provenance is derived rather than stored: it is local, and its
timestamp is the file's own. Precedence resolves through the same rule as
GetTruthAsync, because resolving it twice by different rules is how the two
would drift.

Fourth mutation check: stopping Delete from removing provenance fails UT-027
alone -- a stale record would otherwise outlive its claim and describe data the
next fetch had already replaced.

TRACES: UT-024, UT-025, UT-026, UT-027, UT-028 | JR-010

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:36:08 +02:00
dtourolleandClaude Opus 5 c04d5a3dcc JR-004, JR-005, JR-006: scene-scoped read path
Presence was decided by a LINQ predicate inline in the controller, so the
semantics SR-002 sets were nowhere stated in code -- the read path complied by
accident rather than by requirement. PresenceLookup is now the unit that
decides, tagged, with the reasoning next to it.

JR-004: windows are served exactly as given. UT-021 pins that [0,10] and
[10,20] are not merged despite looking mergeable -- two windows mean a genuine
departure and return, and collapsing them answers a different question from the
one the truth file asked. UT-022 pins a byte-identical round trip.

JR-005: bounds inclusive at both ends, zero-length windows are real sightings
rather than degenerate ones to discard, overlaps resolve.

The wording was the larger half of JR-005. The overlay rendered a bare list: it
asserted nothing, but told the viewer nothing either, and the default reading of
a paused frame is "these people are on screen" -- exactly what SR-002 forbids.
It now carries an "In this scene" heading. ActorAtTime became ActorInScene, and
README no longer contains "on screen" anywhere; it stated the forbidden reading
outright in seven places, including the opening sentence.

JR-006: measured rather than assumed. UT-023 builds 50 actors x 1000 windows and
asserts the response is bounded by actor count, never window count. The lookup
is a full scan on purpose -- an early exit on `start > t` would exploit the
sortedness the format requires, but would silently under-report the moment one
producer emitted windows out of order. UT-020 pins that unsorted input still
resolves; WindowsAreSorted is a diagnostic, not a correctness dependency.

Third mutation check: making the end bound exclusive fails UT-016 and UT-018 and
nothing else. One character turns an inclusive window into a half-open one,
dropping an actor at exactly the moment a scene ends.

TRACES: UT-016, UT-017, UT-018, UT-019, UT-020, UT-021, UT-022, UT-023
TRACES: JR-004, JR-005, JR-006 | SR-002

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:29:46 +02:00
dtourolleandClaude Opus 5 3d210b5bd3 Manifest fetch across the configured servers (JR-025 … JR-037)
🏗️ Build Plugin / build (push) Successful in 44s
Latest Release / latest-release (push) Successful in 40s
🧪 Test Plugin / test (push) Successful in 26s
Satisfies JRay-public-server UR-007. Servers are tried in configured order and
the first result clearing the configured tier wins; first-match rather than
best-match because querying every server for every item multiplies egress and
leaks the library to more parties, and the ordering already encodes which
source the admin prefers.

Every server is untrusted, including the pre-configured community one, so a
fetched manifest is re-validated against the same rules the server applies on
upload: envelope version refused if unknown, identifiers format-checked,
windows bounds-checked against the *local* file's runtime, belief bounded to
[0, 1], control and bidi characters refused in names. Responses are capped
while streaming rather than after buffering, since a hostile server can declare
any Content-Length it likes. HTTPS is required away from loopback. A failing
server is skipped with exponential backoff so one dead server cannot stall a
sweep.

The audio-tier offset is applied once, at store time, so stored truth is always
in the local file's own timebase and no read path needs offset awareness.
Windows are shifted, never reshaped — merging adjacent ones would answer "was a
face visible" rather than "was the actor present" (SR-002).

Also records why there is no `exact` tier, which was missing and led me to
re-add one. The file-hash tier is withdrawn on legal grounds: a TMDB id
discloses "some copy of this film", but an OpenSubtitles hash discloses "this
exact release", which turns a catalogue lookup into a release-identification
service and a server's database into a mapping from file fingerprints to the
instances holding them. The reason now lives on MatchTier and in SPEC.md §JR-036,
`TitleQuery` has no VideoHash property so there is nothing to send, and a test
asserts the enum has no Exact member — the spec had still listed `exact` as a
configurable tier, which is what made the removal look like an oversight.

42 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

TRACES: JR-025, JR-027, JR-028, JR-029, JR-030, JR-031, JR-036, JR-037 | PR-005, PR-006
2026-07-31 10:03:49 +02:00
dtourolleandClaude Opus 5 2926740d03 JR-023: correct the missing-dependency logs, and test them
🏗️ Build Plugin / build (push) Successful in 29s
Latest Release / latest-release (push) Successful in 39s
🧪 Test Plugin / test (push) Successful in 26s
Both failure paths still told admins the overlay was "falling back to patching
index.html on disk". JR-021 deleted that fallback, so the message was false --
and it was false in the worst place, since an admin reads it precisely when
debugging a missing overlay and would go hunting for a patch that no longer
exists. They now name the install URL and say the overlay is disabled while
every other feature is unaffected. The not-found case is a Warning, not
Information: a headline feature being off should not sit among startup chatter.

UT-012..015 cover the branch. The File Transformation assembly is genuinely
absent from the test host, so TryRegister exercises its real not-found path
rather than a seam invented for the test. UT-015 pins that a null payload
returns empty rather than throwing -- this callback runs inside another
plugin's request path on every page served, so throwing would break the web
client itself, not just JRay's overlay.

Making those runnable needed the test project to reference Jellyfin.Controller
and Jellyfin.Model without the plugin's ExcludeAssets=runtime. My earlier claim
that DisableTransitiveFrameworkReferences alone sufficed was too narrow: it
drops the demand for the web *framework*, but ILogger and MediaBrowser.Common
live in the excluded assets, so anything past dependency-free logic failed at
run time with FileNotFoundException. Both settings are needed, and the register
now says so.

Second mutation check: downgrading the warning to Information fails UT-013 and
nothing else. Source restored and re-verified.

JR-023 reaches Done for the detection half. The config-page banner stays T4 --
verifiable only against a live server.

TRACES: UT-012, UT-013, UT-014, UT-015 | JR-023

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:18:40 +02:00
dtourolleandClaude Opus 5 3b24fe1b3c Requirements register, spec rewrite, and TRACES tags
jRay had no requirement IDs, so nothing in this repo could be traced to and
the CI gate had no denominator to read. The other two components had already
moved to registers; this brings the plugin level with them.

Adds docs/requirements.md with 46 permanent JR-nnn IDs, each carrying a parent
requirement, priority, status and verification tier, plus a per-requirement
verification plan. JR is flat rather than split by theme: the plugin is one
deployable with one audience, and JRay-public-server already ships UR/DR, so a
second repo using those prefixes would make UR-007 ambiguous across registers.

Rewrites SPEC.md as requirements prose with Current:/Gap: on every one. It had
drifted into a format-plus-API reference that documented schema_version 1 while
owning a format whose v2 shape was specified only in the other two repos, said
nothing about SR-002's scene-scoped semantics, and carried the manifest
exchange as a "planned" aside while its configuration classes were already
implemented. Plugin-side exchange obligations move here from the server's
spec, where they were an ownership inversion.

Adds JR-038..041 for PR-005, which had no software row in any repo -- it was
held structurally by SR-004 and GR-005 both being prohibitions, and a goal
preserved only by prohibitions is the kind that erodes unnoticed. jRay is the
component that actually opens a socket.

Tags 18 units with the requirements they satisfy. Tags name what the code
satisfies, so FileTransformationRegistration is not tagged JR-021: that
requirement is a prohibition and was still violated elsewhere when this was
written.

Vendors jray-project as a submodule for the system spec and shared gate.

TRACES: JR-001, JR-004, JR-005, JR-007, JR-008, JR-009, JR-010, JR-011
TRACES: JR-012, JR-013, JR-014, JR-015, JR-016, JR-017, JR-018, JR-019
TRACES: JR-020, JR-024, JR-025, JR-036, JR-038

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 18:57:48 +02:00
dtourolle f6762fcf29 feat: prioritise and blacklist media and overview in setting page of progress in adding info
🏗️ Build Plugin / build (push) Successful in 1m16s
Latest Release / latest-release (push) Successful in 27s
🧪 Test Plugin / test (push) Successful in 20s
🚀 Release Plugin / build-and-release (push) Successful in 24s
2026-07-04 21:08:59 +02:00
dtourolle f11e5508ad update readme and spec for current release 2026-06-28 12:16:07 +02:00
dtourolle 51a7365815 update spec
🏗️ Build Plugin / build (push) Successful in 22s
Latest Release / latest-release (push) Successful in 22s
🧪 Test Plugin / test (push) Successful in 18s
🚀 Release Plugin / build-and-release (push) Successful in 25s
2026-06-12 19:03:39 +02:00
dtourolle f1cffa7dfa Added work remaining url for remote extraction client 2026-06-12 19:03:31 +02:00
dtourolle a38122e993 first commit
🏗️ Build Plugin / build (push) Successful in 1m13s
🧪 Test Plugin / test (push) Successful in 20s
Latest Release / latest-release (push) Successful in 25s
2026-06-12 18:16:47 +02:00