Skip to content

Running an Op on fountain

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/lexicons/fountain/runtime/.
Add a fountain profile to `chant.config.ts` as the page shows, with the endpoint and steward I name and the token read from an environment variable, never the token itself.
Run `npx chant run list --on fountain` and `npx chant run status <op> --on fountain` for the Ops I name, and show me the output; they only read.
Open a pull request with the config. Print the `chant run <op> --on fountain` command for me to run; do not start or cancel a run yourself.
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 run <op> --on fountain hands a run to a Managoat steward instead of executing it here. The command line goes onto the steward’s conversation as a prompt, the CLI tails the conversation’s event stream, and the run’s record comes back in the same shape a local run would have written.

Nothing is stored on the client. status, log and list read fountain’s turns; a gate’s resolution is read from chant’s own ledger.

Declare an endpoint and a token, in a profile in chant.config.ts so they are under version control:

import type { ChantConfig } from "@intentius/chant/config";
import "@intentius/chant-lexicon-fountain";
export default {
lexicons: ["fountain"],
fountain: {
profiles: {
prod: {
endpoint: "https://fountain.inevitable.fyi",
token: { env: "FOUNTAIN_TOKEN" },
steward: "prod-steward",
},
},
defaultProfile: "prod",
},
} satisfies ChantConfig;

FOUNTAIN_ENDPOINT and FOUNTAIN_TOKEN in the shell still work when no profile is declared. Once a profile resolves it is authoritative: an unset token variable is an error naming the variable, not a silent fall-through. See Profiles for the full precedence.

Terminal window
chant run prod-watch --on fountain
chant run prod-apply --on fountain --profile staging

The prompt posted is exactly chant run <op>, which is what makes the conversation readable and what status matches turns on later.

Resolved in this order, first hit wins:

  1. A declared Steward that lists this Op. The composite registers the mapping when the project’s declarations load, so this answers whenever the run and the declaration share a process.
  2. The profile’s steward, a steward’s name. (team, its old name, is still read, with a warning.)

For either, the post is POST /api/conversations on the steward’s agent with channel_id chant:steward:<name>. The first run opens that conversation; every later one resumes it, so the steward has one conversation, and one sandbox whose disk survives park and wake. This replaced the team thread, which Managoat removed (managoat/managoat#2793).

  1. --param agent=<name>, then the Op’s labels.Agent. Either opens a fresh conversation on that agent rather than resuming the steward’s.

With none of the four, the run refuses and names all three ways to fix it. Prefer the first: the steward already names the machine the Op runs on, so config needs no second copy of the name.

Terminal window
chant run status prod-watch --on fountain # the latest turn's state and step records
chant run log prod-watch --on fountain # every turn that ran this op, newest first
chant run list --on fountain # one row per declared Op
chant run cancel prod-watch --on fountain --force

list groups by steward before it reads anything, so twenty ops behind one steward cost one round trip rather than twenty.

cancel without --force interrupts the turn and leaves the machine standing. Core’s chant run cancel requires --force, which terminates the conversation and takes its sandbox with it. On a steward that is the more expensive of the two: the checkout and tool cache go with it, and the next run opens a fresh conversation.

A run that reaches an unapproved gate ends its turn with the pending fact and the approve line. The gate is a fact on chant’s ledger, so it is resolved the ordinary way:

Terminal window
chant approve prod-apply deploy-window --approver alice
chant run approve prod-apply deploy-window --on fountain

The first records the resolution. The second posts chant run <op> back onto the steward’s conversation, carrying --approver and --url along so the conversation names who resolved the gate. The sandbox re-runs the op and walks through the gate, reading the resolution back off the ledger.

--durable-requests would instead answer fountain’s own pending permission card. It is refused today by name: the request-answer path it needs is fountain#1635, which Managoat carries and the fountain runtime does not use yet (chant#2391). Drop the flag and use the two commands above.

A conversation runs one turn at a time. A run posted while the steward’s turn is in flight is refused, and this runtime reports it with the conversation’s address and does not retry:

fountain runtime: the steward "prod-steward" is running another op
(https://fountain.inevitable.fyi/conversations/c_01J…). A steward runs one
turn at a time; wait for it to finish and run this again.

Queueing behind it from a CLI that may be interrupted a second later would be a promise this client cannot keep. Open the conversation, see what is running, and run the command again when it settles. A clock that fires chant run <op> --on fountain against a busy steward gets the same refusal, which is the skip overlap a steward’s declaration requires.

fountain closes an idle event-stream connection after 60 seconds. A turn that prints nothing for that long loses its connection and keeps running; the reader reconnects with Last-Event-ID and replays what arrived while it had none.

The wait ends only after FOUNTAIN_STREAM_IDLE_TIMEOUT seconds (default 1800) with no events across reconnects, and then with an error naming the conversation rather than a success nobody observed.

Section titled “The wait ends only after FOUNTAIN_STREAM_IDLE_TIMEOUT seconds (default 1800) with no events across reconnects, and then with an error naming the conversation rather than a success nobody observed.”

The steward’s agent is runtime: "acp" with runtime_command: "chant acp", so the prompt this runtime posts is parsed and executed by chant itself inside the sandbox. See ACP.

chant