↑ subaud · local-first · MIT

Rules

Rules propose. You decide.

A standing rule is a filter plus a proposed decision. Its matches become proposals that wait for you, exactly like the agent's. A rule never overrides a decision, never proposes for an item that already has one, and never proposes again an item you rejected unless the rule changes. This page is the reference: the file, the fields and operators, the until forms, and which decisions each kind allows.

the file

A rule is a TOML file.

Each rule lives at rules/<id>.toml in your casebook repo. The id is lower-case letters, digits and hyphens. The page writes these files for you; you can also edit them by hand.

id = "landed-branches"
name = "Landed branches → delete"
status = "draft"                      # draft | active
created_by = "you"                    # your login, or pi:<session>
created_at = 2026-09-27T10:00:00Z
edited_at = 2026-09-27T10:00:00Z     # last edit of the conditions or the proposal

[[match]]                             # all conditions must hold
field = "kind"
op = "is"
value = "branch"
[[match]]
field = "landed"
op = "is"
value = "all-machines"
[[match]]
field = "worktree"
op = "is-not"
value = "dirty"

[propose]
disposition = "delete"
until = ""
note = "landed ({how}); restore tip {tip}"

[[exclude]]
key = "branch:you/galley@feat/headcount-licensing"
reason = "keep for reference"
by = "you"
at = 2026-09-27T10:05:00Z

[propose] must be a decision valid for every kind the rule can match, as its kind conditions say. With no kind condition that leaves only keep, wait and ignore. A rule that breaks this is not valid: it cannot be activated, and an active one is skipped with a notice.

The note can use {how} (how a branch landed), {tip} (its tip commit), {age}, {repo} and {title}.

fields

What a condition can test.

Each [[match]] names one of these fields. An unknown field, operator or value is a validation error, shown on the rule and refused from the agent.

fieldtypevalues
kindenumrepo · pr · issue · branch · worktree
repotextowner/name
ownertexta GitHub login
relationenumoutgoing · incoming · own
statusenumnew · to-apply · waiting · due · done · drift · conflict
directionenumoutgoing · incoming · own
authortexta GitHub login
botboolthe author is a bot
titletextthe only field that takes matches, a regular expression
labeltexta label name
agedurationtime since the item was created
pusheddurationtime since the last push
updateddurationtime since the last update
landedenumall-machines · some-machines · none · unknown
landed-howenumdefault-branch · merged-pr; a branch can carry both, and is means it contains the value
gone-upstreamboolthe branch merged and its remote ref is gone
unpushedboolthe branch has commits on no remote
dirtybooluncommitted changes
worktreeenumdirty · clean · none
archivedboolthe repo is archived on GitHub
forkboolthe repo is a fork
open-prscountopen pull requests
open-issuescountopen issues
has-decisionboolthe item has a recorded decision
policy-hittexta policy that flags the item: outgoing-stale, incoming-no-reply, dormant, unpushed

landed is squash-aware: a branch has landed when its tip is in the default branch, or matches or is an ancestor of the head of a merged pull request. It is worked out on each machine and combined across their snapshots.

operators

Operators by type.

typeoperatorsvalue
enum, textis, is-not, in, not-inin and not-in take a comma-separated list. Enum values are checked.
titlethe same, plus matchesa regular expression that must compile
boolis, is-nottrue or false
countis, is-not, gt, gte, lt, ltea whole number, 0 or more
durationolder-than, newer-than<n>h, <n>d or <n>w, such as 90d

until

Conditions on a decision.

A decision can carry an until. wait and watch require one. While it is unmet the item is waiting and stays quiet; when it is met, the item is due and comes back to Attention.

formmet when
date(YYYY-MM-DD)the date has come
merged(<pr key>)that pull request merged, such as merged(pr:owner/repo#97)
closed(<pr or issue key>)that pull request or issue closed
inactive(<n>h|<n>d|<n>w)no activity for that long, such as inactive(90d)
released(<repo key>)the repo published a release after the decision

dispositions

What each kind allows.

The same eight dispositions everywhere, filtered by what makes sense for the kind. A decision, a proposal and a rule's [propose] are all checked against this table.

kinddispositions
repokeep · archive · delete · wait · watch · ignore
prkeep · close · merge · wait · watch · ignore
issuekeep · close · wait · watch · ignore
branchkeep · delete · wait · watch · ignore
worktreekeep · delete · wait · ignore

Status is computed, never stored: new has no decision; to-apply is decided but not yet reflected in the world; waiting has an unmet until; due has a met one; done matches what was decided; drift was done and then diverged; conflict means two machines decided the same item between syncs.

lifecycle

Draft, active, and the Rules section.

draftEditable, and no effect. propose once turns its current matches into one-off proposals and leaves it a draft.
activeProposes every match now, then new matches after every sync, when casebook serve sees the casebook repo move and when it starts.

Rules consider only undecided items that have no pending proposal from the same rule. Activating, editing and deactivating are commits: rule <id> → active by <who>, rule <id> edited by <who>, rule <id> deactivated by <who>.

The Rules section: a list of rules with their match counts and draft or active chips, and the open rule's conditions, proposal and live matches. The Rules section: a list of rules with their match counts and draft or active chips, and the open rule's conditions, proposal and live matches.

In the Rules section each rule shows its status and its match, exclusion and pending counts. The open rule reads as a document: a facts line, the conditions table with + condition offering each field's operators, the proposal, and the live matches, grouped by how they qualify (3 in main · 2 via merged PR) and viewable as a list or by repo. Matches recompute on the server as you edit. Unticking a match adds an exclusion, with an optional reason; ticking it again removes it.

A rule the agent drafts with casebook_rule_draft arrives as a draft marked with its author, drafted by pi · awaiting your review. Activating it is always yours.