Skip to content

Alert Triage (local)

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/tutorials/alert-triage-local/.
In `examples/alert-triage`, run `npm install`, then `CHANT_NO_RECORD=1 npm run dev` so the triage run records nothing on the ledger.
When the run stops at the `approve-remediation` gate, show me the output and the approve command it printed. Show me `.chant/triage/current.json` too, and stop there.
Run `npm run build`, `npm run lint` and `npm test` and show me the results as well.
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`.

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 it all on your machine, with the runtime built into chant.

  • Node.js and npm.
Terminal window
cd examples/alert-triage
npm install
npm run dev

npm 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-remediation

The 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.

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:

Terminal window
cat .chant/triage/current.json
chant approve triage approve-remediation --actor you
chant run triage

chant 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. Now nothing is held open and the second run is an ordinary run, so routing everything through the gate costs nothing. risky still does work: it is what the proposal and the notify line say about the change somebody is being asked to clear.

Both start the same Op:

Terminal window
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.

proposeRemediation is a deterministic stub by default, which runs offline with no key. 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.

The app’s Kubernetes surface is typed chant in src/:

Terminal window
npm run build # → k8s.yaml (plain Kubernetes)
npm run lint
kubectl apply -f k8s.yaml # e.g. to a local k3d cluster
PieceWhat it is
src/chant manifests — webhook (WebApp) and runner (WorkerPool)
activities/triage.tsthe triage steps (the agent — stub + Claude opt-in)
activities/run-triage.tsthe CLI the Op shells: propose before the gate, apply after it
ops/triage.op.tsthe 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.

chant