Repository navigation
Conversation
The settlement service remembers the settledAt of each thread whose settle action ran, so a re-emitted thread.settled does not run it twice. Nothing removed that record, so every settled-then-deleted thread kept one for the life of the server process. Drop it when thread.deleted is consumed; live threads keep their per-settlement dedupe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Deleted threads now release their in-memory settle-action record. The cleanup is confined to private bookkeeping, leaving live-thread deduplication and persistent data unchanged. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Note Written by Verified in a real dev environmentverified as-is at
observed the existing map in the actual server through node's inspector, capturing its reference at a breakpoint immediately after the normal action bookkeeping. no source edits, mocked providers, synthetic settlement events, or fixture data were used. one dev server ran at a time.
command transcript and map observations Not verifiednative ios/android and electron clients, macos/windows shells, other providers, production heap impact, and retry/failure races were not exercised on this linux web-client pass. identical-timestamp event deduplication passed in the focused suite; it was not artificially replayed through the running app because deleted threads cannot settle again through the supported client path. no production memory or performance claim is made. merge update at
|
|
Note 🤖 Opus 5.5 replying on behalf of Victor. @maria-rcks I ran the checks this head still needed on 3f6c200, with Node 26.12.1 from the repo root: Output (selected lines, copied without changes)
Settlement suites on 3f6c200: Extended dedupe test, Real server, main 836b86a: Real server, 3f6c200: Real server, 3f6c200, B's settlement and A's delete sent together: |
|
Note 🤖 Opus 5.5 replying on behalf of Victor. Closing this: it is not a bug fix, and this repository now takes only bug fixes from outside contributors. The branch stays on my fork if it is useful. |


Problem
ThreadSettlementServiceremembers thesettledAtof every thread whose settle action ran, so a re-emittedthread.settleddoes not run the action twice. Nothing ever removes an entry. Deleting a thread ends its settlements for good (later commands reject a deleted ID, and creation won't reuse it), but its entry stays in memory until the server restarts. On a long-running server, every thread that reaches the settle-action step (a settled thread with an existing worktree, whatever the script outcome) and is then deleted leaves one entry behind.Change
On
thread.deleted, the service now also removes that thread's entry fromsettleActionRunAt, then queues the full sweep it already queued. Nothing else changes:shellClosedThreads, retry policy, the forced PR refresh and the persisted state are untouched. ClearingshellClosedThreadson delete would not bound it, since a late command completion can add the ID again. That is left out on purpose.Scope and approval
There is no issue. I think this fits CONTRIBUTING's exception for a very small, focused fix of an obvious bug. It frees memory that a terminal state made unreachable, and it changes no product behavior or settings, so it needs no direction call. If maintainers would rather not take housekeeping at this size, closing it is fine.
Nothing on screen changes. The map is private to the server process and only read by the settle-action dedupe check. No projection, event, RPC or contract is touched.
Verification
vp test run src/orchestration-v2/ThreadSettlementService.test.ts: the extended case "runs the settle script once per settlement until the thread is deleted" fails onmain(the replay after delete is still suppressed) and passes with the fix. In the same case, deleting a different thread leaves the live thread's dedupe in place. Again on 2026-11-12, on 3f6c200 merged locally with maindbfbe3993f(not pushed): withThreadSettlementService.tsfrom that main the case fails (expected 'marker-thread' to equal 'repeated-thread', 1 failed, 37 passed), and with this PR's version all 38 pass.vp run check:changed 836b86a5d6passes (format, lint, knip, graphql, typecheck, tests), and the three settlement suites pass 53 tests.main836b86a and on 3f6c200, with the free Replay (dev) provider. Two worktree threads with a project settle action both settled, thread A was deleted, then B settled again. I readsettleActionRunAtthrough the inspector at breakpoints. Onmainthe deleted thread's entry stays; on 3f6c200 the delete removes it and then queues the full sweep, also when another settlement's sweep is already pending.Inspector readback (selected lines, copied without changes)
Real server, main 836b86a:
Real server, 3f6c200:
Real server, 3f6c200, B's settlement and A's delete sent together:
ThreadSettlementServicelayer with the test file's mocked dependencies settled, deleted and replayed 20,000 synthetic threads.mainstill held 20,000 entries after the deletes; this PR held 0 and released 1,230,584 B (about 62 B per thread). Re-engaged and lagging settlements behaved the same on both builds.Not checked:
Prior PRs
The record and its dedupe came from #16290 by t3dotgg, which built on #15991 by spoukyii. #26291 by Wosca changes the settle-action step without removing these entries. #19665 by mikeohuo, merged as 20081ef, made
thread.deletedqueue a full sweep; at 3f6c200 the same case also removes the entry.Generated with Claude Opus 5.5 and GPT-6.1 Terra and GPT-6.1 Sol in T3 Code Nightly