mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-21 21:31:12 +02:00
A sync-tool batch runs under Promise.all, which rejects the moment one tool's result append fails (a transient fs fault, or errorMessage() throwing on a nullish thrown value). That rejection propagated out through withLock and freed the per-turn lock while sibling tools were still executing. A straggler's later append then ran with no lock held and validated against a stale in-memory history rather than the file — so if the caller re-advanced in between (writing the recovery indeterminate result for the not-yet-settled tool), the straggler appended a second tool_result for the same call. Duplicate results make reduceTurn throw on every future read, permanently corrupting the turn file and, through context references, every session behind it. - executeAllowedTools now awaits Promise.allSettled and rethrows the first fault only after every tool has finished appending, so a fault never orphans a sibling. - advance() drains the append queue (settleAppends) before withLock releases the turn, closing the fire-and-forget reportProgress path and any future append-path rejection. - errorMessage() uses optional chaining so a `throw null`/`throw undefined` yields a normal isError tool result instead of a TypeError that escapes as an infrastructure rejection (the same fault that triggers the lock-escape mid-batch). Adds two regression tests (both fail without the fix): a nullish throw records an isError result and completes the turn; a faulted batch does not settle until the slow sibling's append is durable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| .pnpm-store/v11 | ||
| apps | ||
| docs | ||
| packages | ||
| .gitignore | ||
| ANALYTICS.md | ||
| CODE_MODE_ENGINES_PLAN.md | ||
| demo.gif | ||
| eslint.config.mts | ||
| LIVE_NOTE.md | ||
| MINI_APPS_PLAN.md | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| tsconfig.base.json | ||
| VIDEO_MODE.md | ||