Removes `cut.video_hash` and the `exact` match tier on legal grounds. The
OpenSubtitles hash was the strongest technical signal available — it identifies
a specific file, so it cannot produce a false positive — and that is exactly
the problem.
Every tier must be a claim about a *cut*, never about a copy. A TMDB id
discloses "some copy of this film", which is what a library catalogue
discloses. A file hash discloses "this exact release": it made a read endpoint
into a release-level oracle, and made an instance's database a mapping from
file fingerprints to the instances holding them. That is a far more specific
disclosure than PR-005 permits, and a dataset no volunteer operator should be
asked to hold. The audio signature is the replacement: derived from content, it
identifies the cut rather than the copy, so two encodes of the same edit agree.
The field is deleted rather than kept as a vestigial null, on the same
reasoning §2 applied to `anneal_sec` — a key naming a signal the format no
longer has is actively misleading — so an upload carrying one is now an
unknown-field 400, with a test asserting it.
**Every content_id changes**, including for manifests that never carried a
hash, because the canonical `cut` object lost a key. The golden vector is
regenerated and re-verified against an independent Python implementation; the
plugin and extraction repos must adopt the new value or federation
deduplication silently breaks. Free now, pre-release; not free later.
Adds docs/legal-posture.md, the operator-facing half of what §5a asks for:
what an instance holds exhaustively, what it structurally cannot do, and how
that sits against the intermediary-liability regimes that plausibly apply.
208 tests. Coverage 25/32.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TRACES: UR-011 | SR-004, PR-005
Adds jray-project as a submodule at scripts/vendor/jray-project, so this repo
runs the same extractor as every other component rather than its own copy, and
gains the system spec that defines the PR/SR requirements its register traces
up to.
scripts/traceability-gate.sh is a thin wrapper holding only what is specific to
this repo: UR/DR prefixes, .rs sources, and REPO_ROOT — which the shared gate
cannot infer once vendored, since its default resolves to the submodule itself.
Each override fails silently in a way that looks like "no work done" rather
than "misconfigured", so the wrapper documents why each is needed.
Annotates 35 units with TRACES tags, on the code that decides rather than every
helper it calls. Coverage is 23/32 (71.9%) with no orphan tags. The nine
untraced are genuinely unimplemented: UR-007 is plugin-side, UR-008 is
federation, and UR-015..018 are the pending SR-003 schema bump.
The gate caught a real error in the first pass: several tags separated IDs of
different types with commas. A comma joins IDs within one type; a pipe
separates types. Fixed, and the diagnostics are now clean.
MIN_COVERAGE stays 0 deliberately. The gate still fails on orphan tags, a >100%
ratio, a register parsing to nothing, or an empty source scan — raise the
threshold as a ratchet once the remaining work lands.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Implements the core of SPEC.md — the manifest exchange, less audio-tier
matching (§3) and federation (§9a), both of which the spec sequences as
later work.
- §2 Jmanifest format and series bundles
- §3 cut matching: exact / runtime / loose tiers
- §4 API, less POST /manifests/search
- §5 rate limiting; §5a trust model, anonymous bearer tokens
- §6 upload validation, all four stages
- §7 relational storage, no JSON blob on the write path
- §8 Rust + Axum + SQLite, single serialized writer, in-process job queue
- §9a content addressing, computed on upload
Reconciled against the system spec:
- anneal_sec removed, withdrawn upstream by AR-012/AR-013. Presence follows
track extent, so a track survives its own gaps and there is nothing to
anneal. Its successor extinction_sec and the new gallery_scope are accepted
and stored; scope enters the §7 ranking. A manifest still carrying
anneal_sec is a hard 400, not silently ignored — it came from a pipeline
whose window semantics differ from what this server assumes.
- Audio signature: media under 120 s now emits no signature at all, matching
scene-actor-extraction IR-007. The earlier §3 draft allowed a shortened
window under 150 s, which was the weaker rule — a caller-varying length is
the property SR-004 forbids.
- UR IDs regularised to UR-nnn; docs/requirements.md registers 32
requirements, each tracing to an SR-nnn or PR-nnn.
189 tests: unit, end-to-end through the real router, and an injection suite
covering SQL, JSON, header and Unicode payloads. Writing that suite found two
real gaps, both fixed here: compatibility homoglyphs passed the §5a character
class, and a one-frame audio signature was accepted on a feature-length item.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>