Files
deepseek-harness/.agents/notes/implemented/simplification/2026-06-20-remove-agent-boundary-mirror-events.md
T
Tianyi Cui f2c0021941 docs(notes): aggressive archive sweep of low-value decision records
Archive 21 implemented triplets whose shipped decisions are complete and
whose bodies no longer guide future work (one-off UI chrome, generator
applications whose scripts are self-explanatory, superseded implementation
detail, process history owned by current contracts/skills). Delete 4
rejected triplets whose premises are obsolete: the DeepReadonly proposal
(dev-invariants note now carries the alternative inline), the collapse
tool-owned presentation proposal (superseded by the shipped render-intent
union), retire-mid-turn-steering (steering is now load-bearing across
plan-mode/apiproxy/TUI), and single-session-ACP (automation-only ACP
resolved the question; multi-session isolation is pinned by tests).

Repair every inbound link: retarget intentional historical citations to
archived paths, replace decision-current citations with the surviving
authority, and fix the stale example-execute-over-tsx pointer in
pnpm-workspace.yaml. Re-record pairing sidecars and seal the archive
manifest (append-only; existing seals unchanged).
2026-07-28 00:00:51 +08:00

5.7 KiB

Agent Note: Stop mirroring durable boundaries as agent events

Status: implemented

English | 中文

Problem

The loop records the canonical transcript in SessionEvent and also emitted a parallel set of live agent/* boundary mirror events: agent/turn-start, agent/turn-end, agent/step-start, and agent/step-end. The mirrors made consumers choose between two sources of truth for the SAME durable fact. ACP already chose the session log for prompt settlement and committed output because it is the one durable, replayable record; consuming a live mirror would require reconciling its timing with the boundary already stored in that log. The stdio UI was the only production consumer that still rendered turn boundaries from the mirror events; it already rendered tool calls and results from session/event.

This duplication is not free. Every lifecycle change had to update the session event, the mirror event, docs, invariants, tests, and snapshot expectations. The duplicate boundary events also made failure ordering subtle: a turn can be durably closed before a live agent/turn-end listener runs, so a post-boundary listener failure has no valid in-log position left and must be reported out of band.

Decision

Make session/event the single live boundary/transcript stream. Consumers that render turns, tool calls, tool results, assistant messages, and durable boundaries subscribe to session/event and derive their UI from the same event vocabulary persistence uses.

The four durable-boundary mirrors — agent/turn-start, agent/turn-end, agent/step-start, agent/step-end — are removed from the agent event taxonomy. A UI that wants the agent handle at a boundary retains the live target object from agent/created/agent/disposed and compares its session directly; dsh-ui-stdio uses this to label the app-owned agent's [main turn N] header while other sessions render their durable id. The canonical record remains the event-sourced session log.

The step mirrors (which had no consumer at all) were removed first, in the event-domain-semantics Agent Note; that Agent Note KEPT the turn mirrors on the stated justification that the stdio UI needed the Agent handle at the turn boundary. This Agent Note finishes the job: dsh-ui-stdio is a disposable test REPL whose rendering can change freely, so "ui-stdio needs it" is not a reason to keep a mirror — it reads session/event and retains only its live target object.

Scope: what is and isn't removed

Removed (durable-boundary mirrors — the session log is authoritative for each): agent/turn-start, agent/turn-end, agent/step-start, agent/step-end.

RETAINED — NOT durable-boundary mirrors, so out of scope for this decision:

  • agent/steering — not a boundary, so out of scope for THIS decision (the original proposal bundled it into the removal; that would have been scope creep here). It mirrors the durable steering/message control record rather than a boundary, and was removed by its own follow-up: Remove the agent/steering mirror emit.
  • agent/stream-chunk — the live token stream. Out of scope for THIS decision (a mirror of the durable assistant/chunk, not a boundary), it was removed by its own follow-up: Stop mirroring the token stream as an agent event.
  • agent/created, agent/disposed, agent/status, agent/error, agent/queued — lifecycle/control events that are not transcript data. agent/queued in particular is an inbox acknowledgement that fires before any durable event exists (cancelled queued work may never enter the log), so it is deliberately live-only.

Alternatives considered

  • Bundling agent/steering into the removal — the original proposal's shape; narrowed out as scope creep: it mirrors the durable steering/message control record, not a boundary, and was removed by its own later decision (as was agent/stream-chunk, by the stream-chunk-mirror Agent Note).
  • Keeping the turn mirrors for the stdio UIthe event-domain-semantics Agent Note's original stance; rejected here because dsh-ui-stdio is a disposable test REPL, not a load-bearing consumer, and it renders boundaries from session/event plus its live target object instead.

Consequences

A plugin can no longer observe turn/step boundaries from a convenient Agent-first event. It subscribes to session/event and, if it needs the live object, resolves the shared id through ctx.agents or retains the object it already owns. That is an acceptable trade: boundary consumers should not depend on a second event feed that can drift from the durable log.