Files
deepseek-harness/docs/rfc/implemented/simplification/2026-06-20-remove-agent-boundary-mirror-events.md
T
Tianyi Cui e6fad266a6 docs(rfc): define and enforce a uniform RFC format; adopt it across the corpus
Define the in-file RFC contract in docs/rfc/README.md § The file format:
the header block (`# RFC: <title>` plus a dateless Status enum
cross-checked against the lifecycle folder), the per-lifecycle body
skeleton (a Problem opener everywhere; Proposal/Alternatives considered/
Acceptance criteria/Risks in proposed/; present-tense Decision/
Consequences with proposal-era headings banned in implemented/; the
frozen proposal shape in rejected/), and a mandatory Alternatives
considered section with a date-fenced grandfather comment for pre-format
RFCs whose alternatives are not reconstructible from the record.

Enforce it with a new doc-sync gate, scripts/verify-rfc-format.ts, and
normalize all 112 RFCs to it: ~15 Status-line spellings collapse to the
enum, 29 Context openers become Problem, the 39 legacy-format XXX debt
markers are resolved and banned from reappearing, proposal-era sections
in implemented RFCs are rewritten to shipped reality (including the
web/fs/subagent seam RFCs' migration plans and test checklists, closing
the doc-tiers deferred-work item on the web seam), every RFC gains an
Alternatives considered section or the grandfather comment, and the
bilingual pair is re-mirrored and re-recorded.

Move the generated index tables out of README.md into a fully generated
docs/rfc/INDEX.md — gen-rfc-index now writes the whole file, and
verify-rfc-classification checks its freshness and rejects index-shaped
rows in the curated README — which makes room for the format contract to
live in the README front door instead of a separate FORMAT.md.

The decision record, and the first RFC written in the new format, is
docs/rfc/implemented/process/2026-07-05-uniform-rfc-format.md.
2026-07-05 22:58:25 +08:00

5.3 KiB

RFC: Stop mirroring durable boundaries as agent events

Status: implemented

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 the editor-facing transcript because a throwing peer listener can prevent later agent/* listeners from observing a boundary, while the session event was already appended. 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 (or its short id) at a boundary keeps a small map from session id to agent id built from agent/created/agent/disposed; dsh-ui-stdio does exactly this to label its [<agent> turn N] header, since the turn/start session event carries only the turn number. 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 RFC; that RFC KEPT the turn mirrors on the stated justification that the stdio UI needed the Agent handle at the turn boundary. This RFC 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 was migrated to session/event + the id map, and the turn mirrors were removed too.

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 RFC).
  • Keeping the turn mirrors for the stdio UIthe event-domain-semantics RFC'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 + the id map instead.

Consequences

A plugin can no longer observe turn/step boundaries from a convenient Agent-first event. It must either subscribe to session/event or maintain a session-to-agent association. That is an acceptable trade: boundary consumers should not depend on a second event feed that can drift from the durable log.