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.
Two reservation protocols
Section titled “Two reservation protocols”Use the error text to tell them apart.
| Protocol | Who uses it | Pending-mutation error |
|---|---|---|
| Effect observation | Memory 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 ID | A 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.
Effect-observation lifecycle
Section titled “Effect-observation lifecycle”- 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.
- 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.
- 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.
- Commit aftercare.
Commitchecks 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. - 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.
Opaque observation IDs
Section titled “Opaque observation IDs”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.
What else runs beside a direct edit
Section titled “What else runs beside a direct edit”Optional git auto-checkpoint, LSP notification, and optional ripple analysis run beside contract ingest. They fail differently:
| Follow-up | Failure meaning |
|---|---|
| Auto-checkpoint | A warning prefixed Auto-checkpoint warning:. The git commit did not land. The change-contract reservation is a separate result. |
| LSP notification and ripple | Diagnostic and suggestion context on the tool result. They do not accept the edit or complete a parent goal. |
| Contract ingest | persist 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.
While an edit is blocked
Section titled “While an edit is blocked”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>.
Configuration
Section titled “Configuration”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 = truechange_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 = falseenabled defaults to false. Turning it on does not change reservation,
ingest, or readiness.
Limits
Section titled “Limits”- 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.