Repository navigation
Conversation
… the fact Settle history was in the event log, but what was only known at decision time was lost: the reply check's verdict lived in memory and a log line that rolls over in minutes, holds were never recorded, an unsettle had no cause, and nothing said which build or settings decided. - settlement_decisions: one row per auto-settle and per hold change, with the trigger, rule, hold reason, run, the reply check's verdict, the inputs the rules read, the settings, and the build version. Created at startup instead of by a migration, since fork migration ids collide with upstream's. - unsettleCause on the thread: who or what brought a settled thread back. An agent's MCP unsettle now sends reason "agent". - apps/server/scripts/settle-audit.ts labels past auto-settles by what happened next and, with --replay, reruns this checkout's rules on every recorded decision.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
This branch has not been deployed
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.
i want to be able to study auto-settles after the fact and tune the rules. most of what that takes was already in the event log: since PR 47 every settle records its rule, and the PR states, runs and replies are all there. what got lost was everything only known at the moment of the decision
the reply check's verdict (PR 48) lived in memory plus a log line, and server logs here roll over every 15-35 min. so for a merge settle i couldn't tell if the check ran, which reply it read, or what the model said
holds weren't recorded at all. "held for 3 hours by a watch" or "held because the last reply asked me something" was invisible
an unsettle only had a timestamp. telling "i reopened it" from "a schedule reopened it" meant digging up the next message, and last week schedules were 13 of the 17 merge-settle reopens
nothing said which build or settings made a decision, and the settle logic changed 4 times last week
feat(threads): record what settled a thread and show it in MCP #47
fix(settlement): keep a merged thread when its last reply still needs you #48
fix(server): a watched PR's merge wakes the agent before the thread settles #75
what changed
settlement_decisionstable. one row per auto-settle, plus one per hold whenever its rule, reason or run changes, so a hold every sweep repeats is written once. each row hasinputs_json) and the settle settings, so the decision can be replayedCREATE TABLE IF NOT EXISTS, not as a migration, since fork migration ids collide with upstream's (that's whatreconcileForkMigration.tscleans up after). nothing else reads it. a failed write logs a warning and never blocks a settleunsettleCauseon the thread, set whenever a settled thread comes back:useroragent(explicit unsettle, MCP now sendsreason: "agent"),pin,user_message,agent_message,scheduled_task,notification,subagent_result,guest. cleared on settle, same assettlementSourcenode apps/server/scripts/settle-audit.tsreads the db read-only and labels every auto-settle in the window by what happened next: reopened by me within N hours (likely wrong), reopened later, reopened by a schedule or wake (fine), or never. it prints a table by rule, lists the ones i reopened, sums up the recorded holds, and--jsonwrites one line per settle with the request and reply the check read. reopens from before this ships get their cause from the message that came with them--replayreruns this checkout's rules on every recorded decision, with the recorded reply check verdict, and lists the ones that come out different. so a rule change gets checked against real history before it shipsresolveAutoSettlementnow returns settle, hold with a reason, or null, instead of settle or null. settle results don't change: the existing 50 settlement tests pass untouchedfirst run on my data, last 7 days
all 4 i reopened were merge settles right after a PR-watch wake. in 3 of them the last reply says work is still in flight ("i'll pick up when the build finishes", "once the login goes through i'll apply the PR"), which the reply check prompt says should hold. there's no way to see today whether the check ran or what it answered. that's the first thing this log will tell us
tradeoffs
--jsonhas the request and reply pairs to run an eval overunsettleCauseonly exists on new events. older reopens are inferred, and an MCP agent's unsettle from before this reads asuserproof
ThreadSettlementService.test.ts: "tells the orchestrator which rule settled each thread" also checks the logged rows, including a watched thread logged as held bybackground_work. "asks the model once per run" checks that 3 sweeps write one held row per thread with the model's verdict, then a settle row with the next run's scheduled verdictOrchestrator.control-reads.test.ts: new case covering each causesettle-audit.test.ts: replaying inactivity, a snooze, a watch and a reply check hold through JSON gives the same decision. all four fail if the date revival is brokenmigrate-dev-db) for ~2.5 min. the table got created at startup and the sweeps logged 3 held rows (2 merged, 1 closed, allneeds_userwith the model's verdict), with no repeats across later sweeps. a--watchrestart wrote each hold once more, since the dedup map lives in memory.settle-audit --replayon that db: 6 decisions replayed, 0 changedwritten with claude opus 5.5 in claude code