Skip to content

Mutation aftercare

Mutation aftercare is the commit that settles one reserved workspace effect into the session change contract. It records that the reserved observation was ingested. It does not grade the edit, accept a Patch Proposal, or decide a parent outcome.

There is no bmo aftercare command. Operators meet aftercare when a tool reports persist mutation aftercare, or when a later mutation is refused because that reservation is still pending.

This is not Auto-Checkpoint. Auto-checkpoint is an optional git commit. It defaults to off. A git commit does not retire a change-contract reservation, and disabling auto-checkpoint does not skip aftercare. Session checkpoints (bmo checkpoint create or the Sessions dialog) snapshot conversation state. They do not ingest a file effect. bmo checkpoint undo does not rewrite the reservation ledger.

Maturity: Implemented for the memory owner (the default) and the optional file owner. Both admit participating mutations through effect-observation tickets when a change-contract generator is present. Opaque observation IDs remain a compatibility seam for a store that does not implement that protocol.

Use the error text to tell them apart.

ProtocolWho uses itPending-mutation error
Effect observationMemory and file owners, when a generator is present. Direct write, edit, and multiedit, semantic edits, staged acceptance, shell effects, capsule source operations, and proposal apply take this path.effect observation requires reconciliation
Opaque observation IDA store that only implements the older durable mutation seam. The same tools fall back to it when the store has no effect-observation owner.change contract mutation requires reconciliation

An opaque ID does not substitute for a source-bound ticket. Replaying a retired ticket is a no-op. A tool-call string or a copied ticket cannot create a reservation.

The next mutation’s refusal is prefixed change contract mutation admission:. A sticky storage failure is separate: the tool is blocked with change contract snapshot write failed: and the owner error change contract persistence failed. A matching successful aftercare commit clears its own fault. Other storage faults remain until that owner is reopened.

  1. Reserve. Before the effect, the source owner asks the contract owner for a ticket bound to the session, the declared file or workspace scopes, and an aftercare budget. The execution owner keeps that ticket.
  2. Perform the effect. The tool writes only after the reservation exists. A refusal that proves no effect entered can retire the unused ticket. An unclassified error before the write leaves the ticket pending.
  3. Observe. The source owner records the source state, or explicit uncertainty, while it still holds the source boundary. Observation advances the source epoch. Unknown shell or remote state stays unknown.
  4. Commit aftercare. Commit checks that same ticket against the current epochs and merges the contract evidence. Success retires the ticket. Failure leaves it pending and recoverable by that original ticket only.
  5. Readiness. The next mutation in the session requires no pending ticket and no sticky persistence fault.

A commit that does not ingest the reserved observation fails. The tool surfaces that as persist mutation aftercare. On a direct edit whose formatting, after-hook, or history step fails after the write, the owner records protected review evidence and may instead report persist failed direct mutation evidence. Failed owner persistence is not retried by another write.

On the compatibility seam the store records a pending intent for one session and observation ID before the effect. Grouped admission reserves every ID in one snapshot or reserves none of them. Aftercare retires that intent only when the callback ingests the reserved observation. A duplicate ID is rejected, including after aftercare has succeeded.

Aftercare without a pending intent fails with change contract mutation intent missing. A callback that does not ingest the reserved observation fails with aftercare did not ingest the reserved observation. The intent stays pending.

Optional git auto-checkpoint, LSP notification, and optional ripple analysis run beside contract ingest. They fail differently:

Follow-upFailure meaning
Auto-checkpointA warning prefixed Auto-checkpoint warning:. The git commit did not land. The change-contract reservation is a separate result.
LSP notification and rippleDiagnostic and suggestion context on the tool result. They do not accept the edit or complete a parent goal.
Contract ingestpersist mutation aftercare or persist failed direct mutation evidence. The reservation stays pending until that original ticket commits.

Independent verification is a different record. A passing check does not ingest aftercare. Aftercare does not satisfy a verifier or a parent outcome.

contract_list_obligations and contract_preflight_report stay available, including in a prepared targeted-verifier corridor for the current session. Listing does not settle a ticket, satisfy an obligation, or grant another write. contract_waive_obligation is refused with the same admission error while a ticket is pending.

A successful obligations list is JSON with session_id, generation_id, and an obligations array (id, kind, status, target, explanation). Pending aftercare does not add a ticket field to that JSON. A list that succeeds while the next edit returns change contract mutation admission: effect observation requires reconciliation means the ticket is still pending. no active change contract for this session means nothing has been ingested. It does not prove that no reservation exists.

contract_preflight_report returns a dry report (paths, surfaces). It does not run the suggested commands.

Read the workspace diff before assuming the contract matches the files. A pending reservation does not roll the file back.

Staged acceptance is the mutation source with an explicit retry. When the tool says pending staged effect; use recovery_only=true for aftercare, call staged_accept or staged_accept_all with recovery_only set to true. That retry commits the observed ticket. It does not write files, run validation, or run follow-up. A clean batch returns status reconciled. See Troubleshooting for the refusal text, and the file-owner runbook in the repository for the per-source recovery matrix.

A proposal apply that returns persist proposal mutation aftercare marks the proposal conflicted even when the workspace write completed. Startup reconciliation can commit the journaled ticket when the workspace image matches. It does not clear the conflict. Inspect with bmo workstream proposals get <proposal-id> and bmo workstream proposals diff <proposal-id>.

Aftercare has no operator switch. It runs when the session has a change-contract owner. The default owner is in memory for the process lifetime. File mode persists reservations under <data_directory>/change-contracts:

[options]
change_contract_enabled = true
change_contract_store_mode = "file"
data_directory = "/chosen/local/data"

change_contract_store_mode defaults to memory. File mode needs an explicit data directory chosen in the user config or a pinned config file. An active App refuses a mode or directory change. Switching to memory does not hide a saved snapshot.

Auto-checkpoint is the configurable neighbor:

[options.auto_checkpoint]
enabled = false

enabled defaults to false. Turning it on does not change reservation, ingest, or readiness.

  • Memory owners drop pending tickets when the process exits. The workspace file may still have changed. A new process does not inherit that reservation, and a missing contract is unknown, not satisfied.
  • File owners keep pending tickets across restart. Restart alone does not commit them.
  • Aftercare does not accept a Patch Proposal or decide a parent outcome.
  • A successful tool message that includes an auto-checkpoint warning is not evidence that the git commit succeeded.
  • Deleting state.json, pointing the App at a new data directory, or repeating the edit does not retire a pending ticket.