Public: anyone can read this; the crew can change it.

A stopped Talk turn is stored as stopped, and run vitals do not count it as a failure #8880

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A stopped Talk turn is now stored as `stopped`, and the vitals count stopped turns apart from failures. Tests, `bun x tsc --noEmit` and the gate pass; I ran only the Talk and telemetry test files, not the full suite.

shipped production is serving it deploy 78db48e

opened this

A stopped Talk turn is now stored as stopped, and the vitals count stopped turns apart from failures. Tests, bun x tsc --noEmit and the gate pass; I ran only the Talk and telemetry test files, not the full suite.

  • Migration: 20261103130000-conversation-run-stopped.ts adds stopped to the conversation_run state check. SQLite can't alter a check in place, so it rebuilds the table with every column the table has now and recreates both indexes. Existing rows carry over unchanged. The pre-push hook added the migration pin as its own commit.
  • stopTurn: it now stores stopped instead of failed, with the same tokens, cost and finish time. The Talk page still says Stopped.
  • Vitals: no Talk failure-rate query existed. src/flow/talk_turns.ts is new and counts ok, failed and stopped turns, with a failure rate of failed over (ok + failed). flowFor exposes it as vitals.talkTurns. I added that as a sibling of vitals.talk rather than nesting it, so the existing vitals.talk shape is unchanged.
  • Tests: the stop test in test/conversation-turn.test.ts now asserts the run is stored as stopped and the failed count is the same as before the stop. A second test there inserts one ok, one failed and two stopped turns and checks the counts.

The vitals says-line doesn't mention stopped turns yet; the counts are in the data only. I didn't touch the studioWord mapping in src/web/client.ts, because that is the studio's run table, not Talk.

To try it, start a turn in /talk, press Stop, and read flowFor(...).vitals.talkTurns.

Closes #8860

commented

A stopped Talk turn is now stored as stopped, and the vitals count stopped turns apart from failures. Tests, bun x tsc --noEmit and the gate pass; I ran only the Talk and telemetry test files, not the full suite.

  • Migration: 20261103130000-conversation-run-stopped.ts adds stopped to the conversation_run state check. SQLite can't alter a check in place, so it rebuilds the table with every column the table has now and recreates both indexes. Existing rows carry over unchanged. The pre-push hook added the migration pin as its own commit.
  • stopTurn: it now stores stopped instead of failed, with the same tokens, cost and finish time. The Talk page still says Stopped.
  • Vitals: no Talk failure-rate query existed. src/flow/talk_turns.ts is new and counts ok, failed and stopped turns, with a failure rate of failed over (ok + failed). flowFor exposes it as vitals.talkTurns. I added that as a sibling of vitals.talk rather than nesting it, so the existing vitals.talk shape is unchanged.
  • Tests: the stop test in test/conversation-turn.test.ts now asserts the run is stored as stopped and the failed count is the same as before the stop. A second test there inserts one ok, one failed and two stopped turns and checks the counts.

The vitals says-line doesn't mention stopped turns yet; the counts are in the data only. I didn't touch the studioWord mapping in src/web/client.ts, because that is the studio's run table, not Talk.

To try it, start a turn in /talk, press Stop, and read flowFor(...).vitals.talkTurns.

commented

The check failed because my migration rebuilt conversation_run, and a release still in play can't survive that. I replaced it with an additive one. After the change the gate is clean, tsc is clean, and schema-additive, the Talk turn tests and the telemetry tests pass. I didn't re-run the full suite here; the forge will on this push.

  • Migration: 20261103130000-conversation-run-stopped.ts now only adds conversation_run.stopped INTEGER NOT NULL DEFAULT 0. The rebuild was needed to widen the state check to allow a stopped value, and that is what the additive test rejects. The pin is regenerated.
  • Stop: stopTurn now stores state = 'failed' with stopped = 1, so a release still in play reads the row as it always did.
  • Counts: readTalkTurns counts failed as state = 'failed' AND stopped = 0 and stopped as stopped = 1. A stop therefore leaves the failure count and rate unchanged, and vitals.talkTurns is unchanged.
  • Tests: the stop test asserts stopped = 1 and that the failed count didn't move. The counts test seeds one ok, one failed and two stopped turns.

This departs from the issue's wording, which asked for stopped as a state value. Making it a state needs a table rebuild, which the roll-safety guard (#875) forbids until the older releases have gone. If you want the literal state value later, it can follow in a migration once those releases are out of play.

Changes requested — before the latest push

The storage and the counts are right and roll-safe, and the test would fail without the change. Two things block it.

  1. Fix first: the counts are reachable only as raw JSON at flowFor().vitals.talkTurns. Nothing a person reads shows stopped turns apart from failures: the vitals says-line omits them (the builder says so), and there is no terminal screen or docs/features/ line for it. The issue says the vitals and failure-rate queries count them apart, and done means reachable. Add the stopped and failed counts to the says-line (for example via talkResumesWords or a sibling words function) so the page and the terminal both show them. Add a test on that line, and a short docs/features/ note if the flow vitals have one.
  1. The migration comment and the talk_turns.ts header say the same thing twice; trim the doc header in talk_turns.ts to one line.

Box 1 departs from the issue text (a flag, not a state value). The reason, the roll-safety guard, is sound. I do not hold it against the pull, but the box stays open.

I did not find a second place that counts conversation_run failures: the only other writer is the interrupted-turn path at conversation.ts:283, which is correctly a failure.

commented

Both review points are addressed and pushed. tsc, the gate and the Talk turn, resume-count and telemetry tests pass here. I didn't run the full suite; the forge's check does that on the push.

  • Says-line: src/flow/talk_turns.ts gets talkTurnsWords, and flowFor appends it to vitals.says. It reads like Talk: 2 turns failed, 1 stopped by the person and is empty when none failed and none were stopped. test/conversation-turn.test.ts has a test on the words and one that the line shows up in flowFor(...).vitals.says.
  • Where it shows: vitals.says is returned by GET /api/v1/telemetry/flow and the MCP flow tool. I found no page or terminal screen that renders vitals.says, so I didn't claim one in the docs. If you want one drawn somewhere, that is a separate change.
  • Docs: docs/features/talk.md has a short "Stopped is not failed" note covering the flag, the line, and where it appears.
  • Header: the doc comment in talk_turns.ts is now one line.

The pull still uses a flag (stopped = 1, state stays failed) rather than a stopped state value, as you noted.

commented

I merged main into the branch and pushed; the pull should merge cleanly again. tsc, the gate and the four test files I ran pass (conversation-turn, conversation-resume-count, system-telemetry, schema-additive). I didn't run the full suite here; the forge's check does that on the push.

The only conflict was in docs/features/talk.md, next to the new "Stopped is not failed" note. I kept that note and main's updated terminal paragraph. src/crew/conversation.ts merged on its own. I didn't re-read the merged conversation.ts, but the stop test in conversation-turn.test.ts still passes.

The pull still does what it did: a stopped Talk turn is stored with stopped = 1 (state stays failed), and the flow vitals line counts stopped turns apart from failures.

Approved

Earlier asks met: the says-line (talkTurnsWords in vitals.says), the Stopped-is-not-failed note in docs/features/talk.md, and the one-line header are all in. The only change since my last read is the merge of main. The stop path stores stopped=1 with state kept failed, the counts leave stopped out of failed and the rate, and the tests would fail without it. Non-blocking: the flag departs from the issue literal of a stopped state, so once older releases are out of play, a follow-up could widen the state check. Non-blocking: the stop test uses toBeGreaterThanOrEqual(1) for stopped; an exact delta would be tighter.

commented

Left #8860 open: 1 unticked item remain (A migration adds stopped as a state of a conversation run; stopTurn sets it instead of failed.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs