Stop a background extra's action reaching narration
build_scene already dropped an unassigned detection from `characters` and `present`, so an extra never reached the cast list. Its ACTION did. `actions` was built from every detection, and that list is what the script prompt renders and what the verifier uses as evidence, so "standing at the window" arrived as a fact about the panel with no character attached and the verifier confirmed it, because the action really was in the blob. Skip has_face is False, the same gate and the same fail-open semantics as enrollment. Self-check covers all three cases: a real cast member's action survives, a faceless one's does not, and a detection from a panel where the detector never ran keeps its action. decisions/identity-bbox.md#extras-gate-consumers Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,3 +44,4 @@ still live belongs in `caveats/`.
|
||||
| [A stage result proves nothing until the worker is newer than the edit](identity-bbox.md#stale-worker-invalidates) | closed |
|
||||
| [A detection with no detected face never enrolls or binds](identity-bbox.md#face-gates-enrollment) | closed |
|
||||
| [A resolver NONE mints an anonymous character, it does not clear the crop](identity-bbox.md#none-mints-an-anonymous-character) | closed |
|
||||
| [The extras gate runs at enrollment and at narration, not at the speaker prompt](identity-bbox.md#extras-gate-consumers) | closed |
|
||||
|
||||
@@ -153,3 +153,33 @@ changed in the same session.
|
||||
|
||||
Forbids: treating an absent `character_id` as one outcome. A resolver that answered and a resolver that
|
||||
failed are different facts.
|
||||
|
||||
## The extras gate runs at enrollment and at narration, not at the speaker prompt {#extras-gate-consumers}
|
||||
|
||||
**Closed, 2026-08-12. Written and self-checked, not yet proven on a GPU run.**
|
||||
|
||||
`#face-gates-enrollment` stops a faceless detection taking an identity. It does not stop the detection
|
||||
being narrated, because three places read the raw vision character list and only one of them consults an
|
||||
assignment. Panel 7 is the worked example: six detections, four of them extras or scenery.
|
||||
|
||||
`worker_scene.build_scene` already drops an unassigned detection from `characters` and `present`
|
||||
(`worker_scene.py:63`), so an extra never reached the cast list. Its **action** did. `actions` was built
|
||||
from every detection, and that list is what the script prompt renders and what the verifier uses as
|
||||
evidence. So "standing at the window" arrived as a fact about the panel with no character attached, and
|
||||
the verifier confirmed it because the action really was in the blob. Both now skip `has_face is False`.
|
||||
|
||||
`service._beat` picks the cinematographer's "who" from the first three detections, falling back to a
|
||||
detection's action when it has no name. An extra could take a slot and steer the camera. Also gated.
|
||||
|
||||
`service._present_characters` is deliberately **not** gated. It builds the dialogue stage's candidate
|
||||
speaker list and the set-of-mark boxes. Two reasons. The failure is already contained: an extra chosen as
|
||||
the speaker has no identity assignment, so `normalize_dialogue` resolves it to unknown rather than to a
|
||||
wrong name. And the gate's own cost lands hardest here, because a character drawn from behind has no face
|
||||
box, so gating would delete a real speaker from the only list that can attribute their line.
|
||||
|
||||
All three gates test `is False`, never falsiness. A vision blob written before the gate existed carries no
|
||||
`has_face` key, and a panel whose detector failed is marked `True` by the fail-open path. Both keep their
|
||||
previous behaviour.
|
||||
|
||||
Forbids: adding a fourth consumer of `vision["characters"]` without deciding which side of this line it is
|
||||
on. The blob keeps every detection on purpose, so the audit can still see what was gated.
|
||||
|
||||
Reference in New Issue
Block a user