I3320 — A lifecycle command run against an absent or stale executable fails loudly and never falls back to a shell writer
Give the lifecycle commands (session open and close, the provider event shims, checkpoint and handover) one resolution of the Rust executable with a declared lifecycle contract version: with no resolvable executable, or one older than the tree's contract, the command writes a typed provider.event.failed naming the cause, leaves the episode open, exits non-zero (the hook shims stay fail-open for the provider), and nothing falls back to a shell writer.
PŘIPRAVENO vlna 0 · p1 · profil implementation · bezpečné souběžně
Připraveno. Všechny závislosti jsou hotové, takže majordomus plan start I3320 bude přijato.
Cíl
Give the lifecycle commands (session open and close, the provider event shims, checkpoint and handover) one resolution of the Rust executable with a declared lifecycle contract version: with no resolvable executable, or one older than the tree's contract, the command writes a typed provider.event.failed naming the cause, leaves the episode open, exits non-zero (the hook shims stay fail-open for the provider), and nothing falls back to a shell writer.
Proč
Split out of I3141 on 2026-10-18 (verification: I3141 was several pieces of work). The critique of a-session-record-says-what-it-served found that after the cutover the closer, the opener and the hooks need the executable, that lib/rust_bin.sh documents Rust-backed features that degraded in silence when it was missing, and that an old binary would close with old semantics. A fallback to shell would be a second writer, so this is part of close-has-one-writer. Sources: chatgpt-majordomus:c109#3.
Aktuální stav
lib/rust_bin.sh resolves MAJORDOMUS_BIN, then libexec/, then target/debug, with no contract version; test/lib.sh rust_bin returns 3 and a case skips without an executable; bash test/run.sh --no-skips already fails a declining case.
Cílový stav
One resolution with a contract check that every lifecycle command calls first; a missing or stale executable is a typed, visible failure that health reports.
Rozsah
- lib/rust_bin.sh
- lib/common.sh
- apps/majordomus-cli/src/build_identity.rs
- share/events.yaml
Mimo rozsah
- Building the executable from a hook
- Changing the provider's fail-open contract
Závislosti
Žádné. Tento úkol je kořenem grafu.
Co na tom čeká
- I3141BLOCKED majordomus session close writes its record through the typed domain's closer, and the shell closer is deleted
- I3142BLOCKED An episode opens through the typed domain and records the task and issue it began on
- I3143BLOCKED Each provider lifecycle event leaves a typed receipt written by the domain before any guard can return early
Kritéria přijetí
- The lifecycle contract version is declared once and the executable reports the version it implements
- With MAJORDOMUS_BIN pointing at nothing, and with an executable reporting an older contract, a lifecycle command writes provider.event.failed naming the cause, the episode stays open, and no shell writer runs; a case covers both
- health reports the failed event
- Each new case named here takes its number from `scripts/case-numbers next` and is announced on the board before the file is written (ADR 0053); a case number written in this record before that is provisional
Validace
- bash test/run.sh --no-skips 54_session_lifecycle_hooks 220_exactly_once_close
- cargo test --manifest-path apps/majordomus-cli/Cargo.toml --test session_domain
Požadované důkazy
- case_a_lifecycle_command_refuses_a_stale_executable
Důkazy
Nic nezaznamenáno. Za každým tokenem výše musí stát příkaz nebo artefakt, než lze tento úkol dokončit; vyprávění se odmítá.
Časová osa
- zahájeno
- —
- ověřeno
- —
- dokončeno
- —
Tato tři pole, důkazy výše a stav závislostí jsou vše, z čeho se stav skládá. Neexistuje žádné pole stavu, které by s nimi mohlo být v rozporu.
Canonical record: .ai/repo/project/issues/I3320.yaml. Read it back with majordomus plan show I3320.