Skip to content

the-session-domain-is-written-once — The session lifecycle is written by the typed domain, and the shell only parses and renders

Each lifecycle transition has one implementation in the typed domain; the shell commands keep their arguments, output and exit codes and delegate; a closed record states whether it is complete.

PLANNED 0 of 9 issues done

ready
1
blocked
8
active
0
verify
0
done
0
cancelled
0

Problem

ADR 0047 built the typed session domain (apps/majordomus-cli/src/session/) additively and recorded a six-step cutover; none of the steps has happened. At 707a04c the lifecycle is still written by lib/session.sh (1341 lines), lib/capture.sh (2821 lines; wc -l at 707a04c), lib/checkpoint.sh and lib/handover.sh, while session/store.rs has a close() no command calls. Freshness exists twice (mj_freshness in lib/common.sh and Freshness in capability/builtin/continuity.rs, both reading session.freshness), and continuity resolution exists twice (mj_resolve_latest and continuity.rs). Records still close with tasks: [] and issues: [] when a session worked on a task, because the task an episode began on is not recorded as a ledger fact of that episode when it was started in an earlier one. Proposed in ADR 0182 (A closed session record is judged by what it can attribute, not only by its schema) (proposed; it carries no authority until the owner accepts it). Sources: chatgpt-majordomus:c097#2, chatgpt-majordomus:c116#7.

Outcome

Each lifecycle transition has one implementation in the typed domain; the shell commands keep their arguments, output and exit codes and delegate; a closed record states whether it is complete.

Current state

Verified on master 707a04c: exactly-once close is enforced in shell under a lock (case 220_exactly_once_close, I1503 DONE); the record body is composed from the ledger (.ai/repo/sessions/README.md, session.record_sections); the six-day-outage fixture is case 130_lifecycle_survives_a_stale_task; per-provider episodes are case 111. The automation inventory classifies the session libraries as disposition A; that label is hand-authored and shell check validates only its form. Cases that need the executable skip without one unless the run passes --no-skips (test/run.sh). No completeness verdict exists.

Desired state

ADR 0047 steps 1-6 landed one PR each; lib/session.sh, lib/checkpoint.sh and lib/handover.sh are argument parsing and rendering; one freshness function and one resolver; a completeness verdict derivable for every closed record and shown where a resuming session reads.

Scope

  • apps/majordomus-cli/src/session
  • apps/majordomus-cli/src/continuity
  • apps/majordomus-cli/src/capability/builtin/continuity.rs
  • apps/majordomus-cli/src/commands
  • lib/session.sh
  • lib/capture.sh
  • lib/checkpoint.sh
  • lib/handover.sh
  • lib/common.sh
  • .claude/hooks
  • .ai/repo/automation/inventory.jsonl
  • share/schemas/majordomus/session-record
  • docs/CONTINUITY.md
  • lib/rust_bin.sh
  • lib/derive.sh
  • lib/changed.sh

Out of scope

  • A second session store or a session/v2 schema island
  • Changing what a record contains beyond the completeness verdict and the task snapshot
  • Prompt capture, which already joins episodes and redacts (lib/capture.sh)

Acceptance criteria

  • Each of ADR 0047's six cutover steps is one landed change, each independently revertible
  • bash test/run.sh --no-skips 54_session_lifecycle_hooks 60_session_lifecycle 61_session_envelope 62_session_divergence 63_session_records 111_session_per_provider 130_lifecycle_survives_a_stale_task 135_session_store_recovery 220_exactly_once_close passes after every step
  • No lifecycle semantic remains in shell, proved by I3145's structural case; the inventory disposition follows the cutover and is not its proof
  • Every closed record's completeness verdict is derivable, never stored, and reaches the surfaces a resuming session reads

Validation

  • bash test/run.sh --no-skips 54_session_lifecycle_hooks 60_session_lifecycle 61_session_envelope 62_session_divergence 63_session_records 111_session_per_provider 130_lifecycle_survives_a_stale_task 135_session_store_recovery 220_exactly_once_close
  • majordomus plan validate

Risks

  • Several workers edit lib/*.sh at once; ADR 0047 warns a cutover on such a day is a semantic merge conflict. Claim each step on the mesh before starting.
  • Hook shims must stay fail-open for the provider (inventory facts for .claude/hooks/*).

Issues

Each one is an execution contract with its own acceptance criteria, validation command and required evidence. The wave column is the layer of the dependency graph it sits in.

issuestatuswavedepends ontitle
I3141 BLOCKED 1 I3320 majordomus session close writes its record through the typed domain's closer, and the shell closer is deleted
I3142 BLOCKED 2 I3141, I3320 An episode opens through the typed domain and records the task and issue it began on
I3143 BLOCKED 3 I3142, I3320 Each provider lifecycle event leaves a typed receipt written by the domain before any guard can return early
I3144 BLOCKED 3 I3142 Checkpoints and handovers are recorded against the episode, and the shell only parses and renders them
I3145 BLOCKED 4 I3143, I3144 Continuity is resolved and judged fresh by one function, and the shell's copies are deleted
I3146 BLOCKED 3 I3142 A closed session record says whether it is complete, and one that changed the tree without saying what it served is not
I3311 BLOCKED 3 I3142 Generated sequences of episode events never move an episode in a way its transition table forbids
I3313 BLOCKED 4 I3146 A failing verification in a closed session record says whether the ledger already held it failing at the task's base
I3320 READY 0 — A lifecycle command run against an absent or stale executable fails loudly and never falls back to a shell writer

The graph

the-session-domain-is-written-once dependency graphfrom .ai/repo/project
flowchart LR
    I3141["I3141<br/>majordomus session close writes its record through the typed domain's closer, and the shell closer is deleted"]:::blocked
    I3142["I3142<br/>An episode opens through the typed domain and records the task and issue it began on"]:::blocked
    I3143["I3143<br/>Each provider lifecycle event leaves a typed receipt written by the domain before any guard can return early"]:::blocked
    I3144["I3144<br/>Checkpoints and handovers are recorded against the episode, and the shell only parses and renders them"]:::blocked
    I3145["I3145<br/>Continuity is resolved and judged fresh by one function, and the shell's copies are deleted"]:::blocked
    I3146["I3146<br/>A closed session record says whether it is complete, and one that changed the tree without saying what it served is not"]:::blocked
    I3311["I3311<br/>Generated sequences of episode events never move an episode in a way its transition table forbids"]:::blocked
    I3313["I3313<br/>A failing verification in a closed session record says whether the ledger already held it failing at the task's base"]:::blocked
    I3320["I3320<br/>A lifecycle command run against an absent or stale executable fails loudly and never falls back to a shell writer"]:::ready
    I3141 --> I3142
    I3142 --> I3143
    I3142 --> I3144
    I3142 --> I3146
    I3142 --> I3311
    I3143 --> I3145
    I3144 --> I3145
    I3146 --> I3313
    I3320 --> I3141
    I3320 --> I3142
    I3320 --> I3143
    classDef done stroke:#16a34a,fill:#dcfce7,color:#14532d,stroke-width:2px
    classDef active stroke:#2563eb,fill:#eff6ff,color:#1e3a8a,stroke-width:2px
    classDef verify stroke:#7c3aed,fill:#f5f3ff,color:#4c1d95,stroke-width:2px
    classDef ready stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
    classDef planned stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
    classDef blocked stroke:#b45309,fill:#fffbeb,color:#78350f,stroke-width:2px
    classDef cancelled stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2px
    classDef superseded stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2px

Canonical record: .ai/repo/project/milestones/the-session-domain-is-written-once.yaml. Read it back with majordomus plan show the-session-domain-is-written-once.

Searches the titles and descriptions of every page this site publishes.