Files
JRay-public-server/docs/traceability.md
T
dtourolle c41253ef5c feat(audio): store, serve and match the v1 audio signature
Completes UR-009. The register recorded the signature as stored; it was
not. `ingest` validated `cut.audio_signature` and wrote NULL, so every
served manifest came back without one — which also meant the plugin's own
alignment (jRay JR-047, Done) had nothing to align against and could never
run. That is the failure mode a status field is least able to catch: every
validation test passed and the feature delivered nothing.

Now stored, coarse-indexed and served back byte-identically, with a held
manifest adopting an incoming signature it lacked (§9a). `audio`-tier
matching runs on every read endpoint via an `audio_signature` parameter,
and `POST /manifests/search` answers the unknown-providence case with a
runtime prefilter, a bounded scan and honest truncation reporting.

Three rules §3 did not previously state, now normative:

- **±1 frame of slack in the score.** scene-actor-extraction VR-014
  measured the exact-frame rule demoting 27 of 40 correctly aligned
  releases to `loose`, because the two windows are cut on their own
  file's frame grid and those grids do not coincide. With ±1 frame all
  40 reach `audio` (worst 0.906) and the strongest false match is
  unmoved at 0.16.
- **The offset has two terms.** Both windows are anchored at their own
  file's runtime/2, so the slide alone is wrong by half the runtime
  difference on every shifted release. A signature without a runtime
  therefore cannot align, and is refused by name rather than answered
  at a lower tier.
- **A signature verdict is final**, including its refusals. Falling back
  to the runtime tier after the audio declined would let a coincidence
  overturn direct evidence, inverting the ordering the tier table exists
  to state.

The slide precomputes each frame's neighbourhood as a 32-bit bin set
rather than re-deriving it across 1201 slides — 3.3 ms to 1.1 ms per
candidate, with a test asserting exact equivalence to the rule written
the obvious way. The 1000-candidate search cap follows from that
measurement as a ~1.1 s ceiling per request, not a round number.

jRay's matcher still implements the pre-slack rule and will label some
alignments `loose` that this server calls `audio`. Nothing misaligns —
JR-047 makes the local answer win — but that register now carries the
follow-up.

TRACES: UR-008, UR-009 | SR-003
2026-07-31 22:43:26 +02:00

483 lines
27 KiB
Markdown

# Requirements traceability matrix
<!-- GENERATED FILE - do not edit by hand. -->
<!-- Regenerate: scripts/traceability/traceability-gate.sh -->
**Generated:** 2026-07-31T20:41:09+00:00
Denominators are read from [`requirements.md`](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 | 32 |
| TRACES tags found | 69 |
| EXCEPTION tags found | 0 |
| Requirements defined | 33 |
| Requirements covered | 31 |
| **Coverage** | **93.9%** (31/33) |
| Coverage of CI-executable scope | 93.9% (31/33) |
| Tagged but unexecuted in CI | 0 |
| Orphan tags | 0 |
### By type
| Type | Covered | Tagged but unexecuted | Defined |
|---|---|---|---|
| UR | 19 | 0 | 19 |
| DR | 12 | 0 | 14 |
- **PR** tags present (separate taxonomy, not counted in coverage): 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.
_None._
## 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 |
|---|---|---|---|---|---|---|
| UR-001 | Done | T2 | SR-001 | covered | `src/api/exists.rs`, `src/matching.rs`, `tests/audio.rs` | Cheap existence probe, separate from the fetch, returning availabilit… |
| UR-002 | Done | T2 | PR-006 | covered | `src/api/upload.rs`, `src/ingest.rs`, `tests/audio.rs` | Accept a contributed manifest for a media item |
| UR-003 | Done | T1, T2 | SR-004 | covered | `src/api/upload.rs`, `src/castcheck.rs`, `src/model.rs`, `src/validate.rs`, `src/worker.rs` | Content verification: strict schema, size caps, approximate TMDB cast… |
| UR-004 | Done | T1, T2 | SR-004 | covered | `src/auth.rs`, `src/ratelimit.rs` | Rate limiting, per token where present and per source IP otherwise |
| UR-005 | Done | T1, T2 | SR-004 | covered | `src/api/report.rs`, `src/api/upload.rs`, `src/auth.rs`, `src/castcheck.rs`, `src/worker.rs` | Trust without accounts: not usable as a content store, nor for prank … |
| UR-006 | Done | T2 | PR-006 | covered | `src/api/fetch.rs`, `src/api/upload.rs`, `src/validate.rs` | Serve and accept a whole series in one operation |
| UR-007 | In Progress | unset | PR-005 | covered | `src/api/exists.rs` | Plugin queries an ordered, configurable list of servers |
| UR-008 | Done | T1, T2 | PR-006 | covered | `src/api/federation.rs`, `src/db/repo.rs`, `src/worker.rs`, `tests/audio.rs` | Servers replicate manifests between each other |
| UR-009 | Done | T1, T2 | SR-003 | covered | `src/api/mod.rs`, `src/api/search.rs`, `src/audio_sig.rs`, `src/db/repo.rs`, `src/matching.rs`, `src/validate.rs`, `tests/audio.rs` | Store an audio spectral-peak signature for content-based identificati… |
| UR-010 | Done | T1, T2 | SR-001 | covered | `src/api/fetch.rs`, `src/castcheck.rs`, `src/db/repo.rs`, `src/model.rs` | Identity crossing the API boundary is TMDB/IMDB ids, never a name alo… |
| UR-011 | Done | T2 | SR-004 | covered | `src/model.rs`, `src/validate.rs`, `tests/audio.rs` | Reject any field capable of carrying binary or attacker-chosen content |
| UR-012 | Done | T2 | SR-005 | covered | `src/db/repo.rs`, `src/ingest.rs` | Never accept, store, or serve gallery data — reference faces or embed… |
| UR-013 | Done | T1 | SR-002 | covered | `src/api/fetch.rs`, `src/model.rs`, `src/validate.rs` | Windows are scene-scoped claims; never reinterpret their boundaries |
| UR-014 | Done | T1 | SR-003 | covered | `src/api/federation.rs`, `src/model.rs`, `src/validate.rs`, `tests/audio.rs` | Reject an unknown `jmanifest_version` outright, never guess |
| UR-015 | Done | T2 | SR-003 | covered | `src/validate.rs`, `tests/api.rs` | Accept `extraction.extinction_sec` in place of `anneal_sec` |
| UR-016 | Done | T2 | SR-003 | covered | `src/validate.rs`, `tests/api.rs` | Accept and store `extraction.gallery_scope`; rank on it (§7) |
| UR-017 | Done | T1, T2 | SR-003 | covered | `src/model.rs` | Accept per-window belief and identification route; `scenes` are objec… |
| UR-018 | Done | T1 | SR-003 | covered | `src/ingest.rs` | Exclude belief and route from `content_id`, replicating them as attri… |
| UR-019 | Done | T2 | PR-006 | covered | `src/api/upload.rs` | Contributed manifests are CC0 1.0; the grant is delivered with the to… |
| DR-001 | Done | T1 | SR-004 | covered | `src/model.rs`, `tests/api.rs` | Strict parse boundary: unknown fields rejected structurally, not by v… |
| DR-002 | Done | T1 | SR-004 | covered | `src/api/fetch.rs`, `src/db/repo.rs`, `tests/audio.rs` | Fully relational storage — no JSON blob on the write path |
| DR-003 | Done | T1 | PR-004 | covered | `src/db/mod.rs` | Single serialized writer connection, with a read pool alongside |
| DR-004 | Done | T1 | PR-004 | covered | `src/db/repo.rs` | All database access behind a repository layer, not scattered through … |
| DR-005 | Done | T1 | PR-004 | covered | `src/db/repo.rs` | Background work in-process, with the job queue as a table so it survi… |
| DR-006 | Done | T1 | PR-004 | covered | `src/ratelimit.rs` | Rate-limit counters in process memory; no external counter store |
| DR-007 | Done | static | PR-004 | untagged | - | Ship a single static binary plus one database file; container optional |
| DR-008 | Done | T2 | SR-004 | covered | `src/auth.rs`, `src/config.rs` | `X-Forwarded-For` honoured only from explicitly configured proxies |
| DR-009 | Done | T2 | SR-004 | covered | `src/app.rs` | Body caps enforced while streaming, before parsing, per route |
| DR-010 | Done | T1 | SR-003 | covered | `src/api/json.rs` | Request bodies are UTF-8 only, rejected with a diagnosable error othe… |
| DR-011 | Done | T1 | SR-003 | covered | `src/content_id.rs`, `src/validate.rs` | `content_id` canonical form is byte-stable and cross-implementation t… |
| DR-012 | Done | static | PR-004 | untagged | - | Dependency audit: advisories, licence policy, source policy |
| DR-013 | Done | T1 | SR-003 | covered | `src/api/json.rs`, `src/app.rs`, `src/error.rs`, `tests/audio.rs` | API errors use the status codes the spec names, not the framework's d… |
| DR-014 | Done | static | PR-004 | covered | `src/db/mod.rs` | Portable SQL — no SQLite-specific form where a standard one exists |
## Detailed mapping
### DR-001
**Locations:** 2
- [`src/model.rs:324`](../src/model.rs#L324) — `fn unknown_field_at_top_level_is_rejected()`
- [`tests/api.rs:279`](../tests/api.rs#L279) — `async fn unknown_field_anywhere_is_rejected_with_400()`
### DR-002
**Locations:** 4
- [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(`
- [`src/db/repo.rs:426`](../src/db/repo.rs#L426) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
- [`src/db/repo.rs:500`](../src/db/repo.rs#L500) — `pub fn actors_for_manifest(`
- [`tests/audio.rs:258`](../tests/audio.rs#L258) — `async fn a_contributed_signature_survives_storage_byte_for_byte()`
### DR-003
**Locations:** 1
- [`src/db/mod.rs:36`](../src/db/mod.rs#L36) — `struct ReadPool`
### DR-004
**Locations:** 1
- [`src/db/repo.rs:426`](../src/db/repo.rs#L426) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
### DR-005
**Locations:** 1
- [`src/db/repo.rs:797`](../src/db/repo.rs#L797) — `pub fn lease_jobs(tx: &Transaction<'_>, now: &str, limit: usize) -> anyhow::Result<Vec<Jo…`
### DR-006
**Locations:** 1
- [`src/ratelimit.rs:93`](../src/ratelimit.rs#L93) — `impl Default for RateLimiter`
### DR-008
**Locations:** 2
- [`src/auth.rs:76`](../src/auth.rs#L76) — `pub fn client_ip(headers: &HeaderMap, peer: Option<IpAddr>, trusted_proxies: &[IpAddr]) -…`
- [`src/config.rs:10`](../src/config.rs#L10) — `Unknown`
### DR-009
**Locations:** 1
- [`src/app.rs:26`](../src/app.rs#L26) — `pub fn router(state: AppState) -> Router`
### DR-010
**Locations:** 1
- [`src/api/json.rs:100`](../src/api/json.rs#L100) — `fn require_utf8(bytes: &[u8]) -> Result<&str, ApiError>`
### DR-011
**Locations:** 3
- [`src/content_id.rs:57`](../src/content_id.rs#L57) — `pub fn canonical_json(`
- [`src/content_id.rs:132`](../src/content_id.rs#L132) — `pub fn content_id(`
- [`src/validate.rs:102`](../src/validate.rs#L102) — `pub fn to_centiseconds(secs: f64) -> i64`
### DR-013
**Locations:** 5
- [`src/api/json.rs:100`](../src/api/json.rs#L100) — `fn require_utf8(bytes: &[u8]) -> Result<&str, ApiError>`
- [`src/app.rs:26`](../src/app.rs#L26) — `pub fn router(state: AppState) -> Router`
- [`src/error.rs:8`](../src/error.rs#L8) — `Unknown`
- [`tests/audio.rs:442`](../tests/audio.rs#L442) — `async fn a_signature_without_a_runtime_is_refused_by_name()`
- [`tests/audio.rs:460`](../tests/audio.rs#L460) — `async fn a_malformed_signature_is_a_bad_request_not_a_silent_downgrade()`
### DR-014
**Locations:** 1
- [`src/db/mod.rs:30`](../src/db/mod.rs#L30) — `pub mod repo;`
### PR-004
**Locations:** 4
- [`src/config.rs:10`](../src/config.rs#L10) — `Unknown`
- [`src/db/mod.rs:30`](../src/db/mod.rs#L30) — `pub mod repo;`
- [`src/db/mod.rs:36`](../src/db/mod.rs#L36) — `struct ReadPool`
- [`src/db/repo.rs:797`](../src/db/repo.rs#L797) — `pub fn lease_jobs(tx: &Transaction<'_>, now: &str, limit: usize) -> anyhow::Result<Vec<Jo…`
### PR-005
**Locations:** 4
- [`src/api/exists.rs:74`](../src/api/exists.rs#L74) — `pub async fn exists_batch(`
- [`src/api/federation.rs:212`](../src/api/federation.rs#L212) — `pub async fn get_peers(`
- [`src/api/search.rs:113`](../src/api/search.rs#L113) — `pub async fn post_search(`
- [`tests/audio.rs:480`](../tests/audio.rs#L480) — `async fn search_identifies_a_file_with_no_metadata_at_all()`
### PR-006
**Locations:** 10
- [`src/api/federation.rs:66`](../src/api/federation.rs#L66) — `pub async fn get_changes(`
- [`src/api/federation.rs:108`](../src/api/federation.rs#L108) — `pub async fn get_manifest_by_content_id(`
- [`src/api/federation.rs:160`](../src/api/federation.rs#L160) — `pub async fn post_have(`
- [`src/api/fetch.rs:127`](../src/api/fetch.rs#L127) — `pub async fn get_series(`
- [`src/api/upload.rs:29`](../src/api/upload.rs#L29) — `pub async fn post_manifest(`
- [`src/api/upload.rs:101`](../src/api/upload.rs#L101) — `pub async fn post_bundle(`
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`
- [`src/ingest.rs:50`](../src/ingest.rs#L50) — `pub fn persist(`
- [`src/validate.rs:572`](../src/validate.rs#L572) — `pub fn validate_bundle_envelope(b: &SeriesBundle) -> VResult<()>`
- [`src/worker.rs:107`](../src/worker.rs#L107) — `async fn run_federation_pull(&self, payload: &str) -> Result<(), JobError>`
### SR-001
**Locations:** 7
- [`src/api/exists.rs:61`](../src/api/exists.rs#L61) — `pub async fn exists(`
- [`src/api/exists.rs:74`](../src/api/exists.rs#L74) — `pub async fn exists_batch(`
- [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(`
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
- [`src/db/repo.rs:500`](../src/db/repo.rs#L500) — `pub fn actors_for_manifest(`
- [`src/matching.rs:94`](../src/matching.rs#L94) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option<CutMatch>`
- [`src/model.rs:174`](../src/model.rs#L174) — `Unknown`
### SR-002
**Locations:** 4
- [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(`
- [`src/model.rs:174`](../src/model.rs#L174) — `Unknown`
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_stored(s: &str) -> Option<Self>`
- [`src/validate.rs:496`](../src/validate.rs#L496) — `fn validate_scenes(idx: usize, a: &Actor, runtime_sec: f64) -> VResult<Vec<SceneCs>>`
### SR-003
**Locations:** 32
- [`src/api/federation.rs:261`](../src/api/federation.rs#L261) — `pub async fn get_capabilities(State(state): State<AppState>) -> ApiResult<Response>`
- [`src/api/json.rs:100`](../src/api/json.rs#L100) — `fn require_utf8(bytes: &[u8]) -> Result<&str, ApiError>`
- [`src/api/mod.rs:38`](../src/api/mod.rs#L38) — `pub fn client_cut(&self) -> Result<ClientCut, ApiError>`
- [`src/api/search.rs:113`](../src/api/search.rs#L113) — `pub async fn post_search(`
- [`src/audio_sig.rs:113`](../src/audio_sig.rs#L113) — `pub fn compare(reference: &[u8], query: &[u8]) -> Option<SlideMatch>`
- [`src/content_id.rs:57`](../src/content_id.rs#L57) — `pub fn canonical_json(`
- [`src/content_id.rs:132`](../src/content_id.rs#L132) — `pub fn content_id(`
- [`src/db/repo.rs:316`](../src/db/repo.rs#L316) — `pub fn adopt_audio_signature(`
- [`src/error.rs:8`](../src/error.rs#L8) — `Unknown`
- [`src/ingest.rs:406`](../src/ingest.rs#L406) — `fn content_id_excludes_belief_and_route()`
- [`src/matching.rs:94`](../src/matching.rs#L94) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option<CutMatch>`
- [`src/model.rs:192`](../src/model.rs#L192) — `Unknown`
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_stored(s: &str) -> Option<Self>`
- [`src/model.rs:259`](../src/model.rs#L259) — `Unknown`
- [`src/validate.rs:102`](../src/validate.rs#L102) — `pub fn to_centiseconds(secs: f64) -> i64`
- [`src/validate.rs:227`](../src/validate.rs#L227) — `pub fn validate_manifest(mut m: Jmanifest) -> VResult<ValidManifest>`
- [`src/validate.rs:364`](../src/validate.rs#L364) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>`
- [`src/validate.rs:755`](../src/validate.rs#L755) — `fn extinction_sec_replaces_anneal_sec()`
- [`tests/api.rs:324`](../tests/api.rs#L324) — `async fn the_withdrawn_anneal_sec_field_is_rejected()`
- [`tests/api.rs:348`](../tests/api.rs#L348) — `async fn the_schema_bump_fields_round_trip()`
- [`tests/audio.rs:258`](../tests/audio.rs#L258) — `async fn a_contributed_signature_survives_storage_byte_for_byte()`
- [`tests/audio.rs:292`](../tests/audio.rs#L292) — `async fn a_manifest_without_a_signature_serves_no_signature_field()`
- [`tests/audio.rs:304`](../tests/audio.rs#L304) — `async fn a_held_manifest_adopts_a_signature_it_lacked()`
- [`tests/audio.rs:343`](../tests/audio.rs#L343) — `async fn a_matching_signature_reaches_the_audio_tier_on_a_fetch()`
- [`tests/audio.rs:360`](../tests/audio.rs#L360) — `async fn a_shifted_release_matches_and_gets_its_offset()`
- [`tests/audio.rs:402`](../tests/audio.rs#L402) — `async fn a_signature_that_disagrees_is_not_served_on_a_runtime_coincidence()`
- [`tests/audio.rs:419`](../tests/audio.rs#L419) — `async fn exists_reports_the_audio_tier_too()`
- [`tests/audio.rs:442`](../tests/audio.rs#L442) — `async fn a_signature_without_a_runtime_is_refused_by_name()`
- [`tests/audio.rs:480`](../tests/audio.rs#L480) — `async fn search_identifies_a_file_with_no_metadata_at_all()`
- [`tests/audio.rs:506`](../tests/audio.rs#L506) — `async fn search_recovers_the_offset_for_a_differently_trimmed_release()`
- [`tests/audio.rs:532`](../tests/audio.rs#L532) — `async fn search_declines_content_it_does_not_hold()`
- [`tests/audio.rs:567`](../tests/audio.rs#L567) — `async fn search_is_absent_when_the_operator_has_not_enabled_it()`
### SR-004
**Locations:** 20
- [`src/api/report.rs:56`](../src/api/report.rs#L56) — `pub async fn post_report(`
- [`src/api/upload.rs:29`](../src/api/upload.rs#L29) — `pub async fn post_manifest(`
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`
- [`src/app.rs:26`](../src/app.rs#L26) — `pub fn router(state: AppState) -> Router`
- [`src/auth.rs:22`](../src/auth.rs#L22) — `pub fn hash_token(token: &str) -> String`
- [`src/auth.rs:76`](../src/auth.rs#L76) — `pub fn client_ip(headers: &HeaderMap, peer: Option<IpAddr>, trusted_proxies: &[IpAddr]) -…`
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
- [`src/castcheck.rs:215`](../src/castcheck.rs#L215) — `pub fn category_guard_violation(matched: &[MatchedActor], title_is_adult: bool) -> Option…`
- [`src/db/repo.rs:426`](../src/db/repo.rs#L426) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
- [`src/model.rs:151`](../src/model.rs#L151) — `Unknown`
- [`src/model.rs:259`](../src/model.rs#L259) — `Unknown`
- [`src/model.rs:324`](../src/model.rs#L324) — `fn unknown_field_at_top_level_is_rejected()`
- [`src/ratelimit.rs:93`](../src/ratelimit.rs#L93) — `impl Default for RateLimiter`
- [`src/validate.rs:136`](../src/validate.rs#L136) — `fn is_allowed_text_char(c: char) -> bool`
- [`src/validate.rs:227`](../src/validate.rs#L227) — `pub fn validate_manifest(mut m: Jmanifest) -> VResult<ValidManifest>`
- [`src/validate.rs:364`](../src/validate.rs#L364) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>`
- [`src/worker.rs:150`](../src/worker.rs#L150) — `async fn run_cast_check(&self, payload: &str) -> Result<(), JobError>`
- [`tests/api.rs:279`](../tests/api.rs#L279) — `async fn unknown_field_anywhere_is_rejected_with_400()`
- [`tests/audio.rs:460`](../tests/audio.rs#L460) — `async fn a_malformed_signature_is_a_bad_request_not_a_silent_downgrade()`
- [`tests/audio.rs:551`](../tests/audio.rs#L551) — `async fn search_applies_the_same_structural_rules_as_an_upload()`
### SR-005
**Locations:** 3
- [`src/db/repo.rs:426`](../src/db/repo.rs#L426) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
- [`src/ingest.rs:50`](../src/ingest.rs#L50) — `pub fn persist(`
- [`tests/audio.rs:588`](../tests/audio.rs#L588) — `async fn search_never_returns_a_pending_manifest()`
### UR-001
**Locations:** 4
- [`src/api/exists.rs:61`](../src/api/exists.rs#L61) — `pub async fn exists(`
- [`src/api/exists.rs:74`](../src/api/exists.rs#L74) — `pub async fn exists_batch(`
- [`src/matching.rs:94`](../src/matching.rs#L94) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option<CutMatch>`
- [`tests/audio.rs:419`](../tests/audio.rs#L419) — `async fn exists_reports_the_audio_tier_too()`
### UR-002
**Locations:** 3
- [`src/api/upload.rs:29`](../src/api/upload.rs#L29) — `pub async fn post_manifest(`
- [`src/ingest.rs:50`](../src/ingest.rs#L50) — `pub fn persist(`
- [`tests/audio.rs:588`](../tests/audio.rs#L588) — `async fn search_never_returns_a_pending_manifest()`
### UR-003
**Locations:** 6
- [`src/api/upload.rs:29`](../src/api/upload.rs#L29) — `pub async fn post_manifest(`
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
- [`src/model.rs:151`](../src/model.rs#L151) — `Unknown`
- [`src/model.rs:259`](../src/model.rs#L259) — `Unknown`
- [`src/validate.rs:227`](../src/validate.rs#L227) — `pub fn validate_manifest(mut m: Jmanifest) -> VResult<ValidManifest>`
- [`src/worker.rs:150`](../src/worker.rs#L150) — `async fn run_cast_check(&self, payload: &str) -> Result<(), JobError>`
### UR-004
**Locations:** 2
- [`src/auth.rs:76`](../src/auth.rs#L76) — `pub fn client_ip(headers: &HeaderMap, peer: Option<IpAddr>, trusted_proxies: &[IpAddr]) -…`
- [`src/ratelimit.rs:93`](../src/ratelimit.rs#L93) — `impl Default for RateLimiter`
### UR-005
**Locations:** 6
- [`src/api/report.rs:56`](../src/api/report.rs#L56) — `pub async fn post_report(`
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`
- [`src/auth.rs:22`](../src/auth.rs#L22) — `pub fn hash_token(token: &str) -> String`
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
- [`src/castcheck.rs:215`](../src/castcheck.rs#L215) — `pub fn category_guard_violation(matched: &[MatchedActor], title_is_adult: bool) -> Option…`
- [`src/worker.rs:150`](../src/worker.rs#L150) — `async fn run_cast_check(&self, payload: &str) -> Result<(), JobError>`
### UR-006
**Locations:** 3
- [`src/api/fetch.rs:127`](../src/api/fetch.rs#L127) — `pub async fn get_series(`
- [`src/api/upload.rs:101`](../src/api/upload.rs#L101) — `pub async fn post_bundle(`
- [`src/validate.rs:572`](../src/validate.rs#L572) — `pub fn validate_bundle_envelope(b: &SeriesBundle) -> VResult<()>`
### UR-007
**Locations:** 1
- [`src/api/exists.rs:74`](../src/api/exists.rs#L74) — `pub async fn exists_batch(`
### UR-008
**Locations:** 8
- [`src/api/federation.rs:66`](../src/api/federation.rs#L66) — `pub async fn get_changes(`
- [`src/api/federation.rs:108`](../src/api/federation.rs#L108) — `pub async fn get_manifest_by_content_id(`
- [`src/api/federation.rs:160`](../src/api/federation.rs#L160) — `pub async fn post_have(`
- [`src/api/federation.rs:212`](../src/api/federation.rs#L212) — `pub async fn get_peers(`
- [`src/api/federation.rs:261`](../src/api/federation.rs#L261) — `pub async fn get_capabilities(State(state): State<AppState>) -> ApiResult<Response>`
- [`src/db/repo.rs:316`](../src/db/repo.rs#L316) — `pub fn adopt_audio_signature(`
- [`src/worker.rs:107`](../src/worker.rs#L107) — `async fn run_federation_pull(&self, payload: &str) -> Result<(), JobError>`
- [`tests/audio.rs:304`](../tests/audio.rs#L304) — `async fn a_held_manifest_adopts_a_signature_it_lacked()`
### UR-009
**Locations:** 21
- [`src/api/mod.rs:38`](../src/api/mod.rs#L38) — `pub fn client_cut(&self) -> Result<ClientCut, ApiError>`
- [`src/api/search.rs:113`](../src/api/search.rs#L113) — `pub async fn post_search(`
- [`src/audio_sig.rs:113`](../src/audio_sig.rs#L113) — `pub fn compare(reference: &[u8], query: &[u8]) -> Option<SlideMatch>`
- [`src/db/repo.rs:316`](../src/db/repo.rs#L316) — `pub fn adopt_audio_signature(`
- [`src/matching.rs:94`](../src/matching.rs#L94) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option<CutMatch>`
- [`src/validate.rs:364`](../src/validate.rs#L364) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>`
- [`tests/audio.rs:258`](../tests/audio.rs#L258) — `async fn a_contributed_signature_survives_storage_byte_for_byte()`
- [`tests/audio.rs:292`](../tests/audio.rs#L292) — `async fn a_manifest_without_a_signature_serves_no_signature_field()`
- [`tests/audio.rs:304`](../tests/audio.rs#L304) — `async fn a_held_manifest_adopts_a_signature_it_lacked()`
- [`tests/audio.rs:343`](../tests/audio.rs#L343) — `async fn a_matching_signature_reaches_the_audio_tier_on_a_fetch()`
- [`tests/audio.rs:360`](../tests/audio.rs#L360) — `async fn a_shifted_release_matches_and_gets_its_offset()`
- [`tests/audio.rs:402`](../tests/audio.rs#L402) — `async fn a_signature_that_disagrees_is_not_served_on_a_runtime_coincidence()`
- [`tests/audio.rs:419`](../tests/audio.rs#L419) — `async fn exists_reports_the_audio_tier_too()`
- [`tests/audio.rs:442`](../tests/audio.rs#L442) — `async fn a_signature_without_a_runtime_is_refused_by_name()`
- [`tests/audio.rs:460`](../tests/audio.rs#L460) — `async fn a_malformed_signature_is_a_bad_request_not_a_silent_downgrade()`
- [`tests/audio.rs:480`](../tests/audio.rs#L480) — `async fn search_identifies_a_file_with_no_metadata_at_all()`
- [`tests/audio.rs:506`](../tests/audio.rs#L506) — `async fn search_recovers_the_offset_for_a_differently_trimmed_release()`
- [`tests/audio.rs:532`](../tests/audio.rs#L532) — `async fn search_declines_content_it_does_not_hold()`
- [`tests/audio.rs:551`](../tests/audio.rs#L551) — `async fn search_applies_the_same_structural_rules_as_an_upload()`
- [`tests/audio.rs:567`](../tests/audio.rs#L567) — `async fn search_is_absent_when_the_operator_has_not_enabled_it()`
- [`tests/audio.rs:588`](../tests/audio.rs#L588) — `async fn search_never_returns_a_pending_manifest()`
### UR-010
**Locations:** 4
- [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(`
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
- [`src/db/repo.rs:500`](../src/db/repo.rs#L500) — `pub fn actors_for_manifest(`
- [`src/model.rs:174`](../src/model.rs#L174) — `Unknown`
### UR-011
**Locations:** 6
- [`src/model.rs:151`](../src/model.rs#L151) — `Unknown`
- [`src/model.rs:259`](../src/model.rs#L259) — `Unknown`
- [`src/validate.rs:136`](../src/validate.rs#L136) — `fn is_allowed_text_char(c: char) -> bool`
- [`src/validate.rs:364`](../src/validate.rs#L364) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>`
- [`tests/audio.rs:460`](../tests/audio.rs#L460) — `async fn a_malformed_signature_is_a_bad_request_not_a_silent_downgrade()`
- [`tests/audio.rs:551`](../tests/audio.rs#L551) — `async fn search_applies_the_same_structural_rules_as_an_upload()`
### UR-012
**Locations:** 2
- [`src/db/repo.rs:426`](../src/db/repo.rs#L426) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
- [`src/ingest.rs:50`](../src/ingest.rs#L50) — `pub fn persist(`
### UR-013
**Locations:** 4
- [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(`
- [`src/model.rs:174`](../src/model.rs#L174) — `Unknown`
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_stored(s: &str) -> Option<Self>`
- [`src/validate.rs:496`](../src/validate.rs#L496) — `fn validate_scenes(idx: usize, a: &Actor, runtime_sec: f64) -> VResult<Vec<SceneCs>>`
### UR-014
**Locations:** 4
- [`src/api/federation.rs:261`](../src/api/federation.rs#L261) — `pub async fn get_capabilities(State(state): State<AppState>) -> ApiResult<Response>`
- [`src/model.rs:259`](../src/model.rs#L259) — `Unknown`
- [`src/validate.rs:227`](../src/validate.rs#L227) — `pub fn validate_manifest(mut m: Jmanifest) -> VResult<ValidManifest>`
- [`tests/audio.rs:567`](../tests/audio.rs#L567) — `async fn search_is_absent_when_the_operator_has_not_enabled_it()`
### UR-015
**Locations:** 3
- [`src/validate.rs:755`](../src/validate.rs#L755) — `fn extinction_sec_replaces_anneal_sec()`
- [`tests/api.rs:324`](../tests/api.rs#L324) — `async fn the_withdrawn_anneal_sec_field_is_rejected()`
- [`tests/api.rs:348`](../tests/api.rs#L348) — `async fn the_schema_bump_fields_round_trip()`
### UR-016
**Locations:** 2
- [`src/validate.rs:755`](../src/validate.rs#L755) — `fn extinction_sec_replaces_anneal_sec()`
- [`tests/api.rs:348`](../tests/api.rs#L348) — `async fn the_schema_bump_fields_round_trip()`
### UR-017
**Locations:** 2
- [`src/model.rs:192`](../src/model.rs#L192) — `Unknown`
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_stored(s: &str) -> Option<Self>`
### UR-018
**Locations:** 1
- [`src/ingest.rs:406`](../src/ingest.rs#L406) — `fn content_id_excludes_belief_and_route()`
### UR-019
**Locations:** 1
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`