Reconciling Lifecycle
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`.Optional: hand this page to your coding agent
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)Reconcile — cloud → code
Section titled “Reconcile — cloud → code”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.
Apply — code → cloud
Section titled “Apply — code → cloud”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.
See also
Section titled “See also”lifecycle-reconcile-awsexample — the whole loop on a real stack: deploy -> drift -> diff -> reconcile / apply- Live Import — the cloud->code regeneration the reconcile workflow uses
- Ops —
ReconcileOpandApplyOpin depth - Reconciliation — the dial as per-environment configuration, and when to pick reconcile vs. apply vs. observe