fix(ir): every turn but a panorama's is classified, the opening included - #527
Conversation
… alone `classify_coverage` filled any empty team slot with "(the team has not spoken yet)", which only ever reached it on an opening, because the gate kept every other wordless turn away. Her route classifies those turns too, and hands the classifier what reached the Guide as the team's turn (`oralTurn.ts:14,92`) — on an empty take, nothing at all. A silence after the opening read as a team that had never spoken would be a different exchange from the one the Guide answered. The placeholder now goes in only when the caller says the turn is the opening; any other empty slot reaches the classifier empty. It stays ours until the opening carries her note in its place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The settle gate kept the classifier away from any turn with an empty transcript, the live opening included, and from any take the hearing did not trust. Her route classifies every non-panorama turn with no condition, her kickoff included (`route.ts:179,185-194`), and hands the classifier what reached the Guide as the team's turn. So nothing the Guide raised in the opening was recorded as `surfaced`, and the Level-1 axes reached the floor's `surfaced` bar only on later turns. An inaudible or uncertain take and a mother-tongue take went unclassified too — the last one with the room-note the Guide had just answered. The gate is now only "not a panorama", on the live door and on the text seam alike. The team slot carries the room-note on a mother-tongue turn and the transcript otherwise, so a later turn with no words reaches the classifier empty, and the opening is flagged so it alone carries the placeholder. Her classifier marks `surfaced` from the Guide alone and `engaged` only from the team, and the team's necklace counts `engaged`, so the opening moves the Guide's ledger and the Desk and leaves the team's screen as it was. A live opening that lands after the team's first turn is dropped from the record, and it stays out of the classifier with it: it appends no exchange, and the classifier would read a Guide line the session does not hold. This reverses the gate the opening-exception removal put in place on purpose; a fail-safe still hands over what the team said. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…emand The opening the panorama wrote ahead was appended with an empty team slot and never settled, so a passage entered after the panorama recorded nothing the Guide raised in its first line, while the same line written on demand now reaches the classifier. Her route classifies the kickoff with no condition, whichever way its line was produced. The prepared door now names its turn once and schedules the classifier under that name, with the prepared line as the Guide's side and the opening's placeholder as the team's. The reply says a classification is running, and so does the payload a resent `turn_id` replays — the app arms its wait for the settled frame only on that word (`session_notifier.dart:1212-1219`). The replay itself schedules nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| scene=_scene_of(session, outcome.transcript), | ||
| ) | ||
| if _worth_settling(outcome, heard): | ||
| if _worth_settling(session): |
There was a problem hiding this comment.
This is step [5] of the turn loop moving (classifier_system_prompt.md:13), and the golden runner plays through exactly this seam — the kickoff now classifies, and so does every empty and mother-tongue turn. docs/doctrine/vendor/DOCTRINE.md:102: the golden sessions must pass "before anything touching prompts, turn loop, model or canvas reaches the team", and AGENTS.md puts that gate on the release, not only on CI. The body reports the suite and the static gates; no golden/reports/ lands in this diff.
I cannot run it from here — it needs ACCESS_CODE, a key and real model calls. Leaving the thread open: either a line saying the five sessions were already played against this branch, or the run before delivery. It is Henok's and Marcia's to settle, not mine.
There was a problem hiding this comment.
Marcia's golden round runs once, over the whole integration branch (joao/alinhar-setembro), before that branch goes to main and before her 20-minute run. Nothing on this branch reaches a team before that, so no code change here.
| SCENES=_scenes_block(pericope_num), | ||
| COVERAGE_ELEMENTS=_unresolved_block(coverage_state, offered), | ||
| TEAM_UTTERANCE=team_utterance or _NO_TEAM_UTTERANCE_YET, | ||
| TEAM_UTTERANCE=team_utterance or (_NO_TEAM_UTTERANCE_YET if opening else ""), |
There was a problem hiding this comment.
The comment on the constant now says the opposite of this line. classify_coverage.py:46 — "What the classifier's TEAM_UTTERANCE slot carries when nobody has spoken this turn." After this change, nobody-having-spoken is precisely the case that gets "": the inaudible take and the empty one, which is what test_a_later_turn_with_no_words_reaches_the_classifier_empty_not_as_an_opening pins. The placeholder is the opening's alone now. It was accurate before the diff, so this is not a pre-existing lapse — the line under it changed and the line above it did not.
Either way is fine: reword it to "on the opening, where the team truly has not spoken yet", or drop the sentence and let opening carry it.
There was a problem hiding this comment.
Reviewed the gate, both doors, the text seam and the classifier call. Checked P5 rather than taking it: speak_back empties the transcript on an uncertain take and on a mother-tongue one (turn/speech.py:43,65), and reliable_bridge_speech is only those two (hearing.py:96), so nothing carrying words reaches room_note or transcript unguarded, and the written short-circuit keeps the dropped opening out. Two points, and the heavier one is an open question I cannot settle from here rather than a defect — that is the whole set, nothing else in the diff.
Not covering the route.ts and session_notifier.dart citations: they are not in this tree. I did not run the suite.
… alone The comment above `_NO_TEAM_UTTERANCE_YET` still said the slot carries it whenever nobody has spoken that turn, which is exactly the case that now hands the classifier an empty slot: an inaudible or empty take after the opening. The line under it changed and the line above it did not; the comment now names the opening as the only turn that carries it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Read the diff since 03049e36 — one comment changed, nothing else. Both points from the first pass are closed: the placeholder comment on _NO_TEAM_UTTERANCE_YET, and the golden round, which you settled — Marcia's round runs over joao/alinhar-setembro before anything reaches a team. That was the whole ledger from pass 1 and I have nothing further. Not covering the golden sessions themselves: running them needs ACCESS_CODE and real model calls, which I don't have here.
The settle gate kept the coverage classifier away from any turn with an empty transcript, the live opening included, and from any take the hearing did not trust; the prepared opening never reached it at all. Her route classifies every non-panorama turn with no condition, her kickoff included (a3f3c69
app/api/turn/route.ts:179,185-194), and hands the classifier what reached the Guide as the team's turn (src/audio/oralTurn.ts:14). So nothing the Guide raised in the opening was recorded assurfaced, the Level-1 axes reached the floor'ssurfacedbar only on later turns, and a mother-tongue take went unclassified with the room-note the Guide had just answered.The gate is now only "not a panorama", on the live door, the prepared door and the text seam. The team slot carries the room-note on a mother-tongue turn and the transcript otherwise; the opening alone gets our "(the team has not spoken yet)" placeholder, and any later empty slot reaches the classifier empty. The prepared door names its turn once, schedules the classifier under that name, and says
classification_pendingin the reply and in theturn_idreplay payload, which is what arms the app's wait (session_notifier.dart:1212-1219). Her classifier markssurfacedfrom the Guide alone andengagedonly from the team, and the necklace countsengaged(ENG-754's rule stays), so the opening moves the Guide's ledger and the Desk and leaves the team's screen as it was. This reverses ENG-812's gate on purpose; ENG-715 is not reversed.Cost: one extra classifier call per opening and per turn with no words or in the mother tongue, off the voice path; nothing on the device. Side effects the ticket accepts: a hand raised after the opening, before the team speaks, anchors to a bead the opening surfaced (
questions.py:87-88); the Desk shows the opening's beads as surfaced andir_coverage_eventsrows appear at the opening; the floor can be met sooner, never at the opening.Every path through the gate, before → after
turn_idreplayText seam, same gate: kickoff classified with the placeholder, text as before, mother tongue with the note, empty text with an empty slot.
Premises
_worth_settling(session);_settle_laterasks it; both doors call_settle_later; the text seam asks_worth_settling. (Scope 1–4)outcome.room_note or outcome.transcriptin both settle calls. (Scope 5)settle_coverageandclassify_coveragetakeopening: bool, since nothing signalled the opening to the classifier before. ENG-970 swaps the placeholder for her note at this same point. (Scope 5)append_openingdrops is not classified:_write_the_turnreturns whether it wrote. (Done when: "every non-panorama turn that appends an exchange")Sobre a joao/alinhar-setembro (a3b52eb). 3435 testes, mais 10 fresh_interpreter; ruff, format, mypy, import, doutrina e cânone limpos. Cada critério falsificado à mão — 14 reversões, todas vermelhas, todas verdes de volta. Revisor dedicado: zero achados, aprovado (falsificou de novo o portão da abertura descartada).
🤖 Generated with Claude Code