Skip to content

I3141 — majordomus session close writes its record through the typed domain's closer, and the shell closer is deleted

ADR 0047 cutover step 1: route the shell's session close to session::store close, keeping the shell's arguments, human output and exit codes, and delete the shell writer and its lock path.

BLOCKED wave 1 · p1 · implementation profile · parallel safe

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

Blocked. This issue cannot start until I3320 is done. The status is derived from that, not declared.

Objective

ADR 0047 cutover step 1: route the shell's session close to session::store close, keeping the shell's arguments, human output and exit codes, and delete the shell writer and its lock path.

Why

ADR 0047 recorded the closer twice: lib/session.sh writes under mj_lock_take (case 220 holds it), and apps/majordomus-cli/src/session/store.rs has a close() reachable only from the library. Two implementations of one verdict drift: the 2026-10-12 shell reading of the model reported 318 drift findings where the Rust executable reported 26 (.github/workflows/github.yml). Split on 2026-10-18 (verification): the executable contract every lifecycle command relies on is I3320, so this issue is the closer cutover alone. Proposed in ADR 0184 (Every command name is implemented by exactly one executable, and the other only delegates) (proposed; it carries no authority until the owner accepts it). Sources: chatgpt-majordomus:c109#3.

Current state

lib/session.sh owns close; session/store.rs close() has no caller; case 220_exactly_once_close drives three concurrent SessionEnd shims.

Desired state

One closer, in Rust; case 220 unchanged and green; the shell function is argument parsing and rendering.

Scope

  • lib/session.sh
  • apps/majordomus-cli/src/session
  • apps/majordomus-cli/src/cli.rs
  • share/commands.yaml
  • .ai/repo/automation/inventory.jsonl
  • test/cases/220_exactly_once_close.sh
  • apps/majordomus-cli/src/commands/mod.rs
  • apps/majordomus-cli/src/capability/builtin/session_domain.rs
  • apps/majordomus-cli/tests/session_domain.rs
  • docs/generated/cli.yaml

Out of scope

  • session start, checkpoint and handover (I3142, I3144)
  • The executable-resolution contract itself (I3320)

Dependencies

What waits on this

Acceptance criteria

  • case 220 passes unchanged against the new closer
  • Exit codes and machine output of session close are unchanged
  • No close logic remains in lib/session.sh
  • bin/majordomus hands session close to the executable by reading the generated docs/generated/cli.yaml at run time, as every delegated path is (rule project.commands-are-projections, bin/majordomus:100-103); no delegation line is generated into or hand-written in bin/majordomus

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
  • cargo test --manifest-path apps/majordomus-cli/Cargo.toml --test session_domain
  • cargo test --manifest-path apps/majordomus-cli/Cargo.toml --lib session::

Evidence required

  • case_close_asks_the_domain

Evidence

None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.

Timeline

started
—
verified
—
completed
—

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/I3141.yaml. Read it back with majordomus plan show I3141.

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