Procedures

Procedures are multi-step processes defined as YAML-frontmatter cards. The procedure engine runs each step in order, checking preconditions, executing actions, and validating results. Everything is tracked in git.

Running Procedures

bbx procedure run process-pages                   # Run by name
bbx procedure run _config/procedures/my.procedure.card  # Run by path
bbx procedure run process-pages --step intake     # Run one step only
bbx procedure run process-pages --dry-run         # Preview steps
bbx procedure run process-pages --directive "prefer the reading list over trashing"  # Pass directive
bbx procedure list                                   # List available procedures
bbx procedure status                                 # Show latest run status
bbx procedure gc                                     # Delete expired run dirs

Procedure definitions live in _config/procedures/. Each run creates a tracking card in _bookkeeping/procedure/runs/<name>_<timestamp>/. Run dirs are a recent cache, not an archive: a run where every step skips is removed at completion, and finished runs get an expires stamp (30d completed / 90d failed, or the procedure card's run-expiry/failed-run-expiry override) that bbx procedure gc enforces daily. Git history retains every committed run. To pin a specific run, set expires: never on its run card.

Directives

A directive is an opaque runtime string passed when invoking a procedure. It appears as <directive>...</directive> in every agent's system prompt within the procedure, allowing callers to customize behavior without modifying the procedure card.

bbx procedure run process-pages --directive "Only process today's pages"

The directive is also recorded as the directive field on the procedure-run card for auditability. Step prompts can reference "the Directive" to act on it.

How Steps Work

Each step has three optional phases:

  1. Precheck — Should this step run? Shell script that exits 0 (proceed), $CHECK_SKIP (skip), or non-zero (fail).
  2. Run — The main action: a shell command or an agent invocation.
  3. Validate — Did it work? Shell check + optional model evaluation.

The engine enforces a clean git state between steps. Every step's work is committed before the next step begins.

Procedure Card Structure

---
name: my-procedure
description: What this procedure does
steps:
  - id: first-step
    description: Human-readable description of this step
    precheck:
      shells:
        - |
          # Exit 0 to proceed, exit $CHECK_SKIP to skip
          count=$(ls _content/inbox/*.card 2>/dev/null | wc -l)
          if [ "$count" -eq 0 ]; then exit $CHECK_SKIP; fi
          echo "Found $count items"
      whys:
        - Explanation of when/why this step should be skipped
    run:
      agents:
        - model: efficient
          max-turns: 20
          prompt: |
            Agent prompt goes here. The engine prepends context
            (date, procedure name, step ID, working directory).
    validate:
      severity: abort   # warn | abort | review (see Validation Severity)
      shells:
        - |
          # Exit 0 = pass, non-zero = fail (objective gate).
          remaining=$(ls _content/inbox/*.card 2>/dev/null | wc -l)
          echo "Remaining: $remaining"
          [ "$remaining" -eq 0 ]
      instructions:
        - |
          Natural-language success criterion, model-judged against the
          step's git diff. Gates by severity like a shells check. Use for
          judgment a shell can't make; keep objective checks in shells.
      whys:
        - Why this validation matters
---

Each phase (precheck/run/validate) groups its actions by kind: shells, agents, instructions, whys — each a list. Use YAML block scalars (|) for multi-line scripts and prompts.

Building Blocks

Shell Commands

Shell scripts run in the box root via bash -c. Three outcomes:

Important: macOS ships bash 3.2. Avoid bash 4+ features like declare -A (associative arrays). Use shopt -s nullglob instead of for f in glob 2>/dev/null.

Agent Invocations

agents:
  - model: efficient
    max-turns: 25
    prompt: |
      Prompt text here...

Instruction Checks (model-judged)

instructions: in a validate phase are natural-language success criteria that a review model judges against the step's git diff (the whole step — every commit the run made — not just the last one), with the step's whys: as context. The verdict gates by severity exactly like a shells: check. validate.model uses the same portable tiers and defaults to balanced. If the model can't return a verdict, the check fails closed (a check you think gates never silently passes).

Use instructions: for judgment a shell can't cheaply make ("the summary actually reflects the source"); keep objective, deterministic checks in shells:.

Passing Precheck Data to Agents

Add pass-output: true to a precheck to include its stdout in the agent's context:

precheck:
  pass-output: true
  shells:
    - echo "Items to process: 5"

The agent sees this as a <precheck> block in its system prompt. Use this to avoid redundant work — the precheck can compute a manifest that the agent acts on.

Validation Severity

Applies to both shells: and instructions: failures:

A started agent turn that ends after partial assistant activity is logged without gating by itself. An engine failure with no usable assistant response (for example auth or model rejection) fails the step and is recorded precisely. "The agent must have actually done the work" still has to be proven by a shells check or an instructions verdict — never assume the agent finishing means the step succeeded.

Checklists (opt-in thoroughness)

When a step has the agent work through several items and you want an auditable trail, use a checklist — a convention, not an engine feature:

Why Entries

whys entries explain the purpose of a phase. They're shown to:

Writing a New Procedure

  1. Create _config/procedures/my-procedure.procedure.card
  2. Define steps with prechecks that skip gracefully when there's nothing to do
  3. Use bbx procedure run my-procedure --dry-run to verify the structure
  4. Test step-by-step with --step <id>

Tips

Agent Prompt Guidelines

Agent prompts in procedures should:

Important: If an agent doesn't commit, the engine creates a fallback commit with a generic message (tagged Commit-Source: procedure-fallback). Always instruct agents to commit explicitly so the git history is meaningful.

System Procedures and Migration

Procedure cards in _config/procedures/ are installed by bbx init from built-in templates. If you edit a system procedure, your changes are preserved:

To check for updates:

ls _config/_template-updates/procedures/
# If any exist, compare with the main version and merge changes
diff _config/procedures/process-pages.procedure.card _config/_template-updates/procedures/process-pages.procedure.card

After merging, delete the file under _config/_template-updates/procedures/. The next bbx init will see your merged version as the current copy.

Git History

A complete procedure run produces commits like:

abc123f Complete procedure: process-retrospective
abc123e [procedure] Complete step: integrate
abc123d Integrate 4 observations into personality card   ← agent commit
abc123c [procedure] Complete step: scan
abc123b Scan 3 chat sessions for retro observations       ← agent commit
abc123a Start procedure: process-retrospective

Each commit represents a clean, consistent state. You can git reset --hard to any commit to get a valid snapshot.