Skip to content

fix(ir): every turn but a panorama's is classified, the opening included - #527

Merged
joaocarvoli merged 4 commits into
joao/alinhar-setembrofrom
joao/eng-1074-classify-every-non-panorama-turn-the-opening-included-as-her
Sep 24, 2026
Merged

joaocarvoli merged 4 commits into
joao/alinhar-setembrofrom
joao/eng-1074-classify-every-non-panorama-turn-the-opening-included-as-her

Conversation

@joaocarvoli

Copy link
Copy Markdown
Member

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 as surfaced, the Level-1 axes reached the floor's surfaced bar 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_pending in the reply and in the turn_id replay payload, which is what arms the app's wait (session_notifier.dart:1212-1219). Her classifier marks surfaced from the Guide alone and engaged only from the team, and the necklace counts engaged (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 and ir_coverage_events rows appear at the opening; the floor can be met sooner, never at the opening.

Every path through the gate, before → after

# Turn Before After
1 turn_id replay not classified same (replays the stored payload; the prepared opening's now says pending)
2 "say it again" (audio-less, session already open) not classified same
3 prepared opening not classified classified: placeholder, prepared line, pending in reply and replay
4 live opening not classified classified: placeholder
5 live opening that fails safe not classified classified: placeholder, the fail-safe line
6 live opening dropped because the team spoke first not classified not classified (appends no exchange)
7 turn with reliable speech, pass or corrected classified same
8 turn with speech, UNREPAIRABLE fail-safe classified same
9 uncertain transcript (inaudible line) not classified classified: empty slot
10 empty or silent take (inaudible line) not classified classified: empty slot
11 mother-tongue take not classified classified: the room-note
12 first take with audio on an empty session as 7–11 as 7–11
13 panorama opening not classified same
14 panorama turn with speech not classified same
15 panorama empty turn not classified same
16 model error or turn timeout never reaches the gate same
17 prepared-door race or version conflict never reaches the gate same

Text seam, same gate: kickoff classified with the placeholder, text as before, mother tongue with the note, empty text with an empty slot.

Premises

  • P1. The gate is "not a panorama": _worth_settling(session); _settle_later asks it; both doors call _settle_later; the text seam asks _worth_settling. (Scope 1–4)
  • P2. The team slot is outcome.room_note or outcome.transcript in both settle calls. (Scope 5)
  • P3. The placeholder applies only on the opening: settle_coverage and classify_coverage take opening: bool, since nothing signalled the opening to the classifier before. ENG-970 swaps the placeholder for her note at this same point. (Scope 5)
  • P4. A live opening that append_opening drops is not classified: _write_the_turn returns whether it wrote. (Done when: "every non-panorama turn that appends an exchange")
  • P5. The two gate tests that stubbed shapes production never produces (a non-empty transcript on an uncertain or mother-tongue take) are inverted on the real shapes: an empty slot, and the note. (Scope 6)
  • P6. ENG-996 is not on the base; functions are cited, not lines.

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

joaocarvoli and others added 3 commits September 24, 2026 02:50
… 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>
@linear-code

linear-code Bot commented Sep 24, 2026

Copy link
Copy Markdown

ENG-1074

scene=_scene_of(session, outcome.transcript),
)
if _worth_settling(outcome, heard):
if _worth_settling(session):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ""),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@little-henok little-henok Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@joaocarvoli
joaocarvoli requested review from henokteixeira and removed request for henokteixeira September 24, 2026 08:16

@little-henok little-henok Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@joaocarvoli
joaocarvoli merged commit 702e558 into joao/alinhar-setembro Sep 24, 2026
8 checks passed
@joaocarvoli
joaocarvoli deleted the joao/eng-1074-classify-every-non-panorama-turn-the-opening-included-as-her branch September 24, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant