Skip to content

Contributing a Lifecycle

chant’s product is the synthesis core: pure, deterministic, lint-gated TypeScript-to-spec compilation. Everything past synthesis — state, apply, drift, reconcile, retire — is the lifecycle, and it is a bolt-on, not the core. 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 you need durability, on Temporal.

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

  • WatchOp — observe: snapshot + live diff on a schedule.
  • ReconcileOpcloud → code: open PRs when live drifts from source.
  • ApplyOpcode → cloud: apply via the target’s native mechanism, with owned-only deletes.

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 (and optionally a TemporalSchedule), exactly as the built-ins do — see lexicons/temporal/src/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 accessdescribeResources() (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 — the Op executor’s local-vs-Temporal split is a clean interface between declaring a workflow and running it durably.

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 durable rails to build on today (chant makes it durable), and Door 2 means an existing investment isn’t thrown away — it’s plugged in. Own the socket, outlast the lifecycle war.