shelbi zen
View as markdownshelbi zen <SUBCOMMAND> [OPTIONS]shelbi zen is the CLI face of Zen Mode.
The toggle subcommands (on, off, pause) flip the project's mode in
state.json and write a <ts> project=<project> mode=zen <prev> -> <new> reason=user:cli line to ~/.shelbi/events.log (the leading
project=<project> scope keeps one project's toggle from being read by
another project's orchestrator). status is read-only: it prints the current
mode and writes no event. The orchestrator reads those events to know when to
switch behavior.
The remaining subcommands are single-purpose primitives. The
orchestrator sequences them per the merge-conditions policy in the
project's zenmode.md
file; each one does exactly one thing and reports on stdout in a fixed,
parseable shape: probe prints JSON, scan prints one task id per line,
dry-run streams one line per decision, and pr-create, ci-watch, and
pr-merge lead with a single result line (pr-merge follows it with the
post-merge eligibility list). The same primitives are available to you at the shell when
you want to probe a branch or open a PR by hand without flipping the
global mode.
Every subcommand accepts the global -p / --project <PROJECT> flag, omitted
from the per-subcommand tables.
on
shelbi zen onTurn Zen Mode on. The orchestrator may auto-promote and auto-merge finished
work through the exact-provenance PR flow. Idempotent. Mirrors
Alt+Z in the TUI.
on, off, and pause each print the resulting zen status
block. Like probe, pr-create, and pr-merge, they refuse to run when the
hub daemon's version doesn't match the CLI's; status, scan, ci-watch, and
dry-run only warn.
off
shelbi zen offTurn Zen Mode off. Every promotion goes through manual review. In-flight workspaces keep going; nothing already running is cancelled.
pause
shelbi zen pausePause Zen Mode: no new auto-promotions, but tasks already on the Zen track may still complete their merge flow. Use this to triage incoming work yourself while preserving work already in motion.
status
shelbi zen statusPrint the current mode, the configured local check commands, the effective
CI timeout, the resolved danger-paths list (labeled project override, or with
the detected project shapes), any per-workflow zen: overrides, the last crash
(with its crash-record path, or never), the orchestrator's
last heartbeat, and the count of in-flight issues on the Zen track. Cheap and
safe to call repeatedly.
zen mode: on
checks:
- cargo test --workspace
ci timeout: 15m
danger paths (project override):
- .github/workflows/**
- Cargo.lock
last crash: never
orchestrator alive: 2026-10-29T14:24:00+00:00
in-flight zen issues: 1scan
shelbi zen scanPrint backlog task ids that are mechanically eligible for Zen
auto-promotion, one per line, in priority order. "Mechanical" means
not blocked on dependencies, not opted out via task frontmatter, and
no file overlap with anything currently in flight. It also skips tasks you
moved from todo back to backlog by hand, until you move them out of
backlog again. On a GitHub board, it only considers backlog
issues assigned to you, and lists nothing if your login can't be resolved.
Ties in priority are broken by id. File overlap is a heuristic: a file path
named in the task's body also appears in an in-progress task's body. The orchestrator's
prompt applies the judgment categories on top of this list. See
the judgment layer.
dry-run
shelbi zen dry-run [OPTIONS]Preview what Zen Mode would do without changing task, board, PR, or branch
state. On every tick the backlog scan and merge-conditions bar are evaluated,
and each "would have …" decision is logged to stdout, the dedicated dry-run
log (~/.shelbi/logs/zen-dryrun.log), and the activity feed.
No PRs, merges, or board moves happen. Use it before flipping Zen on
for real to confirm the policy matches your intent.
| Flag | Type | Default | Description |
|---|---|---|---|
--for <DURATION> | duration | run until Ctrl-C | Stop after this long. Accepts 30s, 5m, 2h, 1d, or a bare integer of seconds. |
--interval <DURATION> | duration | 5s | Override the per-tick interval. Same duration grammar as --for. Values below 1s are raised to 1s. |
probe
shelbi zen probe <TASK_ID>Run every readiness probe for the task and print the full report as pretty JSON. Covers the four pre-PR conditions the orchestrator checks before opening a PR: local checks, merge-conflict probe, diff size, and danger-path matches.
The task must be assigned to a workspace so the probe can locate the
repository that holds its named branch. Shelbi checks out that exact branch
commit in an isolated temporary worktree, so a newer task reusing the assigned
workspace is not modified or tested by mistake. A successful rebase advances
the durable task branch. The report freezes repository, repository_id,
base_branch, base_sha, integration_sha, and head_sha. Together those fields identify the
exact repository and workflow base used for every check and fact, plus the
reviewed task commit and exact squash candidate. Pass the same six values to
every later PR command.
The squash candidate (integration_sha) is always the base at base_sha plus
the branch's own changes. When the branch already contains the base, that is
exactly the branch's tree. When it does not (an unrebased or conflicted tip),
Shelbi builds the real three-way merge instead of taking a branch tree that
predates the base. diff_size measures base_sha to integration_sha, the
commit that actually lands, so a candidate that would revert work on the base
shows it there. When the three-way merge conflicts, merge_conflict names the
files and integration_sha is null: there is nothing to publish or land.
The probe fails closed (exit 3 with a JSON error naming the reason) instead of
reporting a candidate it can't vouch for: a branch whose history carries merge
commits and doesn't rebase cleanly onto the base, a candidate that would
drop or rewrite a file present at base_sha that the branch never touched, or a
local task ref that doesn't hold the published work.
That last case comes up when a card built on one machine is reassigned to a slot
on another, where the local ref can still sit where dispatch cut it. The error
names the published tip and the git fetch that brings the ref up to date. A
branch whose content already landed on the base under another commit isn't this
case: the probe reports it with branch_divergence.already_merged.
| Argument | Type | Default | Description |
|---|---|---|---|
<TASK_ID> | string | — | Task id to probe (required). |
pr-create
shelbi zen pr-create <TASK_ID> --match-repository <REPOSITORY> --match-repository-id <REPOSITORY_ID> --match-base-branch <BRANCH> --match-base-commit <BASE_SHA> --match-integration-commit <INTEGRATION_SHA> --match-head-commit <HEAD_SHA> [--match-published-head-commit <SHA>]Push the task's named branch and open a PR. Idempotent. If an open PR for the branch already exists, Shelbi updates the branch and returns its number only after the PR head matches the exact task branch commit that was pushed. Otherwise, it opens a new PR. Prints the PR number on stdout.
Exits 0 on success. Exits 75 with empty stdout when the push landed but
GitHub could not yet confirm the PR head; that's retry-safe, so re-run the same
command. Exits 1 on a provenance mismatch that must not be merged.
Reuse also requires the exact task branch, resolved workflow base branch and
commit, and origin repository identity. A same-named PR aimed at another
base or coming from another repository is rejected.
The six --match-* flags above are required. Copy them from the immediately
preceding probe report without re-resolving configuration or origin.
Shelbi rejects the operation if any part of that identity moved after the
probe, so mutable workflow, remote, or branch state cannot silently replace
what was reviewed.
A seventh flag, --match-published-head-commit <SHA>, is optional and only
pr-create reads it. Pass the probe report's published_head_sha when the
probe rebased the task branch onto a moved base — so head_sha is a
never-pushed local commit while the remote task branch still points at the
pre-rebase tip. It lets pr-create recognize that pre-rebase remote tip as the
flow's own prior state rather than a concurrent update. Omit it to preserve the
default acceptance rules. (ci-watch and pr-merge also accept the flag
because they share the same pinned-identity group, but neither acts on it.)
pr-create refuses to replace a remote task branch that has changes with an
integration commit that has none, and pushes nothing. If the work really landed
another way, close the PR by hand.
| Argument or flag | Type | Default | Description |
|---|---|---|---|
<TASK_ID> | string | — | Task id whose branch to push and PR (required). |
--match-repository <REPOSITORY> | string | required | Require the origin repository selector to equal probe repository. |
--match-repository-id <REPOSITORY_ID> | string | required | Require the immutable GitHub repository id to equal probe repository_id. |
--match-base-branch <BRANCH> | string | required | Require the PR target to equal probe base_branch. |
--match-base-commit <SHA> | string | required | Require the resolved target commit to equal probe base_sha. |
--match-integration-commit <SHA> | string | required | Require the published PR head to equal probe integration_sha. |
--match-head-commit <SHA> | string | required | Require the durable task branch to match probe head_sha. |
--match-published-head-commit <SHA> | string | — | Optional. Probe published_head_sha — the pre-rebase remote tip, recognized as the flow's own prior state when the probe rebased onto a moved base. |
ci-watch
shelbi zen ci-watch <PR_NUMBER> --match-repository <REPOSITORY> --match-repository-id <REPOSITORY_ID> --match-base-branch <BRANCH> --match-base-commit <BASE_SHA> --match-integration-commit <INTEGRATION_SHA> --match-head-commit <HEAD_SHA> [OPTIONS]Watch the PR's checks until they settle or the timeout fires. Prints one of:
green— every watched check passed; exit 0.red:<check>:<summary>— at least one check failed; exit 1.timeout— checks still pending when the deadline hit; exit 2.
Which checks it watches is auto-selected from the target branch: with branch-protection required checks configured, it watches only those; on an unprotected branch (or one with no required set) it falls back to every check reported on the PR and requires GitHub's merge state to be clean. When required rows exist, a blocked merge state keeps waiting for a required context that has not reported, while an optional failed check does not override passing required checks.
All six --match-* values must be the unchanged probe identity passed to
pr-create. Each poll verifies that original repository, base name and commit,
and head commit. Shelbi reads the PR identity and status rollup together,
including which contexts are required, and grades that one atomic snapshot.
There is no separate check-result read that a brief A-to-B-to-A head change
could confuse. Incomplete, paginated, or moved-head results fail closed and
the whole pinned flow must restart from a fresh probe.
The landing is pinned to what CI graded
probe, pr-create, and ci-watch keep a record of each task's flow in
<project>/zen-flow/<task-id>.json, so the done move that lands the task takes
no extra flags. While Zen is on or paused, the workflow's merge action reads
that record and refuses to land a probed task unless all of these hold:
ci-watchlast reportedgreenfor the commit the latest probe pinned.- The task's open PR is the one CI graded, and its head is still that commit.
- The base branch is still at the commit CI graded against.
A refusal fails the move and merges nothing, and the card stays where it is. To
recover, run shelbi zen probe <task-id> again and restart the flow. To override,
merge the PR by hand: the move then finds it already merged and completes.
The merge action also refuses a PR with an empty diff, with Zen on or off,
because every check passes on one and merging it lands nothing. The exception is
a workflow that produces no branch: none of its edges push, open a PR, merge, or
restack. If the work really landed another way, close the PR and move the card
with --skip-transition-actions.
| Flag | Type | Default | Description |
|---|---|---|---|
<PR_NUMBER> | integer | — | The PR number returned by pr-create (required). |
--match-repository <REPOSITORY> | string | required | Require probe repository on every snapshot. |
--match-repository-id <REPOSITORY_ID> | string | required | Require probe repository_id on every snapshot. |
--match-base-branch <BRANCH> | string | required | Require probe base_branch on every snapshot. |
--match-base-commit <SHA> | string | required | Require probe base_sha on every snapshot. |
--match-integration-commit <SHA> | string | required | Require the live PR head to stay at probe integration_sha. |
--match-head-commit <SHA> | string | required | Bind the candidate back to probe head_sha. |
--timeout <DURATION> | duration | project's zen.ci_timeout (default 15m) | Override the wait. Accepts 30s, 5m, 2h, 1d, or a bare integer of seconds. |
--task <TASK_ID> | string | — | Resolve the default timeout against the task's workflow's zen.ci_timeout (falling back to the project value). Without it, the project default is used directly. |
pr-merge
shelbi zen pr-merge <PR_NUMBER> --match-repository <REPOSITORY> --match-repository-id <REPOSITORY_ID> --match-base-branch <BRANCH> --match-base-commit <BASE_SHA> --match-integration-commit <INTEGRATION_SHA> --match-head-commit <HEAD_SHA>Land the prebuilt squash candidate after ci-watch reports green. Shelbi
verifies that integration_sha has sole parent base_sha and exactly the
three-way integration tree of head_sha onto it, then advances only refs/heads/<base_branch> from base_sha to
integration_sha with Git's compare-and-swap lease. A concurrent same-head PR
retarget cannot redirect that update. This atomicity applies to the remote ref
update, not to GitHub PR metadata.
Rulesets are checked rule by rule. Non-PR rules such as deletion and
non_fast_forward don't block the leased push. When the base branch has a
pull_request rule Zen can satisfy (no required approvals or code-owner
review, and squash allowed), Zen lands through GitHub's own squash merge of the
PR, pinned to integration_sha, instead of the leased push; the landed commit
is then a new GitHub-authored SHA. Classic branch protection (or protection
state that can't be read), a merge-queue rule, required approvals or
code-owner review, a disallowed squash method, auto-merge or a merge queue on
the PR, fork heads, or a non-squash merge_strategy all stop the flow: Zen
leaves the base unchanged and tells a human to review and merge through the
repository's required GitHub workflow.
On success it prints the landed SHA (sha-pending if GitHub hasn't recorded
its merge commit yet), deletes the task branch on the remote, and then prints
the next mechanically eligible backlog ids, or next eligible: none.
| Argument or flag | Type | Default | Description |
|---|---|---|---|
<PR_NUMBER> | integer | — | The PR number to merge (required). |
--match-repository <REPOSITORY> | string | required | Require probe repository. |
--match-repository-id <REPOSITORY_ID> | string | required | Require probe repository_id. |
--match-base-branch <BRANCH> | string | required | Require probe base_branch. |
--match-base-commit <SHA> | string | required | Require probe base_sha. |
--match-integration-commit <SHA> | string | required | Require probe integration_sha. |
--match-head-commit <SHA> | string | required | Require probe head_sha. |
Examples
Flip Zen on for the current project:
shelbi zen onSee the resolved danger-path list and configured local checks:
shelbi zen statusInspect what Zen would consider promoting right now (without promoting anything):
shelbi zen scanWatch the policy in motion for ten minutes without committing to it:
shelbi zen dry-run --for 10mRun the full pre-merge probe by hand on a finished branch and pipe it
through jq:
shelbi zen probe docs-write-cli-reference-pages | jq .Drive the full merge flow manually, one primitive at a time, exactly the sequence the orchestrator runs internally:
REPORT=$(shelbi zen probe docs-write-cli-reference-pages)
REPOSITORY=$(printf '%s\n' "$REPORT" | jq -r .repository)
REPOSITORY_ID=$(printf '%s\n' "$REPORT" | jq -r .repository_id)
BASE_BRANCH=$(printf '%s\n' "$REPORT" | jq -r .base_branch)
BASE_SHA=$(printf '%s\n' "$REPORT" | jq -r .base_sha)
INTEGRATION_SHA=$(printf '%s\n' "$REPORT" | jq -r .integration_sha)
HEAD_SHA=$(printf '%s\n' "$REPORT" | jq -r .head_sha)
PIN=(--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")
PR=$(shelbi zen pr-create docs-write-cli-reference-pages "${PIN[@]}")
shelbi zen ci-watch "$PR" "${PIN[@]}" --timeout 10m
shelbi zen pr-merge "$PR" "${PIN[@]}"Pause Zen during a release window without aborting in-flight merges:
shelbi zen pauseSee also
- Zen Mode — the full mental model, the high-confidence bar, and how to tune the judgment categories per project.
- Orchestrator — how the prompt template that drives Zen's policy is loaded.
- The events log — the
mode=zenandorchestrator:zen-*lines the toggles and primitives emit. - Review workspaces — the user-driven counterpart, where approving a reviewed task lands its branch without the Zen bar.