Expand description
Step-assignment pass (FR-3.4): 1-based step ordinals for semantic events.
The pass is a pure function over a slice of EventRows: it assigns each
event’s step in place. The store reads rows via
crate::store::Store::list_events, runs this pass, and writes the
ordinals back in one transaction
(crate::store::Store::assign_steps).
§Rules (FR-3.4)
terminal_outputevents are not steps →step = None.- Every other semantic event gets its own incrementing 1-based step, except
a
tool_resultwhosecorrelatespoints to atool_callpresent in the session: that result shares the call’s step (a call and its result are one step). - A
tool_resultwithcorrelates = None, or pointing to an id absent from the session (orphan / concurrent result-before-call from another source), gets its own step.
The pass is order-independent for correlation: it builds an id→step map of the calls in pass 1 and resolves deferred results against it in pass 2, so a result that sorts before its call (e.g. a tool_result block emitted ahead of the tool_use block in the same record, or concurrent sources) still resolves to the call’s step.
§Deviation (flagged, ADR-0002)
ADR-0001 said step ordinals are “derived at read time”. Storing them at
finalize (authoritative events.step column) + self-healing on
crate::store::Store::open is a deliberate deviation recorded in
docs/adr/0002-stored-step-ordinals.md: it makes hh list’s step count a
trivial COUNT(DISTINCT step), and self-heal repairs both a crashed
finalize and the attached-MCP-proxy late-event race on the next hh
invocation.
Functions§
- assign_
steps - Assign 1-based step ordinals to
eventsin place (FR-3.4). See the module docs for the rules. Idempotent: re-running on already-assigned rows yields the same ordinals.