- Status: proposed
- Priority: EXT — blocking for the Phase 2 exit (the
rights-awarequalifier 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/analysesandPOST /analyses/:id/executenever callresolveRights,evaluateRightsoractiveGrantFor. The whole analysis path asks nothing.- The single rights touch on the path is inside
createAnchor, and it callsactiveGrantFor, 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 thattranscription,model-analysisorframe-extractionwas granted. grepfinds noAnalysisMethod→RightsActionmap anywhere in the tree.REPRESENTATION_ACTIONS(YSD-2102) maps five representations to actions,DERIVATIVE_OPERATION_ACTION(YSD-4031) maps five derivative operations, andACTION_CONSENT_SCOPEmaps 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:
- Does a model-backed analyzer that reads frames also spend
frame-extraction? A grant may carryframe-extractionand withholdmodel-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. - What does a manual anchor spend?
createAnchortoday throwsRightsDeniedError('playback', …)while checking something weaker thanplayback. 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, followingREPRESENTATION_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-analysisas 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-analysisaction toRIGHTS_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. Recordingundecidedkeeps 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-extractionandmodel-analysisinto 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: nullforever. 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, andplaybackfor 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
playbackcan no longer run shot detection, and that is the deny-by-default rulerights-gate.tsalready applies everywhere else, arriving late at the one path that skipped it. Fixtures across the study suites grantplaybackalone and will have to say what they actually permit. programme-loudnessandscene-segmentationkeep running and stop being able to pass the exit. That is a visible, bounded debt with a named owner rather than an invisible one.createAnchorstarts checking the action its error message names. A grant that covers a subject but withholdsplaybackno longer yields anchors.- Every analysis run gains a durable record of the rights position it was given,
which is the input
ObservedRightswas 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.