Moves the exchange envelope to version 2 in lockstep with the truth file's schema_version, per SR-003's requirement that breaking changes be batched and ship together rather than piecemeal. The plugin had already moved to schema_version 2; the server declaring 1 while accepting the new fields defeated the point of having a version at all. Flag day, not dual-accept (JR-003): version 1 is now rejected outright. All three components are pre-release, and a v1 read path would be the one nobody exercises, so it is the one that would rot while being dragged through every later change to the reader. A pipeline still emitting v1 is incompatible until updated — stated plainly rather than papered over with a shim nobody tests. scenes become objects carrying belief and route (extraction AR-017) instead of float pairs. Belief is bounded to [0, 1] rather than merely stored: §5a's Threat 1 argument rests on every accepted value being bounded, and an unbounded float is a 64-bit channel however harmless it looks. route is a closed enum, so an invented value cannot be stored. UR-018 is the requirement with the trap in it, and the reason content_id.rs is untouched by this commit: belief is a producer-side estimate that may legitimately differ between pipeline versions for identical timings, so including it in the canonical form would give two servers different ids for the same content — the exact failure mode §9a quantises centiseconds to avoid, reintroduced one field along. It replicates as an attribute, exactly as audio_signature does. The golden vector still passes unchanged, which is the evidence rather than the claim. 191 tests. UR-015..018 move from Planned to Done; coverage 24/32 (75%). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: UR-014, UR-015, UR-016, UR-017, UR-018 | SR-003
364 lines
18 KiB
Markdown
364 lines
18 KiB
Markdown
# Requirements traceability matrix
|
|
|
|
<!-- GENERATED FILE - do not edit by hand. -->
|
|
<!-- Regenerate: scripts/traceability/traceability-gate.sh -->
|
|
|
|
**Generated:** 2026-07-31T07:12:27+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 | 26 |
|
|
| TRACES tags found | 37 |
|
|
| EXCEPTION tags found | 0 |
|
|
| Requirements defined | 32 |
|
|
| Requirements covered | 24 |
|
|
| **Coverage** | **75.0%** (24/32) |
|
|
| Coverage of CI-executable scope | 75.0% (24/32) |
|
|
| Tagged but unexecuted in CI | 0 |
|
|
| Orphan tags | 0 |
|
|
|
|
### By type
|
|
|
|
| Type | Covered | Tagged but unexecuted | Defined |
|
|
|---|---|---|---|
|
|
| UR | 14 | 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 | untagged | - | 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/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:267`](../src/api/fetch.rs#L267) — `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:76`](../src/ratelimit.rs#L76) — `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:** 1
|
|
|
|
- [`src/api/exists.rs:74`](../src/api/exists.rs#L74) — `pub async fn exists_batch(`
|
|
|
|
### PR-006
|
|
|
|
**Locations:** 5
|
|
|
|
- [`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<()>`
|
|
|
|
### 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:267`](../src/api/fetch.rs#L267) — `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:267`](../src/api/fetch.rs#L267) — `pub fn reconstruct(`
|
|
- [`src/model.rs:179`](../src/model.rs#L179) — `Unknown`
|
|
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_str(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:** 10
|
|
|
|
- [`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:233`](../src/model.rs#L233) — `pub fn from_str(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: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:259`](../src/model.rs#L259) — `Unknown`
|
|
- [`src/ratelimit.rs:76`](../src/ratelimit.rs#L76) — `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:96`](../src/worker.rs#L96) — `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: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:96`](../src/worker.rs#L96) — `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:76`](../src/ratelimit.rs#L76) — `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:96`](../src/worker.rs#L96) — `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-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:267`](../src/api/fetch.rs#L267) — `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: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: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:267`](../src/api/fetch.rs#L267) — `pub fn reconstruct(`
|
|
- [`src/model.rs:179`](../src/model.rs#L179) — `Unknown`
|
|
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_str(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:** 2
|
|
|
|
- [`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>`
|
|
|
|
### UR-017
|
|
|
|
**Locations:** 2
|
|
|
|
- [`src/model.rs:197`](../src/model.rs#L197) — `Unknown`
|
|
- [`src/model.rs:233`](../src/model.rs#L233) — `pub fn from_str(s: &str) -> Option<Self>`
|
|
|