Skip to content

Contributing a 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/contributing/lifecycles/.
Write the lifecycle I describe as an Op composite in `*.op.ts`, built from the lifecycle activities the page lists. Give it gate steps and `onFailure` compensation where it holds an approval, and write a test for it.
Run `npx chant build`, `npx chant lint` and the tests. Run the Op against a local emulator with `chant run <op> --no-record` and show me the output. Then open a pull request.
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`.

chant’s core is a compiler: pure, deterministic, lint-gated TypeScript-to-spec compilation. Everything after synthesis, such as apply and drift, is the lifecycle. You build it on the core and can swap it out. The shape of the project is:

Pluggable lexicons in. Pluggable lifecycles out. Pure synthesis in the middle.

Lexicons are the established pluggable axis. This page is about the other one: how you bring your own lifecycle. There are two doors.

The first door ships today. A lifecycle is just orchestration over chant’s primitives, and you declare orchestration as Ops in *.op.ts files — named, phased workflows that run on the local executor or, when they need a machine of their own, on a fountain steward.

The built-in lifecycle composites are themselves nothing more than Ops you could have written:

  • WatchOp — observe: snapshot + live diff on a schedule.
  • ReconcileOp — cloud → code: open PRs when live drifts from source.
  • ApplyOp — code → cloud: apply through the target’s own delete path, owned-only on every target (what bounds each one).

To contribute a lifecycle this way, compose the primitives your workflow needs:

  • chant lifecycle plan <env> — the typed change set against live (CLI).
  • chant import --from <env> — regenerate TypeScript from live state (Live Import).
  • The pre-built Op activities (lifecycleSnapshot, lifecycleDiff, reconcilePr, nativeApply, compensateApply) plus your own.
  • Gate steps and onFailure compensation when the workflow must survive a crash or hold a human approval.

Package a reusable one as a composite that returns an Op carrying its own schedule, exactly as the built-ins do — see packages/core/src/op/composites/ for reference. That is a complete, shippable lifecycle.

The second door is the direction, not a finished feature. The architecture is pluggable: the seams a foreign lifecycle would attach to already exist —

  • Per-lexicon live access — describeResources() (scrubbed observation) and exportResources() (full-fidelity export) give a lifecycle a typed view of live infrastructure without a hosted state file.
  • Ownership markers — provider-native tags/labels (ownership marking) let any lifecycle answer “is this mine?” from the resource itself.
  • A runtime boundary — chant run --on <lexicon> is a clean interface between declaring a workflow and choosing what hosts it.

The intended end state is that a vendor’s whole product becomes one plugin under chant’s pure synthesis — you don’t rip out Terraform, you plug it in as a lifecycle backend, and chant owns the socket.

The two doors close the gap BYOL could otherwise open. “Bring your own lifecycle” must never read as “you’re on your own”: Door 1 gives you rails to build on today (chant runs the lifecycle you compose), and Door 2 means an existing investment isn’t thrown away — it’s plugged in.

chant