Commit Graph

11 Commits

Author SHA1 Message Date
kami 0d281016e7 Cut the dead attic workers, and file what the audit left standing
attic/worker_ocr.py and attic/worker_parse.py are 224 lines imported by nothing
and named in no doc. The OCR stage was removed when narration moved to the
director beat. The two design notes in attic/ stay, they are history.

worker_vision._panel_size had one reference and it was the definition.

The audit's larger finding is filed rather than fixed: call_gemma4,
_extract_json and _strip_thought exist in both worker_vision and worker_script
and have already diverged. That matters because the JSON repair pass can
fabricate dialogue, so a fix would land in one copy and not the other. It is
caveats/audit-open.md#gemma-helpers-duplicated with its revisit trigger.

HANDOFF.md carries the rest: _wrap2 against textwrap, the duplicated ONNX
preprocessing, and worker_layers pointing at a legacy/ directory that was never
tracked in git.

Checked: ruff clean, worker_vision and worker_render self-checks pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:06:34 +04:00
kami bec9411af3 Put every S3 URI in one place, and add a lint gate
Five workers built output URIs with inline f-strings, so the bucket-per-artifact
layout was spread across worker_tts, worker_identity, worker_crop, worker_layers
and worker_render. Moving a class between buckets meant a grep. They are now
templates in transport.py, formatted at each call site.

Three of those workers also each reimplemented the same parse to recover
manga_id and chapter_id from an input uri, because the orchestrator does not
send them. That is transport.ids_from_uri now, and it raises on a uri too short
to carry the ids rather than returning a wrong pair.

ruff.toml makes `ruff check .` exit 0, so CI can gate on it and a new finding
means a new defect. Fixed: an implicit Optional in 8 signatures, an unparenthesized
implicit concatenation in the ASS filter list, 5 subprocess.run calls now saying
check=False out loud, an unused import, a duplicate exception handler and a
non-executable shebang. Every rule left off carries its reason in ruff.toml.

The ASYNC rules are off because ffmpeg on the event loop is real and already
recorded at caveats/audit-open.md#blocking-event-loop. It needs a refactor per
handler, not a lint fix.

Checked: transport, collage, bubble_detect, test_vision_parse, worker_crop,
worker_scene, worker_script, worker_identity, worker_tts, session_manager,
worker_vision and worker_render self-checks all pass. worker_layers still fails
on a missing legacy/qwen_layered_workflow.json, which predates this branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:02:06 +04:00
kami a386e9d910 Send the live cast to the resolver, not cosine's top-k
The gallery gemma chose from was the union of the tracklet members' cosine
shortlists. Cosine cannot separate people on this data, so a metric that
cannot tell two men apart decided who gemma was allowed to consider, and the
right character was often not on the list.

The gallery is now the live cast: gender-compatible rows from
get_known_characters, named first, capped, re-read per tracklet so a minted
character is visible to later ones. Every crop reaches the resolver now,
including one whose cosine top-k was empty; those used to be dropped.

worker_vision spreads reference images across a budget instead of sending 3
per candidate, so a 9-character cast costs 9 images and not 27.

Ran on a GPU, 19:44-19:52 UTC. The lead's assignments drop from 36 to 16 and
14 of the 16 are him; the other two are art inside a panel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 00:15:52 +04:00
kami 54bd126cd7 Order bbox corners, and find the ground truth is for another manga
`_bbox_to_pixels` sorts each coordinate pair after clamping. Run B returned
`p007 person_1` as `[226, 417, 130, 551]`, x1 > x2, which cropped to nothing
and lost that detection silently. The third GPU cycle came back with 0
degenerate boxes over 119 detections.

The plan's first item cannot run as written. All 145 rows of `identity_labels`
belong to chapter 8ca8249b, a different manga. Chapter 7c944dd4 has none, so
`/review/identity` reports `accuracy: null`. Scoring 8ca8249b gives 7/138 on
an identity run that predates every fix.

The cycle also settled the coverage question. All 68 assignments landed on
face-bearing detections and none on a gated one, so recall among face-bearing
detections is 96%, up from 82%. Coverage is the `has_face` gate and nothing
else. What is now open is precision: the lead holds 36 of 68 assignments.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 22:21:52 +04:00
kami 491dad1c67 Ship each crop's embedding so a resolver NONE can mint
Two identity fixes, neither yet run on a GPU.

has_face, the extras gate: fixing the bbox coordinate space made extras worse,
because crops finally landed on their subjects and a background extra bound to
the lead at 1.00. /vision stamps has_face per character using face_detect and
_pair_faces_to_present, so containment and its margin match the speaker path.
Identity skips has_face is False before it crops or embeds. Fails open on a
missing or raising detector, and gates on `is False` so an older vision blob
behaves as before.

The NONE mint: /identity/resolve now writes each crop's embedding to the crop's
key with a .npy suffix and returns emb_uri. The orchestrator cannot compute an
embedding of its own (siglip and gemma cannot both be resident), which is why it
was clearing a deliberate NONE instead of minting an anonymous character.

Also: audit_registry.py, and the tmux respawn-window trap, which
leaves a bare shell rather than re-running the window command.

decisions/identity-bbox.md#face-gates-enrollment
decisions/identity-bbox.md#none-mints-an-anonymous-character

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 19:08:38 +04:00
kami 8113bdfc8b Fix the real A/V gap: a stream copy across mixed frame rates
The rebuild after 1457556 came out byte-identical to the broken file,
which proved the xfade fix never runs for this chapter. An all-cut chapter
goes down the concat demuxer with -c copy, which writes the output in the
FIRST input's time_base and reinterprets every later packet in it. 14 of
49 clips are 30/1 at 1/15360 against 35 at 25/1 at 1/12800, so those 14
play 15360/12800 = 1.2 too long with their audio untouched. collage_cmd
hardcoded -r 30 and yesterday's FPS sweep missed it.

collage_cmd now emits -r FPS, and assemble probes r_frame_rate across the
clips and routes mixed rates through the re-encoding tree. Rebuilt
chapter.mp4 is 364.120s video against 364.122s audio at 25/1, from
436.392 over 363.675.

Also settle the bbox coordinate space, measured over all 113 detections:
47 boxes have x2 past the 900px panel width, none has y2 past 1000 on
panels up to 2307px tall, and the range is exactly [0, 1000]. It is
gemma's normalized grid, not pixels, whatever the prompt asks for.
/vision converts before returning, which fixes identity's crop, the gated
face pairing that was comparing pixel face boxes against grid boxes, the
set-of-mark boxes and the review UI at once. Checked by eye on panel 7:
five of six boxes now land on their subject, including the foreground
character who had no identity.

The registry still holds boxes and embeddings enrolled from the wrong
space. vision and identity have to re-run, which is GPU work and was not
started.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 12:16:22 +04:00
kami a965077e6b Do not stamp som_face on a mark that paired to nobody
_set_of_mark labels a detected face `unknown` when gated pairing matched it to no
present character. An answer pointing at that mark grounds nothing, yet it carried
som_face, the highest-trust provenance. All 7 som_face lines in the first 36
panels of the rerun were this case. Same defect class as the fake tail label.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 23:44:22 +04:00
kami 80711372ba Map gemma's speaker answer back to a present character
The dialogue prompt renders each present character as
`- person_1: brown ponytail, green dress (f)`, and gemma answers with what it was
shown: the description (15 lines), a bare local_id (9), a stale mark label (2),
or a name with the gender marker attached (2). All of them fell through
normalize_speaker as free-form names and never matched the registry, so 28 of 51
speech lines on job 778297bc lost a speaker the pipeline had already identified.
_apply_speaker_labels now resolves every string the prompt showed, drops an
id-shaped answer that names nobody present, and strips a trailing gender marker
so an off-panel name can still match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 23:39:30 +04:00
kami e8941d8ceb Stop labelling a model guess as a read tail
_annotate_speaker_methods stamped `tail`, the highest-trust provenance, on any
line whose speaker matched a present local_id, at gemma's confidence of 1.0. No
balloon was read. Three of three sampled two-character panels had the speakers
swapped, so a multi-character guess is now dropped to unknown, and a solo-panel
guess is kept as model_solo at 0.7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 23:23:56 +04:00
kami 0cc6302245 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
2026-08-11 10:16:24 +04:00
kami ff6a512630 Reconstruct repo from Claude Code + codex transcripts
Working tree (including .git) was lost to an rm. Rebuilt by replaying Write/Edit/
Read/attachment events from 25 Claude sessions and 22 successful codex apply_patch
blocks into one timestamp-ordered timeline.

Verified against ground truth recorded in the transcripts: wc -l on 10 files and
ls -l on 5 files at 2026-07-18T13:13:44Z both match exactly; 18 files are
byte-identical to their newest ~/.claude/file-history blob.

See HANDOFF.md for sources, gaps, and how to rebuild .venv.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 02:42:41 +04:00