Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Agent Workflow: ah check as Specification Gate

This document describes how AI agents discover and use ah check — the espectacular verification gate — in the standard development cycle. It is extracted from observed patterns across the charly-vibes project ecosystem.


How Agents Discover ah check

Agents learn about ah check through the ah:managed block in a project’s AGENTS.md (or CLAUDE.md). This block is deployed by ah init and refreshed by ah init.

<!-- ah:managed:start -->
## espectacular

Run `ah check` to verify spec-test correspondence before committing.

- `ah check` — validate all deployed specs
- `ah check --changes <name>` — validate with a change overlay
- `ah init` — set up or refresh espectacular project files
- `ah doctor` — diagnose setup issues
- `ah explain <topic>` — playbook guidance for finding kinds
- `ah doctor --enable <adapter>` — write adapter config into config.toml
- `ah signals` — emit dont drift signals
<!-- ah:managed:end -->

Key design decisions:

  • The block is compact (fits in ~15 lines of agent context) — agents scan it early in a session and recognize ah check as a commit gate.
  • It lists only the most common commands — ah explain provides the full playbook for less frequent tasks.
  • The ah:managed delimiter signals to agents that this section is auto-generated and authoritative.

Typical Usage Patterns

1. Pre-commit gate (most common)

The standard loop when working on OpenSpec projects:

edit → ah check → fix → ah check → commit

Concrete example:

This pattern was observed in testaruda pi sessions, where ah check was the primary feedback loop during contract wiring and spec changes. Source sessions: 2026-07-08T15-35-34-510Z, 2026-07-16T01-38-10-034Z.

# 1. Make changes to spec or code
# 2. Run the gate
ah check

# 3. If findings appear, fix them
# 4. Re-run to verify
ah check

# 5. Commit when green
git add -p
git commit -m "fix: ..."

When to run ah check:

SituationWhether to run
Modified spec filesAlways
Modified contract TOML filesAlways
Modified production code tracked by contractsAlways
Edited tests onlyOptional (contracts pass/fail)
Edited docs onlyUsually not needed
Changed a change overlayah check --changes <name>

2. Change-overlay validation

When a staged OpenSpec change is in progress:

ah check --changes add-parser-validation

This validates the base spec plus the overlay’s additional scenarios, without requiring the overlay to be archived first.

Multiple overlays can be validated together by repeating the flag:

ah check --changes add-parser-validation --changes fix-dangling-contracts

Useful when interdependent changes are being developed simultaneously.

3. Fresh project setup

ah init            # Scaffold .espectacular/, stub contracts
ah doctor          # Verify setup is complete
ah doctor --enable cargo   # Configure runner
ah check           # Run initial validation

4. Post-ah init verification

After bootstrapping a new project, ah init stubs contracts for every scenario missing one. The pattern is:

ah init            # Stub missing contracts
ah check           # Verify — expect structural findings for no-tests-declared
# Fill in test commands for each stub contract, then:
ah check           # Green — all contracts wired
git add -A
git commit -m "chore: init espectacular and stub contracts"

How Findings Are Interpreted

ah check emits a JSON envelope with a findings array. Each finding has a kind, category, and suggested_action that agents can act on.

Finding categories

CategoryMeaningExit code impact
structuralSpec/contract mismatch (missing contract, orphan, duplicate ID)Non-zero
executionContract test failed or timed outNon-zero
quality-*Mutation score below threshold, property coverage gapZero (advisory)

Common findings and agent responses

Finding kindWhat it meansAgent action
missing-contractA scenario has no contract fileah explain run_ah_scenario_new → ah scenario new
orphan-contractA contract file has no matching scenarioRemove or archive the contract
no-tests-declaredContract exists but has no test commandsFill in [[tests.shell]] or [[tests.flags]]
collisionTwo specs declare the same scenario IDRename one scenario heading
test-failingA contract test exited non-zeroInspect test output, fix code
no-tests-ranShell test ran no tests (exit 0, no output)Run ah check --json to inspect the full captured output. The contract’s [[tests.shell]] command may not match any test files, or the adapter may not be detecting the test framework. Check config and test command.
duplicate-idSame scenario ID appears in two specsDeduplicate
mutation-below-thresholdMutation kill rate too lowAdd or improve test cases

Before starting work, ensure ah is available in PATH:

which ah || cargo install --git git@cv:charly-vibes/espectacular.git

Quick reference: interpreting output

{
  "summary": { "structural": 0, "execution": 0, "passed": 3, "counts_by_kind": {} },
  "findings": []
}

→ Green. All checks pass. Ready to commit.

{
  "summary": { "structural": 1, "execution": 0, "passed": 0, "counts_by_kind": { "missing-contract": 1 } },
  "findings": [ { "kind": "missing-contract", "category": "structural", ... } ]
}

→ Structural finding. A scenario has no contract file. Run ah scenario new to create one, then ah check again.

{
  "summary": { "structural": 0, "execution": 1, "passed": 2, "counts_by_kind": { "test-failing": 1 } },
  "findings": [ { "kind": "test-failing", "category": "execution", ... } ]
}

→ Execution finding. A contract test failed. Fix the code, then ah check again.

For full guidance on any finding kind:

ah explain <finding-kind>

CI/script usage

In CI pipelines or automated scripts, use --json for machine-readable output:

ah check --json
# Exit code: 0 if no structural/execution findings, 1 otherwise

The --run-tests flag forces contract test execution even when no spec changes are detected (useful in CI when dependencies may have changed).

Test selection (avoiding full contract runs)

By default ah check --run-tests executes every declared contract test. When a changed-file set is known, pass it to narrow the run:

# Explicit file list (repo-relative, comma-separated)
ah check --run-tests --files src/check.rs,openspec/specs/compiler/spec.md

# Omit --files to default to the worktree git diff
ah check --run-tests

# Escape hatch: ignore changes and run everything
ah check --run-tests --all-tests

Selection is layered and conservative — it never produces a hidden green:

  1. Capability selection (always on): changed files are mapped to capabilities via spec provenance (deployed specs, change-overlay specs, and contract trees). Only affected capabilities’ contracts run. Files that map to no capability trigger a conservative run-all, reported as source: "bypassed" in the JSON selection object.
  2. testaruda refinement (when a .testaruda/ store exists): within the run, contract bindings whose runner filter matches nothing in testaruda select --agent’s selected test set are pruned. Guards keep this safe: an EMPTY selection (exit 20 — no code affected) skips the refinement but still runs the capability set; unresolved changed files or a selection where no binding matches disable pruning entirely. Pruning is reported as selection.testaruda.pruned in JSON.

The JSON selection object summarizes what ran:

"selection": {
  "selected": ["compiler"],
  "skipped": 3,
  "source": "spec-provenance",
  "unmapped_files": 0,
  "testaruda": { "pruned": 1 }
}

source is spec-provenance (capability selection narrowed the run), all-tests (escape hatch), or bypassed (conservative run-all — e.g. the changed set contains files that map to no capability).

Forcing a commit through findings

On a WIP branch or when findings are acceptable, you can commit despite non-zero exit. This is common during staged change development:

ah check --changes my-change
# If findings are scoped to the change and expected, proceed
git commit -m "wip: my change"

For CI-only enforcement, run ah check in CI and allow local commits to pass — the managed block in AGENTS.md should still instruct agents to resolve findings.


Commit Workflow with ah check as Gate

Standard workflow

1. bd claim <ticket>        # Claim work
2. git pull --rebase        # Sync
3. [edit code / specs]      # Make changes
4. ah check                 # Verify spec-test correspondence
5. [fix findings]           # Iterate until green
6. git add <files>          # Stage specific files (never git add -A)
7. git commit -m "..."      # Commit with descriptive message
8. ah check                 # Verify pre-push (catches regressions from merge)
9. bd close <ticket>        # Close ticket

When findings are expected

Some tickets produce findings that are expected and resolved within the same ticket. Example: a ticket that adds a new scenario creates a missing-contract finding until the contract file is written.

In those cases, the workflow is:

1. ah check                 # Baseline — note existing findings
2. [edit code / specs]      # Make changes
3. ah check                 # Verify — expected findings should match ticket scope
4. [fix unexpected findings]
5. ah check                 # Green — all expected findings resolved
6. git add && git commit

Pre-push hooks (optional)

Projects with lefthook or prek can integrate ah check as a pre-push hook. This is configured by ah init and runs automatically:

# .lefthook.yml (auto-generated by ah init)
pre-push:
  commands:
    ah-check:
      run: ah check
      skip: false  # Block push on failure

If you want ah check to run but not block the push (advisory mode), set skip: true. This lets you push while still seeing the output.

The actual config generated by ah init uses skip: false by default.


---

## Agent Workflow Diagram

┌─────────────────────────────────────────────────────────┐ │ Session Start │ │ │ │ 1. Agent reads AGENTS.md → discovers ah:managed block │ │ 2. Agent runs ah check to establish baseline │ │ 3. Agent reads any existing findings │ └─────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Development Loop │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Edit │───▶│ah check │───▶│ Fix │ │ │ │ code/ │ │ │ │ findings │──┐ │ │ │ specs │ └──────────┘ └──────────┘ │ │ │ └──────────┘ ▲ │ │ │ │ findings remain │ │ │ └────────────────────────┘ │ │ │ │ │ green │ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ ah check passes │ │ │ └────────┬──────────┘ │ └─────────────────────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Commit │ │ │ │ 1. git add │ │ 2. git commit -m “fix: …” │ │ 3. (optional) ah check pre-push │ │ 4. bd close │ └─────────────────────────────────────────────────────────┘


---

## Adding `ah check` to a New Project

For agent operators who want to replicate this pattern:

```bash
# 1. Install ah
cargo install --git git@cv:charly-vibes/espectacular.git

# 2. Ensure the project has an openspec/ directory
#    (the root AGENTS.md or CLAUDE.md will be updated
#     automatically by ah init)

# 3. Run init
ah init

# 4. Verify
ah doctor    # Should show healthy
ah check     # Should have findings from stub contracts

# 5. Verify the ah:managed block was written to AGENTS.md
#    (ah init does this automatically — no manual edits needed)

Sources

This document is extracted from observed agent workflow patterns in the following pi sessions:

  • 2026-07-08T15-35-34-510Z — Heavy ah check usage during contract wiring for the v0.2.0 release (espectacular project)
  • 2026-07-16T01-38-10-034Z — Recent workflow showing the edit → ah check → fix → ah check → commit cycle (testaruda project)

Both sessions demonstrate the ah check feedback loop in practice. Testaruda was the first OpenSpec project to adopt ah check as a regular development gate, and its patterns were replicated across the charly-vibes ecosystem.