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.
This commit is contained in:
+15
-16
@@ -1,6 +1,6 @@
|
||||
# RFC: Fold trace-only session facts into load-bearing events
|
||||
|
||||
Status: implemented (proposed and accepted 2026-06-20)
|
||||
Status: implemented
|
||||
|
||||
## Problem
|
||||
|
||||
@@ -8,27 +8,26 @@ The session event vocabulary includes first-class events that are not part of re
|
||||
|
||||
These events make the canonical transcript look more useful as telemetry than it currently is. They add event variants, invariants, tests, snapshots, and persistence cases, but they are not load-bearing as separate records. The facts they carry can still be useful: token usage should remain available for accounting, and an error's step number should not silently disappear. The simplification is to fold those facts into nearby events consumers already must understand, not to record less information.
|
||||
|
||||
## Proposal
|
||||
## Decision
|
||||
|
||||
Remove standalone trace-only events only where their information can be preserved without a parallel record:
|
||||
Standalone trace-only events are removed exactly where their information is preserved without a parallel record:
|
||||
|
||||
- Fold successful-step usage into the matching `assistant/message`, e.g. `assistant/message { turn, step, content, usage? }`, so the assembled model output and its accounting travel together.
|
||||
- For a failed or aborted step that has usage but no `assistant/message`, carry the usage on the terminal turn reason or another load-bearing failure record in the same turn. The implementing design must prove no usage chunk that is currently persisted becomes unrepresented.
|
||||
- Fold the step number from the standalone `error` event into `turn/end.reason` for `kind: 'error'`, e.g. `{ kind: 'error', step, message, code? }`. `turn/end` is the durable turn outcome ACP and resume already consume.
|
||||
- Keep `agent/error` and logging for live diagnostics; do not add a second session-log error record after `turn/end`.
|
||||
- Successful-step usage folds into the matching `assistant/message` (`assistant/message { turn, step, content, usage? }`), so the assembled model output and its accounting travel together.
|
||||
- A failed or aborted step that has usage but no assistant content carries the usage on an empty-content `assistant/message` (the implementation note below carries the no-information-loss proof) — no persisted usage chunk goes unrepresented.
|
||||
- The step number from the standalone `error` event folds into `turn/end.reason` for `kind: 'error'` (`{ kind: 'error', step, message, code? }`) — `turn/end` is the durable turn outcome ACP and resume already consume.
|
||||
- `agent/error` and logging stay for live diagnostics; there is no second session-log error record after `turn/end`.
|
||||
|
||||
If analytics become real, add a projection helper or a dedicated telemetry store with its own retention policy. The user conversation log should contain what is needed to render, resume, audit, and account for the interaction without requiring consumers to reconcile duplicate trace rows.
|
||||
The user conversation log contains what is needed to render, resume, audit, and account for the interaction without consumers reconciling duplicate trace rows.
|
||||
|
||||
## Acceptance criteria
|
||||
## Alternatives considered
|
||||
|
||||
- `SessionEventMap` drops standalone `usage` and `error` only after their fields are represented on load-bearing session events.
|
||||
- The loop no longer appends a separate `usage` event for a usage chunk.
|
||||
- The loop records durable failures through `turn/end { kind: 'error', step, message, code? }` or an equivalent no-information-loss shape and reports live diagnostics through `agent/error`.
|
||||
- ACP snapshots and persistence tests stop asserting trace-only lines.
|
||||
- Documentation explains exactly where token usage and operational errors are observed.
|
||||
- Recorded fixtures are refreshed for the new event shape; the session format version stays pinned at `0` (unstable/pre-release) and backends reject any non-`0` stored log per the pre-release format policy.
|
||||
**Keep the standalone rows as telemetry** — the events made the canonical transcript look more useful as telemetry than it was, at the cost of event variants, invariants, tests, snapshots, and persistence cases nothing consumed. If analytics become real, the shape is a projection helper or a dedicated telemetry store with its own retention policy — not duplicate trace rows in the conversation log.
|
||||
|
||||
## What we give up
|
||||
## Verification
|
||||
|
||||
`SessionEventMap` carries no standalone `usage` or `error`; the loop appends no separate usage event and records durable failures through `turn/end { kind: 'error', step, message, code? }`; ACP snapshots and persistence tests assert no trace-only lines; recorded fixtures are on the new event shape with the session format version pinned at `0` (backends reject any non-`0` stored log per the pre-release format policy); and the docs state where token usage and operational errors are observed.
|
||||
|
||||
## Consequences
|
||||
|
||||
A consumer can no longer filter the canonical log for standalone `usage` or step-level `error` rows. It must read those facts from the assistant/failure events that carry them. That is a reasonable simplification only if the implementing PR proves the same facts remain present; otherwise the standalone events should stay.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user