Zen Mode
View as markdownBy default the orchestrator is the scheduler and you are the priority-setter + reviewer. Zen Mode flips it: the orchestrator takes the lead. It triages mechanically-eligible backlog into todo on its own and lands finished work past a pre-defined confidence bar.
The mental model is lead vs. scheduler. Zen on, the agent makes calls; you redirect when you disagree. Zen off (the default), the agent waits to be told. Same prompt, same workspaces, same board. The only thing that changes is who initiates.
Turning it on
Three ways to flip the switch:
- CLI:
shelbi zen on,shelbi zen off,shelbi zen pause. - TUI:
Alt+Ztoggles between On and Off from any sidebar focus. - Status check:
shelbi zen statusprints the current mode, configured local checks, resolved danger paths, the last crash timestamp (if any), and the count of in-flight Zen tasks.
When Zen is on, the sidebar shows a green ZEN ON pill anchored to the bottom of the workspaces column. That pill is the at-a-glance "the agent is in lead mode" signal. If it's not there, Zen is off or paused.
First-run hotkey probe
Some terminals (notably macOS Terminal.app, where Option/Alt types
accented characters) swallow Alt+Z. The first time you launch the
sidebar, Shelbi runs a tiny probe: it shows a centered overlay and
waits a few seconds for Alt+Z. If it arrives, the binding is saved
and you never see the prompt again. If it doesn't, a chooser pops up
with three fallback chords:
Ctrl+\Ctrl+GCtrl+Shift+Z
…plus a "skip" option that leaves the toggle unbound (use the CLI
instead). Your pick is persisted to ~/.shelbi/shelbi.yaml.
Turning it off
Three levels of "stop":
shelbi zen off(orAlt+Ztoggle): hard off. No new auto-promotions, and the orchestrator stops initiating the merge bar on anymerge-action transition. Anything already mid-flight in the conditions flow runs to completion on its current trajectory.shelbi zen pause: softer. No new auto-promotions, but the merge bar keeps applying to in-flight tasks that reach amerge-action transition. Use this when you want to triage incoming work yourself without losing validation already in motion.- Per-task pull-back:
shelbi task move <id> --to backlogfrom a handoff status pulls a specific card out of Zen's hands without changing the global mode.
Crash recovery
A crash disables Zen — on purpose
If the orchestrator pane dies mid-session (kill signal, SIGHUP,
machine power loss), Zen Mode auto-disables on the next orchestrator
start. The wrapper script around the orchestrator runner writes a
heartbeat every 60s; if the previous run ended without a graceful
exit and Zen was on, the next start flips it off, writes a
zen=off reason=crash-recovery event line, and prints a stderr
warning so you see it immediately.
Re-enable manually with shelbi zen on once you've reviewed any
in-flight work. The orchestrator's bootstrap also scans recent event
lines for this pattern and calls it out in its first reply.
The zenmode.md file
This is the power-user invitation. Both halves of Zen policy, the auto-promote judgment categories and the merge conditions, live in one user-owned file per project:
~/.shelbi/projects/<name>/zenmode.md(For an in-repo project it resolves to <repo>/.shelbi/zenmode.md
instead.)
zenmode.md is the live source of truth for what Zen Mode means in
that project. The orchestrator reads it in full at bootstrap and
re-reads it periodically, prompted by the heartbeat, to keep its
behavior aligned with your intent. The Rust side never inspects the
prose. The file exists purely for the orchestrator, so you can edit it
freely to tune Zen, and your changes take effect without a rebuild.
shelbi reload preserves your edits to
zenmode.md, leaving your prose intact.
The first line is the heartbeat summary
The first line of zenmode.md is the one-line summary the heartbeat
echoes back to the orchestrator. While Zen is on, every few ticks a
zen=on heartbeat carries that
first line verbatim as a live reminder of what Zen means here, read
fresh each time so an edit shows up immediately. Roughly once an hour
the heartbeat instead injects a fuller "re-read zenmode.md now"
instruction, so the orchestrator refreshes the full policy before its
next Zen decision rather than acting on a faded memory.
Because that first line is what gets re-injected, keep it a single accurate line: if you change what Zen means below, change the first line too. It is the fastest lever on the orchestrator's live behavior.
The file shape
A zenmode.md is the summary line, then the policy sections the
orchestrator applies. This is the exact file shelbi writes for a new
project:
Zen: auto-promote eligible backlog, review each handoff yourself (diff vs the task's acceptance criteria + scope), and run the exact-provenance merge flow without asking.
# Zen Mode policy
This file is the source of truth for what Zen Mode means in **this** project.
The orchestrator reads it (and re-reads it periodically, prompted by the
heartbeat) to keep its behavior aligned with your intent. The Rust side never
inspects the prose below, so edit it freely to tune Zen for your project.
The **first line of this file is the one-line summary** the heartbeat echoes
back to the orchestrator every few ticks. Keep it a single line and keep it
accurate: if you change what Zen means here, change that line too, because that
is what gets re-injected as a live reminder.
When Zen is on the orchestrator acts as lead rather than scheduler. It may:
1. Auto-merge work that passes the merge-conditions flow below.
2. Auto-promote `backlog`-category tasks into `ready` when they fit at least
one of the judgment categories below.
When Zen is `paused` it stops starting new auto-promotions but keeps applying
the merge-conditions flow to in-flight tasks that reach `handoff`. When Zen is
`off` the orchestrator is a plain scheduler: it dispatches what you promote and
leaves handoffs for you.
## Auto-promote judgment categories
The mechanical eligibility scan (`shelbi zen scan`) hands the orchestrator
`backlog`-category task ids that are safe from a state-machine standpoint: not
blocked, not opt-out, no file overlap with anything in flight. From that list,
**only auto-promote** a candidate if **at least one** of these is true:
1. **It's the kind of work the user generally trusts you with.** Look at the
`done`-category column. Does the user routinely accept tasks of this shape
without changes? (Examples: docs typo fixes, dependency bumps, content
sweeps that match a recently-stated convention.) If their done-history shows
pattern acceptance, this is in scope.
2. **It's part of fixing an issue the user recently raised.** Did the user
mention this bug, feature, or concern in conversation in the last few turns?
Tasks that respond to something the user explicitly asked for are in scope.
3. **It's part of a larger body of work the user explicitly kicked off.** If
the user filed a batch of related tasks (e.g. 13 vs-pages, a multi-step
refactor), the remaining items in that batch are in scope.
If a candidate fits none of these, **leave it in `backlog`** and surface it in
your next user-facing reply: *"I considered promoting `<task>` but wasn't sure
if it fits your intent, want me to?"* Also emit
`reason=orchestrator:zen-decline reason-text=<short>` on the task so the
activity feed shows what you considered.
For ones you do promote, run `shelbi task move <id> --to <ready-status>
--reason "orchestrator:zen-promote category=<n>"` (`<ready-status>` is the
literal `ready`-category status name from the active workflow, `todo` in the
default) and then dispatch per the normal auto-dispatch contract.
## Merge conditions
**First, the review-workspace gate.** If the project declares review
workspaces, a task reaching `handoff` is destined for a **human** to inspect on
a running server, that's the entire point of the review workspace. So do
**not** run the merge-conditions flow on it, and do **not** merge it: leave it
in the review status for the human to load from the sidebar and resolve (there
is no `shelbi review` command for you to run). The human accepts by moving it
to `done` (which is when it merges per existing rules) or bounces it back to
`ready`. Zen never auto-merges a review-routed task out from under the human.
Only when the project has **no** review workspace does `handoff` mean "ready to
auto-merge", the case the rest of this section covers.
When a task enters `handoff` and Zen is on (and the project has no review
workspaces), run `shelbi zen probe <task-id>` to get a JSON report covering
local checks, merge conflicts, diff size, and danger-path matches. A passing
mechanical probe is necessary but not sufficient; your own review of the diff
(step 5) is what checks the branch against the task's intent. Apply these
conditions to the report:
1. **All `local_checks` must have `exit_code == 0`.** If any failed, leave in
`handoff` and emit `reason=zen:failed-checks` with the failing command +
output tail.
2. **`merge_conflict.conflicts` must be `false`.** Otherwise emit
`reason=zen:merge-conflict` with the files.
3. **`diff_size.files` <= 30 AND `diff_size.lines_added + lines_removed` <=
2000.** Otherwise emit `reason=zen:diff-too-large` with the stats.
4. **`danger_paths.matched` must be empty.** Otherwise emit
`reason=zen:danger-path` with the paths.
If all four pre-PR probe conditions pass, review the diff against the task
yourself before opening a PR; only if your review passes do the
pr-create/ci-watch/pr-merge steps run:
5. **Review the diff against the task.** A green mechanical probe proves the
branch builds and does not conflict; it does not prove the branch does what
the task asked. Read the task's acceptance criteria and the branch diff
(`git diff <base_branch>...<branch>`) yourself and confirm:
- the diff satisfies every acceptance criterion the task states;
- the diff stays within the task's declared scope, with no unrelated files
and, in particular, no large deletions or reverts of code the task never
mentions (the signature of a branch cut from a stale base);
- the branch carries real new commits ahead of `<base_branch>` and its
content is not already present in the base under a different commit (a
branch whose diff is dominated by undoing recently-merged work, or whose
change is already merged under another SHA, must never be merged).
You are the reviewer here, not the builder: a workspace developer agent
wrote this code, so reviewing it yourself is not grading your own work.
Read the criteria and diff with fresh eyes and don't wave a branch through
just because you dispatched it. If it fails any check, bounce the task to
`ready` with concrete findings and emit `reason=zen:review-reject` with a
one-line summary. For a project that wants a genuinely independent,
de-biased reviewer instead of this self-review, wire a `qa`/`adversarial`
gate into the workflow (a first-class status) rather than reviewing here.
6. Freeze the probe's `repository`, `repository_id`, `base_branch`, `base_sha`,
`integration_sha`, and `head_sha`. Run `shelbi zen pr-create <task-id> --match-repository <repository> --match-repository-id <repository_id> --match-base-branch <base_branch> --match-base-commit <base_sha> --match-integration-commit <integration_sha> --match-head-commit <head_sha>`
using those exact values, and also pass `--match-published-head-commit <published_head_sha>`
from the probe's `published_head_sha` field. When the probe rebased the branch
onto a moved base, `head_sha` is a local-only commit while `published_head_sha`
is the pre-rebase tip the remote branch still points at; that flag lets
pr-create replace the tip it reviewed instead of refusing it as a concurrent
update. Capture the PR number it prints. Exit code 75 means the push landed
but GitHub's PR view had not caught up yet: re-run the exact same command (it
is idempotent). Exit code 1 is a real mismatch: leave the task in `handoff`
and re-probe.
7. Run `shelbi zen ci-watch <pr-number> --match-repository <repository> --match-repository-id <repository_id> --match-base-branch <base_branch> --match-base-commit <base_sha> --match-integration-commit <integration_sha> --match-head-commit <head_sha> --timeout 15m`
using that same probe identity. Stdout is `green`,
`red:<check>:<summary>`, or `timeout`. The condition: must be `green`.
Any identity movement fails closed; never recompute or substitute a probe
field after CI begins.
8. If green, run `shelbi zen pr-merge <pr-number> --match-repository <repository> --match-repository-id <repository_id> --match-base-branch <base_branch> --match-base-commit <base_sha> --match-integration-commit <integration_sha> --match-head-commit <head_sha>`
with that same identity. The command verifies the candidate has sole parent
`base_sha` and the exact tree of `head_sha`, then atomically advances only
`refs/heads/<base_branch>` from `base_sha` to `integration_sha` with Git's
compare-and-swap lease. If branch protection, rulesets, a merge queue, a
fork, permissions, or a non-squash strategy prevent that exact-ref path,
leave the task in `handoff` and follow the command's human-review action.
Only after a candidate SHA result, move the task into `done` and emit
`reason=orchestrator:zen-merge` with the merge SHA.
9. If red or timeout: leave in `handoff`; emit `reason=zen:failed-checks` (red)
or `reason=zen:ci-timeout`.
## Customization
Edit this file to tune Zen Mode per project. The prose is the source of truth
for both auto-promote and merge policy, and for the one-line summary at the
top.
- **Summary line** — rewrite the first line to match whatever policy you set
below. It is what the heartbeat re-injects, so it is the fastest lever on the
orchestrator's live behavior.
- **Auto-promote judgment categories** — add ("anything tagged `automation:`
is always in scope"), tighten ("only auto-promote if the user has accepted
>=3 tasks of this shape without changes"), or replace the list entirely.
- **Merge conditions** — raise/lower the diff-size thresholds, accept partial
CI red ("if only the integration-test job is red, treat as green"), add new
pre-merge probes ("before merging anything touching `migrations/`, also
require a passing `shelbi db dry-run`"), or change which conditions are
strict vs warnings.The default ships with these categories and conditions filled in. Edit the sections in place to reshape them.
Pre-merge review is the orchestrator's own
Step 5 of the merge conditions is a self-review: the orchestrator reads
the task's acceptance criteria and the branch diff itself and confirms the
branch does what the task asked before opening a PR. It does not spawn a
separate evaluator subagent for this. The "don't let the builder grade its
own work" worry does not apply, because the orchestrator is not the builder:
a workspace developer agent wrote the code, and the
orchestrator already owns the review column under Zen and holds the task's
full context. A green mechanical probe proves the branch builds and does not
conflict; the self-review is what proves it satisfies the task's intent and
stays in scope. On failure the orchestrator bounces the task back to ready
with findings (reason=zen:review-reject).
If you want a genuinely independent, de-biased reviewer rather than the
orchestrator reviewing its own dispatch, that is a first-class
qa/adversarial agent
wired into the workflow as a dedicated status, not an ad-hoc subagent.
Give the status owner: agent and agent: qa (or adversarial-review), with
a pass edge forward and a plain bounce edge back to in-progress:
# workflows/default.yaml
statuses:
- { id: in-progress, owner: agent, agent: developer }
- { id: qa-review, owner: agent, agent: qa } # the gate
- { id: review, owner: user, agent: orchestrator }
transitions:
- { from: in-progress, to: qa-review, actions: [push_branch, open_pr] }
- { from: qa-review, to: review, actions: [] } # pass: forward
- { from: qa-review, to: in-progress, actions: [] } # bounce: backThe full walkthrough (declaring the status in
statuses.yaml, referencing it from the
workflow, and why the bounce edge must be
listed) is in
Add it to a workflow.
Relationship to the orchestrator prompt
The judgment categories and merge conditions live in zenmode.md, not in
the orchestrator's prompt. The ## Zen Mode section of
agents/orchestrator/instructions.md tells the orchestrator to treat
zenmode.md as the source of truth and to re-read it on the heartbeat cues
above. So
edit zenmode.md to tune Zen policy, not the prompt. The
orchestrator instructions
remain yours to customize for how the orchestrator reacts to events,
but the Zen policy itself belongs in zenmode.md.
A starter override that tightens Zen's judgment, edited directly in
zenmode.md:
Zen: auto-merge docs/content and automation-tagged tasks; hold everything else for me.
## Auto-promote judgment categories
Promote a candidate only if **at least one** is true:
1. **Tagged `automation:` in the title.** Always in scope: these are
bot-filed PR-bumps and lint sweeps we have years of acceptance
history on.
2. **Touches only `docs/**` or `site/content/**`.** Content edits are
reversible and never reach prod.
3. **The user has accepted ≥3 tasks of the same shape (same
verb-prefix, same target directory) without changes in the last
week.** Track this against the `done` column.
## Merge conditions
(Inherit from the default; see the bundled template.)Note the first line changed to match the tighter policy. That new summary is what the heartbeat now re-injects.
Other places to tune, all in the same file:
- Raise/lower the diff thresholds: change
files ≤ 30to whatever fits the project's typical PR size. - Accept partial CI red: "if only the integration-test job is red, treat as green" is a common project-specific carve-out.
- Add pre-merge probes: "before merging anything touching
migrations/, also require a passingshelbi db dry-run." - Replace categories entirely with a project-specific taxonomy.
The Rust side never parses any of this, so the prose is the source of
truth. That is the whole design: Zen's policy is one markdown file you
own. For the CLI that flips Zen on and off, see
shelbi zen.
What Zen does: two paths
Zen Mode does exactly two things the default scheduler doesn't:
- Auto-merge: when a task is queued for a workflow transition
whose
actions:includemerge, Zen runs the high-confidence bar below. If everything passes, it lands the reviewed head on the exact probed base and moves the task to the transition'stostatus. - Auto-promote: when a workspace frees up, Zen scans the backlog
for tasks that are mechanically eligible (see
shelbi zen scan) and then applies the judgment categories from the orchestrator's prompt to decide which ones to promote into todo.
The mechanical layer is in Rust. The judgment layer is in the orchestrator's prompt, which means you own it and can tune it per project. That's the most important point on this page.
The high-confidence bar
The bar fires on any workflow
transition whose actions: list includes merge, not on the
Review column per se. The canonical default workflow happens to put
merge on the Review → Done edge, so in practice a task that hands off
to review trips the bar. But a
trunk-based workflow that skips Review entirely and merges straight
from InProgress → Done runs through the same probe. Gating is
action-based, not status-pair-based. See What fires Zen Mode's
high-confidence bar.
When such a transition is queued, the orchestrator runs
shelbi zen probe <task-id> (which threads the task's workflow
through probe_in_workflow so per-workflow overrides take effect; see
below) to get a single JSON report covering every dimension below.
Then it applies the conditions in order. Any failure leaves the task
where it was, with a tagged reason event so the activity feed shows
what happened.
merge-transition -> probe -> local checks -> merge conflict -> diff size
-> danger paths -> exact-provenance PR flow -> landingThe conditions, in order:
- All
local_checkspass (exit 0). These are the commands you listed underzen.checks.localin the project YAML, or under the workflow's ownzen.checksblock when one is set (see per-workflow overrides). - No merge conflicts with the transition's target branch (the
workflow's resolved
base_branch, or a per-transitiontarget:if one is set). - Diff size within bounds: by default
files ≤ 30andlines_added + lines_removed ≤ 2000. Tunable in the prompt. - No danger-path matches: anything matching the resolved
danger-paths list (built-ins + detected shape + project
zen.danger_paths.extend+ the active workflow'szen.danger_pathsoverride, if any) bails out. - PR opens cleanly via
shelbi zen pr-create <id> --match-repository <repository> --match-repository-id <repository_id> --match-base-branch <base_branch> --match-base-commit <base_sha> --match-integration-commit <integration_sha> --match-head-commit <head_sha>. All six values are copied directly from the probe report. The PR is returned only after its remote identity matches the exact repository, resolved workflow base name and commit, task branch, reviewed head, and prebuilt integration commit. The PR head is the integration commit so CI runs on the exact object eligible to land. - CI is green:
shelbi zen ci-watch <pr> --match-repository <repository> --match-repository-id <repository_id> --match-base-branch <base_branch> --match-base-commit <base_sha> --match-integration-commit <integration_sha> --match-head-commit <head_sha> --task <id>polls the PR number returned by that creation step, using the same complete probe identity. One GraphQL response per poll binds the repository, base, PR head, latest commit, required contexts, their results, and GitHub's merge state. A blocked state prevents a not-yet-reported required context from being mistaken for no check, while an optional failed check does not override passing required checks. The--taskflag resolves the timeout against the task's workflow (zen.ci_timeoutoverride, then project default of15m); plain--timeout <duration>still overrides explicitly. Anything red or a timeout bails. Each poll compares against the frozen probe identity and grades only that atomic snapshot, so persistent movement and a brief A-to-B-to-A change both bail instead of authorizing a merge. A snapshot that would require pagination also fails closed. - Landing via
shelbi zen pr-merge <pr> --match-repository <repository> --match-repository-id <repository_id> --match-base-branch <base_branch> --match-base-commit <base_sha> --match-integration-commit <integration_sha> --match-head-commit <head_sha>, passing the same probe identity again. The candidate has sole parentbase_shaand exactly the tree ofhead_sha. Shelbi then updates onlyrefs/heads/<base_branch>frombase_shatointegration_shawith--force-with-lease. A same-head PR retarget cannot redirect that ref update. The lease is atomic at the remote Git ref, not across GitHub PR metadata. Protected branches, required-PR rules, forks, merge queues, and non-squash strategies fail closed with a human-review instruction.
The probe's repository, repository_id, base_branch, base_sha,
integration_sha, and head_sha form the contract for the whole sequence. They are not recomputed
between steps: PR creation publishes into that repository and base, CI grades
that identity continuously, and the landing boundary verifies it again. A
workflow, origin, base, or head change requires a fresh probe and a new flow.
Each step uses a single-purpose CLI primitive. The Rust side does mechanical I/O; the orchestrator's prompt holds the policy. That separation is what lets you raise/lower the bar by editing prose instead of recompiling.
Example project-level config:
# <project>.yaml
zen:
checks:
local:
- 'cargo test --workspace'
- 'cargo clippy --workspace --all-targets -- -D warnings'
ci_timeout: 15m
danger_paths:
extend:
- 'site/public/install.sh'
- 'crates/shelbi-state/src/migrations/**'Per-workflow overrides
Each workflow may carry its own zen:
block that overrides any subset of the three project-level Zen knobs:
checks, ci_timeout, danger_paths. Anything you don't set falls
back to the project default, so a workflow can swap just its
checks without restating the rest.
# ~/.shelbi/projects/<project>/workflows/research.yaml
name: research
description: Long-running investigations — no code-style checks, longer CI.
# References statuses by id from statuses.yaml (drafting is declared there
# with name: Drafting, category: active).
statuses:
- { id: drafting, owner: agent, agent: developer }
- { id: review, owner: user }
- { id: done, owner: user }
zen:
checks:
local:
- 'pytest -k research'
ci_timeout: 3600 # seconds — 1h, vs the project's 15m
danger_paths:
override:
- 'fixtures/**'Why this exists: a research: workflow doesn't want the project's
cargo clippy -D warnings gate, and an integration-test-heavy
workflow may legitimately need an hour of CI. Pinning those to the
workflow rather than the project lets one project's default
workflow stay strict while another workflow in the same project runs
on relaxed rules.
Resolution rules:
checks: when set on the workflow, the workflow's list replaces the project's list outright. Per-taskchecks_only/checks_additionalstill apply on top (see per-task overrides).ci_timeout: when set on the workflow, the workflow's value wins.shelbi zen ci-watch <pr> <all-probe-match-flags> --task <id>resolves through this override; without--task, the project default is used.danger_paths: uses the sameextend:vsoverride:shape as the project block.extend:adds to the project's resolved list (built-ins + detected shapes + projectextend).override:replaces the whole resolved list for tasks in this workflow.
An empty block (zen: {}) parses but overrides nothing, semantically
identical to omitting the block.
The judgment layer
The high-confidence bar decides whether a finished branch is safe to land. The judgment layer decides which backlog items the orchestrator should promote on its own in the first place.
The Rust side (shelbi zen scan) gives the orchestrator a list of
backlog ids that are mechanically safe: not blocked on
dependencies, not explicitly opted out, no file overlap with anything
currently in flight. That list is intentionally generous. It doesn't
know what the user wants.
The orchestrator's prompt then applies three judgment categories that ship as the default. A candidate gets promoted only if at least one is true:
- It's the kind of work the user generally trusts you with.
Pattern from the
donecolumn: does the user routinely accept tasks of this shape without changes? - It's part of fixing an issue the user recently raised. The user mentioned this bug or feature in the last few turns.
- It's part of a larger body of work the user explicitly kicked off. A multi-step refactor, a sweep across a dozen pages: the remaining items in that batch are in scope.
If nothing fits, the candidate stays in backlog and the orchestrator
calls it out in its next reply ("I considered promoting <task> but
wasn't sure. Want me to?"). It also emits
reason=orchestrator:zen-decline reason-text=<short> so the
activity feed shows what was considered and why.
Per-status automation is declarative
Zen's judgment (which backlog items are in scope, where the confidence
bar sits) is prompt prose, covered below. But the more basic question of
which statuses the orchestrator is allowed to act on, and which agent it
uses, is not prompt prose at all. It's the
owner and agent fields on
each workflow status.
Two examples:
- "Auto-merge but don't auto-promote." Make the merge-bearing handoff
status
owner: agent, but leaveBacklogasowner: user. The orchestrator lands finished work while your triage queue stays yours. It's twoowner:values. - "Review with QA before a human signs off." Give the
Reviewstatusowner: agent, agent: qa. Theqaagent runs the review pass automatically; a downstreamowner: userstatus holds the final accept. Which agent reviews is data, not a prompt instruction.
Flipping Zen on/off still gates whether the orchestrator exercises this autonomy at all. But once Zen is on, the workflow YAML (not the prompt) decides per status whether the orchestrator acts and which agent it loads. The prompt holds only the genuinely judgment-shaped policy: scope and the confidence bar. See owners and agents for the field semantics and the four validation rules.
Per-task overrides
Sometimes a single task needs different treatment than the project
default: kept on the manual-review path because it's sensitive, or
given an extra check the rest of the project doesn't need. Put a zen:
block in the task's frontmatter:
---
id: refactor-payment-flow
title: Refactor payment processor adapter
column: backlog
priority: 0
zen:
enabled: false # opt this task out of Zen entirely
------
id: docs-rewrite-tutorial
title: Rewrite the getting-started tutorial
column: backlog
priority: 0
zen:
checks_additional: # extend the project checks for this task
- 'cargo test --package shelbi-docs'
------
id: ui-restyle-sidebar
title: Restyle the sidebar pill
column: backlog
priority: 0
zen:
checks_only: # replace project checks for this task
- 'cd site && npm run lint && npm run build'
---Resolution rules:
enabled: falsekeeps the task on the manual-review path even with Zen on;enabled: trueopts a task in even when Zen is off (rare but supported).checks_onlytakes precedence overchecks_additional. If both are set,checks_onlywins andchecks_additionalis ignored.checks_additionalextends whichever list the workflow layer resolved to: workflowzen.checks.localif set, else projectzen.checks.local.- Absent both, the task inherits that resolved list verbatim. Full
precedence: per-task
checks_only> per-taskchecks_additional> per-workflowzen.checks.local> projectzen.checks.local.
Configuration reference
Zen Mode reads config from two places: the project YAML (project-wide defaults) and per-workflow YAMLs (workflow-scoped overrides, covered in Per-workflow overrides above).
The full project-level schema:
zen:
# Shell commands run from the worktree root before the merge bar.
# Each must exit 0. Default: empty.
checks:
local:
- 'cargo test --workspace'
- 'cargo clippy --workspace --all-targets'
# How long to wait for required GitHub checks. Accepts seconds as
# a bare integer, or a string like `30s`, `5m`, `2h`, `1d`.
# Default: 15m.
ci_timeout: 15m
# Glob patterns considered too sensitive to auto-merge. Choose
# one of `extend:` (keep built-ins + detected-shape paths and add
# yours) or `override:` (replace everything with your list).
# Default: an empty `extend:` — built-ins + detected shape only.
danger_paths:
extend:
- 'site/public/install.sh'
- 'crates/shelbi-state/src/migrations/**'
# or to replace everything Shelbi knows about:
# override:
# - 'config/**'
# - 'deploy/**'The built-in danger paths Shelbi always includes (in extend mode):
.github/workflows/** scripts/install.sh *.yaml *.yml
LICENSE package-lock.json Cargo.lockOn top of that, Shelbi detects the project shape from sentinel files in your repo root and adds shape-specific paths:
| Shape | Detected from | Adds |
|---|---|---|
| cargo workspace | Cargo.toml with [workspace] | Cargo.toml, Cargo.lock, rust-toolchain.toml, .cargo/config.toml |
| node / next.js | package.json | package.json, package-lock.json, next.config.*, vercel.json, .npmrc |
| github | .github/ | .github/CODEOWNERS, .github/dependabot.yml |
| docker | Dockerfile or compose.yaml | Dockerfile, compose.yaml |
| shelbi | shelbi.yaml or .shelbi/ | .shelbi/**, shelbi.yaml |
Run shelbi zen status to see the resolved list for your project
(it labels the detected shapes inline so you know which contributions
came from where). When any workflow declares its own zen: block, the
status output also lists each one and which dimensions it overrides,
with the resolved per-workflow values printed underneath.
The activity feed
Zen-driven events get distinct visual treatment in the sidebar's activity feed: a small ZEN avatar badge in the same column the per-workspace avatars use, and a subtle dark-tinted row background so machine-driven actions are visually separable from user actions in the same stream.
The reason strings the feed recognizes (each renders with its own specific phrasing):
| Reason | Meaning |
|---|---|
orchestrator:zen-promote category=<n> | Promoted a backlog task to todo under judgment category <n>. |
orchestrator:zen-decline reason-text=<short> | Considered a candidate, decided not to promote. |
orchestrator:zen-merge sha=<short> | Auto-merged a reviewed task to main. |
zen:failed-checks cmd=<…> exit=<…> | Local check failed; task stays in review. |
zen:diff-too-large files=<…> lines=<…> | Diff exceeded the size threshold. |
zen:danger-path paths=<…> | One or more danger-path globs matched. |
zen:merge-conflict files=<…> | Branch wouldn't merge cleanly. |
zen:ci-timeout duration=<…> | CI didn't settle within ci_timeout. |
These tags also surface in ~/.shelbi/events.log. Grep there for a
full audit trail across all projects.
The orchestrator also wakes on the project's periodic heartbeat line
(see the events log). For Zen
Mode that's the recurring nudge to check in-flight reviews and stuck
auto-promotions when nothing else has fired. The merge bar can spend
minutes waiting on CI, and heartbeats keep the orchestrator from
sleeping through the settle. The cadence comes from the heartbeat
key in project.yaml (default 3m).
See also
- Project config — the
zen:block's field reference (checks.local,ci_timeout,danger_paths). shelbi zen— CLI reference for every Zen subcommand.- Workflows — how a workflow's
transitions:declares which edges fire the merge bar, and where the per-workflowzen:override block lives. - Orchestrator — how the underlying scheduler is wired and where the prompt template lives.
- The events log — the canonical
format of the
mode=zenandorchestrator:zen-*lines Zen emits. - Workflows
— what
ReviewandDonemean in the default flow Zen accelerates, and how custom workflows declare their ownhandoffanddonestatuses.