Skip to content

Reconciling Lifecycle

llms.txt lists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/chant/guide/reconciling-lifecycle/.
Declare the `ReconcileOp` or `ApplyOp` I describe in an `*.op.ts` file, with `env`, `scope`, `onDrift`, `target`, `delete` and `gate` as I name them. Give every `ApplyOp` a gate.
Run `npx chant build` and `npx chant lint`, and `npx chant run --generate <forge>` if the Op has a schedule, and open a pull request.
Do not run either Op.
Never deploy to a real cloud account or shared environment (with `chant run`, `chant components` or a platform's own CLI), approve (a pull request review, `chant approve` or `chant run approve`), override a policy denial (`chant approve policy-override`), or merge; never cut or publish a release (`chant release cut --yes` or `chant release publish`), push or delete a `ci/*` tag (`chant ci tick` pushes them), or change the value of an existing flag; never touch `.chant/allowed_signers` or `chant/lifecycle`.

Drift detection tells you the cloud and your source disagree. Reconciliation closes the gap. There are two directions, and chant exposes each as an Op composite. Which one you use, if either, is the dial, set per environment.

observe → reconcile → authoritative
(report) (cloud→code (code→cloud
PRs) apply)

When someone has created or changed a resource by hand, pull the live state back into source. ReconcileOp does this by opening a PR:

import { ReconcileOp } from "@intentius/chant/op";
const { op } = ReconcileOp({
name: "prod-reconcile",
env: "prod",
schedule: "0 * * * *", // hourly
scope: { owned: true }, // only chant-owned resources
onDrift: "pull-request", // or "issue" | "comment" | "report"
});
export default op; // discovered by `chant run prod-reconcile`

Phases: Snapshot -> Plan -> Reconcile, where the Reconcile phase’s reconcilePr activity regenerates source (live import) and opens the PR in one step. Run chant run prod-reconcile for a one-shot reconcile here. For the continuous form, give it a schedule and hand it to whatever fires the cadence: a generated CI workflow, chant operator, or a fountain Steward. The PR’s diff is the regenerated TypeScript, so review is a normal code review.

onDrift: "comment" is the mode for a reconcile that runs on a pull request rather than on a cron: the body lands as one comment on the pull request that triggered the run, re-edited in place on each push, and the generated workflow costs contents: read plus pull-requests: write and nothing else. It needs that trigger, so the github Op generator refuses it by name on a cron or push trigger, and gitlab and forgejo refuse it outright.

Reconcile never mutates the cloud; it changes only source. That makes it the safer direction and a good first step past observation.

When the cloud should match source, ApplyOp pushes source to the live system (a Kubernetes server-side apply, a CloudFormation deploy, an ARM deployment, or a Grafana API apply, selected by target):

import { ApplyOp } from "@intentius/chant/op";
const { op } = ApplyOp({
name: "prod-apply",
env: "prod",
target: "kubectl",
delete: "gated", // "never" | "owned-only" | "gated"
gate: { gate: "approve-apply" },
});
export default op;

The platform holds the state; chant keeps no state file. Deletes use the target’s own delete path and are owned-only on every target: marker-scoped on kubectl, tag-scoped on arm, and bounded by the stack on cloudformation. See delete and what bounds it for the mechanism in each case. A destructive apply gets an approval gate and saga-style rollback; see Gates and compensation on a destructive apply for what each buys.

  • lifecycle-reconcile-aws example — the whole loop on a real stack: deploy -> drift -> diff -> reconcile / apply
  • Live Import — the cloud->code regeneration the reconcile workflow uses
  • Ops — ReconcileOp and ApplyOp in depth
  • Reconciliation — the dial as per-environment configuration, and when to pick reconcile vs. apply vs. observe

chant