Runs and operations
Guild Stack writes everything it does into one folder, .guild/, at the top of
your project. Nothing is hidden. Start by reading a run; the Power layer
adds status, the dashboard, ops runbooks, initiatives, and workspaces; the
Architect layer is the full observability record.
Try it. The first line reports where your project stands. The second lists every run Guild Stack has recorded:
/guild:status
ls .guild/runs/
Reading what Guild Stack wrote
Guild Stack writes everything it does into one folder, .guild/, at the top of
your project. Nothing is hidden. This page shows where to look.
Settings
.guild/settings.json: this project's Guild Stack settings. It is plain JSON.
Project notes (the wiki)
.guild/wiki/index.md: the start page..guild/wiki/concepts/architecture-map.md: how your code fits together..guild/wiki/sources/: notes made from documents you added..guild/wiki/decisions/: decisions you asked Guild Stack to record, with the options you turned down.
These are Markdown files. Open them in any editor.
Maps of your code
.guild/indexes/codebase-map.json: every file Guild Stack found, and which file imports which.
What init did
.guild/init/<project>.md: a short report of the setup.
Plans
.guild/plan/*.md: the plan for a task. Each piece of work (a lane) is listed here, and so is any lane it waits for.
Runs: one folder per task
Each Guild Stack task gets its own folder in .guild/runs/:
.guild/runs/<run-id>/run.yaml: which command ran..guild/runs/<run-id>/handoffs/: one receipt per lane. Each receipt has a## changed_filessection that lists the files that lane touched..guild/runs/<run-id>/review.md: the review of the work..guild/runs/<run-id>/verify.md: the final check, with a pass or fail result.
Tip: to see what happened in a task, read verify.md first, then the
receipts.
Knowledge waiting for approval
.guild/knowledge/sources/: copies of documents you added to the notes..guild/knowledge/candidates/: new knowledge, such as a decision, waiting for your approval before it goes into the wiki.
Status, stats, and the dashboard
/guild:status is the one place to read where a project stands. The bare form
writes no .guild/ data.
Bare status
/guild:status shows the current run state, the furthest phase, the next gate,
blockers, the layout version, and the active team for each phase. It resolves
state by a filesystem scan, or by an optional SQLite read-through cache that
lives on platform state, off the repo. --no-index skips the cache.
A legacy single-file team.yaml is reported once as legacy; status never acts
on it.
Sub-verbs
| Sub-verb | What it does | Writes? |
|---|---|---|
resume | Continues the active run from its next pending gate. It hands off to that phase's own command. --restart clears run state after it asks you. | yes, via the phase it resumes |
stats | Usage and telemetry over .guild/runs/**. --rebuild-index rebuilds the optional cache. | no |
dashboard | Serves the benchmark web UI over this project's runs and knowledge. --stop shuts it down. | on stable 3.0.0, only under .guild/cache/; the upcoming beta analysis view additionally triggers plugin commands that write analysis and recommendation artifacts (see below) |
The dashboard
/guild:status dashboard launches the local benchmark UI: a runs table, a
Knowledge view, and a Wiki browser. It imports the project's run logs and prints
the URL.
- Port: default 3055. If that port is taken, it moves to the next free one.
--port Npicks another. - Checkout: it looks for the benchmark repo as a workspace sibling, then
in-repo, then as a cached clone under
.guild/cache/benchmark. - No network by default. A clone or an install happens only after you confirm it.
- Managed, not a daemon. The server logs to
.guild/cache/dashboard.logand records its PID in.guild/cache/dashboard.json, which--stopreads.
Use stats when you want text in the terminal. Use dashboard when you want to
browse runs and the knowledge graph in a browser.
Run analysis in the dashboard (upcoming beta)
The upcoming beta (next) releases add a behavioral-analysis view to the
dashboard. You
can start an analysis of one run, a comparison of several, or an analysis of an
initiative or an independent-run group, and watch the job's progress. The
analysis itself always runs in the plugin; the dashboard only displays its
artifacts and triggers the plugin's own commands.
Three rules keep what you see honest:
- Evidence states are explicit. A report renders as current, stale, missing, malformed, or failed — each distinctly. A stale report (its inputs changed since it was computed) is never actionable; refresh the analysis first.
- Partial evidence stays partial. Not every event class has a producer wired yet, so some conclusions report unobservable instead of a verdict. Absence of evidence never proves success.
- A recommendation decision is scoped and explicit. Before you decide, the
panel shows exactly where the decision lands: accepting a plugin
recommendation writes a local draft under
.guild/recommendations/drafts/(nothing is filed anywhere external); accepting a project recommendation moves it to the queue in.guild/recommendations/queue.jsonl. Neither accept implements anything — implementation is always separate, later work, and the plugin re-checks freshness at decision time and refuses a stale decide.
The status line
Guild Stack ships a small script that prints one line of run state: the phase, the review round, the loop cap, the active loops, and the restart count. It is off by default.
Turn it on
statusline is a settings-schema key with default false. It is not a policy
key, so /guild:config set statusline … is refused in v3.0.0. Use one of these:
- Pass
--statuslinethrough Guild Stack's runner. - Export
GUILD_STATUSLINE=1yourself before you start the host.
The script prints nothing unless GUILD_STATUSLINE is exactly 1.
When the key is true, the agent-team launcher exports GUILD_STATUSLINE=1 and
passes it to every specialist pane. If you already exported it, the launcher keeps
your value. If settings cannot be read, it leaves the variable unset.
What it shows
With a run id and a counters file, the line is:
phase: build | round: 2 | cap: 16 | loops: all | restarts: 0
| Field | Source |
|---|---|
phase | GUILD_PHASE. unknown when unset. |
round | From counters.json in the run directory. With GUILD_LANE_ID set, the highest of that lane's L3, L4, and security rounds. Otherwise the higher of the global L1 and L2 rounds. |
cap | GUILD_LOOP_CAP. 16 when unset. |
loops | GUILD_LOOPS (none, spec, plan, implementation, all, or a comma list). none when unset. |
restarts | The lane's restart_count, or 0 without a lane. |
The run directory is GUILD_RUN_DIR if set, else <cwd>/.guild/runs/<GUILD_RUN_ID>.
Fallback lines
| State | Output |
|---|---|
GUILD_STATUSLINE is not 1 | Nothing. |
GUILD_RUN_ID unset | phase: unknown |
Run id set, no counters.json yet | phase: <run-id> (initialising) |
The script always exits 0 and prints no errors. It reads the host's stdin JSON so the host does not block, but it does not use its fields.
What the source does not cover
The plugin ships the script and the opt-in key. The v3.0.0 source does not register
the script as a host's status-line command, and hooks/hooks.json does not reference
it. The HELP text for the key describes a tmux status pane. This page does not claim
which hosts render the line. Treat it as a script you point your host at yourself.
See Config reference for the statusline key and
Status, stats, and dashboard for the status command.
Ops runbooks
/guild:ops is the operations phase. It runs a runbook: a written,
reviewed procedure for a release, an alert, an outage or a revert. Every runbook
runs behind a mandatory pre-flight dry-run. For the phase overview see
Lifecycle in depth.
Try it
/guild:ops # Guild suggests a runbook and asks you to confirm
/guild:ops <runbook> # name the runbook yourselfEither way the class is confirmed with you before anything runs.
The five classes
| Class | Posture | Producer specialist |
|---|---|---|
release | interactive by default | devops-ci-cd-pipeline (plus technical-writer-release-notes as advisor) |
monitoring | interactive by default | devops-observability-setup |
incident | interactive always, never autonomous | devops-incident-runbook |
rollback | interactive always, never autonomous | devops-incident-runbook |
maintenance | interactive by default | devops-infrastructure-as-code |
The five runbooks are chapters of one assembler skill, not separate skills. Approving a runbook lowers only the soft gate. It never lowers the hard set.
Where runbooks live
A runbook's trust root is .guild/wiki/standards/runbooks/<name>.md. It is
promoted through the decisions flow. See Knowledge.
Four safety rails
The router enforces these for every class, before dispatch. Rail 4 has two
legs, so four rails produce five checkable booleans in guild.ops.v1. All five
must be true.
| # | Rail | Boolean |
|---|---|---|
| 1 | An unproven runbook is not autonomous. The first run is interactive. | unproven_runbook_was_interactive |
| 2 | Incident and rollback are never autonomous. | incident_rollback_not_autonomous |
| 3 | The always-ask hard set is unconditional, even under --auto-approve=all. | hard_set_prompted_inline |
| 4a | The pre-flight dry-run is mandatory. | preflight_dry_run_ran |
| 4b | The autonomy allowlist holds no hard-set class. | no_hard_set_in_allowlist |
The hard set is release, security, ops and destructive. The gates that
can never run autonomously are ops and destructive.
An op_class_allowlist may never contain a destructive, network or spend class.
Plan validation rejects it with exit code 2.
The pre-flight dry-run
The dry-run is rail 4. It is mandatory and the router checks it before the
runbook is dispatched. The result is recorded as preflight_dry_run_ran. Every
hard-set step is also recorded as autonomy: prompted_inline.
Risky or destructive steps prompt you every time.
Refusing on a quality BLOCK
Ops reads the quality report from QA. If its
recommendation is block and no human force_pass is recorded:
releaseandrollbackrefuse, and route you back to QA.monitoring,maintenanceandincidentmay proceed.
Review and evidence
The ops team is composed and approved before any dispatch. Its advisory
challengers are security (always) and architect (only on the
multi_component signal). A cross-host reviewer is optional and policy-gated, as in
QA. Neither changes the safety rails.
Records go under .guild/runs/<run-id>/ops/, with per-step evidence in
ops/evidence/. Every run writes guild.ops.v1. Incident and rollback also
write guild.incident.v1. Release also writes guild.release.v1.
Ops only supplies evidence for closing an initiative. The release record carries
outcome.status, doc_sync_status and release_evidence_refs. Closing stays
with the initiative close gate. See Initiatives.
Initiatives
An initiative is work that spans more than one run. It is opt-in. A one-off
/guild:guild run never creates one. Attachment is always explicit, in one of
three ways:
- You run
/guild:initiative …directly. - You pass
--initiative=<id|new>to a phase command. - Your brief names a durable goal. Then
/guild:guildasks you: attach to a new initiative, an existing one, or keep it one-off. It never attaches on its own.
Initiatives live under .guild/initiatives/active/<id>/ and
.guild/initiatives/archived/<id>/.
Sub-verbs
| Sub-verb | What it does |
|---|---|
new | Writes initiative.yaml and its definition ledger, behind the definition-ready gate. |
status | Read-only view of one initiative. |
list | Read-only list. --archived includes archived ones. |
resume | Re-enters the next open work item. |
update | Amends the ledger, for example --add-goal "…". |
archive | Moves it from active to archived. |
restore | Moves it from archived back to active. |
close | Runs the close gate and refuses to close on a failure. |
The close gate
close runs a deterministic gate script (the D8 gate) and refuses on a non-zero
exit. It also refuses when any cited evidence path does not exist: a
close_gate.evidence entry or a work-item evidence_refs entry. The skill never
re-decides the outcome in prose.
An unknown sub-verb prints usage and runs nothing.
Workspaces
A workspace is a folder whose immediate children are separate projects. Each
child has its own .git/ or .guild/. Guild Stack has exactly two state levels: the
workspace root (the umbrella) and its immediate children (the
sub-guilds). Each level has its own .guild/. There is no third nested
level. The umbrella reads child state in place. It never replicates a child's
agents, skills, wiki pages, initiatives, or runs.
How Guild Stack detects one
/guild:init and /guild:learn map run a detection script before they scan
anything. It looks at immediate children only. Depth is fixed at 1. A child
counts if it holds a nested .git/ or .guild/. Plain folders such as docs/
match neither and are ignored. Guild Stack shows you the verdict ("detected N
sub-guilds: …, by rule …"). It is never silent, and you can override it.
On a regular repo, none of this runs and nothing changes.
The workspace.mode setting
Set workspace.mode in .guild/settings.json to override detection.
| Value | Meaning |
|---|---|
auto | Detect from the children, as above. |
on | Treat the folder as a workspace. |
off | Treat the folder as a regular repo. |
The default is auto.
What init writes on a workspace
.guild/workspace.json(schemaguild.workspace.v1), the federation manifest. It lists each detected sub-guild. A script writes it, and re-running init refreshes it without clobbering other state.- A root wiki, only when the manifest says
root_wiki: true. That is the case when the root itself has scannable top-level code. A pure federation root (for example, justdocs/and the child repos) gets no root wiki. - For each child that has no
.guild/yet, Guild Stack offers to run/guild:initon it. It registers the child either way. It never auto-inits and never forces.
All writes land under the workspace's own .guild/. Detection and
registration are read-only on every child's .guild/.
Federation: query, not copy
When the root has a .guild/workspace.json, a wiki query does not stop at the
root's own (often empty) wiki. It fans out across every registered sub-guild
that has a wiki, then merges the results into one ranked list. Each hit is
tagged with the sub-guild it came from. The root wiki joins as one more
source when root_wiki is true.
- A query that names one sub-guild searches only that sub-guild.
- A query that names none searches all of them.
- The same fan-out feeds a lane's context bundle. No child knowledge is copied into the bundle, and the bundle size cap is unchanged.
The same rule holds for the knowledge graph. Each child keeps its own
codebase-map.json and knowledge-graph.json under its own .guild/. The
root graph never merges them. Cross-repo references are counted and left
unresolved, not turned into root-graph nodes.
When pages conflict
The plugin at v3.0.0 has no rule that ranks the umbrella wiki above a child
wiki. A federated query uses the normal contradiction policy instead. When two
returned pages contradict on the same claim, the newer page wins, unless the
older page has confidence: high and the newer one does not. Guild Stack never picks
silently. It shows both pages with their paths, updated_at, confidence, and
a resolution: line, and suggests guild:wiki-lint so a person decides.
If your team wants the umbrella wiki to win cross-project conflicts, keep that
as a workspace convention (for example in the umbrella AGENTS.md). Guild Stack does
not enforce it.
Learning across children
/guild:learn map on a workspace registers the children. It does not
auto-run a deep scan on each. The no-argument /guild:learn does fan out. Its
behavior follows the workspace knowledge setting learn_fanout:
auto(default): fans out to the children after showing a cost estimate.plan-only: writes the fan-out plan to.guild/runs/<run-id>/learn/workspace-plan.mdand stops.
--dry-run always behaves as plan-only.
Once children have their own harvest candidates, a script can stage the
cross-cutting ones for the umbrella. It writes only a reference manifest at
<root>/.guild/runs/<run-id>/upstream-candidates.json. It writes nothing under
.guild/wiki/. A candidate lands in the umbrella wiki only through the
separate, human-gated wiki-ingest step.
Observability
Every tracked run writes to its own directory, .guild/runs/<run-id>/. Nothing leaves your machine (see Security and trust). You read the record with plain files, scripts/trace-summarize.ts, or the optional guild-telemetry MCP server.
Try it
With the guild-telemetry server loaded, call trace_list_runs to find run ids, then trace_summary with one of them. The server is optional. Guild Stack works without it.
Files in a run directory
Path under .guild/runs/<run-id>/ | Written by | Content |
|---|---|---|
logs/v1.4-events.jsonl | post-tool-use, capture-telemetry, run-trace-* | Canonical event log. One JSON object per line. |
events.ndjson | capture-telemetry | Legacy mirror. Same lines for every event type, including PostToolUse. Best-effort. |
logs/payloads/<span_id>.json | capture-telemetry | guild.trace_payload.v1 sidecar: a redacted structured body. Raw provider prompts are never stored. |
logs/security-events.jsonl | PreToolUse and the scrub path | Security events. |
logs/tier-dispatch.jsonl, logs/backend-degradation.jsonl | PreToolUse | Tier-dispatch and backend-degradation records. |
run.yaml, provenance.json | run lifecycle | Run status, and the provenance pointer written at close. |
resolved-settings.json | run-start preflight | The resolved settings snapshot, referenced from run.yaml's settings_ref. Best-effort. |
Event schema
The v1.4 log is a closed union of 16 event types: phase_start, phase_end, specialist_dispatch, specialist_receipt, loop_round_start, loop_round_end, tool_call, hook_event, gate_decision, assumption_logged, escalation, codex_review_round, harvest_event, redirect_event, cas_event, curator_event. Validators reject other values.
Common fields are ts, event, run_id, and an optional lane_id. Run ids match ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$. A tool_call event adds:
| Field | Meaning |
|---|---|
tool | One of 17 closed values (Read, Write, Edit, Grep, Glob, Bash, Agent, Skill, AskUserQuestion, TaskCreate, TaskUpdate, TaskList, WebFetch, WebSearch, NotebookEdit, BashOutput, KillShell) |
command_redacted, result_excerpt_redacted | Redacted text |
status | ok, err or n/a |
latency_ms | From Pre/Post pairing |
tokens_in, tokens_out | Optional |
phase_start and phase_end use the phases brainstorm, team-compose, plan, context, execute, review, verify, reflect. A hook_event names one of 12 hook events.
Trace event v2 fields
Lines written by capture-telemetry also carry ts, event, tool, specialist, payload_digest, ok, ms, and for loop events loop_layer, loop_round, loop_gate, loop_terminated. Additive guild.trace_event.v2 fields are optional:
span_id:sha256(run_id|event|ts|actor_id)[0:16].parent_span_id: fromGUILD_PARENT_SPAN_IDwhen threaded.tier: fromGUILD_TIER(cheap,mid,powerful).model: fromGUILD_MODEL, else the payload.tokens:{input, output, cached, cost_usd}, on LLM-call events only.payload_ref: path of the payload sidecar.
Run lifecycle markers, run_started and run_closed, are guild.trace_event.v1 lines appended to the same log.
The hooks
| Hook | Event | What it does |
|---|---|---|
capture-telemetry | UserPromptSubmit, PostToolUse, SubagentStop | Appends event lines. It skips the canonical write for PostToolUse, because post-tool-use already writes the validated tool_call line. Otherwise every tool call counted twice. The legacy mirror still receives it. |
run-trace-start | UserPromptSubmit | Emits run_started once per run. Later prompts are no-ops. With no resolvable run id it does nothing. |
run-trace-close | Stop | Closes the run: writes provenance.json, flips run.yaml status, appends run_closed. It acts only for a run that was started. |
Stop fires after every assistant turn, not only at session end. If a run was closed and then saw more tool activity, run-trace-close closes it again so provenance reflects the latest state. A run that stayed quiet is left alone.
All three hooks exit 0 and never block the session. The run id comes from GUILD_RUN_ID and is verified against the run's minted binding. Commands and skills start runs through hooks/run-trace.js start --command=.... A full run stays open for the Stop hook. A --run-class=lightweight run, such as status, starts and closes at once.
Telemetry text goes through secrets scrubbing first. The UserPromptSubmit prompt honors secrets_policy.fail_mode_telemetry.
guild-telemetry MCP
The server is read-only. It reads logs/v1.4-events.jsonl first, then falls back to legacy events.ndjson. v1.4 fields are normalized (status to ok, latency_ms to ms, hook_name to tool), so every tool works on both shapes. Bad lines are skipped. Output is sorted by run_id then ts.
| Tool | Arguments | Returns |
|---|---|---|
trace_list_runs | since?, limit?, cwd? | Runs with event_count, started_at, ended_at |
trace_summary | run_id, cwd? | The stored summary.md (source: "file"), else one synthesized in memory (source: "synthesized") |
trace_query | run_id?, event?, specialist?, since?, limit?, cwd? | Filtered events, each annotated with run_id |
trace_cost_rollup | run_id?, since?, cwd? | Token totals plus by_tier, by_model, by_specialist |
Cost rollup
trace_cost_rollup adds up guild.trace_event.v2 token usage. It merges two sources: the v2 tokens object (input, output, cached) and the v1.4 tool_call tokens_in / tokens_out. Each breakdown row is {key, input, output, cached, total}, sorted by total descending, then key. Events without token fields are skipped, and llm_event_count says how many counted. Omit run_id to roll up all runs. For tiering policy, read Cost and tiering.
Which project it reads
The root resolves in this order: the per-call cwd argument; the GUILD_TELEMETRY_CWD environment variable; the server process cwd. The Codex manifest launches the server with --no-cwd-fallback. In that mode the process-cwd step is removed, cwd must be absolute, and a call with no usable root fails with isError: true. Otherwise it would read the plugin's own .guild/.
Behavioral run analysis (upcoming beta)
In the upcoming beta (next) releases the plugin also analyzes what it
recorded. The
command surface is deterministic and filesystem-canonical: analyze one run,
compare several, or analyze an initiative or an independent-run group. A
read-only analyze inspect form shows a report's identity, inputs and
freshness without writing anything.
- Analysis artifacts land under
.guild/analysis/runs/**and.guild/analysis/comparisons/**; recommendations under.guild/recommendations/**. The benchmark dashboard reads these files; it never computes conclusions of its own. - Each report binds the exact inputs it was computed from. If a trace or sidecar changes afterwards, the report shows as stale and a decision on it is refused until it is recomputed.
- Conclusions are honest about coverage: an event class with no wired producer makes the dependent conclusion unobservable rather than guessed, and missing identity, snapshots, or required event classes lower completeness. Wiring more producers is ongoing work in the beta cycle.
- Recommendation writes are serialized and audited: decisions append to an activity log, and an interrupted multi-file write is rolled back before the next writer proceeds.
Limits
- The MCP server is optional:
scripts/trace-summarize.tsand plain filesystem reads work without it. - Payload sidecars are size-capped and share the legacy log's retention story. This page does not state a cap or a retention period.