Skip to content

fix(ai): keep a pass alive through tool errors, file close-outs as partials - #903

Open
Makisuo wants to merge 1 commit into
inv/04-drop-lane-schemafrom
inv/05-tool-failures-and-partials
Open

Makisuo wants to merge 1 commit into
inv/04-drop-lane-schemafrom
inv/05-tool-failures-and-partials

Conversation

@Makisuo

@Makisuo Makisuo commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Stack 5 of 5 — based on the schema PR. Four fixes found by running the single agent end to end against a local stack.

What the runs showed

  • A tool that failed ended the whole pass. Maple's tool handlers declared MapleToolFailure as each tool's failure type, and the engine ends the run on a declared failure — which is how an approval-gated proposal becomes the turn's last word. So a rejected run_sql (missing $__orgFilter) on the agent's very first call killed the investigation before it had read anything. This predates the stack and affects chat turns too. Tool errors are now returned as the call's result text and the model rewrites the call; only the approval gate still fails the run. Verified on the next run: a list_source_repositories error came back as text and the agent carried on.
  • A close-out's report landed as diagnosed. Whatever the close-out files is a partial by construction, so SubmitDiagnosisRequest carries partial and the service routes it to the inconclusive writer: low confidence, no severity, no diagnosed_at, no issue-side writes.
  • A stalled model stream sat until the stale sweep. One run stopped mid-sentence and never resumed; the engine's ten-minute duration rail did not interrupt the in-flight stream, so the investigation sat for 15 minutes and was marked failed with nothing filed and no close-out. The model stream now fails after two minutes without a chunk, which hands the run to the close-out.
  • Empty text deltas flooded the session log. The provider streams one per reasoning token; a single minute put two thousand empty events into the Durable Object's SQLite, and that log is what every reconnect replays. They are dropped now.

Verified

End to end on a local alchemy dev stack (api, ai, web, electric-sync) from the worktree: freeform investigations started from the hub ran as one agent turn on the chat Durable Object, the page followed them live over the investigation shape, the close-out filed a partial when the first pass died, and the stale sweep closed a run whose worker was rebuilt underneath it. Migration 0058 applied cleanly to a throwaway Postgres database with the lane table gone from the Electric publication. Scoped typechecks on packages/domain, packages/backend, apps/ai; vitest for the chat tools, events, run, the tool handlers, the model instrumentation and the investigation service.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…rtials

Found running the single agent end to end against a local stack.

A tool that failed ended the whole pass. Maple's tool handlers declared
`MapleToolFailure` as the tool's failure type, and the engine ends the
run on a declared failure — that is how an approval-gated proposal
becomes the turn's last word — so a rejected `run_sql` on the first
call killed the investigation before it had read anything. Tool errors
are now returned as the call's text and the model rewrites the call;
only the approval gate still fails the run.

A close-out's report landed as `diagnosed`. Whatever the close-out
files is a partial by construction, so `SubmitDiagnosisRequest` carries
`partial` and the service routes it to the inconclusive writer: low
confidence, no severity, no `diagnosed_at`, no issue-side writes.

A model stream that stalls now fails after two minutes without a chunk.
One did, mid-sentence, on the second run; the engine's ten-minute rail
never interrupted it, and the investigation sat until the 15-minute
stale sweep marked it failed with nothing filed. Failing the stream
hands the run to the close-out instead.

Empty text deltas are dropped from the session log. One provider
streamed one per reasoning token, which put two thousand empty events
into a Durable Object's SQLite in a minute — and the log is what every
reconnect replays.
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 160152d9-ff07-482e-bfcd-68b3ce103a84

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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