docs: tag the untagged verifications, and record the UR-007 conflict
CI / fmt, clippy, test (pull_request) Failing after 54s
CI / static musl binary (pull_request) Skipped
CI / advisories and licences (pull_request) Successful in 39s

Five tests and the schema constant implemented requirements without
carrying a tag, so those requirements read as uncovered when they were
not. No behaviour changes here — every edit is a comment.

Also documents `static` as a real tier rather than an exemption: it is
already in `ci_executable_tiers` and its checks run in CI, and it exists
because DR-007's single binary and DR-012's licence policy are properties
of the build that a unit test could only assert as theatre.

The UR-007 note records a cross-repo status conflict rather than
resolving it. `jRay` JR-025 is `Done` and claims UR-007, but the fetch
path that would exercise it (`jRay` JR-031) is still `Planned`. Either
JR-025 is scoped to selection alone or it over-claims; until that is
settled neither register should be trusted for UR-007 coverage.

The gate reports 0 orphan tags and 19/19 UR coverage.

TRACES: DR-001, DR-014, UR-015, UR-016, UR-018 | SR-003, SR-004
This commit is contained in:
2026-07-31 16:26:10 +02:00
parent 4afa36e7a2
commit 7eb5c175af
7 changed files with 98 additions and 23 deletions
+23 -1
View File
@@ -53,6 +53,14 @@ requirement and no fixture-generation step, unlike `scene-actor-extraction`.
server list and its per-server trust settings, with the community instance
pre-configured but disabled. The fetch path that consumes it does not exist yet.
> **The two registers disagree about this and the disagreement is unresolved.**
> `jRay` JR-025 is recorded `Done` and claims to satisfy UR-007, but `jRay`
> JR-031 — the fetch endpoints that would exercise the ordered list — is still
> `Planned`. Either JR-025 is scoped to the *selection* logic alone (in which
> case UR-007 stays open until JR-031 lands) or it over-claims. **Settle this
> before either register is trusted for coverage**; it is the only cross-repo
> status conflict currently outstanding.
**UR-008 is `Done` for the replication surface**: change feed, fetch by
`content_id`, batch `have`, the peer directory, and a pull worker that
re-validates everything it ingests. Peer *administration* — adding and enabling
@@ -107,10 +115,18 @@ make the test suite hermetic.
|---|---|---|
| **T1 — unit** | Yes | Pure logic: validation, cut matching, cast-check scoring, canonicalisation, rate limiting |
| **T2 — integration** | Yes | End-to-end through the real router against a temporary on-disk database |
| **static** | Yes | Build and dependency properties no runtime test can assert — `cargo deny check`, the musl release build, architectural greps |
There is no tier that does not run. A requirement here is either verified or
visibly not.
**`static` is a real tier, not an exemption.** `ci_executable_tiers` in
[`../traceability.toml`](../traceability.toml) already lists it, and the checks it
names execute in CI like any other. It exists because a handful of requirements
are properties of the *build* rather than of the running program — DR-007's single
binary, DR-012's licence and advisory policy — and asserting those from a unit
test would be theatre.
**Integration tests use an on-disk temporary database, not `:memory:`.** DR-003
specifies one writer connection plus a read pool, and in-memory SQLite is
per-connection — the readers would see an empty database. Testing the real
@@ -126,7 +142,7 @@ topology is the point, so this is a deliberate choice rather than an oversight.
| UR-004 | T1 + T2 | Limits engage and carry the documented headers | Window reset; a rejected request does not extend its own lockout; surfaces have independent budgets |
| UR-005 | T1 + T2 | Prank manifests rejected; no free-text channel | Uncredited cast rejected; name-only matches capped; automatic revocation needs a minimum sample |
| UR-006 | T2 | Bundle accepted per-episode, non-atomically | One bad episode rejected while its neighbours are accepted; envelope errors are whole-request `400` |
| UR-007 | — | *No server-side test.* Plugin-side (`jRay` JR-025); the register there carries it | — |
| UR-007 | **external** | *No server-side test, and cannot have one.* The obligation is the plugin's: `jRay` JR-025, which is `Done` and tagged in that register | Verified there, not here — counted as covered by cross-reference, never by a test in this repo. **See the status note: JR-025 being `Done` does not by itself close UR-007**, because the fetch path (`jRay` JR-031) is still `Planned` |
| UR-008 | T1 + T2 | Feed, fetch-by-hash, batch have, peer directory | **Cursor is strictly monotonic** — a ULID would sort out of write order within a millisecond and silently skip entries; a peer retraction flags rather than delists; only the opt-in abuse channel delists; `pending` is never replicated; **no endpoint can create a peering** |
| UR-009 | T1 | Signature structurally validated | Fixed length; reserved high bit; **media < 120 s must send no signature at all** |
| UR-010 | T1 + T2 | Actors persist as TMDB person ids | A name the upload invented does not round-trip |
@@ -136,7 +152,10 @@ topology is the point, so this is a deliberate choice rather than an oversight.
| UR-014 | T1 | Unknown `jmanifest_version` rejected | Version `2` and version `0` both refused, naming the field |
| UR-019 | T2 | `POST /tokens` returns the licence and its terms | The terms **bound the grant to the manifest** — a reading that covers the underlying work is the failure, not a missing field |
| DR-001 | T1 | Unknown field at any nesting depth fails to parse | `movie` and `jellyfin_id` named in the error |
| DR-002 | T1 | Every manifest field lands in a typed column; no JSON blob on the write path | A round-trip through storage reconstructs the manifest from columns alone |
| DR-003 | T1 | Concurrent writes serialize rather than returning `SQLITE_BUSY` | Failed transaction rolls back fully |
| DR-004 | T1 | Handlers reach the database only through `db::repo` | No `Connection` or raw SQL outside the repository layer |
| DR-006 | T1 | Rate-limit counters live in process memory | Counters reset on restart — the documented trade-off, not a bug |
| DR-005 | T1 | Jobs lease once, reschedule with backoff, survive restart | Stranded lease released at startup; future job not leased early |
| DR-008 | T2 | Forged `X-Forwarded-For` cannot mint a fresh budget | Untrusted peer ignored; trusted proxy honoured; client-supplied entries to the left cannot spoof |
| DR-009 | T2 | Oversized body rejected as `413` | **A lying `Content-Length` does not bypass the cap**; per-route limits differ |
@@ -147,6 +166,9 @@ topology is the point, so this is a deliberate choice rather than an oversight.
| UR-017 | T1 + T2 | Windows carry belief and route through storage | Belief outside `[0, 1]` rejected; an invented `route` rejected |
| UR-018 | **T1** | Belief and route absent from `content_id` | Same windows at different belief hash identically; **a real timing change still does not**, so the test cannot pass vacuously |
| DR-013 | T1 | Schema mismatch is `400`, not the framework's `422` | §4 names `400` for a forbidden field, and a client checking for it would mishandle `422` |
| DR-007 | **static** | The musl release target builds and links one binary needing only a database file | Build property, not a runtime one — a unit test asserting it would assert nothing |
| DR-012 | **static** | `cargo deny check` passes advisories, licences and sources | A copyleft-incompatible transitive dependency must fail the build, not be discovered later |
| DR-014 | **static** | `schema.sql` uses no SQLite-specific form where a standard one exists | `INSERT OR REPLACE` must not reappear in place of `INSERT ... ON CONFLICT` |
Three are worth singling out, because each verifies a claim that would otherwise
be an assertion: