* feat(W-024): reboot_event audit trail entry (L10b.3, ADR-0158) L10 scope §Sub-question 3: a reboot_event analog of TurnEvent, written to the telemetry JSONL, lets future audit reconstruct when this engine instance lost and regained its lifetime. - serialize_reboot_event / format_reboot_event_jsonl in chat/telemetry.py emit type="reboot" with restored_turn_count, stored/current revisions, revision_matched, recognizers_count, candidates_count - ChatRuntime._load_engine_state() buffers the JSONL line in _pending_reboot_payload (str|None); ChatRuntime.attach_telemetry_sink() flushes it exactly once when a sink is first attached - Reboot event precedes all turn events in the session audit stream - Pinned by 11 tests: serializer structure, determinism, revision_matched logic, runtime integration (emit-once, no-checkpoint, no-load-state, revision match, ordering) Closes L10b: W-022 (atomic writes) + W-023 (revision warning) + W-024 together satisfy ADR-0146's atomic/observable/auditable checkpoint triad. * fix(W-024): expose cached public git revision helper
3.8 KiB
ADR-0158 — reboot_event audit trail entry (W-024 / L10b.3)
Status: accepted Date: 2026-05-26
Context
L10 scope §Sub-question 3 asked:
What does reboot record? A
reboot_eventanalog ofTurnEvent, written to the audit trail, that lets future audit reconstruct the fact that this engine instance lost and regained its lifetime here.
ADR-0156 §"Out of scope" deferred this to L10b.3 / W-024. After W-022 (atomic writes) and W-023 (revision-mismatch warning), the checkpoint path is now correct and observable. The missing piece is the audit record itself: without it, the telemetry JSONL cannot distinguish a fresh start from a reboot recovery, making the engine's lifetime history uninterpretable.
Decision
Serializer (chat/telemetry.py)
Add serialize_reboot_event(...) and format_reboot_event_jsonl(...),
following the same pattern as serialize_correction_event / ADR-0059:
{
"type": "reboot", // discriminator
"restored_turn_count": 5, // manifest["turn_count"]
"stored_revision": "abc123", // manifest["written_at_revision"]
"current_revision": "def456", // _git_revision() at load time
"revision_matched": false, // true iff both known and equal
"recognizers_count": 2, // DerivedRecognizers restored
"candidates_count": 1, // DiscoveryCandidates restored
"timestamp": "..." // optional, caller-provided
}
revision_matched is False when either side is "unknown" or "" —
the same guard as W-023's warning suppression, keeping the two features
consistent.
Runtime wiring (chat/runtime.py)
The telemetry sink is None at __init__ time — attached later via
attach_telemetry_sink(). Two-step buffered emission:
-
_load_engine_state()— builds and stores the JSONL string inself._pending_reboot_payload: str | None. The string is the fully serialized line; no further computation required at flush time. -
attach_telemetry_sink(sink)— if_pending_reboot_payloadis set andsinkis not None, emits the line and clears the field. Subsequent calls toattach_telemetry_sink(e.g. replacing the sink) do NOT re-emit; the payload is consumed exactly once.
This placement guarantees the reboot event appears before any turn events in the audit stream for the session, matching the semantic intent of "lifetime boundary record".
Trust boundary
Metadata only — no surface text, no tokens, no versor coordinates.
_git_revision() returns a short SHA derived from the local git repo;
already used for the manifest and W-023 warning.
Invariants pinned by tests
tests/test_adr_0158_reboot_event.py (11 tests):
Serializer:
- Structure, fields, and
revision_matchedlogic (match / mismatch / unknown) - Optional
timestampfield - Byte-identical output for identical inputs (determinism)
Runtime integration:
- Reboot event emitted to sink on
attach_telemetry_sinkafter checkpoint load - Emitted exactly once — second
attach_telemetry_sinkcall does not re-emit - No event when no checkpoint directory exists (fresh start)
- No event when
no_load_state=True revision_matched: truewhen revisions agree- Reboot event precedes synthetic turn events in the stream
L10b closure
With W-024, the L10b sequence is complete:
| Chunk | Work item | ADR | What it does |
|---|---|---|---|
| L10b.1 | W-022 | ADR-0156 | Atomic checkpoint writes (write-temp + fsync + rename) |
| L10b.2 | W-023 | ADR-0157 | Revision-mismatch RuntimeWarning on load |
| L10b.3 | W-024 | ADR-0158 | reboot_event audit trail entry |
The checkpoint path now satisfies all three of ADR-0146's runtime-model commitments: atomic, observable (warning on staleness), and auditable (reboot recorded in the telemetry stream).