Skip to content

fix: reconcile CLI and Desktop messages by canonical identity - #3803

Draft
Astro-Han wants to merge 4 commits into
mainfrom
fix/canonical-message-projection
Draft

fix: reconcile CLI and Desktop messages by canonical identity#3803
Astro-Han wants to merge 4 commits into
mainfrom
fix/canonical-message-projection

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Refs #3633

This is replacement PR 2 after #3721 and #3716 landed on main.

  • CLI and Desktop now generate one stable messageId before ordinary send or Follow Up admission, render that LocalIntent immediately, and submit the same identity through turn.message.submit.
  • Runtime Host remains the only delivery/lifecycle authority. Its admission result says only turn_started, steering, or followup; the Host subscription introduces the real Turn, while queue projection describes only the live queue.
  • Canonical transcript and live steering rows reconcile the transient row in place by messageId. Sparse historical ranges omit latest transients without deleting them; latest replacement, reconnect, remount queue echo, and terminal refresh preserve or deterministically replace them.
  • Desktop ordinary sends no longer arm/rebind a renderer-owned live Turn or manufacture an empty Turn. Explicit orchestration still uses exact turn.start; Side Conversation keeps its independent fix(desktop): bind Side Conversation events to Host admission #3716 admission and event ownership.
  • CLI Enter, mid-turn steering, and Alt+Enter Follow Up share one submission seam. The old steer/queueMessage fallback outcome, CLI delivery retry ownership, and six obsolete fallback-only skipped tests are removed.

Before: optimistic rows and Host rows could have different identities, stale transcript replacement could erase the tail, and ordinary admission could create an empty pseudo Turn. After: the submitted message is immediately visible once, stays at one timeline position, and is replaced only by the canonical row with the same identity.

Verification

  • Rebased onto live origin/main at e9d314fd8a15a906d1323c599a1bd300a312d174.
  • Built affected workspaces: @maka/core, @maka/storage, @maka/runtime, @maka/runtime-host, @maka/ui, @maka/desktop main, and maka-agent.
  • 393 focused tests passed across Desktop composer/Follow Up admission, queue/remount projection, streaming handoff, UI materialization, CLI transcript/TUI long-running turn behavior, Runtime Host driver, and root-turn coordinator.
  • Coverage includes active Turn send, immediate Follow Up, two successive sends, canonical transcript advance, stale terminal events, sparse replacement, remount/reconnect queue echo, and cross-client follow-up message identities.
  • Scoped Biome check and git diff --check passed. Repository-wide tests were not run locally; CI owns that coverage.

Authority boundaries

  • No Runtime Host storage, settlement, recovery, provider/root proof, or durable lifecycle authority changed.
  • Transcript remains presentation authority; Host queue and handoff events are transient projections.
  • CLI and Desktop own LocalIntent presentation only. They do not own delivery, retry, settlement, or recovery.
  • fix(desktop): bind Side Conversation events to Host admission #3716 Side Conversation/Quote Companion fork, admission, Stop, event ownership, and disposal/retry semantics are unchanged.

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Codex authored the implementation, tests, simplification audit, and PR text under human review. Claude was consulted for an independent architecture critique. Material commits retain Generated-by: Codex trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update on bda2eccf2d:

[P2] busy-race fallback leaves ghost transient row

When renderer thinks idle but host is busy (another window/Bot/Goal continuation holds root), startTurn throws session_busy and falls back to submitMessage({messageId: renderer turnId}). Host then creates a new turnId for the turn while the durable message id stays as renderer turnId. The IPC returns host turnId without messageId, and renderer uses host id for optimistic row — never matches durable id → permanent duplicate (transient ghost).

Fix: return messageId on the turn_started branch and prefer it as transient key; add test for busy→turn_started path.

Note: PR is still draft; this review is preparatory and pending re-review after head stabilizes.

简体中文busy 情况下回退导致重复行。

@Astro-Han
Astro-Han force-pushed the fix/canonical-message-projection branch from bda2ecc to f5ff581 Compare August 25, 2026 19:57
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