Audit Phase 1: correctness and scheduling safety
Implements every P0 from AUDIT.md plus four P1s, across both halves of the pipeline. Verified by CPU-only self-checks and the orchestrator test suite. No GPU work ran and no pipeline ran. workpc: - worker_scene: read speaker_ref, not the rewritten speaker field. Every line narrated as "Someone" before this. Emit `actions` for the verifier. - worker_script: declare beat + verifier_feedback (pydantic dropped both, so the retry was blind) and render them as a repair prompt. - worker_vision: gate face->identity pairing on containment, assign globally shortest-first, map an out-of-range resolver index to `unresolved` instead of minting a character, parse JSON with raw_decode. - session_manager: tear down a server whose lease vanished mid-load, and spawn the supervisor respawn unlocked. orchestrator (edited in place, NOT committed there): - tracklets: canonicalize gender, add co-presence cannot-links, block transitive bridges across a hard constraint. - correctness: stop failing valid narration on sentence-initial capitals and short quotes; read action evidence from the singular key. - db: stop orphan flags leaking into every chapter; resolve by flag id. - service: TTS returns instead of raising under GATES, auto-resolves under autonomous mode; job admission control; registry names on dialogue resume. - session_proxy: queue on 409 instead of stealing the lease; run heartbeats. Docs restructured per the repo-structure layout: CLAUDE.md is a pointer table, NEXT.md replaces HANDOFF.md, plus ROADMAP.md, JOURNAL.md, decisions/ and caveats/. AUDIT.md now points at those instead of restating them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XD7cAy81MZrc7gCr6aZGWr
This commit is contained in:
+32
-3
@@ -70,20 +70,32 @@ def build_scene(data: SceneInput):
|
||||
|
||||
# Speaker attribution is the vision/dialogue model's job now (it reads bubble tails + turn-taking).
|
||||
# speaker is a local_id -> map to character_id via identity; None for narration/sfx/off-panel.
|
||||
# The orchestrator's normalize_dialogue already rewrote `speaker` to a character_id and put the
|
||||
# typed answer in `speaker_ref`, so the local-id map no longer matches it. Read speaker_ref FIRST;
|
||||
# every lookup used to miss and every line narrated as "Someone".
|
||||
dialogue = []
|
||||
for d in data.vision_result.get("dialogue", []):
|
||||
raw = d.get("speaker")
|
||||
ref = d.get("speaker_ref") or {}
|
||||
# "unknown" is a DELIBERATE off-panel/indeterminate speaker (narrated as "someone"); it maps
|
||||
# to None just like narration. id_by_local.get already yields None for it.
|
||||
dialogue.append({"speaker": id_by_local.get(raw), "text": d.get("text", ""),
|
||||
if ref.get("kind") == "character_id" and ref.get("value"):
|
||||
cid = ref["value"]
|
||||
else:
|
||||
cid = id_by_local.get(raw) or (raw if raw in name_by_id else None)
|
||||
dialogue.append({"speaker": cid, "text": d.get("text", ""),
|
||||
"type": d.get("type", "speech"),
|
||||
"confidence": d.get("confidence"),
|
||||
"speaker_method": d.get("speaker_method", "unknown")})
|
||||
|
||||
vchars = data.vision_result.get("characters", [])
|
||||
action = "; ".join(c.get("action", "") for c in vchars if c.get("action")) or ""
|
||||
# `actions` is the list the orchestrator's beat builder reads for verifier evidence; `action` is the
|
||||
# joined string the script prompt renders. Emitting only the string left verification blind.
|
||||
actions = [c["action"].strip() for c in vchars if str(c.get("action") or "").strip()]
|
||||
action = "; ".join(actions)
|
||||
return {"panel_id": data.panel_id, "characters": characters, "dialogue": dialogue,
|
||||
"action": action, "entities": data.vision_result.get("entities", []),
|
||||
"action": action, "actions": actions,
|
||||
"entities": data.vision_result.get("entities", []),
|
||||
"camera": data.vision_result.get("camera", {}), # #8 direction passthrough -> render
|
||||
"transition": data.vision_result.get("transition", "cut")} # #6 transition out
|
||||
|
||||
@@ -167,4 +179,21 @@ if __name__ == "__main__":
|
||||
))
|
||||
assert out5["dialogue"][0]["confidence"] == 0.3
|
||||
assert out5["dialogue"][0]["speaker_method"] == "turn_taking"
|
||||
|
||||
# orchestrator-normalized rows: `speaker` is already a character_id and `speaker_ref` is typed.
|
||||
# the local-id map cannot resolve either — speaker_ref must win, or everything narrates as Someone.
|
||||
out6 = build_scene(SceneInput(
|
||||
panel_id="p007",
|
||||
vision_result={"characters": [{"local_id": "person_1", "bbox": [0, 0, 10, 10]}],
|
||||
"dialogue": [{"speaker": "c1", "speaker_ref": {"kind": "character_id", "value": "c1"},
|
||||
"type": "speech", "text": "Hey."},
|
||||
{"speaker": None, "speaker_ref": {"kind": "unknown", "value": None},
|
||||
"type": "speech", "text": "..."}]},
|
||||
identity_assignments=[{"local_id": "person_1", "character_id": "c1"}],
|
||||
characters_registry=[{"character_id": "c1", "name": "Teto"}],
|
||||
))
|
||||
assert out6["dialogue"][0]["speaker"] == "c1", out6["dialogue"][0]
|
||||
assert out6["dialogue"][1]["speaker"] is None, out6["dialogue"][1]
|
||||
# actions survive as a list for the verifier, not only as the joined prompt string
|
||||
assert out["actions"] == ["waving"] and out["action"] == "waving"
|
||||
print("worker_scene self-check ok")
|
||||
|
||||
Reference in New Issue
Block a user