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
382 lines
19 KiB
Markdown
382 lines
19 KiB
Markdown
# Requirements traceability matrix
|
|
|
|
<!-- GENERATED FILE - do not edit by hand. -->
|
|
<!-- Regenerate: scripts/traceability/traceability-gate.sh -->
|
|
|
|
**Generated:** 2026-07-31T07:49:04+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 | 32 |
|
|
| Requirements covered | 25 |
|
|
| **Coverage** | **78.1%** (25/32) |
|
|
| Coverage of CI-executable scope | 78.1% (25/32) |
|
|
| Tagged but unexecuted in CI | 0 |
|
|
| Orphan tags | 0 |
|
|
|
|
### By type
|
|
|
|
| Type | Covered | Tagged but unexecuted | Defined |
|
|
|---|---|---|---|
|
|
| UR | 15 | 0 | 18 |
|
|
| 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 <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` | 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… |
|
|
| 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<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:** 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<Vec<Jo…`
|
|
|
|
### PR-005
|
|
|
|
**Locations:** 2
|
|
|
|
- [`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(`
|
|
|
|
### PR-006
|
|
|
|
**Locations:** 9
|
|
|
|
- [`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/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: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<CutMatch>`
|
|
- [`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<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:** 11
|
|
|
|
- [`src/api/federation.rs:258`](../src/api/federation.rs#L258) — `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/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<Self>`
|
|
- [`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<ValidManifest>`
|
|
- [`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:223`](../src/api/upload.rs#L223) — `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: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<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>`
|
|
|
|
### 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<CutMatch>`
|
|
|
|
### 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<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:223`](../src/api/upload.rs#L223) — `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<AppState>) -> ApiResult<Response>`
|
|
- [`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<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:** 3
|
|
|
|
- [`src/api/federation.rs:258`](../src/api/federation.rs#L258) — `pub async fn get_capabilities(State(state): State<AppState>) -> ApiResult<Response>`
|
|
- [`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<ValidManifest>`
|
|
|
|
### 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<Self>`
|
|
|