I0804 — Close a session into an envelope derived from the ledger
Implement `session close`: read the active record, derive the episode's references from the ledger between its start and now, write an immutable closed record with computed end-state git facts and an optional authored summary body, remove the active record, and append the close event to the ledger.
DONE wave 2 · p1 · deep-work profile · runs alone
Part of M003 — A session is a durable envelope, and knowledge is compiled from what is already canonical.
Objective
Implement `session close`: read the active record, derive the episode's references from the ledger between its start and now, write an immutable closed record with computed end-state git facts and an optional authored summary body, remove the active record, and append the close event to the ledger.
Why
This is the mechanism that makes the session an envelope rather than a narrative. Nothing accumulates references during the episode, so no command outside session pays for them; and because the ledger is append-only and machine-written, the envelope is a derivation of existing evidence rather than a second account of it.
Current state
The ledger records task, checkpoint, handover, finish, decision, question, plan and maintenance events, each with a timestamp, a branch and a head. Nothing reads a time range out of it.
Desired state
A closed record under .majordomus/state/sessions/ carries the computed identity, started_at and closed_at, start and end head and working tree, the outcome, and reference lists of task ids, issue ids, milestone ids, checkpoint paths, handover paths, decision entries and question ids observed in the ledger window. It copies no body. An authored summary on stdin is optional and is stored as the record body, subject to the identity-field refusal every other record uses.
Scope
- lib/session.sh
- test/cases/60_session_lifecycle.sh
- test/cases/61_session_envelope.sh
Out of scope
- Reading a closed record back, which is its own issue
- Retention and archiving
- Any copying of a checkpoint, handover or decision body into the session file
Dependencies
What waits on this
Acceptance criteria
- Closing writes an immutable record, removes the active record, and appends one ledger event
- The reference lists match the events the ledger holds in the window, and nothing outside the window appears
- An episode that spans two tasks names both; a task that spans two sessions is named by both
- No body of any referenced record appears in the session file, and a case asserts that
- An authored summary containing an identity field is refused, as it is for a checkpoint
Validation
- bash test/run.sh 60_session_lifecycle
- bash test/run.sh 61_session_envelope
Evidence required
- session_closes
- envelope_is_references
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| session_closes | test | test/run.sh 61_session_envelope | 1 passed. Closing writes an immutable record, removes the open one and appends exactly one session.closed event; closing with nothing open exits 12; a summary carrying an identity field exits 10 and leaves the open record in place; a second close writes a new file and leaves the earlier record byte-identical; every record is mode 0600 | 7ca51d2 |
| envelope_is_references | test | test/run.sh 61_session_envelope | 1 passed, and the mutation back to a time window fails it. The case proves the record names checkpoint, handover and decision references and carries none of their bodies, that every referenced state path resolves, that an episode across two tasks names both while a task across two sessions is named by both, and that an event stamped by another session or by none is excluded. The first live run of the time-window implementation collected the other worker's records into my envelope; selection is now by the session stamp each ledger line carries | 7ca51d2 |
Risk
The ledger window is bounded by timestamps with second resolution, so two episodes that abut within one second could each claim an event. The ledger's line order is the existing tiebreak for exactly this and has to be used rather than the timestamps alone.
Timeline
- started
- 2026-09-04T17:26:46Z
- verified
- —
- completed
- 2026-09-04T17:38:33Z
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I0804.yaml. Read it back with majordomus plan show I0804.