18b49c43bd
No worker code changed. This is the evidence from the 2026-08-12 16:39 and 17:38 runs, and where each finding now lives. The fourth session's four identity changes all work on real panels. Panel 7's two wrong bindings are gone. The lead going unassigned there is correct and was measured, not assumed: face_detect finds one face on the whole panel at conf 0.599, nothing else above 0.056 even at a 0.04 threshold, and the crop shows him drawn from behind. Two decisions, both closed: a roster name is a guess so it never reaches detection, and merged_into is exactly one hop deep. Two caveats, both open: detection can order a bbox backwards (1 in 117), and identity coverage has fallen on every run since the gate landed (70 -> 61 -> 50). Coverage is the thing to settle next, and not by reading the number. identity_labels already holds 145 rows of ground truth. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
161 lines
8.6 KiB
Markdown
161 lines
8.6 KiB
Markdown
# HANDOFF, 2026-08-12 (fifth session)
|
|
|
|
Live state is in `NEXT.md`. This file is only what this session did. The previous handoff is in
|
|
`JOURNAL.md`.
|
|
|
|
## Asked
|
|
|
|
"go ahead" on the GPU cycle the fourth session left staged. Then "what do we do now". Then go ahead on
|
|
the fixes its evidence asked for, and run it again.
|
|
|
|
## Result
|
|
|
|
Two full GPU cycles ran. All four of the fourth session's identity changes are now proven on real panels.
|
|
Four more fixes were written on top, all orchestrator-side, all deployed. Coverage fell twice and that is
|
|
the open question.
|
|
|
|
| cycle | detections | assignments | coverage | registry | named |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| baseline (13:11, pre-change) | 110 | 77 | 70% | 8 | 1 |
|
|
| run A (16:39-16:45) | 110 | 67 | 61% | 16 | 2 |
|
|
| run B (17:38-17:44) | 117 | 59 | 50% | 20 | 2 |
|
|
|
|
Vision is non-deterministic, so detection counts move between runs. Each cycle is ~6 minutes:
|
|
vision ~3m50s, identity ~1m25s, reconcile ~50s.
|
|
|
|
## First, a correction the session started with
|
|
|
|
The vision/identity/reconcile timestamps in `/job/status` are UTC. The git log is local, UTC+4. The run
|
|
that looked like a completed rerun was the pre-change baseline: it finished 13:17 local, and
|
|
`_mark_has_face` was not committed until 19:07. The fourth session's handoff was right that nothing had
|
|
run.
|
|
|
|
## Run A: the four staged changes, verified
|
|
|
|
| change | verdict |
|
|
| --- | --- |
|
|
| `has_face` stamp | on all 110 detections, 67 true / 43 gated (39%) |
|
|
| extras gate | panel 7's two wrong bindings gone |
|
|
| NONE mints | 16 characters where 8 existed |
|
|
| non-destructive merge | 7 rows carry `merged_into`, 9 assignments stamped `merged_from:` |
|
|
|
|
Panel 7, the worked example:
|
|
|
|
```
|
|
person_1 Seonho 1.00 -> -- none -- has_face=False
|
|
person_2 character_f7a4fd 0.00 -> character_519d2b 0.00 has_face=True
|
|
person_4 character_d72710 0.94 -> -- none -- WRONG binding removed
|
|
person_5 Seonho 1.00 -> -- none -- WRONG binding removed
|
|
```
|
|
|
|
`person_1` going unassigned is correct, and this was measured, not assumed. Ran `face_detect.detect_faces`
|
|
on `p006.png` directly: one face on the whole panel, conf 0.599, inside `person_2`. Swept the threshold to
|
|
0.04 and nothing else appears above 0.056. The crop shows the lead drawn from behind at his desk, back of
|
|
the head and headphones. Detector right, gate right, lead unidentifiable in that panel.
|
|
|
|
`person_1`'s box now frames the lead. The old "empty window mullion" note was written against pre-fix
|
|
boxes and is retired.
|
|
|
|
## Run A's new defect: a roster hint named the wrong man
|
|
|
|
Verified against the art, three real people:
|
|
|
|
- `character_92129ac7` "Lim Seonho", 22 assignments. The p010 caption reads `LIM SEONHO (29)`, yellow
|
|
plaid shirt, headphones, matching the roster's description. Correct.
|
|
- `character_556aef60`, 25 assignments, unnamed. The woman with short black hair and pearls, the roster's
|
|
second character, whom the roster itself calls "Unknown". Correct.
|
|
- `character_dbadfff7` "Seonho", 15 assignments. A different man, glasses, dark clothing, wearing the
|
|
lead's roster name.
|
|
|
|
p020 assigned `Seonho` to `person_1` and `Lim Seonho` to `person_3`, two people in one panel.
|
|
|
|
## What was changed, and where the cause was
|
|
|
|
**Roster hints no longer seed detection.** Deleted the two lines at `service.py:882` that appended
|
|
`_roster_char_hints` to `known_characters`. `build_detect_prompt` drops any hint without a name. Passing a
|
|
nameless hint would have contributed nothing, so removing the seeding was the only real option.
|
|
Names now reach detection from registry rows only, which carry embeddings and were named from an in-panel
|
|
caption or address. The roster still feeds `roster_cast` in `run_stage_dialogue`, where names match
|
|
against speech rather than faces.
|
|
|
|
**`merge_characters` keeps `merged_into` one hop deep.** Two halves, one per direction, and the second was
|
|
missed on the first attempt:
|
|
|
|
- resolve the keeper to its chain root before merging (a bounded 64-step walk, the cap only so a cycle
|
|
cannot hang reconcile)
|
|
- repoint the loser's own dependents to the keeper when the loser is retired
|
|
|
|
The keeper walk alone does not work. Run B still produced `477c1894 -> a92d9df4 -> 4fb94c15`. At merge
|
|
time that pair was fine. The chain formed later, when a row that was already somebody's keeper was itself
|
|
retired.
|
|
|
|
**`reset_registry` deletes `name_claims`** for the manga's panels and reports the count. Confirmed live:
|
|
run B's reset reported `name_claims: 5`, the orphans that had pointed at `character_afa7623b` since two
|
|
resets earlier.
|
|
|
|
**`audit_registry.py` is in the repo.** It had only ever been `docker cp`'d, so every rebuild dropped it.
|
|
`Dockerfile` has `COPY . .`, so it is baked now and that trap is gone.
|
|
|
|
## Run B: what the fixes did
|
|
|
|
The registry split is fixed. "Lim Seonho" came back as ONE row holding 25 assignments. That answers the
|
|
open risk from before the run: dropping the roster hint did not split the lead across the panels before
|
|
his caption. "Seonho" fell from 15 assignments to 1.
|
|
|
|
Two things run B surfaced:
|
|
|
|
- **A second naming mechanism, untouched by the fix.** Even with no roster hint, p011 and p026 emit
|
|
`name: "Seonho"` on `person_2` from in-panel text. Detection reads a name off the panel and attaches it
|
|
to the wrong body. That is what keeps the glasses man named, now at 1 assignment instead of 15.
|
|
- **One degenerate bbox.** `p007 person_1` is `[226, 417, 130, 551]`, x1 > x2. One in 117. It crops to
|
|
nothing, so that detection can never enroll or match. `_bbox_to_pixels` clamps each coordinate but never
|
|
orders the corners.
|
|
|
|
## Not done
|
|
|
|
- **The degenerate-box guard.** It is a worker change (`worker_vision.py:_bbox_to_pixels`), so it needs a
|
|
vision worker restart and a third GPU cycle to prove. Not started, nothing half-edited.
|
|
- **The coverage trend.** 70% -> 61% -> 50%. Part is the `has_face` gate, which is stable at ~39% of
|
|
detections across both runs. Framed against face-bearing detections only, run B assigned 59 of 72, or
|
|
82%. Nothing establishes whether the rest is correct abstention or lost cast. Settle that number before
|
|
trusting the registry.
|
|
- **Nothing downstream re-ran.** The job is parked at `dialogue waiting` with every stage below it
|
|
cleared. No clip or chapter has been rebuilt against the new cast.
|
|
|
|
## Checks
|
|
|
|
```bash
|
|
/usr/bin/ssh kami@192.168.1.104 "cd /home/kami/docker-apps/manga-infra/orchestrator && python3 -m pytest -q --ignore=test_api.py" # 118 passed, was 116
|
|
./check_stale.sh # exit 0, all 9 workers current
|
|
```
|
|
|
|
Both new merge tests were confirmed to fail with their fix disabled, then the fix was restored and the
|
|
suite re-run. The deployed container was verified by parsing its source, not grepping it. The first
|
|
attempt gave a false negative, matching `_roster_char_hints` inside the comment that explains its removal.
|
|
|
|
## Next command
|
|
|
|
The third cycle, after adding the corner-ordering guard to `_bbox_to_pixels` and restarting the vision
|
|
worker. Watch coverage against 50% and the degenerate box count against 1.
|
|
|
|
```bash
|
|
cd /home/kami/Programs/n8n-worker && ./check_stale.sh # must exit 0
|
|
J=778297bc-e7ce-439d-91b5-8a027060d17f; M=ef105a86-4b7e-4ac4-b45c-b7d83b8f5b5e
|
|
/usr/bin/ssh kami@192.168.1.104 "curl -s -X POST http://127.0.0.1:9090/characters/reset -H 'Content-Type: application/json' -d '{\"manga_id\":\"$M\",\"confirm\":true}'"
|
|
/usr/bin/ssh kami@192.168.1.104 "curl -s -X POST http://127.0.0.1:9090/stage/clear -H 'Content-Type: application/json' -d '{\"job_id\":\"$J\",\"stage\":\"vision\"}'"
|
|
# restart the identity worker here, see the trap below
|
|
for S in vision identity reconcile; do /usr/bin/ssh kami@192.168.1.104 "curl -s --max-time 5400 -X POST http://127.0.0.1:9090/stage/run -H 'Content-Type: application/json' -d '{\"job_id\":\"$J\",\"stage\":\"$S\"}'"; done
|
|
/usr/bin/ssh kami@192.168.1.104 "docker exec manga-orchestrator python3 /app/audit_registry.py 7c944dd4-e972-42c7-ba60-9f6939548e80 6"
|
|
```
|
|
|
|
## Traps confirmed or found
|
|
|
|
- **`kill $(pgrep -f "worker_identity:app")` kills the shell running it.** The pattern matches the calling
|
|
command line. It killed this session's own script mid-way, so the `send-keys` relaunch never fired and
|
|
the identity worker sat dead. Restart it with the two `tmux send-keys` lines only, then poll `/health`.
|
|
- **Verify deployed code by parsing it, not by grepping it.** A comment explaining a removal contains the
|
|
name of the thing removed.
|
|
- **A test that cannot fail proves nothing.** Both merge tests were run with their fix disabled first.
|
|
- Panel-7 line numbers move between runs, because vision is non-deterministic and `local_id` is assigned
|
|
top-to-bottom per run. In run B the lead is `person_5`, not `person_1`. Compare by bbox, not by id.
|