Alert Triage (local)
The alert-triage
example is the capstone (L5) of the getting-started
golden example. An alert comes in and a phased Op takes it from there. It
classifies the alert and gathers context, then proposes a remediation and stops
for a person before applying it (a clearly-stubbed step). chant synthesizes the
Kubernetes manifests the app runs on, and the triage itself is an ordinary
*.op.ts whose steps shell out to this project’s own code.
This tutorial runs the whole thing locally with no cloud, no cluster, and no runtime beyond the one built into chant.
Prerequisites
Section titled “Prerequisites”- Node.js and
npm.
Run it
Section titled “Run it”cd examples/alert-triagenpm installnpm run devnpm run dev starts the webhook receiver and sends one demo alert, which starts
chant run triage. The run reaches the gate in the Approve phase and stops
there:
Op "triage" is gated on "approve-remediation" after 1.4s Approve the proposed remediation in .chant/triage/current.json approve : chant approve triage approve-remediationThe run exits 3, not 1. Nothing failed and nothing is being held open. The
run read the gate ledger, found nothing standing, and stopped after writing the
pending fact. app/start-triage.ts reads exit 3 as “waiting on a human”, which is
what lets the webhook answer 202 with a status instead of a 500.
Read the proposal, then clear it
Section titled “Read the proposal, then clear it”The proposal is on disk between the two runs, so what gets applied is what somebody actually read rather than a fresh classification that moved under them:
cat .chant/triage/current.jsonchant approve triage approve-remediation --actor youchant run triagechant approve writes the resolution to the gate ledger on the
chant/lifecycle branch. The second run reads both facts and walks through the
gate, applying the remediation and notifying. Ordering is what makes this safe: a
resolution counts only if it is newer than the pending fact it answers, so last
week’s approval cannot clear this morning’s proposal.
Every remediation passes the gate, which is a change from the workflow this
example replaces. That one paid for its gate with a twelve-hour open wait, so it
spent that only on remediations the classifier called risky and let the routine
ones through unattended. Nothing is held open now, and the second run is just
another run, so there is no cost left to route everything through the gate.
risky still does work: it is what the proposal and the notify line say about
the change somebody is being asked to clear.
Two event sources
Section titled “Two event sources”Both start the same Op:
npm run alert # external alert via the webhook (POST /alert)npm run drift -- --demo # a drift event (the WatchOp/lifecycle counterpart)The webhook is the receiver the WebApp manifest deploys. The drift source runs
chant lifecycle plan --json and triages each drifted resource, so out-of-band
cluster changes go through the same triage as external alerts. --demo injects a
sample drift so you can see the pipeline without making a real change.
The agent (optional)
Section titled “The agent (optional)”proposeRemediation is a deterministic stub by default — no key, runs offline.
Set ANTHROPIC_API_KEY (and npm i @anthropic-ai/sdk) to have it call Claude;
override the model with ANTHROPIC_MODEL (default claude-sonnet-4-6). The first
run shows chant, not an LLM. The agent may only escalate risk, never de-escalate:
a high or critical alert stays risky even if the model calls it safe.
Deploy the manifests
Section titled “Deploy the manifests”The app’s Kubernetes surface is typed chant in src/:
npm run build # → k8s.yaml (plain Kubernetes)npm run lintkubectl apply -f k8s.yaml # e.g. to a local k3d clusterHow it fits together
Section titled “How it fits together”| Piece | What it is |
|---|---|
src/ | chant manifests — webhook (WebApp) and runner (WorkerPool) |
activities/triage.ts | the triage steps (the agent — stub + Claude opt-in) |
activities/run-triage.ts | the CLI the Op shells: propose before the gate, apply after it |
ops/triage.op.ts | the Op: Propose → Approve (gate) → Remediate |
app/ | the event sources (webhook, drift) and the demo alert |
Unit tests cover the triage steps and the event mappers (npm test). See the
example README
for the full layout, and Gate-as-fact for
what the ledger is doing underneath.