Implements §9a. The replication surface is four reads and no writes: a change feed, fetch by content_id, a batch have, and a human-facing peer directory — plus a capabilities endpoint carrying the accepted envelope versions, which lets a client discover a schema mismatch in one request instead of a 400 per manifest across a library sweep. Pull, never push: a pulling server chooses what it ingests and when. Push would let any peer inject work into the validation queue — the same abuse surface as anonymous upload, at higher volume. Nothing inherits a peer's judgement. A pulled manifest runs the full §6 stage 1 and 2 validation and this server's own cast check, and the fetched body must hash to the content_id that was asked for — the check that stops an intermediary or a misbehaving peer substituting content under a trusted id. A peer's retraction flags for review rather than delisting, because auto-delisting would hand every peer a remote delete primitive; only the opt-in per-peer abuse channel delists, because a takedown propagating at the speed of manual review is the wrong failure mode for that one case. A test caught a real bug in the first cut: the feed cursor was a ULID, and ULIDs are only monotonic *between* milliseconds — two generated in the same millisecond carry independent random components and can sort opposite to write order. A peer resuming from `seq > cursor` would then silently skip an entry: replication losing manifests with no error anywhere. The cursor is now an AUTOINCREMENT integer, and the test asserts strict monotonicity rather than merely sortedness. Peer administration is deliberately not an API. §9a requires that a peering exist only because an operator typed a URL, so nothing a remote server returns can establish or widen one; there_is_no_endpoint_that_creates_a_peering asserts that absence rather than trusting it. 212 tests. Coverage 25/32 (78%). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: UR-008 | PR-006
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:28: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 | Planned | unset | 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:270`](../src/api/fetch.rs#L270) — `pub fn reconstruct(`
|
|
- [`src/db/repo.rs:337`](../src/db/repo.rs#L337) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
|
|
- [`src/db/repo.rs:412`](../src/db/repo.rs#L412) — `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:337`](../src/db/repo.rs#L337) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
|
|
|
|
### DR-005
|
|
|
|
**Locations:** 1
|
|
|
|
- [`src/db/repo.rs:709`](../src/db/repo.rs#L709) — `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:52`](../src/content_id.rs#L52) — `pub fn canonical_json(`
|
|
- [`src/content_id.rs:129`](../src/content_id.rs#L129) — `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:709`](../src/db/repo.rs#L709) — `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:128`](../src/api/fetch.rs#L128) — `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:586`](../src/validate.rs#L586) — `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:270`](../src/api/fetch.rs#L270) — `pub fn reconstruct(`
|
|
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
|
|
- [`src/db/repo.rs:412`](../src/db/repo.rs#L412) — `pub fn actors_for_manifest(`
|
|
- [`src/matching.rs:52`](../src/matching.rs#L52) — `pub fn match_cut(client: &ClientCut, stored: &StoredCut) -> Option<CutMatch>`
|
|
- [`src/model.rs:179`](../src/model.rs#L179) — `Unknown`
|
|
|
|
### SR-002
|
|
|
|
**Locations:** 4
|
|
|
|
- [`src/api/fetch.rs:270`](../src/api/fetch.rs#L270) — `pub fn reconstruct(`
|
|
- [`src/model.rs:179`](../src/model.rs#L179) — `Unknown`
|
|
- [`src/model.rs:238`](../src/model.rs#L238) — `pub fn from_stored(s: &str) -> Option<Self>`
|
|
- [`src/validate.rs:510`](../src/validate.rs#L510) — `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:52`](../src/content_id.rs#L52) — `pub fn canonical_json(`
|
|
- [`src/content_id.rs:129`](../src/content_id.rs#L129) — `pub fn content_id(`
|
|
- [`src/error.rs:8`](../src/error.rs#L8) — `Unknown`
|
|
- [`src/model.rs:197`](../src/model.rs#L197) — `Unknown`
|
|
- [`src/model.rs:238`](../src/model.rs#L238) — `pub fn from_stored(s: &str) -> Option<Self>`
|
|
- [`src/model.rs:264`](../src/model.rs#L264) — `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:378`](../src/validate.rs#L378) — `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:337`](../src/db/repo.rs#L337) — `pub fn insert_manifest(tx: &Transaction<'_>, m: &NewManifest<'_>) -> anyhow::Result<()>`
|
|
- [`src/model.rs:156`](../src/model.rs#L156) — `Unknown`
|
|
- [`src/model.rs:264`](../src/model.rs#L264) — `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:378`](../src/validate.rs#L378) — `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:337`](../src/db/repo.rs#L337) — `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:52`](../src/matching.rs#L52) — `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:156`](../src/model.rs#L156) — `Unknown`
|
|
- [`src/model.rs:264`](../src/model.rs#L264) — `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:128`](../src/api/fetch.rs#L128) — `pub async fn get_series(`
|
|
- [`src/api/upload.rs:101`](../src/api/upload.rs#L101) — `pub async fn post_bundle(`
|
|
- [`src/validate.rs:586`](../src/validate.rs#L586) — `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:378`](../src/validate.rs#L378) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>`
|
|
|
|
### UR-010
|
|
|
|
**Locations:** 4
|
|
|
|
- [`src/api/fetch.rs:270`](../src/api/fetch.rs#L270) — `pub fn reconstruct(`
|
|
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
|
|
- [`src/db/repo.rs:412`](../src/db/repo.rs#L412) — `pub fn actors_for_manifest(`
|
|
- [`src/model.rs:179`](../src/model.rs#L179) — `Unknown`
|
|
|
|
### UR-011
|
|
|
|
**Locations:** 4
|
|
|
|
- [`src/model.rs:156`](../src/model.rs#L156) — `Unknown`
|
|
- [`src/model.rs:264`](../src/model.rs#L264) — `Unknown`
|
|
- [`src/validate.rs:136`](../src/validate.rs#L136) — `fn is_allowed_text_char(c: char) -> bool`
|
|
- [`src/validate.rs:378`](../src/validate.rs#L378) — `pub fn validate_audio_signature(sig: &str, runtime_sec: f64) -> VResult<()>`
|
|
|
|
### UR-012
|
|
|
|
**Locations:** 2
|
|
|
|
- [`src/db/repo.rs:337`](../src/db/repo.rs#L337) — `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:270`](../src/api/fetch.rs#L270) — `pub fn reconstruct(`
|
|
- [`src/model.rs:179`](../src/model.rs#L179) — `Unknown`
|
|
- [`src/model.rs:238`](../src/model.rs#L238) — `pub fn from_stored(s: &str) -> Option<Self>`
|
|
- [`src/validate.rs:510`](../src/validate.rs#L510) — `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:264`](../src/model.rs#L264) — `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:197`](../src/model.rs#L197) — `Unknown`
|
|
- [`src/model.rs:238`](../src/model.rs#L238) — `pub fn from_stored(s: &str) -> Option<Self>`
|
|
|