- name
- weft-running
- description
- Read when running, activating or debugging a program: the CLI command map, the daemon (never yours to restart), the build and run flow, trigger activation and the three deactivate modes, the infra lifecycle, sizing a command's timeout, journal inspection (executions, events, logs), and the debugging playbook.
# Running and debugging
The `weft` CLI is a thin client of [the daemon] plus the front end for
building. [the daemon] is the dispatcher process that owns projects,
executions, triggers, and infra, and listens on `http://localhost:9999`
(override: `--dispatcher <url>`, `WEFT_DISPATCHER_URL`, or `[dispatcher] url`
in `weft.toml`). The user's install (`setup.sh`) starts it, once per machine.
`weft daemon start`, `stop` and `restart` re-run that install, and a restart
from a project has wiped shared keys before, so you never run them. A `.env`
near the project auto-loads.
A [color] is one execution: a UUID minted when a run starts. Everything in it
is journaled, node by node, with the values on the wires. Every command that
takes a [color] also takes its first characters (`weft events 3f2a`), at
least four, as long as they name a single run.
## How you run a command
Every command you run cannot ask you anything. These are the ones that ask,
with the answer already in them. While you are building, you type them as
they are written here:
| Instead of | type |
|---|---|
| `weft resync` | `weft resync --mode wipe` |
| `weft deactivate` | `weft deactivate --mode wipe` |
| `weft rm`, `weft clean`, `weft prune` | the same with `--yes`, once the user has said yes |
| a long `weft run` | `weft run --detach` |
`--mode wipe` is the answer while you are building; `hibernate` and `park`
are for a program people are using, and the three modes below say when. A
command that asks anyway gets killed and reported with what it asked.
A command that changes the deployed code and the thing you do next go in ONE
line, joined by `&&`: `weft resync --mode wipe && curl ...`. Run them
separately and a resync that failed leaves the next command talking to the
old code, which reads as a bug in what you just wrote.
**You never sit on a quiet command.** Thirty seconds is the most you wait
without looking. At thirty seconds you check whether the thing is still
moving: new lines from a build, a changed `weft status --json`, a new line
in `weft daemon logs`. Moving, you give it thirty more and look again;
quiet for thirty seconds, it is stuck, and you get out and find out why.
Nothing weft does while you are building is silent for that long when it
is healthy, so silence is the finding. Reads (`status`, `executions`,
`events`, `logs`, `connect`, `describe-nodes`) get fifteen seconds; `weft
run` gets `--detach` and a check later; a dev server starts in the
background. Check what unit your own tool wants before you type a number.
Some take seconds and some take milliseconds, and getting it backwards is
the difference between thirty seconds and two days.
Getting out is cheap and safe: Ctrl+C on the CLI (nothing weft was doing
is left half done; the daemon finishes or rolls back on its own). Then read
`weft daemon logs`, which is where a wait says what it is waiting on. The
one wait that used to hide here was `activate` sitting behind a worker
from an older build; it no longer waits by default (see `--running-policy`
under the command map), and `weft status` reports an activation still in
flight, which `weft cancel-activate` ends before you run the verb again.
You raise a number only once you can say what is taking the extra time,
and a run that is longer than its shape says is something you report.
If you want to wait on something long (a build, [the daemon] coming up, a run
settling), start it detached and check its state between other steps instead
of writing a loop: `weft run --detach` hands you the [color], then `weft
executions --json` for a run and `weft status --json` for a build or [the
daemon].
If you do write a loop, it is this line and no other, cap included:
```bash
timeout 30 bash -c 'until <check>; do sleep 5; done'
```
When the cap trips you read the state and say what it is waiting on. A run
parked on a timer or a person never finishes on its own, so an uncapped
`until` on one hangs until somebody kills it, which has happened.
Before you write any loop, say what will make the check true, and check that
it is not you. A loop waiting for the open runs to reach zero while you keep
starting runs never ends. When nothing outside the loop can make the check
true, do not write the loop: do the thing that ends the wait.
**A `pkill` or `pgrep` pattern matches your own command line too.** The
shell running it has the pattern in its arguments, so a broad pattern kills
the shell mid-command: you get an exit code in the 140s, no output, and
nothing saying what happened. Match on the executable instead of a substring
of the whole line (`pkill -x <name>`, or `pgrep -f` on a path that cannot
appear in your own invocation), and prefer the tool that owns the process:
weft's own commands stop what weft started, and [the daemon] is never yours
to kill anyway.
`--json` is a global flag with two meanings. The long commands (`build`,
`bake`, `run`, `activate`, `deactivate`, `resync`, `infra`, `rm`, the
cancels) stream progress as one JSON object per line. The readers (`status`,
`ps`, `executions`, `events`, `logs`, `files`, `listener inspect`, `token`,
`stop`, `connect`, `tree`, `examples`, `diff`, `checkpoint`, `branch`,
`freeze`, `prune`, `wake`) print what [the daemon] answered, which you read
with `jq` instead of parsing the human columns; `test-node` prints its
reports as one JSON array. `new`, `follow`, `daemon`, `catalog`, `clean` and
`update` ignore it.
## Naming a node
Every command that takes a node takes its whole path from the entry file,
dot-joined, the way the source reads: `classify` for a node written in
`src/main.weft`, `review.classify` for one inside the group `review`,
`triage.classify` for one inside the file the site `triage` includes
(`triage = @include("triage.weft")`), and `triage.review.classify` when it
sits in a group inside that file. Sites, groups and nodes are one tree, and a
name is the walk down it from the top. There is no short form: a bare
`classify` names nothing once the node sits inside a group or an included
file, and the refusal spells the name that works. The same file included
twice is two places with two names (`triage.classify` and `again.classify`),
each with its own runs, waits and display. This is what `--from`, `--emit`,
`--target`, `--before`, `--seed-until`, `--seed-before`, `--group`, `--fire`,
`weft events --node`, `weft wake`, `weft freeze --expect`, `weft infra
node-stop`, `weft infra node-terminate`, `weft infra logs` and `weft token
mint --display` take, and what `weft events`, `weft executions`, `weft infra
status` and the graph print back.
## The command map
| Command | What it does |
|---|---|
| `weft build` | compile, resolve assets, build the worker image (content-addressed) and register the project with [the daemon]. Starts nothing. You never need it while building a program: `weft run`, `weft activate` and `weft resync` build on their own, so running `weft build` before them only builds twice. Its one use is the final deployment, `weft build --referenced`, which ships only the node types the program uses. Also the repair when [the daemon] no longer holds the code a past run ran: registering records the compiled program under its own hash, the one the run names, so unchanged files make the run readable again. Adds nothing to the version tree; `weft checkpoint` does that |
| `weft validate --file src/main.weft < src/main.weft` | strict compile + validate, diagnostics as JSON, nothing runs |
| `weft run [<example>] [--referenced] [--seed] [--root] [--from <node>=<ports-json>]... [--emit <node>=<ports-json>]... [--target <id>]... [--before <id>]... [--group <id>=<ports-json>] [--fire <trigger>=<wake-json>] [--save <name>]` | build and start one execution; `--detach` returns its [color]. `--from` supplies backup inputs at a start, `--emit` supplies outputs without executing that node. Real producers take precedence over backups, and under `--seed` the earlier run's result IS a real producer: a `--from` value at a node history already feeds goes unused (the run warns). To hand a new value in, run without `--seed`, or `--emit` the upstream output. `--target` includes the endpoint; `--before` excludes it. `--group` selects a whole group or included file, with its input payload. `--fire` runs exactly one trigger using a matching bake. Ordinary groups can be cut precisely; loops stay whole. `--seed-before` / `--seed-until` bound compatible reuse. A named example supplies saved starting parameters; current code runs. Clear and replacement rules are in `weft-sdp` |
| `weft checkpoint [<label>]` | record the files as a version under head, no run, no build; `already at <id>` when identical |
| `weft branch <version\|label\|color>` | restore that version's files and move head (a checkpoint label names its version; a [color] makes that run the next seed). Refuses on a dirty tree naming the files; `--discard` overrides |
| `weft tree` | the version tree: versions with what changed, their runs beneath, head marked (`--json` adds `disk_version`) |
| `weft diff <ref> <ref> [--full]` | compare observed outputs for human or AI review, including frozen focus nodes. A ref is a [color], its unambiguous prefix or `example:<name>`. Differences are evidence and do not fail the command |
| `weft freeze <name> [<run>] [--expect <node>]...` | save that run's starting parameters and observed outputs in `examples/<name>.json`; default is head's run. `--expect` marks nodes to focus on during review. Run and diff leave the accepted file intact; freeze again after accepting its replacement |
| `weft examples` | list saved parameters and frozen examples; inspect them, rerun one with `weft run <name>`, then compare with `weft diff` |
| `weft bake [--referenced]` | prepare trigger inputs without arming listeners. A manual `--fire` requires a matching bake; use `--referenced` here when the run uses `--referenced`. Activation also prepares and records a bake before arming. Takes `--running-policy` like `activate` (the setup runs on a worker, and a stale one is replaced first) |
| `weft wake <color> <node>` | resolve a pure time wait now; refused for a wait expecting a value, naming its kind |
| `weft prune <version>` | delete a version, everything under it, and their runs; asks first, `--yes` for scripts. Refuses on head's version, under a frozen example's origin, and while a run in the subtree is running |
| `weft stop <color>` | cancel an execution |
| `weft status` | registration, build state, listener, infra, drift |
| `weft ps` | every registered project |
| `weft executions [--limit N] [--project <id>] [--phase fire]` | past executions, newest first (see Reading a run) |
| `weft events <color> [--node <id>] [--kind <kind>] [--full] [--json]` | a run's events in order, one compact line each (see Reading a run) |
| `weft logs [color]` | a run's log (no argument: the latest execution of the project in the current directory; see Reading a run) |
| `weft follow <project>` | live events for a project |
| `weft activate` / `weft deactivate` | turn triggers on / off. `deactivate` on an active project needs `--mode <wipe\|hibernate\|park>` (a [mode], defined under The three modes). Both take `--running-policy <cancel\|wait>`, default `cancel`: on `activate` it says what happens to a worker still up from an older build (cancel what it runs and replace it now, or `wait` for its executions to land, up to `--drain-timeout` seconds); on `deactivate` the same for the project's running executions |
| `weft resync` | deactivate + activate against a fresh build, after editing a trigger subgraph. Only for an ACTIVE project (a parked or hibernated one refuses: `weft activate` first), and it needs the same `--mode` answer as `deactivate`; without it, it stops and asks |
| `weft infra start` / `status` / `stop` / `upgrade` / `terminate` / `cancel` / `logs` | the project's [infra] (see The infra verbs) |
| `weft token mint` / `ls` / `revoke` | signal tokens: scoped access for an outside listener such as the browser extension. `mint` prints the connect URL, then the bare token on its own line for a script |
| `weft daemon start` / `status` / `logs` | [the daemon]; only `status` and `logs` are yours |
| `weft catalog update` | re-sync `nodes/base_catalog/` to the installed weft's stdlib |
| `weft describe-nodes --list` | one line per node type; how you find one |
| `weft describe-nodes --node <Type> --compact` | one node's wiring view; read it before wiring. With no flags you get the whole catalog as JSON, which is large |
| `weft test-node <target>` | run node self-tests (`--tier live` spends money, asks first) |
| `weft connect` | the editor's Connect panel as a CLI verb: `--list` the stored connections, `--node <id> --grant <id>` to pick one for a node, connect new accounts through both doors, `--upgrade`, `--forget`, `--disconnect` |
| `weft rm [--journal] [--local] [--all] --yes` | unregister the project, terminate [infra], reclaim data. `--journal` also drops its run history, `--local` its build artifacts, `--all` implies every flag. Asks first; pass `--yes`, and only after the user confirmed |
| `weft clean --yes` | journal and image cleanup, per subject, and naming a subject takes all of it: a [color] takes that one run, `--project <id>` takes a whole project's history (removing a project leaves its runs behind, so this is how you erase them), no subject takes everything older than `--keep-days` (30 by default), `--all` takes the lot. A version the deletion left bare (no runs, nothing under it, no checkpoint name, not head) goes with the runs; a named checkpoint never does. `--images` and `--build-cache` touch no journal rows. Deleting runs asks first; pass `--yes`, and only after the user confirmed |
How a run picks which nodes execute is in the `weft-language` skill. A
trigger fires on its own event only once the project is activated; to try a
trigger's program before activating, `weft bake`, then `weft run --fire
'<trigger>=<wake-json>'` (the `weft-sdp` skill).
## The build and run flow
`weft run` compiles, registers the project with [the daemon], builds the
worker image if sources changed (Cargo runs inside Docker, never on the
host), then fires. Compile failures print `compile failed:` then
`line:column message` lines. HTTP errors surface [the daemon]'s own message
verbatim.
`weft build` skips [the runtime tier] (a connection not yet picked), so a
half-wired program still builds and a CLI-started run still starts; the node
fails at execution, in the journal, naming the service and what to do
("no telegram connection picked; connect one on the node"). What `weft
validate` and the editor print before a run is the same fact in fuller
words, naming the node too.
The editor's Run, Activate and Resync buttons refuse until every
connection is picked; the CLI does not, so before a run you check with `weft
validate --file src/main.weft < src/main.weft`, which reports the
`rule-runtime` findings in seconds. The fix is a picked connection (`weft
connect --node <id> --grant <id>`, or the user on the node's Connect button),
never a source edit.
A program with triggers listens only after `weft activate`. An edit to a
trigger's subgraph takes effect only after `weft resync`.
`project files changed while building; run the command again` means a file of
the project was written while the build was reading it, which is almost always
a helper of yours writing into `nodes/` at the same time. Nothing is broken:
run the command again. What matters is that the deployment did NOT change, so
the live program is still the previous one. Anything you call before a resync
succeeds is exercising the OLD code, and results from it tell you nothing
about the edit you just made.
A run that touches
[infra] is refused, from the CLI and from the editor's Run button, until that
[infra] is running: `weft infra status`, then `weft infra start`. Activating
is refused the same way, so on a program with [infra] the very first command
is `weft infra start`, not `weft activate`: it is what builds the node's
images, and nothing else does.
### The three modes
A [mode] is what happens to the runs parked on a person or a timer when the
triggers go down (`deactivate`, `resync` on an active project, the [infra]
verbs that deactivate on the way):
- `wipe`: their forms and timers are dropped and the runs end cancelled. You
pass it only when nothing is in flight (`weft executions` shows no
suspended run of the project) or the user said to drop the waiting work.
- `hibernate`: the runs stay alive for a grace window (`--grace <minutes>`,
15 unless set); a fire arriving inside it is held and delivered when the
project comes back. Past the window new fires are refused; the waiting runs
and the project survive (`wipe` is the [mode] that drops them).
- `park`: the runs stay alive with no time limit; every fire is held until
the project is reactivated. Your pick when the user is editing and people
are mid-conversation.
**`wipe` is what you pass while you are building.** Nothing waiting on the
program is anyone's conversation yet, so dropping it costs nothing and the
command lands at once. `hibernate` and `park` exist for a program people are
actually using: pass one when the user says so, or when `weft executions`
shows a run parked on a person you would be throwing away.
`--running-policy cancel` (the default) stops the executions already running
now; `wait` lets them finish first, new fires held meanwhile, and you pass
it only when the user asked for the running work to land. A `wait` waits
under `hibernate` and `park` alike (the mode says what happens to the parked
work, the policy what happens to the running work: two separate answers),
and it ends by cancelling whatever is still running at its cap
(`--drain-timeout`, 60 seconds unless set); under `wipe` waiting is refused.
With no `--mode` and no terminal (which is every command you run) the mode
is `wipe`. That is the right answer while you are building, so you rarely
type it; you type `--mode hibernate` or `--mode park` when the program is one
people are using and the work in flight has to survive.
## The infra verbs
عرض على GitHub