Skip to content

CL-6365: a relaunch mints a fresh run; the room stops being a run id - #106

Closed
TheGreatAxios wants to merge 2 commits into
cl-6365-relaunchfrom
cl-6365-unfuse
Closed

CL-6365: a relaunch mints a fresh run; the room stops being a run id#106
TheGreatAxios wants to merge 2 commits into
cl-6365-relaunchfrom
cl-6365-unfuse

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

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:

  1. deriveWorkflowRunId(address) is the address's local part (vendor/intx/types/src/workflow-run-id.ts).
  2. The code-sourced deploy front refuses an incoherent (anchorRunId, agentAddress) pair — emitSourceRefDeployFrame's coherence guard, vendor/intx/hub-sessions/src/session-service.ts:724.
  3. receiveWorkflowRunPack marks 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 — teardownDeployment removes deployment.json and the step scratch, but the sibling git repo holding runs/<runId>/events/ survives. That is exactly why redeploying the same address inherits the terminal log.

The seam

chat.workbench_launch is 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.ts owns both directions:

  • Outbound (sendMail, ensureAwake, subscribeToWorkbench, fetchBlob, refreshAgentInstanceFromDefinition) resolves the stable id to the live address.
  • Inbound (chat-orchestrator.ts's resolveMemberWorkbenches) resolves a live address the sidecar reported back to the room address that participant records, mention handles, and every already-posted message's sender_address carry.

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 orphaned isFoldedRunSettled: a terminal workflow_run.status that is not a folded run parked between messages is a run beyond waking, and wakeByAddress relaunches it via launchFoldedRun (fresh anchor row → the adopting code-sourced front adopts the row mintFoldedRun just 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

  • The relaunch is send-triggered. It fires on the next sendMail through 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.
  • No relaunch notice is posted. The turn-drop notice fires on 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 on createHubChatPlatform wired in the hub beside roomMessages. Neither is here.
  • Pre-relaunch attachments. fetchBlob reads 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 are chat.workbench_messages rows, not mail).
  • The e2e proof has not been re-run on this branch. Proof 4's deterministic red is unchanged; the green half is asserted only by unit suites so far. Re-running it needs the boot sweep above to clear hop 3 regardless.

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 --write clean on every touched file.

Dev-data note

0020_workbench_launch_current_run backfills current_run_id = instance_id for existing rows — the identity mapping those rows have always implied, not a compat shim. No legacy path is left beside the new one.

Sawyer Cutler 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.
@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Landed via the stack tip: PR #108 (merge fbf0852) contains this branch's commits in full.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant