fix(notice): stop calling an answered round a backend outage #1003
No reviewers
Labels
No labels
move-to-repo
coilyco-bridge-deploy
move-to-repo
coilyco-flight-deck-agent-compose
move-to-repo
coilyco-gaming-eco-app
move-to-repo
coilysiren-inbox
move-to-repo
unknown
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
c#
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-gaming/sirens-echo!1003
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/claude/model-answered-not-unavailable"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #933.
First, the question #933 was left open on
Darren's note there says the issue is not one of the six because the #939 work would very likely close it too, and asks for it to be said either way. Said: #939 closed at 22:09Z and does not close this. Its fix was the queue-timeout notice throttle, on the path where a turn never entered the pipeline. This defect is downstream of a model round that already returned 200. Different path, different cause, no overlap.
The defect, from the source
Three paths produced the member-facing string
model backend unavailable, retry shortlyon a round the backend had answered:proxy.go:1173- a body pastmaxAgentProxyResponseBytesmodelstream.go:119- a stream past the same boundproxy.go:1179- a body that would not decodeAll three returned a bare
fmt.Errorfcarrying no sentinel.turnFailureNoticematches on sentinels first and then falls through to a switch on the stage, and the stage here ismodel, so all three collectednoticeModelFailed.failureCausefell through the same way tostage_failed.That is exactly the trace in the issue:
model.response status: 200at 00:43:27,turn.stage.failedwithstage: modelone second later, and a 90-byte notice telling the member the backend was down.The fix
Two sentinels, in the idiom
ErrToolRoundsExhaustedandErrBudgetExhaustedalready set for the same class of problem ("the backend answered, so it must never read as an outage"):ErrResponseTooLarge→answer too long to read, ask for something narrower. Narrowing genuinely shortens the answer, so the advice is actionable.ErrResponseUnreadable→model backend sent an unreadable answer, retry shortly. Not the member's to fix, and retrying is honest because a malformed body is plausibly transient.Both sit ahead of the stage switch, and both get their own
failureCauselabel, so an oversized answer and an outage cannot collapse into one series on a dashboard. That collapse is what the issue names as the reason an operator could not tell them apart.What deliberately did not change
model backend unavailable. That phrase now means only "the backend did not answer", which is the second half of this issue's acceptance.context.DeadlineExceeded, which is matched ahead of everything here, so it reports as a timeout. A test pins that so this change cannot quietly move it.Verification
just gatepasses: build, policy-check, vet, test, test-skips, pre-commit.go test -race ./...clean.Six new tests. Checked non-vacuous by reverting the two classification cases rather than assuming, and both fail with the exact string from the issue's trace:
The other four pin the halves that must not move: a genuine outage still says unavailable, a 502 still says unavailable, a slow answer still reports as a timeout, no notice for an answered round contains the word "unavailable", and the cause labels are all distinct.
Not established
Whether this is what the owl.glass member actually hit. The issue's trace shows
Agent Proxy response exceeded the size limit.onmodel.chatin the same window, which is the oversize path, so it is the strong candidate. I read the classification path rather than reproducing the trace, and #933 itself marks the oversize-to-model_failedlink as inference. This change makes all three paths honest regardless of which one fired.No live verification. Acceptance is a repeat of that window showing no availability notice on a 200.
One thing for review, not part of the fix
docs/sirens-echo-delivery.mdwas at 7976 of its 8000-character cap, leaving 24 characters to document a new failure class. To fit, I replaced the paragraph's inline enumeration of six notice phrases with the classification rule. The enumeration duplicatednotice.go, which the same paragraph already names as the home of every phrase, and it grows by one entry per condition, which is the mechanism that filled this page.This is the second page in a row I have hit this on (
sirens-echo-commands.mdwas at 7958/8000 on #1001). The docs count is also at 40 of 40, so a new page is not available either. Worth a decision about the band rather than another round of shaving prose.