Skip to content

The episode boundary is drawn below the model, and its working context is local

The episode boundary is drawn below the model, and its working context is local

majordomus://rule/majordomus.session-lifecycle@1active sessionproviderevidence

referenced by

Rationale

The same argument that puts prompt capture below the model puts the episode boundary there. A worker asked to open its own session opens one when it remembers to, which is never the episode that mattered — the one that ended in a crash, a compaction, or somebody closing the window. An instruction in a bootstrap file is a request, and a request that is honoured sometimes produces a record of sessions that went well and silence about the rest.

The provider knows. It fires an event when a sitting begins and another when it ends, and it fires them whether or not a model is in a position to notice. So the boundary is drawn there or it is fiction, and this rule is what keeps a repository from presenting the second as the first.

Freezing the context at the open follows from the boundary existing. .ai/local/session-contexts/ was named by the layer from the beginning and had no producer: a directory the skeleton creates and nothing fills is a promise the layer does not keep, and a reader cannot tell an empty store from an unimplemented one. What belongs in it is the one fact neither neighbour holds — the closed record says what the episode produced, the prompt archive holds the person's half of the exchange, and neither says what the worker was actually told when it began.

That document stays local for two reasons that are not the same one. It names this machine, and a fact about a disk is not a fact about the repository. And it is a snapshot of a projection: re-resolving it later gives a different document, so publishing it would publish something no surface can reproduce.

Required behaviour

A provider with lifecycle events has an adapter; one without is reported as unsupported and never assumed to be silent. Where a repository declares the wiring — an enforcement entry with wired_by: provider-hook:<provider>:session — the configuration names the shims this tool wrote, both are executable, and a synthetic payload driven through the end shim reaches the command. The dry run is what makes that safe to prove: everything on the path runs except the mutation, because closing somebody's open episode is not a price a diagnostic may charge.

Every event is idempotent, because the events themselves are: a start fires again on a resume and keeps the open episode rather than opening a second, an end with nothing open writes nothing and is not a failure, and a compaction with no active task records nothing.

Only the start event writes to standard output, and only the briefing the policy declares. The provider adds that output to the context it is about to build, which is the one moment at which a continuation record can reach a worker without the worker remembering to ask for it — and a continuation record nothing loads is a record nobody reads. What it may carry is bounded by session.briefing_budget_lines and limited to references, divergence labels, the blockers that refuse acceptance, and the one section of a handover a resuming worker acts on. It may never carry a conversation, and session.briefing_on_start: false restores the older silence for a repository that wants it. The end and compaction events write nothing to standard output: each fires inside a turn that is already under way, where output would alter what somebody is doing rather than furnish it.

This is the one exception to the layer's rule that nothing under local/ is loaded into a context implicitly, and it is narrow on purpose. That rule protects three things — no transcripts, no unbounded growth, and no fact about a disk becoming a fact about the repository — and a bounded, declared, transcript-free briefing at the episode boundary defeats none of them. What stays unconditional is the other half: nothing under local/ is published by a generator or served on a public surface, ever.

session start writes the working context of the episode and session close appends what the close knows to the same document; nothing else writes one, and the document is appended to rather than rewritten, so what a worker typed into it survives. Every open episode has one. Under the store, nothing is tracked by git and the ignore boundary covers it, every document opens with schema: session-context/v1, its front matter carries only the declared keys, and a field naming a transcript, a message list or a model's reply is a violation — the derived half is the builder's output and the authored half is a summary of the work, not of a conversation.

A failure to write a working context never costs an episode: it is logged beside the store, and a non-empty log is a failure of the repository, because otherwise the store stops filling in silence.

Failure behaviour

A violation is a FAIL finding under the category session, and the command that found it exits 10. Under watch the same violation is reported as drift and the command exits 11. An unwired provider is not a violation: a repository that declares nothing reports that it draws no boundary.

Verification

mj_validate_session_lifecycle decides the store's invariants and the wiring verifier decides the provider state, both dispatched from doctor, watch. The behavioural case test/cases/54_session_lifecycle_hooks.sh proves the wiring, the idempotence of every event, the briefing the start event writes and the silence of the other two; test/cases/55_session_context.sh proves the document's contract and the findings each mutation of it produces. ADR 0015 records the decision; ADR 0017 records the briefing and the compaction event.

Where this comes from

canonical file
.ai/repo/rules/vendor/majordomus/rules/session-lifecycle.v1.md
identity
majordomus.session-lifecycle@1
also served as
MCP resource majordomus://rule/majordomus.session-lifecycle@1 · majordomus entity show rule/majordomus-session-lifecycle-1 · GET /api/v1/entity?uri=majordomus://rule/majordomus.session-lifecycle@1
in the Cockpit
/cockpit/objects/rule/majordomus-session-lifecycle-1

This page is generated from the file above by scripts/generate-site-data; editing the page changes nothing, editing the file changes every surface. The relations above are edges of docs/generated/graph.json, which the executable writes from the same index.

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