Conversation
A kaijutsu review on the `crusoe` cast ran 29 minutes before the caller cancelled it. ClickStack had the answer the whole time (GLM-5.3 at ~100 s a call, output speed down from 102 to 7 tok/s over three days — the endpoint, not the prompt), but nothing in the tool surface pointed at it: every span was Unset and the only signal a caller had was elapsed time. Amy: "take a look at the lift, good idea, and we can check our tracing health while we're there." The completion wrapper already measured every call and threw the number away after the metrics write. It now emits `PhaseEvent::ChatCompleted` labeled with the cast role, success or failure. `ProgressLog` keeps the durations per role and `job_get` renders them beside the latest beat: `synth chat: last 107 s, p50 98 s over 9 calls`. A chat beat does not touch the "currently" line or its step count — those track tool activity. A call at or past `[defaults] slow_chat_secs` (default 60, `0` off, env `KAIBO_SLOW_CHAT_SECS`) is promoted to Warn, so `job_wait` returns it. The threshold lives on `TracingSink`, not `PhaseContext`: it is an audience decision (who is told), and every one of the 17 `PhaseContext` literals is exhaustive. `promotes_to_caller` stays a pure predicate by taking the threshold as an argument. The phase runner's sink became an owned `Arc` so the wrapper rig clones into each agent can hold it, and the runner takes the `ModelRole` so the beat says "synth" or "explorer". Single-shot lanes (`oneshot`, `deliberate` direct) are unchanged; the caller waits on those anyway. Nine new tests. Each guarded behavior was sabotaged and its test went red: no emit in the wrapper, no beat attached in the engine, a chat call never promoting, a chat beat overwriting the latest line, `0` not honored. Suite 1372/0, clippy clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two findings from the deepseek cast's review of the first commit, both verified against source before acting. The docs claimed a synchronous call's log stream carries the slow-call warn. It does not: only the async job lanes build a TracingSink; a sync consult routes beats to progress notifications, which have no level. The three texts now say what the code does. A synchronous caller still sees every call's time in its progress line. The beat's elapsed time included kaibo's own retry backoff — up to a minute per attempt — so a promoted warning could blame the model for kaibo's waiting. The watcher now sits inside the retry wrapper, so the beat, the client metrics, and `inference_calls` see one attempt each, timed on its own. The `attempts` doc comment was already claiming this; the code now does it. `run_phase_loop` is generic over the model again rather than typed on `Watched`, which the reorder needed. Smaller: the warn string's advice was ungrammatical and said "past" a mark the predicate treats as inclusive (now "reaching", "job_cancel it"); whole seconds floor so `60 s` means the mark was reached; the three TracingSink construction sites fold into one `job_progress_log` helper. New test pins one beat per attempt; it fails with the watcher moved back outside the retry. Suite 1373/0, clippy clean. Reviewed-by: kaibo cast `deepseek` (deepseek-flash explorer + synth) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The decision
A kaijutsu review on the
crusoecast ran 29 minutes before the caller cancelled it. ClickStack had the answer the whole time — GLM-5.3 at ~100 s a call — but nothing in the tool surface pointed at it: every span was Unset and the only signal a caller had was elapsed time. The tracker entry proposed two things, per-call latency in the progress line and a warn past a threshold. Amy: "take a look at the lift, good idea, and we can check our tracing health while we're there."Tracing health, checked first: spans and logs land through today, zero error-status spans since 09-05. And the traces settle what the slow review was. GLM-5.3 on Crusoe took the same ~100k-token prompts on 09-10 and 09-12, but per-call p50 went 3.8 s → 40 s → 104 s and output speed 102 → 21 → 7 tok/s over three days. The endpoint degraded, not the prompt. The lift would have shown that at the first poll.
What ships
ChatCompletedbeat from the completion wrapper.Watchedalready measured every provider call and threw the number away after the metrics write. It now emits the duration as a progress event labeled with the cast role, success or failure.job_getshows each role's latency beside the latest beat:synth chat: last 107 s, p50 98 s over 9 calls. A chat beat does not touch the "currently" line or its step count; those track tool activity.[defaults] slow_chat_secswarns the caller throughjob_wait. Default 60,0turns it off, envKAIBO_SLOW_CHAT_SECS, rendered inkaibo://config, documented in the template and the guide.inference_callssee the provider's time and never kaibo's backoff sleep. Theattemptsdoc comment was already claiming this; the code now does it.Where the threshold lives, and why
On
TracingSink, notPhaseContext. Two reasons. Every one of the 17PhaseContextliterals in the tree is exhaustive, so a field there is 17 edits for one number. And the mark is an audience decision — who is told — which is exactly what the sink's level convention already routes (mcp_log.rs: Warn means "the calling model should see this").promotes_to_callerstays a pure predicate by taking the threshold as an argument. The three async job lanes build the sink through one helper,job_progress_log, so the mark cannot be dropped at one lane and kept at another.The phase runner's sink became an owned
Arc(the wrapper rig clones into each agent has to hold it) and the runner takes theModelRole, so the beat sayssynthorexplorer. The single-shot lanes (oneshot,deliberatedirect) are unchanged; their caller waits on one call anyway.Review, and what changed because of it
The deepseek cast mapped the path before I wrote a line (all nine citations I spot-checked held) and reviewed the change after. Every citation in the review was checked against source before acting. It found two real things:
TracingSink; a syncconsultroutes beats to progress notifications, which have no level. Fixed by narrowing the three texts to what the code does. A synchronous caller still sees every call's time in its progress line; a warn-level log notification would not reach a Claude Code agent mid-call anyway.retried(watched(model))), with a test that pins one beat per attempt and fails under the old order.Also from the review: the warn string's advice was ungrammatical and said "past" a mark the predicate treats as inclusive (now "reaching", and "
job_cancelit"); whole seconds are floored so60 smeans the mark was reached and59 smeans it was not.One finding is a deliberate trade, flagged rather than changed: on a uniformly slow backend every call promotes, so a parked
job_waitwakes once per call. That is the signal the knob exists to send; an operator who wants the park back raisesslow_chat_secs.Verification
Ten new tests. Each guarded behavior was sabotaged and its test went red: no emit in the wrapper, no beat attached in the engine, a chat call never promoting, a chat beat overwriting the latest line,
0not honored, and the watcher moved back outside the retry. Full suite and clippy clean; counts in the commit messages.Reviewed-by: kaibo cast
deepseek(deepseek-flash explorer + synth), two passes🤖 Generated with Claude Code