All checks were successful
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 5s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 5s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m33s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m6s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 5s
WAI conformance / does this change touch it (pull_request) Successful in 5s
WAI energy receipts / does this change touch it (pull_request) Successful in 4s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 4s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 4s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 5s
wai-web wasm32 / does this change touch it (pull_request) Successful in 4s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 46s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m0s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m2s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 25s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m10s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 1m50s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 18m59s
wai-pack / wai-pack · linux (pull_request) Successful in 1m19s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m44s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m18s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 7m56s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m53s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 24s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 10s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 30s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m52s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 14m38s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 24m37s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m27s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 1m2s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m52s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 6m20s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 40m17s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 8m2s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 14m35s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 20s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 46s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 5m4s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 2m23s
wai-pack / wai-pack · macos (pull_request) Successful in 1m6s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 2m16s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
774 lines
45 KiB
Markdown
774 lines
45 KiB
Markdown
# WAI Extension: Direction Records
|
||
|
||
> Status: Draft. Registers the record `wai.media.direction`: a person's direction of generated
|
||
> or edited video, as signed evidence bound to the output. A director, a person named by an
|
||
> Ed25519 key (as a `did:key`, the same key), signs **direction records** at four **levels** of
|
||
> control: a **storyboard** fixes the shots, each with its duration, its subjects by reference
|
||
> picture and its framing (the **shot** level), and may fix their order (the **sequence**
|
||
> level); **keyframes** fix, at stated times of a shot, a subject's box on screen, its size
|
||
> class, its screen position and its angle, relative to the subject, and a camera window, with a
|
||
> pinned interpolation between keys (the **frame** level), and **regions** hold samples of
|
||
> stated frames to a reference picture (the **pixel** level); a **sequence** fixes the edit:
|
||
> which shots, which part of each, in which order, cut or dissolved (the sequence level). Each
|
||
> record states the levels it sets. A tool that makes a video under direction signs a
|
||
> **directed output** record naming the direction records, the layout of every frame of every
|
||
> shot, the timeline of the edit and the output's frames by digest. Where the output was made
|
||
> by the pinned render of this extension from layers that `wai.generate.spectral` regenerates
|
||
> or that the verifier holds, every frame recomputes, and the checks are **exact**: each cut
|
||
> falls at its directed frame, each duration and the order are the directed ones, each subject
|
||
> is where the keyframes and their interpolation put it under a pinned integer **framing
|
||
> measure**, and each held region holds. Where a model made the frames in floating point, the
|
||
> same checks run on the layout the tool states, and the outcome is **attested** with the class
|
||
> of what stated it, never exact. Every directed element **governs** the output frames that show
|
||
> what it directed, and the record states them: the per-frame map shows which person directed
|
||
> each frame's framing, timing, held samples and place in the sequence, whoever made its
|
||
> samples, and each element maps onto a `direct` step of the contribution record and onto a
|
||
> C2PA action. WAI decides no legal question. No new transport. The reference implementation is
|
||
> the `direction` module of `wai-rs` (feature `direction`); the corpus is
|
||
> [`live-conformance/direction/`](../live-conformance/direction/), and its second
|
||
> implementation is `live-conformance/direction_verify.py`.
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
>
|
||
> Texts read for this revision, each pinned to the version named: the C2PA Technical
|
||
> Specification, version 2.4, §6.4, §18.2 (§18.2.2.1, §18.2.2.4 and §18.2.3 among it), §18.15.1,
|
||
> §18.15.2, §18.15.4.1 and §18.15.4.6; the Creator Assertions Working Group's Identity
|
||
> Assertion, version 1.2, §5.1, §5.1.2 and §6.1; the serialized schemas of OpenTimelineIO
|
||
> release 0.18.1 (`Timeline.1`, `Stack.1`, `Track.1`, `Clip.2`, `Transition.1`,
|
||
> `RationalTime.1`, `TimeRange.1`) and its transition types; the abstract of arXiv 2606.01900;
|
||
> and *Copyright and Artificial Intelligence, Part 2: Copyrightability*, a report of the United
|
||
> States Copyright Office (January 2025), its Part II.F and its Part V. A section number of one
|
||
> of those texts in this extension is that version's.
|
||
|
||
## 1. Scope
|
||
|
||
### 1.1 Why
|
||
|
||
Direction of generated video works at four levels: the pixel, the frame, the shot and the
|
||
sequence. A director decides what each shot shows and how it is framed, where a subject sits on
|
||
screen and how large, how the camera moves, which samples stay fixed, where each cut falls and
|
||
in which order the shots run. arXiv 2606.01900 states framing as functions of the subject's pose
|
||
and motion (shot size, angle and composition) and directs a virtual camera with sparse keyframes
|
||
deterministically interpolated into a trajectory. A generator then makes the samples.
|
||
|
||
The cited report concludes that outputs made with generative AI may be protected in whole or in
|
||
part where a person was able to determine the expressive elements they contain, that prompts
|
||
alone are unlikely to suffice at this stage, and that the questions are decided case by case
|
||
(its Part V); and that a person may select, coordinate and arrange generated material, or modify
|
||
it, in a way that contributes authorship (its Part II.F). Whether a director's choices are such
|
||
a contribution is a question for a registration authority or a court, under its own rule.
|
||
|
||
This extension makes the director's choices signed facts, bound to the output they governed, and
|
||
checks them: exactly, frame by frame, where the output recomputes; as the tool's statement, with
|
||
its class, where it does not. It states, for every frame, who directed what. It does not say what
|
||
any of that makes anyone.
|
||
|
||
### 1.2 In scope and out of scope
|
||
|
||
In scope: the records and their encodings (§2); the levels and the elements of direction (§3);
|
||
the storyboard (§4); keyframes, their interpolation and held regions (§5); the sequence and the
|
||
timeline arithmetic (§6); the framing measure (§7); the directed output (§8); the render (§9);
|
||
governance, the per-frame map and the contribution record's `direct` act (§10); the mappings
|
||
onto C2PA actions, identity assertions and an open timeline interchange format (§11);
|
||
verification (§12).
|
||
|
||
Out of scope:
|
||
|
||
- **Legal questions.** WAI decides none. A record states which person directed which frames at
|
||
which level, and how much of that a verifier checked; a reader applies its own rule to those
|
||
facts. Registering a work, and contacting any authority about it, are its owner's decisions.
|
||
- **How a generator follows direction.** A model conditioned on a storyboard, a keyframe or a
|
||
pose is the tool's; this extension checks what came out.
|
||
- **Measuring a subject in arbitrary pixels.** Where the subject is a known layer, the render of
|
||
§9 places it and its box is exact. A detector or tracker that finds a subject in a generated
|
||
frame is a method of the tool's, and what it states is attested with its class (§8.1).
|
||
- **Audio** and its direction, and direction of a live room's composition, which
|
||
[live-compose](live-compose.md) lays out from the room's events.
|
||
- **Camera paths in world space.** The camera of this revision is a window over a plate (§5.1);
|
||
a path through a scene is a later revision's.
|
||
|
||
### 1.3 Relationships
|
||
|
||
Relationship to the core spec: `wai.media.direction` is a **record** ([SPEC](../SPEC.md) §5):
|
||
signed records a WAI tool emits and verifies. It names no payload format and is not
|
||
dispatchable.
|
||
|
||
Relationship to `wai.generate.spectral`: a layer of a directed output may be a picture that
|
||
generator makes; a verifier holding the model file regenerates it (SPEC §5, *Payload of
|
||
`wai.generate.spectral`*).
|
||
|
||
Relationship to [deriving-edge](deriving-edge.md): the render of §9 places layers as that
|
||
extension's `wai.derive.overlay_composition` does, opaquely at integer offsets, and resamples by
|
||
the nearest sample, the reference rule of its `wai.derive.scaling` (§4.2 there), which this
|
||
extension pins for its own render.
|
||
|
||
Relationship to [synthesis](synthesis.md): a directed output's origin claim is that extension's;
|
||
direction changes no class. A video whose layers or frames a model generated is synthesized,
|
||
however closely a person directed it.
|
||
|
||
Relationship to the contribution record, `wai.media.contribution`: that record reserves the act
|
||
`direct` for this extension. §10.3 defines the step, its parameters and its dependency rule.
|
||
|
||
Relationship to the exploration record, `wai.media.exploration`: a subject's view, a shot's
|
||
reference or a plate may be a picture an exploration archive holds, chosen by a pick; this
|
||
extension names it by its samples digest, and the archive and the pick are that record's.
|
||
|
||
## 2. Records
|
||
|
||
Every record of this extension is a JSON object (RFC 8259) under the encodings of
|
||
[jwp-receipts](jwp-receipts.md) §2.4.3, read as synthesis §2 reads a record: strict JSON, no
|
||
member named twice, at most 32 levels of nesting, byte strings in lowercase hexadecimal,
|
||
integers unsigned and at most 2^53 − 1, no other number, no boolean, **terms** and
|
||
**date-times** as defined there. Each record has exactly the members its section lists, no
|
||
member missing and none added, at any level, and a `kind` naming it; one that does not is
|
||
`record_malformed`. So is a member outside the values or ranges its section gives, or one that
|
||
breaks a rule its section states of the record alone.
|
||
|
||
Every record carries `signer_pubkey` (32 bytes, an Ed25519 key of RFC 8032) and `sig` (64
|
||
bytes):
|
||
|
||
```
|
||
sig = Ed25519(signer_key, "wai:direction\x01" || JCS(record without "sig"))
|
||
link_hash = BLAKE3("wai:direction-link\x01" || JCS(record))
|
||
```
|
||
|
||
JCS is RFC 8785. The signature is verified strictly ([SPEC](../SPEC.md) §7 *Signatures*): a key
|
||
that fails step 1 or 2 is `bad_key`, and a signature that fails a later step `bad_signature`. A
|
||
record is named by its link hash wherever another names it. `issued_at` holds the instant its
|
||
signer signed it, self-asserted.
|
||
|
||
Values used throughout:
|
||
|
||
- A **signed integer** is a string: `0`, or an optional `-` followed by one to seven of the
|
||
digits `0`–`9` of which the first is not `0` (no `+`, no space, no `-0`), with a value from
|
||
−1 048 576 to 1 048 576. The encodings admit no negative number, and a subject's box may
|
||
begin left of or above the frame.
|
||
- A **box** is `{"x", "y", "w", "h"}`: `x` and `y` signed integers, the box's left and top edge
|
||
in pixels from the frame's top-left corner; `w` and `h` integers from 1 to 1 048 576. It may
|
||
extend past any edge of the frame.
|
||
- A **rect** is `{"x", "y", "w", "h"}`, four integers, `w` and `h` at least 1: a rectangle of
|
||
pixels within a picture, `x` + `w` at most its width and `y` + `h` at most its height, checked
|
||
where the picture is known (§8.5, §12.2 step 2).
|
||
- A **picture** is `width` × `height` pixels as the `RGB8` buffer of
|
||
[reconstruction-binding](reconstruction-binding.md) §2 (row-major, three bytes a pixel, no row
|
||
padding), each side from 1 to 4096 and `width` · `height` at most 2^23, the sizes
|
||
`wai.generate.spectral` makes. Its **samples digest** is SHA-256 (FIPS 180-4) over the
|
||
buffer. A **picture reference** is `{"samples", "width", "height"}`: a samples digest and the
|
||
size it is of. A pixel is addressed (*x*, *y*), *x* to the right and *y* down from the
|
||
top-left.
|
||
- A **frame range** is a pair `[a, b]` of integers, *a* < *b*: the frames *a* to *b* − 1. A
|
||
**frame set** is an array of frame ranges in ascending order, each beginning after the one
|
||
before it ends and not where it ends (`b` of one below `a` of the next): its one encoding.
|
||
- ⌊*a* / *b*⌋, for *b* above 0, rounds toward negative infinity. Every quantity of this
|
||
extension is an integer; none needs more than 127 bits with its sign.
|
||
|
||
## 3. Levels and elements
|
||
|
||
A **level** is one of `pixel`, `frame`, `shot` and `sequence`, in that order. Each direction
|
||
record has a member `levels`: the levels it sets, in that order, none twice. A record whose
|
||
`levels` is not exactly the levels its members set (§4, §5, §6) is `record_malformed`.
|
||
|
||
An **element** is one directed choice of a record, named by a **path**:
|
||
|
||
| level | element | path | what it fixes | its record |
|
||
|---|---|---|---|---|
|
||
| `shot` | a shot | `["shot", i]` | the shot's duration, its subjects and the framing it opens on | storyboard (§4) |
|
||
| `frame` | a shot's keys | `["keyframes", e]` | each subject's box and classes, and the camera, at each key and between keys | keyframes (§5) |
|
||
| `pixel` | a held region | `["region", j]` | the samples inside rects of stated frames | keyframes (§5.3) |
|
||
| `sequence` | the order and cuts | `["sequence"]` | which shots in which order, which part of each, each cut and dissolve | storyboard (§4) or sequence (§6) |
|
||
|
||
*i* is a shot's position in the storyboard, *e* an entry's and *j* a region's position in their
|
||
keyframes record. A record's **elements** are, in this order: a storyboard's shots by position,
|
||
then `["sequence"]` where it sets that level; a keyframes record's entries by position, then its
|
||
regions by position; a sequence record's `["sequence"]`.
|
||
|
||
## 4. The storyboard
|
||
|
||
A director signs a **storyboard**, `kind` `"wai-storyboard"`:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `kind` | string | `"wai-storyboard"` |
|
||
| `levels` | array | `["shot"]`, or `["shot", "sequence"]` when `order` is not `null` |
|
||
| `width`, `height` | integer | every frame's size: a picture's sides (§2) |
|
||
| `rate` | object | `{"num", "den"}`, each from 1 to 1 048 576: frames per second, as *num* / *den* |
|
||
| `subjects` | array | 0 to 16 subjects (§4.1) |
|
||
| `shots` | array | 1 to 256 shots (§4.2) |
|
||
| `order` | array or `null` | 1 to 1024 shot positions, in the order the shots run, each a shot whole, joined by cuts; `null` where the storyboard does not fix an order |
|
||
| `issued_at` | date-time | when the director signed it |
|
||
| `signer_pubkey`, `sig` | | §2: the director's key |
|
||
|
||
### 4.1 Subjects
|
||
|
||
A **subject** is `{"views"}`: 1 to 8 **views**, each `{"angle", "samples", "width", "height"}`,
|
||
a term naming the angle the subject is seen from and a picture reference: the subject's
|
||
reference picture as seen from that angle, its whole extent edge to edge. No angle appears twice
|
||
in one subject. Angles are the director's terms; `front`, `three-quarter`, `profile`, `back`,
|
||
`high` and `low` are examples, not a list. A subject is named by its position.
|
||
|
||
### 4.2 Shots
|
||
|
||
A **shot** is `{"duration", "subjects", "framing", "reference"}`:
|
||
|
||
- `duration`: its number of frames, from 1 to 65 536;
|
||
- `subjects`: 0 to 16 subject positions, none twice, in drawing order, the first drawn first;
|
||
- `framing`: one entry per subject of `subjects`, in its order: `{"size", "position",
|
||
"angle"}`, each `null` or a class: `size` and `position` the classes of §7, `angle` an angle
|
||
of that subject's views. It is the framing the shot opens on: the measure of its first frame
|
||
(§7);
|
||
- `reference`: a picture reference (§2), a reference picture for the shot, or `null`.
|
||
|
||
A position in `subjects` or `order` that is not a subject's or a shot's, or an `angle` that is
|
||
not one of the subject's views', is `record_malformed`.
|
||
|
||
## 5. Keyframes
|
||
|
||
A director signs a **keyframes** record, `kind` `"wai-keyframes"`:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `kind` | string | `"wai-keyframes"` |
|
||
| `storyboard` | 32 B hex | the link hash of the storyboard it directs |
|
||
| `levels` | array | `["frame"]` when only `keyframes` is not `null`, `["pixel"]` when only `regions` is not, `["pixel", "frame"]` when neither is `null` |
|
||
| `keyframes` | array or `null` | 1 to 256 entries, each `{"shot", "keys"}`: a shot's position and 1 to 1024 keys (§5.1); no shot twice |
|
||
| `regions` | array or `null` | 1 to 256 held regions (§5.3) |
|
||
| `issued_at` | date-time | when the director signed it |
|
||
| `signer_pubkey`, `sig` | | §2: the director's key |
|
||
|
||
A record whose `keyframes` and `regions` are both `null` is `record_malformed`.
|
||
|
||
### 5.1 Keys
|
||
|
||
A **key** is `{"t", "interp", "camera", "subjects"}`:
|
||
|
||
- `t`: the frame of the shot it is at, from 0; the keys of an entry in strictly ascending `t`;
|
||
- `interp`: `hold`, `linear` or `ease`: how its values move to the next key's (§5.2);
|
||
- `camera`: `null`, or a rect: the **camera window**, the part of the shot's plate the frame
|
||
shows, scaled to the frame (§9.1);
|
||
- `subjects`: one entry per subject of the shot, in the shot's order, each `{"box", "size",
|
||
"position", "angle"}`: `box` `null` or a box (§2), the subject's whole extent on screen, actor
|
||
relative: where the subject is and how large; `size`, `position` and `angle` each `null` or a
|
||
class (§7).
|
||
|
||
Within an entry, every key's `subjects` has one length, and for each subject either every key
|
||
gives a box or none does; likewise the camera. Otherwise `record_malformed`.
|
||
|
||
A subject's boxes, and the cameras, are **tracks**. A track begins at the entry's first key.
|
||
The classes of a key are checked at its `t` alone.
|
||
|
||
### 5.2 Interpolation
|
||
|
||
A track's value at frame *t* of the shot, from the first key's `t` to the shot's last frame, is
|
||
computed member by member (`x`, `y`, `w`, `h`): for *t* from a key *k* (at *t*ₖ, value *v*ₖ)
|
||
up to the next key (at *t*ₖ₊₁, value *v*ₖ₊₁), with *n* = *t* − *t*ₖ and *D* = *t*ₖ₊₁ −
|
||
*t*ₖ, by key *k*'s `interp`:
|
||
|
||
| `interp` | value |
|
||
|---|---|
|
||
| `hold` | *v*ₖ |
|
||
| `linear` | *v*ₖ + ⌊(*v*ₖ₊₁ − *v*ₖ) · *n* / *D*⌋ |
|
||
| `ease` | *v*ₖ + ⌊(*v*ₖ₊₁ − *v*ₖ) · *n*² · (3*D* − 2*n*) / *D*³⌋ |
|
||
|
||
From the last key to the shot's end the value is the last key's. `ease` is the cubic that
|
||
starts and ends with zero speed, *s*²(3 − 2*s*) of *s* = *n* / *D*, in integers. A value lies
|
||
between its two keys' values, so `w` and `h` stay at least 1 and a camera window between two
|
||
windows inside the plate stays inside it.
|
||
|
||
### 5.3 Held regions
|
||
|
||
A **held region** is `{"shot", "from", "to", "rects", "source"}`: a shot's position; its frames
|
||
`from` up to, not including, `to` (`from` < `to`); 1 to 64 rects within the frame; and a picture
|
||
reference of the frame's size, the **source**. In those frames, every pixel inside a rect is the
|
||
source's pixel at the same position. A region holds a reference picture, a drawn matte, a fixed
|
||
element or a locked background to exact samples, whatever made the rest of the frame.
|
||
|
||
## 6. The sequence
|
||
|
||
A director (an editor directs the sequence) signs a **sequence**, `kind` `"wai-sequence"`:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `kind` | string | `"wai-sequence"` |
|
||
| `storyboard` | 32 B hex | the link hash of the storyboard it edits |
|
||
| `levels` | array | `["sequence"]` |
|
||
| `events` | array | 1 to 1024 events (§6.1) |
|
||
| `issued_at` | date-time | when the director signed it |
|
||
| `signer_pubkey`, `sig` | | §2: the director's key |
|
||
|
||
### 6.1 Events
|
||
|
||
An **event** is `{"shot", "in", "out", "transition"}`: a shot's position; the frames of the shot
|
||
it shows, `in` up to, not including, `out` (`in` < `out` ≤ 65 536), its **length** *L* = `out`
|
||
− `in`; and the transition into it: `{"kind": "cut"}`, or `{"kind": "dissolve", "frames"}`, a
|
||
dissolve over `frames` ≥ 1 frames. With *d*ᵢ the dissolve frames into event *i* (0 for a cut):
|
||
the first event's transition is a cut, and *d*ᵢ + *d*ᵢ₊₁ ≤ *L*ᵢ for every event *i* (with
|
||
*d* = 0 past the last), so no frame shows more than two events. Otherwise `record_malformed`.
|
||
|
||
A storyboard's `order` is the events `{"shot": i, "in": 0, "out": duration of i, "transition":
|
||
{"kind": "cut"}}`, one for each position *i* of `order`, in its order.
|
||
|
||
### 6.2 The timeline
|
||
|
||
Events run in order. Event *i* **starts** at output frame *S*ᵢ: *S*₀ = 0 and *S*ᵢ = *S*ᵢ₋₁ +
|
||
*L*ᵢ₋₁ − *d*ᵢ, and occupies the output frames *S*ᵢ up to, not including, *S*ᵢ + *L*ᵢ. The
|
||
output has *N* = *S*ₘ₋₁ + *L*ₘ₋₁ frames, *m* the number of events. A dissolve into event *i*
|
||
overlaps the last *d*ᵢ frames of event *i* − 1 with the first *d*ᵢ of event *i*.
|
||
|
||
Output frame *f* **shows** frame `in` + (*f* − *S*ᵢ) of event *i*'s shot for each event *i* that
|
||
occupies it: one event, or two in a dissolve. The **cuts** are *S*₁, …, *S*ₘ₋₁: the frames where
|
||
each event after the first begins to show, cut or dissolved.
|
||
|
||
## 7. The framing measure
|
||
|
||
The **framing measure** of a subject in a frame of `width` *W* and `height` *H* is computed from
|
||
its box (*x*, *y*, *w*, *h*) and the angle of the view it is drawn from:
|
||
|
||
- **size**: with *q* = ⌊1000 · *h* / *H*⌋, the subject's height in thousandths of the frame's:
|
||
|
||
| class | *q* |
|
||
|---|---|
|
||
| `extreme_wide` | below 250 |
|
||
| `wide` | 250 to 699 |
|
||
| `full` | 700 to 1099 |
|
||
| `medium_wide` | 1100 to 1499 |
|
||
| `medium` | 1500 to 2199 |
|
||
| `medium_close` | 2200 to 3199 |
|
||
| `close` | 3200 to 4999 |
|
||
| `extreme_close` | 5000 or more |
|
||
|
||
The box is the subject's whole extent, so a close shot's box runs far past the frame. The
|
||
bounds are this measure's: a director's vocabulary maps onto them, and they define no
|
||
craft's terms.
|
||
- **position**: the cell of a three-by-three grid holding the box's centre: column *c* = min(2,
|
||
max(0, ⌊3 · (2*x* + *w*) / (2*W*)⌋)) and row *r* = min(2, max(0, ⌊3 · (2*y* + *h*) /
|
||
(2*H*)⌋)), named `R-C` with *R* `top`, `middle` or `bottom` for *r* 0, 1, 2 and *C* `left`,
|
||
`centre` or `right` for *c* 0, 1, 2: `top-left` … `middle-centre` … `bottom-right`.
|
||
- **angle**: the angle of the view.
|
||
|
||
A directed class **matches** when the measure gives it. A directed box matches when it equals the
|
||
subject's box member for member; a directed camera window when it equals the frame's window.
|
||
|
||
## 8. The directed output
|
||
|
||
### 8.1 The record
|
||
|
||
The tool that made the output signs the **directed output**, `kind` `"wai-directed"`:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `kind` | string | `"wai-directed"` |
|
||
| `direction` | array | 1 to 64 link hashes of the direction records it was made under, the storyboard first; none twice |
|
||
| `directors` | array | 1 to 64 keys (32 B hex), the people who directed it; none twice, none the tool's key |
|
||
| `check` | string | `replicate` or `attest` |
|
||
| `attestation` | object or `null` | `null` for `replicate`; for `attest`, `{"generator", "class"}`: a term naming what made the frames, and a term naming the class of method that stated the layout (a tracker, a detector, the generator's own conditioning) |
|
||
| `layers` | array | 0 to 256 layers (§8.2) |
|
||
| `clips` | array | 1 to 256 clips (§8.3) |
|
||
| `timeline` | array | 1 to 1024 events (§6.1): the edit as made |
|
||
| `output` | object | `{"width", "height", "frames", "digest", "content_hash"}`: the output's frame size, its number of frames (1 to 2^20), the digest of its frames (§8.4), and the content hash (jwp-receipts §2.1) of the object it was published as, or `null` |
|
||
| `governs` | array | 0 to 1024 entries (§10.1) |
|
||
| `issued_at` | date-time | when the tool signed it |
|
||
| `signer_pubkey`, `sig` | | §2: the **tool's key** |
|
||
|
||
`replicate` states that every frame is the render of §9 over the layers; `attest` that something
|
||
else made the frames and the layout is the tool's statement, of the class it names. The record
|
||
reads only if its timeline does (§6.1).
|
||
|
||
### 8.2 Layers
|
||
|
||
A **layer** is a picture reference with its source, `{"samples", "width", "height",
|
||
"source"}`; no samples digest twice. Its `source` is one of:
|
||
|
||
- `{"generator", "model", "prompt", "seed", "steps"}` with `generator`
|
||
`"wai.generate.spectral"`: the picture that generator makes with the model file whose SHA-256
|
||
is `model` (32 B hex), the prompt (0 to 256 bytes, each from 0x20 to 0x7E other than `"` and
|
||
`\`), the seed (0 to 2^32 − 1) and the steps (1 to 64), at the layer's size: SPEC §5,
|
||
*Payload of `wai.generate.spectral`*, `generate(model, prompt, seed, steps, height, width)`;
|
||
- `{"generator", "model"}`, `generator` any other term: a picture another generator made, with
|
||
the model whose digest is `model`;
|
||
- `null`: a picture given as it is, such as a person's drawing.
|
||
|
||
A layer is named by its samples digest wherever a clip or a direction record names a picture.
|
||
|
||
### 8.3 Clips
|
||
|
||
A **clip** is the layout of every frame of one shot as made, `{"frames", "plate", "camera",
|
||
"subjects", "holds"}`; the clips are the storyboard's shots, in order:
|
||
|
||
- `frames`: its number of frames, from 1 to 65 536;
|
||
- `plate`: the samples digest of the picture the frames are drawn over, or `null` (§8.5);
|
||
- `camera`: `null`, or one rect per frame: the camera window over the plate;
|
||
- `subjects`: 0 to 16 entries, in drawing order, each `{"subject", "views", "boxes"}`: a
|
||
subject's position; one view position per frame (the view drawn); and one box per frame;
|
||
- `holds`: 0 to 64 entries `{"from", "to", "rects", "source"}`: frames `from` up to, not
|
||
including, `to` (`from` < `to` ≤ `frames`), 1 to 64 rects and a samples digest: the regions
|
||
held, as made.
|
||
|
||
A `camera`, `views` or `boxes` whose length is not `frames` is `record_malformed`.
|
||
|
||
### 8.4 The frames' digest
|
||
|
||
```
|
||
digest = BLAKE3("wai:direction-frames\x01" || D_0 || D_1 || … || D_{N−1})
|
||
```
|
||
|
||
with *D*ₖ the samples digest of output frame *k*, `width` × `height` `RGB8` (§2), in order. A
|
||
frame is named by its samples digest, as a frame of a `wai.video.int_motion` clip is checked by
|
||
its state.
|
||
|
||
### 8.5 Layout rules
|
||
|
||
Against the storyboard, a directed output whose layout breaks one of these is `layout_invalid`,
|
||
or `layer_missing` where a picture it needs is not one of its layers:
|
||
|
||
1. There is one clip per shot of the storyboard.
|
||
2. Each clip's subjects are subjects of the storyboard, and each view position is one of that
|
||
subject's views.
|
||
3. For `replicate`: each clip's `plate` is not `null`, and is a layer (`layer_missing`); with no
|
||
camera, the plate
|
||
is the frame's size; each camera window is a rect within the plate; each view drawn is a
|
||
layer of the size the storyboard states for that view (`layer_missing` where it is not a
|
||
layer). For `attest`: each `plate` is `null`.
|
||
4. Each hold's rects are within the frame; for `replicate` its source is a layer
|
||
(`layer_missing`) of the frame's size.
|
||
5. Each timeline event names a shot of the storyboard and `out` is at most its clip's
|
||
`frames`.
|
||
6. The output's `width` and `height` are the storyboard's, and its `frames` is the timeline's
|
||
*N* (§6.2).
|
||
|
||
## 9. The render
|
||
|
||
The render is pinned: every byte of every frame it makes is a pure function of the layers and
|
||
the record, integer throughout.
|
||
|
||
### 9.1 A clip's frame
|
||
|
||
Frame *t* of a clip, `width` *W* by `height` *H*, is drawn in this order:
|
||
|
||
1. **The plate.** With no camera, out(*X*, *Y*) = plate(*X*, *Y*). With the camera window
|
||
(*cx*, *cy*, *cw*, *ch*) of frame *t*, out(*X*, *Y*) = plate(*cx* + ⌊*X* · *cw* / *W*⌋, *cy* +
|
||
⌊*Y* · *ch* / *H*⌋): the window resampled to the frame by the nearest sample.
|
||
2. **The subjects**, in the clip's order. With the box (*bx*, *by*, *bw*, *bh*) of frame *t* and
|
||
the view *V* of frame *t*, *vw* × *vh*: every pixel (*X*, *Y*) of the frame with *bx* ≤ *X*
|
||
< *bx* + *bw* and *by* ≤ *Y* < *by* + *bh* is *V*(⌊(*X* − *bx*) · *vw* / *bw*⌋, ⌊(*Y* −
|
||
*by*) · *vh* / *bh*⌋): the view scaled to its box by the nearest sample and drawn opaquely,
|
||
the part off the frame dropped.
|
||
3. **The holds**, in order: for each hold whose frames include *t*, every pixel inside each of
|
||
its rects is the source's pixel at the same position.
|
||
|
||
### 9.2 An output frame
|
||
|
||
Output frame *f* that shows one event is that event's clip frame (§6.2). One that shows events
|
||
*i* − 1 and *i*, in a dissolve of *d* = *d*ᵢ frames, is a blend of their clip frames *A* and *B*:
|
||
with *j* = *f* − *S*ᵢ + 1 (1 to *d*), each byte is
|
||
|
||
```
|
||
out = ⌊(A · (d + 1 − j) + B · j + ⌊(d + 1) / 2⌋) / (d + 1)⌋
|
||
```
|
||
|
||
so *B*'s weight rises by 1/(*d* + 1) each frame and neither picture is ever shown alone inside
|
||
the dissolve.
|
||
|
||
## 10. Governance
|
||
|
||
### 10.1 What an element governs
|
||
|
||
An element (§3) **governs** the output frames that show what it directed, on the timeline the
|
||
directed output states:
|
||
|
||
| element | the output frames it governs |
|
||
|---|---|
|
||
| `["shot", i]` | each frame that shows any frame of shot *i* |
|
||
| `["keyframes", e]` | each frame that shows a frame *t* of the entry's shot with *t* in its **keyed frames**: from its first key's `t` to the shot's end where it has a track, and otherwise the `t` of each key that gives a class |
|
||
| `["region", j]` | each frame that shows a frame of the region's shot from `from` up to, not including, `to` |
|
||
| `["sequence"]` | every output frame |
|
||
|
||
Within those frames, a region governs the samples inside its rects, and every other element every
|
||
sample. A frame that shows two events is governed by what governs either.
|
||
|
||
The directed output states this in `governs`: one entry for each element of each record it
|
||
names, in the order of `direction` and then of each record's elements (§3), each `{"record",
|
||
"element", "frames"}`: the record's position in `direction`, the element's path and the frame
|
||
set (§2) it governs, `[]` for none. An entry that is not that element's, or not its frame set, is
|
||
`governance_mismatch`. So a tool cannot claim for a director frames the director's choice did not
|
||
govern, and cannot leave out frames it did.
|
||
|
||
### 10.2 The per-frame map
|
||
|
||
The **frame map** gives each output frame: the frames it shows, `[shot, t]` for each event
|
||
occupying it (§6.2); and `directed_by`, an object of four members, `pixel`, `frame`, `shot` and
|
||
`sequence`, each the positions in `directors` of the signers of the elements of that level that
|
||
govern it, ascending, none twice. It shows, frame by frame, which person directed the framing,
|
||
the timing, the held samples and the order, beside whatever made the frame's samples: the
|
||
layers' sources (§8.2) for `replicate`, the attested generator for `attest`.
|
||
|
||
### 10.3 The contribution record's `direct` act
|
||
|
||
The contribution record, `wai.media.contribution`, reserves the act `direct` for this
|
||
extension. Each element that governs at least one frame becomes one step of that record's session:
|
||
|
||
| member of the step | value |
|
||
|---|---|
|
||
| `party` | the director: a `human` party whose key is the element's record's signer |
|
||
| `act` | `direct` |
|
||
| `inputs` | none |
|
||
| `output`, `region` | `null` |
|
||
| `params` | `{"record", "element", "frames"}`: the direction record's link hash, the element's path, and the frame set it governs (§10.1) |
|
||
| `check` | `replicate` when the directed output's `check` is `replicate`, so every check of §12.2 the element has ran on recomputed frames; `attest` otherwise |
|
||
| signer | the director |
|
||
|
||
Its dependency rule: the step introduces no sample and changes none; it adds the director to
|
||
`directed_by` of every sample the element governs (§10.1), in every frame of `frames`, and
|
||
leaves `made_by` as it is. A verifier of the contribution record holding the direction records and
|
||
the directed output recomputes `frames` by §10.1 and refuses a step whose `frames` differ, as
|
||
§12.2 refuses a `governs` entry. So the authorship map shows, per frame, that a person directed
|
||
framing and timing where a model made the samples. The contribution record's revision of
|
||
2026-10-21 maps still pictures only; the step applies, sample by sample, in the revision that maps
|
||
video frame by frame, and to a still picture as an output of one frame.
|
||
|
||
## 11. Mappings
|
||
|
||
### 11.1 C2PA actions
|
||
|
||
For each director with at least one element that governs a frame, an accepted case gives one
|
||
C2PA statement, `{"actions": A}`, written in JCS: `A` is an actions assertion's actions (C2PA 2.4
|
||
§18.15.1), one for each of that director's elements that governs a frame, in the order of §10.1:
|
||
|
||
```
|
||
{"action": "c2pa.edited", "changes": C, "description": T}
|
||
```
|
||
|
||
- `T`, the action's description (C2PA 2.4 §18.15.4.1), by level: `"directed shot: framing,
|
||
duration and subjects"`, `"directed keyframes: framing over time"`, `"directed region: samples
|
||
held to a reference"` and `"directed sequence: order, cuts and transitions"`.
|
||
- `C`, its `changes` (C2PA 2.4 §18.15.4.6): one region map (§18.2) for each frame range of the
|
||
element's frame set, in order, each `{"region": [{"type": "frame", "frame": {"start": a,
|
||
"end": b − 1}}]}`, a frame range of §18.2.2.4, whose end is inclusive. For a held region, one
|
||
region map for each frame range and each rect, by range and then by rect, whose `region`
|
||
holds that frame range and then `{"type": "spatial", "shape": {"type": "rectangle", "unit":
|
||
"pixel", "origin": {"x": X, "y": Y}, "width": W, "height": H}}`, the rect, the coordinates as
|
||
integers (a manifest in CBOR writes them as the floats §18.2.3 types them).
|
||
|
||
C2PA 2.4 §18.2.2.4 recommends temporal ranges for media over time. Direction is exact in frames,
|
||
and a frame's time at a rate such as 24000/1001 has no exact decimal form, so the statement uses
|
||
frame ranges; a manifest that also wants times derives them from `rate`.
|
||
|
||
Each director's statement is one actions assertion of the output's manifest, labelled by C2PA
|
||
2.4 §6.4's rule for several assertions of one type. It is never the first actions assertion,
|
||
which begins with the manifest's `c2pa.created` or `c2pa.opened` action (C2PA 2.4 §18.15.2):
|
||
the generation's, or the contribution record's statement where there is one. Embedding the
|
||
statements in a manifest, and signing it, is C2PA's.
|
||
|
||
### 11.2 Identity assertions (informative)
|
||
|
||
A director may give the output's manifest an identity assertion (Identity Assertion 1.2 §5.1,
|
||
label `cawg.identity`) whose referenced assertions include the actions assertion of §11.1 that
|
||
holds that director's actions, which §6.1 there gives as an example of an assertion that lets an
|
||
actor describe its relationship to the content. A role of §5.1.2 there is the director's own
|
||
statement; this extension derives none. The director's key in the records is an Ed25519 key, and
|
||
the relation between it and the assertion's credential is the director's to state.
|
||
|
||
### 11.3 An open timeline interchange format (informative)
|
||
|
||
A sequence (§6) maps onto OpenTimelineIO's serialized schemas, release 0.18.1: a `Timeline.1`
|
||
whose `tracks` is a `Stack.1` holding one `Track.1` of `kind` `"Video"`, whose children are, for
|
||
each event *i* in order, a `Clip.2` whose `source_range` is a `TimeRange.1` from `start_time`
|
||
`in` to a `duration` of *L*ᵢ − *d*ᵢ₊₁ frames, each a `RationalTime.1` at `rate` *num* / *den*,
|
||
naming the shot's clip as its media; and, before each event entered by a dissolve, a
|
||
`Transition.1` of `transition_type` `"SMPTE_Dissolve"` with `in_offset` 0 and `out_offset` *d*ᵢ
|
||
frames. The cut point is then *S*ᵢ, the outgoing clip's range ends there, and the dissolve
|
||
draws its last *d*ᵢ frames from past that range, as this extension's timeline shows them. An edit
|
||
decision list that records each event's source in and out points and its record in point (*S*ᵢ),
|
||
with a cut or a dissolve of a duration, carries the same values. Neither format carries the
|
||
framing of §5 or the governance of §10, and neither is signed: they are the edit's interchange,
|
||
and the direction records are its evidence.
|
||
|
||
## 12. Verification
|
||
|
||
### 12.1 Inputs
|
||
|
||
A verifier is given: the directed output record; the direction records it holds, in a list; the
|
||
model files it holds, each named by its SHA-256; the pictures it holds, each a buffer named by
|
||
its samples digest; and a **policy** of one member:
|
||
|
||
| member | values | default | meaning |
|
||
|---|---|---|---|
|
||
| `require` | `attested`, `exact` | `attested` | the lowest status it accepts (`not_exact`) |
|
||
|
||
### 12.2 Steps
|
||
|
||
The verifier runs these steps and stops at the first refusal, which it reports with its reason
|
||
and its place: `["output"]`, `["record", i]`, `["direction", k]`, `["layer", i]`, `["clip", i]`,
|
||
`["clip", i, t]`, `["timeline"]`, `["timeline", i]`, `["governs", i]` or `["policy"]`, each *i*
|
||
a position in its list (a record's among the records given, a direction record's *k* among
|
||
`direction`), and *t* a frame of a clip.
|
||
|
||
1. **Reading** (§2, §4–§6, §8): the directed output at `["output"]`, then each record given at
|
||
`["record", i]`: `record_malformed`, `bad_key`, `bad_signature`.
|
||
2. **The direction**, each link of `direction` in order at `["direction", k]`:
|
||
1. a record given has that link hash (`record_unknown`);
|
||
2. the first is a storyboard and no other is; each keyframes or sequence record's
|
||
`storyboard` is the first's link hash (`storyboard_mismatch`);
|
||
3. its signer is one of `directors` (`signer_not_party`);
|
||
4. no second record sets the sequence level, and no keyframes entry directs a shot an
|
||
earlier record's entry directs (`direction_conflict`);
|
||
5. against the storyboard, every shot, subject and angle it names exists; every key's `t` is
|
||
below its shot's duration; every key has one subject entry per subject of its shot; every
|
||
region's `to` is at most its shot's duration, its rects are within the frame and its
|
||
source is the frame's size; every event's `out` is at most its shot's duration
|
||
(`direction_inconsistent`).
|
||
3. **The check**, at `["output"]`: a `replicate` record with a layer of another generator
|
||
than `wai.generate.spectral` (§8.2) is `check_mismatch`: a float generator's picture is
|
||
never claimed exact.
|
||
4. **The layout** (§8.5): rule 1 at `["output"]`; rules 2 to 4 clip by clip at `["clip", i]`;
|
||
rule 5 event by event at `["timeline", i]`; rule 6 at `["output"]`: `layout_invalid`,
|
||
`layer_missing`.
|
||
5. **The layers**, for `replicate`, each in order at `["layer", i]`: a generated layer whose
|
||
model file the verifier does not hold, or a given layer whose buffer it does not hold, is
|
||
`layer_missing`; a held buffer that is not `width` · `height` · 3 bytes is
|
||
`samples_mismatch`; a generated layer whose picture's samples digest is not `samples` is
|
||
`regeneration_mismatch`.
|
||
6. **The frames**, for `replicate`, at `["output"]`: the verifier renders every clip frame (§9.1)
|
||
and every output frame (§9.2) and computes the digest of §8.4; another digest than
|
||
`output.digest` is `frame_mismatch`.
|
||
7. **The direction against the layout**: each element of each direction record, in the order of
|
||
§10.1, against the clips and the timeline the directed output states:
|
||
- `["shot", i]`: clip *i*'s `frames` is the shot's duration (`duration_mismatch`, at
|
||
`["clip", i]`); its subjects are the shot's, in its order (`subject_mismatch`, at
|
||
`["clip", i]`); each class of the shot's `framing` matches the measure (§7) of clip frame
|
||
0 (`framing_mismatch`, at `["clip", i, 0]`);
|
||
- `["keyframes", e]`: for each keyed frame *t* of the entry's shot in ascending order, each
|
||
subject's box is its track's value (§5.2), the camera window is the camera track's, and,
|
||
at a key, each class it gives matches the measure (`framing_mismatch`, at `["clip", s,
|
||
t]`, *s* the shot);
|
||
- `["region", j]`: the shot's clip has a hold with the region's `from`, `to`, rects and source
|
||
(`region_mismatch`, at `["clip", s]`);
|
||
- `["sequence"]`: with the directed events (§6.1), the timeline holds the same shots in the
|
||
same order (`order_mismatch`, at `["timeline"]`); then, event by event, each starts at its
|
||
directed frame (`cut_mismatch`, at `["timeline", i]`); then each has its directed
|
||
transition (`transition_mismatch`); then its directed `in` and `out`
|
||
(`duration_mismatch`).
|
||
8. **Governance** (§10.1), each entry in order at `["governs", i]`, and a missing entry at the
|
||
position it would have: `governance_mismatch`.
|
||
9. **Policy**: an `attest` record under `require` `exact` is `not_exact`, at `["policy"]`.
|
||
|
||
A sequence level no record sets is not checked: the order is then the tool's own, and no element
|
||
governs it.
|
||
|
||
### 12.3 Status
|
||
|
||
The outcome's **status** is `exact` for `replicate`: the frames recompute, so the layout the
|
||
checks of step 7 read is what the frames hold, cut for cut and pixel for pixel. It is `attested`
|
||
for `attest`, with the attestation's generator and class: the same checks ran on what the tool
|
||
stated, which nothing recomputed. An `attest` record is never reported exact.
|
||
|
||
### 12.4 What an accepted case states
|
||
|
||
- `output`: the directed output's link hash;
|
||
- `status`, `generator` and `class`: §12.3, the last two `null` when exact;
|
||
- `records`: each record of `direction`, in order, its `kind`, its link hash, its signer's
|
||
position in `directors` and its `levels`;
|
||
- `elements`: each element in the order of §10.1, its `record`, `element`, `level`, `director`
|
||
and governed `frames`;
|
||
- `cuts`: the cuts of the stated timeline (§6.2);
|
||
- `frames`: the frame map (§10.2), one entry per output frame;
|
||
- `frame_sha256`: for `exact`, the samples digest of each output frame, in order; `null`
|
||
otherwise;
|
||
- `layers`: each layer's status, in order: `regenerated` or `held` for `exact`, `stated` for
|
||
`attested`;
|
||
- `c2pa`: the statements of §11.1, each `{"director", "statement"}`, by director.
|
||
|
||
## 13. Reasons
|
||
|
||
The codes this extension adds, each with where it is raised:
|
||
|
||
| reason | raised at |
|
||
|---|---|
|
||
| `record_malformed` | §2, §3, §4, §5, §6.1, §8, §12.2 step 1 |
|
||
| `bad_key` | §2 |
|
||
| `bad_signature` | §2 |
|
||
| `record_unknown` | §12.2 step 2 |
|
||
| `storyboard_mismatch` | §12.2 step 2 |
|
||
| `signer_not_party` | §12.2 step 2 |
|
||
| `direction_conflict` | §12.2 step 2 |
|
||
| `direction_inconsistent` | §12.2 step 2 |
|
||
| `check_mismatch` | §8.1, §12.2 step 3 |
|
||
| `layout_invalid` | §8.5 |
|
||
| `layer_missing` | §8.5, §12.2 step 5 |
|
||
| `samples_mismatch` | §12.2 step 5 |
|
||
| `regeneration_mismatch` | §8.2, §12.2 step 5 |
|
||
| `frame_mismatch` | §8.4, §12.2 step 6 |
|
||
| `duration_mismatch` | §4.2, §6.1, §12.2 step 7 |
|
||
| `subject_mismatch` | §4.2, §12.2 step 7 |
|
||
| `framing_mismatch` | §5, §7, §12.2 step 7 |
|
||
| `region_mismatch` | §5.3, §12.2 step 7 |
|
||
| `order_mismatch` | §6, §12.2 step 7 |
|
||
| `cut_mismatch` | §6.2, §12.2 step 7 |
|
||
| `transition_mismatch` | §6.1, §12.2 step 7 |
|
||
| `governance_mismatch` | §10.1, §12.2 step 8 |
|
||
| `not_exact` | §12.1, §12.3 |
|
||
|
||
## 14. Conformance
|
||
|
||
[`live-conformance/direction/`](../live-conformance/direction/) holds the corpus, in one file,
|
||
`vectors.json`, written from fixed seeds by `wai_direction_vectors gen`. It holds the model file
|
||
of `wai.generate.spectral` its layers are generated with, the pictures given as they are, the
|
||
**measures** (the framing measure of §7 alone: boxes at both sides of every size bound and every
|
||
column and row bound, past every edge and at the limits of a signed integer, each with the classes
|
||
it measures), the **interpolations** (§5.2 alone: each mode, rising and falling, at the keys,
|
||
between them and with floors below zero, each with its value), and the **vectors**: each a case
|
||
of §12.1, its outcome and, accepted, everything §12.4 states; refused, its reason and its place.
|
||
Its frames are small (64 × 36 pixels at 24 frames a second), so every one recomputes. It has:
|
||
|
||
- **Accepted, `exact`**: a storyboard of three shots and two subjects, a cinematographer's
|
||
keyframes (a camera that eases across a plate in one shot, a push in from a medium to a close
|
||
framing in another) and a held region of a drawn reference, and an editor's sequence that
|
||
reorders the shots, trims one and dissolves into it, every frame recomputed; the same
|
||
storyboard fixing its own order, with no other record; a storyboard and keyframes with no
|
||
sequence level, the order then the tool's own; and the first under a policy that requires
|
||
exact.
|
||
- **Accepted, `attested`**: the first direction with frames a float generator made, the layout
|
||
stated by a tracker.
|
||
- **Refused**: each reason of §13 at least once, each a control of one defect: among them a cut
|
||
one frame from its directed frame, a keyframe box one pixel from its track, a size class the
|
||
box does not measure, a direction record signed by a key that is not a director's, the edit's
|
||
shots in another order, a float generator's plate claimed exact, frames that do not recompute,
|
||
a governance entry claiming frames its element does not govern, and a missing held region.
|
||
|
||
A conforming verifier gives every vector the outcome, the reason, the place and the statements
|
||
the corpus states. `wai_direction_vectors verify` and the test `tests/direction.rs` check that
|
||
the file regenerates byte for byte, that `wai-rs` gives every vector its outcome, that the corpus
|
||
refuses with every reason of §13, and that §13 lists exactly the reference's codes.
|
||
`live-conformance/direction_verify.py` is the second implementation, written from this text: it
|
||
shares no code with `wai-rs`; it runs the generator in its own arithmetic and checks it first
|
||
against the generator's conformance vector (`wai-rs/tests/vectors/generate/sha256.json`),
|
||
renders every frame, interpolates every track and measures every box itself, writes every
|
||
statement and C2PA statement itself, reads records with synthesis's second implementation's
|
||
JSON reader, verifies every signature with WAI's strict rule (`claim-conformance/_ed25519.py`)
|
||
and every link hash with BLAKE3 written from its specification (`claim-conformance/_blake3.py`);
|
||
it mutates copies of the corpus and requires every copy to fail; and it drops each of its own
|
||
checks in turn and requires the corpus to catch the weakened verifier.
|
||
|
||
## 15. Security and privacy considerations
|
||
|
||
- **What is checked and what is stated.** A direction record is its director's signed
|
||
statement of a choice; WAI checks the output against it, not whether the director made the
|
||
choice unaided. Under `replicate` the frames recompute, so the layout the checks read is what
|
||
the frames hold: a cut, a box, a held sample is exact. Under `attest` the layout is the tool's
|
||
statement, of the class it names, and the outcome says so.
|
||
- **No enrolment without a signature.** A person is credited with direction only for elements of
|
||
records that person signed, and a directed output names its directors; a record by any other
|
||
key is refused. A tool cannot credit or blame a director for frames the director's elements do
|
||
not govern (§10.1).
|
||
- **Direction is not making.** Directing a frame does not make its samples. The frame map keeps
|
||
the two apart: `directed_by` per level beside the layers' or the generator's making, never one
|
||
merged figure, and an origin claim keeps its class ([synthesis](synthesis.md) §3.2).
|
||
- **Not a judgment.** The records state facts frame by frame; they rank no contribution. What
|
||
any of it means for authorship is decided elsewhere, by others, under their own rules.
|
||
- **Privacy.** Records name directors by keys, never by name; subjects by reference pictures,
|
||
which a director discloses as it chooses. A layer's prompt is in the clear in the directed
|
||
output; a tool that keeps prompts private names the layer as given (`source` `null`) and
|
||
commits the prompt in a contribution record instead. This extension sets no consent
|
||
requirement for a service's own mechanics: rendering under a director's records, measuring a
|
||
frame, stating governance. Using a distributor's WAI service is the person's agreement to how
|
||
that service works. Where a regime asks for a lawful basis for processing a director's key or
|
||
a subject's reference picture, the service records it in a `lawful-basis` claim
|
||
([jwp-receipts](jwp-receipts.md) §2.4.6).
|