Ship the SR-003 schema bump: jmanifest_version 2
CI / fmt, clippy, test (push) Failing after 1m22s
CI / static musl binary (push) Has been skipped
CI / advisories and licences (push) Successful in 26s

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
This commit is contained in:
2026-07-31 09:13:14 +02:00
co-authored by Claude Opus 5
parent c73f417d45
commit 88c7264094
12 changed files with 494 additions and 179 deletions
+23 -16
View File
@@ -41,6 +41,10 @@ requirement and no fixture-generation step, unlike `scene-actor-extraction`.
| UR-012 | Never accept, store, or serve gallery data — reference faces or embeddings | SR-005 | High | Done |
| UR-013 | Windows are scene-scoped claims; never reinterpret their boundaries | SR-002 | High | Done |
| UR-014 | Reject an unknown `jmanifest_version` outright, never guess | SR-003 | High | Done |
| UR-015 | Accept `extraction.extinction_sec` in place of `anneal_sec` | SR-003 | High | Done |
| UR-016 | Accept and store `extraction.gallery_scope`; rank on it (§7) | SR-003 | Medium | Done |
| UR-017 | Accept per-window belief and identification route; `scenes` are objects | SR-003 | High | Done |
| UR-018 | Exclude belief and route from `content_id`, replicating them as attributes | SR-003 | High | Done |
### Notes on status
@@ -128,6 +132,10 @@ topology is the point, so this is a deliberate choice rather than an oversight.
| DR-009 | T2 | Oversized body rejected as `413` | **A lying `Content-Length` does not bypass the cap**; per-route limits differ |
| DR-010 | T1 | Non-UTF-8 rejected by name | UTF-16 with and without BOM; UTF-8 BOM; declared `charset=utf-16` |
| DR-011 | T1 | Canonical form is stable and order-independent | Accumulated float error hashes identically; `audio_signature` and `extraction` excluded; **golden vector verified against an independent Python implementation** |
| UR-015 | T2 | `extinction_sec` accepted and range-checked | A manifest still carrying `anneal_sec` is a hard `400` naming the field |
| UR-016 | T2 | `gallery_scope` stored and served | An unrecognised scope is a closed-vocabulary `400`, not a free string |
| 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` |
Three are worth singling out, because each verifies a claim that would otherwise
@@ -162,27 +170,26 @@ requirement — so nothing is orphaned by its removal.
---
## Pending — the SR-003 schema bump
## The SR-003 schema bump — shipped at version 2
These are `Planned` rather than absent, because the bump is coordinated across
three repos and this register should show the work rather than imply the server
is finished.
`jmanifest_version` moved to **2** in lockstep with the truth file's
`schema_version`, per SR-003's requirement that breaking changes be batched and
ship together. The two fields stay independent by design; they coincide at 2
only because this bump touched both.
| ID | Requirement | Traces to | Priority | Status |
|---|---|---|---|---|
| UR-015 | Accept `extraction.extinction_sec` in place of `anneal_sec` | SR-003 | High | Planned |
| UR-016 | Accept and store `extraction.gallery_scope`; rank on it (§7) | SR-003 | Medium | Planned |
| UR-017 | Accept per-window belief and identification route; `scenes` becomes objects | SR-003 | High | Planned |
| UR-018 | Exclude belief from `content_id`, replicating it as an attribute | SR-003 | High | Planned |
**Flag day, not dual-accept** (`jRay` JR-003): version 1 is 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.
**UR-018 is the one with a trap in it.** Belief is a producer-side estimate that
may legitimately differ between pipeline versions for identical timings, so
**UR-018 was the one with a trap in it.** 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 `content_id`s
for the same content — the exact failure mode §9a quantises centiseconds to
avoid. It follows `audio_signature`'s precedent: replicated as an attribute, not
part of identity.
---
avoid, reintroduced one field along. It follows `audio_signature`'s precedent:
replicated as an attribute, never as identity. `src/content_id.rs` is unchanged
by the bump, and its golden vector still passes — which is the evidence, not the
claim.
## Notes on coverage