Never
sleepon a blocking wait. P9 is a wait optimizer — turn any blocking external operation (PR CI, push-triggered deploys, builds, long indexing) into work on the next priority. PR CI is the canonical implementation today; the primitive is broader.
When an agent kicks off a long external operation and waits for it, the dumb pattern is sleep. P9 replaces it with productive-wait discipline:
- Notification via
run_in_background— for PR CI, the observer isgh pr checks --watch. No polling. - Productive wait — the agent drains a context-scoped queue (session → memory → graph → docs → Linear) while the operation runs.
- Self-heal — when CI fails for known categories (lint, format, codegen drift, flaky tests), P9 classifies and applies the heal command. Unknown failures escalate to a Linear ticket.
- Merge stays governed — P9 emits
MERGE_READY; the existing.control/policy.yaml+ control-gate-hook authorizes the actual merge. P9 owns mechanism, not policy.
P9 currently tracks PR-scoped waits through the p9 watch <pr> CLI. That covers ~95% of agent waits in a typical session.
The remaining 5% — non-PR waits — are not yet wired into p9 watch. These include:
- Push-triggered deploys — a push to
mainthat fires a Vercel/Cloudflare deploy hook without opening a PR - Long-running test suites or builds — outside the CI surface
- External index / sync operations — content pipelines, embeddings, search-index rebuilds
For these, the productive-wait discipline still applies: kick off next-priority work, then do one direct check on the operation's completion. Never sleep. Wiring these into p9 watch <kind> <ref> is on the roadmap.
# 1. Install dependencies (stdlib-only at runtime; pytest for tests)
python3 -m venv .venv && source .venv/bin/activate
pip install -r tests/requirements-dev.txt
# 2. Wire policy blocks
# Add ci_watch: and ci_heal: to your .control/policy.yaml. See
# tests/fixtures/policy-good.yaml for a complete example.
# 3. Verify install
python3 scripts/p9.py doctor
# → p9 doctor: ok
# 4. Use it
gh pr create ... ; PR=42
python3 scripts/p9.py watch $PR --background
# In parallel: pull productive work while CI runs
python3 scripts/p9.py wait-queue pop
# When the bg-task notification fires, check terminal state:
python3 scripts/p9.py status --pr $PR --json| Command | Purpose |
|---|---|
p9 watch <pr> |
Spawn gh pr checks --watch in background, transition to WATCHING. |
p9 status [--pr N] |
Show in-flight PRs and their state-machine position. |
p9 wait-queue {push,pop,list,clear} |
Manage the productive-wait queue (5 sources, priority-ordered). |
p9 heal <pr> --classify |
Read failure log, classify against the rubric (read-only — does not execute heals). |
p9 events tail |
Stream P9 events (P1 conversation-bridge consumes these). |
p9 merge-ready <pr> |
Mark a green PR as ready for metalayer-authorized merge. |
p9 doctor |
Health-check: gh auth, state directory, policy blocks, rubric. |
Pure Python, stdlib-only at runtime. State lives in ~/.config/broomva/p9/state.jsonl (append-only, JSONL with flock serialization, partial-write recovery on read). Failure classification is regex-only — no LLM in the hot path — driven by references/scoring-rubric.md (markdown is human-canonical; _builtin_rubric() in scripts/p9.py is code-canonical; a unit test asserts they stay in sync). The evaluator scores progress with a four-term weighted sum (signature change + failures decreased + budget remaining + classifier confidence) and forces escalation after two consecutive sub-floor cycles. Multi-PR concurrency is bounded by ci_watch.max_concurrent_prs (default 1). Everything fails closed on missing or malformed policy blocks.
P9 never silently drops state. Every failure produces (a) a
state.jsonlevent, (b) a Linear ticket via the escalation channel, or (c) both. If neither write succeeds, P9 crashes loudly (exit 99) — degraded silent operation is forbidden.
P3 (Linear ticket) → P4 (PR pipeline) → P9 (CI watcher + heal) → metalayer authorizes merge
↓ drains during wait
{context queue, P5 worktree, P6 bookkeeping}
Composes with — does not duplicate — existing primitives. See AGENTS.md in the workspace repo for the full bstack specification.
The full design lives at broomva/workspace under docs/superpowers/specs/2026-05-04-p9-ci-watcher-design.md. Seven rounds of clarifying Q&A locked the architecture before a line of code was written.
python3 -m pytest tests/
# 46 passedMIT — see LICENSE.
- broomva/bookkeeping — bstack P8 (knowledge graph engine)
- broomva/workspace — unified workspace + governance