Give Fisherman's run one RequestHaul and thin the finish tool - #169
Merged
Conversation
One `Haul` now owns "the requests this run made": it captures the start mark once, after `refreshAuth()`, and both the stuck-detector and the tools read their run views from it. The failure predicate lives once, as `isFailedRequest` in the data tier, and `finish` delegates its verification to `verifyFinish` instead of judging inline. `src/utils/request-map.ts` is gone — `Haul.byId()` replaces it, so `src/utils/` no longer depends on `src/api/`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B46vQvvc47LJeCmqQxAhme
The class is used by Fisherman alone, so it belongs beside the agent rather than in the shared request data tier: `src/ai/fisherman/request-haul.ts`, matching how other agents keep their utility classes (`ai/researcher/*`). `isFailedRequest` stays in `request-store.ts` — it is still the one shared failure predicate, called by both `RequestHaul.failed()` and Fisherman's `isStuckOnEndpoint`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BYfsBh3TXThD5XLxa23X5d
DenysKuchma
approved these changes
Sep 2, 2026
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.
Implements
plans/028-fisherman-haul.md. Pure refactoring — no behavior change.The problem
PR #160 made Fisherman's success verdict depend on the requests made during a run. But "this run's requests" and "a failed request" were each defined in several places:
createFishermanToolscaptured its ownledgerStartmarkFisherman.prepareDatacaptured a second one on the next line(r.status >= 400 || r.error)was written twiceThe two marks agreed only because the two statements happened to be adjacent. Any reordering would make the stuck-detector and the result-synthesizer disagree about which requests belong to the run — reporting "success" on an abandoned run, exactly the bug #160 set out to kill.
The change
One
RequestHaulowns the run — what one outing brings in. It lives atsrc/ai/fisherman/request-haul.ts, beside the only agent that uses it, the way other agents keep their utility classes (ai/researcher/*). Constructed once when the run starts (immediately afterrefreshAuth(), so auth probes never count), it exposes everything made after that point —requests(),failed(),successfulWrites(),byId()— so the stuck-detector, the result-synthesizer andfinishall read the same definitions.The failure predicate lives once, as
isFailedRequestin the data tier (request-store.ts).RequestHaul.failed()andisStuckOnEndpointboth call it; there is no second textual copy.finishis thin again. Itsexecutebody held Fisherman's most important policy — what counts as created — as inline judgment, violating the repo's glue-tier rule (CLAUDE.md: "Tools = schema + result parsing only, delegating every real operation to an agent or data module in one call"). That verification moved verbatim into an exportedverifyFinish(haul, input); the tool is now one call plus result parsing.src/utils/request-map.tsis deleted. A 19-line class with exactly one consumer, and the only file insrc/utils/importing from../api/.RequestHaul.byId()replaces it.Verification
bun test tests/unit— 1244 pass / 0 failbun test tests/integration/fisherman.test.ts— 5 pass / 0 failbun run format/bun run lint— exit 0grep -rn 'getMadeRequests().length' src/— one match, theRequestHaulconstructor (the run's start mark exists nowhere else)grep -rn 'RequestMap\|request-map' src/ boat/ bin/ tests/— no matchesNotes for the reviewer
grep 'status >= 400' … | wc -l → 1reports 3. The other two hits are pre-existing per-response status checks insidegetEndpointSpecand therequesttool — both explicitly out of scope, and neither is the run-failure predicate the criterion means. They were left alone rather than rewritten to force the count. The predicate itself now exists in exactly one place,request-store.ts.RequestHaul's unit test stays intests/unit/request-store.test.ts— it drives a realRequestStoreand reuses that file'smakeRequestfixture, which is defined nowhere else.CHANGELOG.mdentry: the plan scopes it out and gates on "no out-of-scope files modified", and there is no user-visible behavior change to describe. Happy to add one.RequestStore's dedup/alias cleanup (that is plan 030, which also editsrequest-store.ts— land this first),src/api/request-result.ts, and every prompt string infisherman.ts.🤖 Generated with Claude Code
https://claude.ai/code/session_01B46vQvvc47LJeCmqQxAhme