# Requirements traceability matrix **Generated:** 2026-07-31T08:06:49+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 | 29 | | TRACES tags found | 43 | | EXCEPTION tags found | 0 | | Requirements defined | 33 | | Requirements covered | 26 | | **Coverage** | **78.8%** (26/33) | | Coverage of CI-executable scope | 78.8% (26/33) | | Tagged but unexecuted in CI | 0 | | Orphan tags | 0 | ### By type | Type | Covered | Tagged but unexecuted | Defined | |---|---|---|---| | UR | 16 | 0 | 19 | | DR | 10 | 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 `). 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` | Cheap existence probe, separate from the fetch, returning availabilit… | | UR-002 | Done | T2 | PR-006 | covered | `src/api/upload.rs`, `src/ingest.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/worker.rs` | Servers replicate manifests between each other | | UR-009 | In Progress | T1 | SR-003 | covered | `src/validate.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` | 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` | Reject an unknown `jmanifest_version` outright, never guess | | UR-015 | Done | T2 | SR-003 | untagged | - | Accept `extraction.extinction_sec` in place of `anneal_sec` | | UR-016 | Done | T2 | SR-003 | untagged | - | 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 | untagged | - | 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 | untagged | - | Strict parse boundary: unknown fields rejected structurally, not by v… | | DR-002 | Done | unset | SR-004 | covered | `src/api/fetch.rs`, `src/db/repo.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 | unset | 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 | unset | PR-004 | covered | `src/ratelimit.rs` | Rate-limit counters in process memory; no external counter store | | DR-007 | Done | unset | 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 | unset | 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` | API errors use the status codes the spec names, not the framework's d… | | DR-014 | Done | unset | PR-004 | untagged | - | Portable SQL — no SQLite-specific form where a standard one exists | ## Detailed mapping ### DR-002 **Locations:** 3 - [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(` - [`src/db/repo.rs:331`](../src/db/repo.rs#L331) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>` - [`src/db/repo.rs:405`](../src/db/repo.rs#L405) — `pub fn actors_for_manifest(` ### DR-003 **Locations:** 1 - [`src/db/mod.rs:30`](../src/db/mod.rs#L30) — `struct ReadPool` ### DR-004 **Locations:** 1 - [`src/db/repo.rs:331`](../src/db/repo.rs#L331) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>` ### DR-005 **Locations:** 1 - [`src/db/repo.rs:702`](../src/db/repo.rs#L702) — `pub fn lease_jobs(tx: &Transaction<'_>, now: &str, limit: usize) -> anyhow::Result, 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:** 3 - [`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` ### PR-004 **Locations:** 3 - [`src/config.rs:10`](../src/config.rs#L10) — `Unknown` - [`src/db/mod.rs:30`](../src/db/mod.rs#L30) — `struct ReadPool` - [`src/db/repo.rs:702`](../src/db/repo.rs#L702) — `pub fn lease_jobs(tx: &Transaction<'_>, now: &str, limit: usize) -> anyhow::Result 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:405`](../src/db/repo.rs#L405) — `pub fn actors_for_manifest(` - [`src/matching.rs:54`](../src/matching.rs#L54) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option` - [`src/model.rs:173`](../src/model.rs#L173) — `Unknown` ### SR-002 **Locations:** 4 - [`src/api/fetch.rs:269`](../src/api/fetch.rs#L269) — `pub fn reconstruct(` - [`src/model.rs:173`](../src/model.rs#L173) — `Unknown` - [`src/model.rs:232`](../src/model.rs#L232) — `pub fn from_stored(s: &str) -> Option` - [`src/validate.rs:496`](../src/validate.rs#L496) — `fn validate_scenes(idx: usize, a: &Actor, runtime_sec: f64) -> VResult>` ### SR-003 **Locations:** 11 - [`src/api/federation.rs:258`](../src/api/federation.rs#L258) — `pub async fn get_capabilities(State(state): State) -> ApiResult` - [`src/api/json.rs:100`](../src/api/json.rs#L100) — `fn require_utf8(bytes: &[u8]) -> Result<&str, ApiError>` - [`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/error.rs:8`](../src/error.rs#L8) — `Unknown` - [`src/model.rs:191`](../src/model.rs#L191) — `Unknown` - [`src/model.rs:232`](../src/model.rs#L232) — `pub fn from_stored(s: &str) -> Option` - [`src/model.rs:258`](../src/model.rs#L258) — `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` - [`src/validate.rs:364`](../src/validate.rs#L364) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>` ### SR-004 **Locations:** 16 - [`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, 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:331`](../src/db/repo.rs#L331) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>` - [`src/model.rs:150`](../src/model.rs#L150) — `Unknown` - [`src/model.rs:258`](../src/model.rs#L258) — `Unknown` - [`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` - [`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>` ### SR-005 **Locations:** 2 - [`src/db/repo.rs:331`](../src/db/repo.rs#L331) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>` - [`src/ingest.rs:50`](../src/ingest.rs#L50) — `pub fn persist(` ### UR-001 **Locations:** 3 - [`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:54`](../src/matching.rs#L54) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option` ### UR-002 **Locations:** 2 - [`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(` ### 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:150`](../src/model.rs#L150) — `Unknown` - [`src/model.rs:258`](../src/model.rs#L258) — `Unknown` - [`src/validate.rs:227`](../src/validate.rs#L227) — `pub fn validate_manifest(mut m: Jmanifest) -> VResult` - [`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, 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:** 6 - [`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:258`](../src/api/federation.rs#L258) — `pub async fn get_capabilities(State(state): State) -> ApiResult` - [`src/worker.rs:107`](../src/worker.rs#L107) — `async fn run_federation_pull(&self, payload: &str) -> Result<(), JobError>` ### UR-009 **Locations:** 1 - [`src/validate.rs:364`](../src/validate.rs#L364) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>` ### 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:405`](../src/db/repo.rs#L405) — `pub fn actors_for_manifest(` - [`src/model.rs:173`](../src/model.rs#L173) — `Unknown` ### UR-011 **Locations:** 4 - [`src/model.rs:150`](../src/model.rs#L150) — `Unknown` - [`src/model.rs:258`](../src/model.rs#L258) — `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<()>` ### UR-012 **Locations:** 2 - [`src/db/repo.rs:331`](../src/db/repo.rs#L331) — `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:173`](../src/model.rs#L173) — `Unknown` - [`src/model.rs:232`](../src/model.rs#L232) — `pub fn from_stored(s: &str) -> Option` - [`src/validate.rs:496`](../src/validate.rs#L496) — `fn validate_scenes(idx: usize, a: &Actor, runtime_sec: f64) -> VResult>` ### UR-014 **Locations:** 3 - [`src/api/federation.rs:258`](../src/api/federation.rs#L258) — `pub async fn get_capabilities(State(state): State) -> ApiResult` - [`src/model.rs:258`](../src/model.rs#L258) — `Unknown` - [`src/validate.rs:227`](../src/validate.rs#L227) — `pub fn validate_manifest(mut m: Jmanifest) -> VResult` ### UR-017 **Locations:** 2 - [`src/model.rs:191`](../src/model.rs#L191) — `Unknown` - [`src/model.rs:232`](../src/model.rs#L232) — `pub fn from_stored(s: &str) -> Option` ### UR-019 **Locations:** 1 - [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`