CL-6365: a relaunch mints a fresh run; the room stops being a run id - #106
Closed
TheGreatAxios wants to merge 2 commits into
Closed
CL-6365: a relaunch mints a fresh run; the room stops being a run id#106TheGreatAxios wants to merge 2 commits into
TheGreatAxios wants to merge 2 commits into
Conversation
added 2 commits
August 20, 2026 03:36
Covers both directions of the mapping a relaunch needs: the room's own address resolving to whichever run is live now, and a live deployment address resolving back to the participant the room has been addressing all along. Also separates a run that is beyond waking (terminal, its durable log already sealed) from a folded run merely parked between messages, which still wakes. The two existing chat fakes gain the launch row every chat run has in production, since that row is now the mapping every lookup goes through.
A run that dies mid-turn commits its terminal event to the durable log before exiting, so waking its address again comes straight back as workflow_run_terminal and the next message is dropped in silence. The fix is a fresh run — and never reclaiming the dead one's log, which is the audit trail this shape exists to keep. The platform fuses a run's id to its address in three independent places, so a fresh run necessarily carries a fresh address. That is only survivable because the room stops being the run: chat.workbench_launch now maps a stable participant id (the address the room uses forever) to the current run id behind it, re-pointed on every relaunch. agent-binding.ts owns both directions — outbound sends resolve the live address, inbound events resolve back to the room address that participant records, mention handles, and posted messages all carry. Detection is workflow_run.status plus folded-runs' isFoldedRunSettled, which had no caller until now: a terminal status that is not a parked folded run means relaunch rather than wake. The relaunch is send-triggered; a boot-time sweep and an in-room notice are still open. See docs/revendor-inventory.md.
Contributor
Author
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.
What this is
CL-6365's fix, and Phase 1.4's core un-fusing: a relaunch mints a fresh run, and the room's identity stops being a run id.
Builds on PR #105's deterministic red. This is the green half — with an honest boundary on what is and is not covered (see "What is still open").
The ruling this implements
Never reclaim or erase the durable log. It is the audit trail, and a resurrection that overwrote it would trade away the one thing this shape is better at. A relaunch mints a fresh run — new id, new address, new event-log repo — that adopts the room's continuity: the room is data (
chat.workbench_messages), and the participant's address becomes a mapping to the current run rather than an identity equal to it.Why a fresh run cannot keep the old address
Three independent points fuse a run's id to its address, and none of them can be worked around from here:
deriveWorkflowRunId(address)is the address's local part (vendor/intx/types/src/workflow-run-id.ts).(anchorRunId, agentAddress)pair —emitSourceRefDeployFrame's coherence guard,vendor/intx/hub-sessions/src/session-service.ts:724.receiveWorkflowRunPackmarks runs terminal by an id read out of the address-derived repo id; a mismatch leaves the run"running"in Postgres forever.So the address only has to be stable for the room, never for the sidecar. That is the un-fusing.
Incidental finding:
apps/sidecar/src/workflow-deployment-record.ts's header claims "a single teardown reclaims both". It does not —teardownDeploymentremovesdeployment.jsonand the step scratch, but the sibling git repo holdingruns/<runId>/events/survives. That is exactly why redeploying the same address inherits the terminal log.The seam
chat.workbench_launchis where the two identities come apart:instance_id— the stable participant id, the address the room uses forever (the workbench id for a host, the first-mint id for an invited agent).current_run_id(new, unique) — the run actually executing behind it, re-pointed on every relaunch.packages/chat/src/agent-binding.tsowns both directions:sendMail,ensureAwake,subscribeToWorkbench,fetchBlob,refreshAgentInstanceFromDefinition) resolves the stable id to the live address.chat-orchestrator.ts'sresolveMemberWorkbenches) resolves a live address the sidecar reported back to the room address that participant records, mention handles, and every already-posted message'ssender_addresscarry.Nothing else in the room moves — none of chat's tables were ever keyed on the run, only on the id that is now the stable one.
Detection wires
@corbits/folded-runs' previously orphanedisFoldedRunSettled: a terminalworkflow_run.statusthat is not a folded run parked between messages is a run beyond waking, andwakeByAddressrelaunches it vialaunchFoldedRun(fresh anchor row → the adopting code-sourced front adopts the rowmintFoldedRunjust wrote) instead of redeploying an address whose log already says terminal. The repoint is written after a successful deploy, never inside the launch transaction, so a rolled-back launch cannot leave the room addressing a run that no longer exists.What is still open — the precise remainder
sendMailthrough the wake choke point. Nothing sweeps for terminal runs at boot, so a room whose agent died stays silently dead until somebody writes into it.message.run.ended, which a killed sidecar never sends. Proof 4's "the turn the kill interrupted surfaces visibly" hop asserts a notice with no new message sent — that hop needs the boot-time sweep + a notice port oncreateHubChatPlatformwired in the hub besideroomMessages. Neither is here.fetchBlobreads through the live run's session, so mail attachments written under a previous run's session are unreachable after a relaunch. Room messages are unaffected (they arechat.workbench_messagesrows, not mail).Old runs' terminal logs remain readable through the ordinary run routes — the audit trail is intact by construction (a fresh run gets its own repo), but that is not yet asserted by a test.
Checks
bun run typecheck(whole repo): clean.packages/chat: 568 pass, 0 fail (587 across 50 files).apps/hub: 130 pass, 0 fail.prettier --writeclean on every touched file.Dev-data note
0020_workbench_launch_current_runbackfillscurrent_run_id = instance_idfor existing rows — the identity mapping those rows have always implied, not a compat shim. No legacy path is left beside the new one.