# YSD-19047 — Which right each analyzer spends when it reads a source

- **Status:** proposed
- **Priority:** EXT — blocking for the Phase 2 exit (the `rights-aware`
  qualifier faults on every output of every deployment until this is answered,
  and no amount of measurement changes that)
- **Decision owner:** @GreyChimp
- **Drafted:** 2026-08-17 by Claude Code (Opus 5)
- **Approval:** pending

## Question

`run-phase-two-exit.ts` was built on 2026-08-17 and pointed at a deployment's
own analysis runs for the first time. Four of the five output qualifiers hold
over real records. The fifth reports one fault per output:

```
  rights-aware           4 fault(s)
  analysis-without-a-rights-decision — no rights decision covers running this
  analysis on this source; an output produced without one was produced on an
  assumption
```

That is not a defect in the reader. It is what the reader found, and it was
measured rather than assumed:

- `requestAnalysis`, `executeAnalysisRun`, `POST /editions/:id/analyses` and
  `POST /analyses/:id/execute` never call `resolveRights`, `evaluateRights` or
  `activeGrantFor`. The whole analysis path asks nothing.
- The single rights touch on the path is inside `createAnchor`, and it calls
  `activeGrantFor`, whose own comment reads: _"Probe with the grant's own first
  action so the action clause of grantFailure never fails."_ An anchor therefore
  proves that SOME live grant covered the subject. It never proves that
  `transcription`, `model-analysis` or `frame-extraction` was granted.
- `grep` finds no `AnalysisMethod` → `RightsAction` map anywhere in the tree.
  `REPRESENTATION_ACTIONS` (YSD-2102) maps five representations to actions,
  `DERIVATIVE_OPERATION_ACTION` (YSD-4031) maps five derivative operations, and
  `ACTION_CONSENT_SCOPE` maps actions to consent scopes. The twelve analyzers
  Phase 2 ships are in none of them.

So `rights-aware` cannot be evidenced, and it cannot be evidenced _by a reader_:
resolving the position now would read today's grants at yesterday's instant, and
a grant is amended by superseding it, so that answers "would this be permitted
under the licence as it now reads" — a model of the decision, not the decision.
The read model returns `rights: null` for exactly that reason.

The question is therefore: **which rights action does each of the twelve
analysis methods exercise, and what happens to the two whose read the action
vocabulary cannot express?**

The twelve, with what each actually reads:

| Method                 | What it reads                | Backed by | Candidate action   |
| ---------------------- | ---------------------------- | --------- | ------------------ |
| `shot-detection`       | video frames (scdet)         | code      | `frame-extraction` |
| `scene-grouping`       | video frames (appearance)    | code      | `frame-extraction` |
| `scene-segmentation`   | video frames **+ audio bed** | code      | **unexpressible**  |
| `camera-motion`        | video frames                 | code      | `frame-extraction` |
| `programme-loudness`   | **audio only**               | code      | **unexpressible**  |
| `causal-attribution`   | video frames, two editions   | code      | `frame-extraction` |
| `project-shot-scale`   | video frames, whole corpus   | code      | `frame-extraction` |
| `palette-extraction`   | video frames                 | code      | `frame-extraction` |
| `speech-transcription` | audio → text                 | model     | `transcription`    |
| `object-detection`     | video frames                 | model     | `model-analysis`   |
| `speaker-diarization`  | audio                        | model     | `model-analysis`   |
| `pose-estimation`      | video frames (person boxes)  | model     | `model-analysis`   |

Two of the twelve have no honest entry. `RIGHTS_ACTIONS` (YSD-4002) is twelve
separately-controlled actions and **none of them names reading the audio track
to measure it**. `transcription` is speech becoming text and neither method
produces text; `proxy-generation` is generating a rendition and a loudness
figure is not one — though `REPRESENTATION_ACTIONS` does gate a `waveform` on
it; `model-analysis` is model processing and both are code-backed meters.
Picking any of the three would be inventing a rights rule, and picking the
loosest would under-gate a real read of licensed audio.

Two further questions ride along and are answered by the recommendation rather
than left implicit:

1. **Does a model-backed analyzer that reads frames also spend
   `frame-extraction`?** A grant may carry `frame-extraction` and withhold
   `model-analysis` — that distinction is the point of having both — but the
   converse combination is a licence that permits running a model and not
   looking at the frames, which is not a state of the world.
2. **What does a manual anchor spend?** `createAnchor` today throws
   `RightsDeniedError('playback', …)` while checking something weaker than
   `playback`. The error message has been naming an action the code does not
   check since the function was written.

## Recommendation

Adopt the candidate column for the ten expressible methods, with
`model-analysis` alone for the three model-backed frame/audio readers and
`transcription` alone for the recognizer — **one action per method, the action
that names what the analyzer IS** — and refuse the two unexpressible ones as
`undecided` rather than mapping them.

`ObservedRights.decision` already has three values and the third is `undecided`.
A run of `programme-loudness` or `scene-segmentation` records `undecided`,
proceeds, and the Phase 2 exit refuses it as `analysis-over-a-denied-source` —
"the rights decision for … was undecided, and the analyzer read it anyway". That
is a true sentence about those two analyzers and the honest place for the gap to
sit until the action vocabulary grows an entry for reading audio to measure it,
which is a change to YSD-4002 and belongs to whoever owns that.

On question 1: **one action, not a set.** A grant that lists `model-analysis`
has permitted a model to process the material; requiring `frame-extraction`
alongside it would deny object detection to a licence that plainly intended to
allow it, and would make the two actions mean the same thing wherever a model is
involved. On question 2: **`playback`**, which is what `createAnchor`'s own
error message has claimed all along.

The mapping ships as `ANALYSIS_METHOD_RIGHTS_ACTION`, declared
`satisfies Record<AnalysisMethod, RightsAction | 'undecided'>` next to
`DETECTION_CORRECTION_ROUTES`, so an analyzer added to Phase 2 does not compile
until somebody says which right it spends — the same shape, and for the same
reason, as the table that made "every output remains correctable" checkable.

`requestAnalysis` then resolves one decision per (pinned edition, method action)
at request time, records it on the run, and — deny-by-default, the posture
`rights-gate.ts` states in its first paragraph — **refuses the run** when the
resolution is `deny`. It does not refuse `undecided`: those two analyzers ship
today and turning them off is a product decision this one does not make.

## Options considered

- **Map the two audio readers to `proxy-generation`, following
  `REPRESENTATION_ACTIONS.waveform`.** Rejected. The precedent is real but it is
  about generating a rendition of the audio, and neither method generates one. A
  grant that permits proxies would silently permit measurement, and a licensor
  who granted proxy generation did not thereby agree to loudness metering.
- **Map them to `model-analysis` as the catch-all processing action.** Rejected
  as plainly false: both are code-backed meters with no model, and the workspace
  already refuses to let a code-backed analyzer wear a model's clothes
  (`confidence: { status: 'missing', reason: 'not-applicable' }` on every one of
  their outputs, because a threshold is not a probability).
- **Add an `audio-analysis` action to `RIGHTS_ACTIONS`.** Not rejected —
  deferred. It is very likely the right end state, and it is a change to the
  twelve separately-controlled actions of YSD-4002: every grant in every
  deployment would need re-review to say whether it carries the new action, and
  a decision that rewrites a rights vocabulary is not one to take inside a phase
  exit. Recording `undecided` keeps the gap visible until it is taken.
- **Require the union of every action an analyzer touches.** Rejected for the
  reason under question 1: it collapses `frame-extraction` and `model-analysis`
  into one right wherever a model runs, and denies analyses that licences
  obviously intended to permit.
- **Leave the path ungated and let the gate report `rights: null` forever.**
  Rejected. That is today's state, and it means every machine reading of
  licensed material in this product is performed on an assumption nobody
  recorded — which is the sentence the fault prints.
- **Ten mapped methods, two recorded `undecided`, refuse on deny, and `playback`
  for manual anchors** — recommended.

## Consequences

- The Phase 2 exit becomes passable for a deployment whose grants carry the
  right actions, and stays failed for one whose grants do not — which is the
  distinction the qualifier was written to draw.
- Analyses start being refused. A deployment whose grants list only `playback`
  can no longer run shot detection, and that is the deny-by-default rule
  `rights-gate.ts` already applies everywhere else, arriving late at the one
  path that skipped it. Fixtures across the study suites grant `playback` alone
  and will have to say what they actually permit.
- `programme-loudness` and `scene-segmentation` keep running and stop being able
  to pass the exit. That is a visible, bounded debt with a named owner rather
  than an invisible one.
- `createAnchor` starts checking the action its error message names. A grant
  that covers a subject but withholds `playback` no longer yields anchors.
- Every analysis run gains a durable record of the rights position it was given,
  which is the input `ObservedRights` was declared for and has never had.

## Machine-enforced outcome

On approval, `ANALYSIS_METHOD_RIGHTS_ACTION` carries the approved mapping and no
other, declared `satisfies Record<AnalysisMethod, …>` so a new analyzer cannot
be added without an entry; `requestAnalysis` records one resolved decision per
pinned edition and method action on the run it creates, and throws
`RightsDeniedError` when a resolution denies; `assembleObservedAnalysisRuns`
reads those decisions back into `ObservedRights` instead of returning `null`;
and a method mapped `undecided` reaches the gate as `undecided` rather than as
any permitted action.

## Pending control

The Phase 2 exit refuses, once per output, while this decision is unmade.
`assembleObservedAnalysisRuns` sets `rights: null` on every `ObservedOutput`
because nothing on the analysis path records a decision, and `phaseTwoExit`
turns that into `analysis-without-a-rights-decision` — so no deployment can
report the `rights-aware` qualifier as holding, and no reader can clear it by
resolving rights after the fact. The refusal is asserted against a real run on
production persistence in `phase-two-exit.integration.spec.ts`, which expects
one such fault per detection; a change that quietly started manufacturing the
decision would fail that suite rather than pass it.
