Files
deepseek-harness/docs/rfc/rejected/simplification/2026-06-20-drop-durable-step-boundaries.md
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

2.7 KiB

RFC: Drop durable step boundary events

Status: rejected — step/end is the durable indication that a model step finished, and keeping the symmetric step/start / step/end pair makes crash repair, invariants, and transcript inspection clearer than inferring completion from adjacent step-scoped events.

Problem

The session log stores step/start and step/end events even though every step-scoped event already carries { turn, step }: assistant chunks, assistant messages, tool calls, tool results, usage, and errors. deriveMessages() ignores step boundaries, ACP ignores them for UI, and the main consumers are invariants, tests, snapshot goldens, and crash repair.

The rejected argument was that boundary events make the log more ceremonial than informative. In practice, step/end is concrete information: a reader can tell whether a model request finished, crashed, or is being repaired without deriving that state from the next event. A bare step/start is likewise useful for a model request that began but produced no chunks before failing.

Proposal

Make the turn the only durable boundary. Remove step/start and step/end from SessionEventMap; keep the numeric step field on events that need grouping. The loop increments the step counter and records step-scoped events with that number, but it no longer appends open/close boundary events. Consumers infer step groups from contiguous events sharing (turn, step).

The invariants plugin should enforce that step-scoped events have valid positive step numbers within an open turn, not that separate boundary records surround them. Crash repair should not synthesize step/end; if an interrupted turn is preserved, the repair path can still close the turn without inventing step boundary records.

Acceptance criteria

  • SessionEventMap no longer includes step/start or step/end.
  • The loop has no closeStep() finalization path.
  • ACP snapshots and persistence contract fixtures stop expecting step-boundary lines.
  • deriveMessages() and replay derive the same message history from step-scoped events.
  • The event taxonomy docs describe turns as the durable boundary and steps as a field on step-scoped records.
  • The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.

What we give up

The log no longer records "a model request started but produced no event before the process died" as a durable fact, and no longer has an explicit "this step completed" marker. That loss is not acceptable while the session log is the durable replay and audit surface.