AGENTS.xml template
The template the bootstrap agent (core/prompts/00-bootstrap.md) copies into a consumer repo. <<PLACEHOLDER>> tokens are replaced from Phase-1 spec reading and Phase-2 calibration answers.
<?xml version="1.0" encoding="UTF-8"?><!-- AGENTS.xml — the delegation grammar for <<PROJECT_NAME>>.
Read by the orchestrating Claude session at the start of every interaction. Read by subagents when they need to know who to hand off to. Read by humans when they wonder "who does what".
Schema rules (the orchestrator enforces these): 1. Every <subagent> has exactly one <name>, at least one <spawns_on>, at least one <reads>, at least one <writes>, at least one <output_contract>, at least one <refusal_conditions>. 2. <handoff_to> may be empty (terminal subagent like reviewer-final). 3. Every milestone ID referenced in <reads> or <writes> must exist in MILESTONES.md. 4. Every invariant ID (I-N) referenced must exist in CLAUDE.md. 5. Every <spawns_on> event must be one of the events declared in <event_catalog> below. 6. The <handoff_to> graph must be a DAG when filtered to *intra-phase* edges only. Inter-phase edges (trigger=phase.close) and remediation back-edges (trigger=audit.fail) are exempt — they are legitimate loops at phase / milestone boundaries.
Customise the placeholders, add subagents as the project grows. Don't delete the schema rules above — they are load-bearing.--><agents project="<<PROJECT_NAME>>" version="1.0" generated="<<ISO_DATE>>">
<!-- The event catalog defines every event that can trigger a subagent. Adding a new event requires updating the orchestrator prompt that interprets this XML. Keep this list small and meaningful. --> <event_catalog> <event id="phase.open">A phase has just opened (decision doc YYYY-MM-DD-phase-N-open.md was just written).</event> <event id="phase.close">A phase is being closed (acceptance gate met, verification round starting).</event> <event id="milestone.start">A new M{N}.{n} subphase has just begun.</event> <event id="milestone.acceptance_check">A subphase is claiming its acceptance check passes — verify.</event> <event id="decision_doc.created">A new docs/decisions/ file was just written; downstream subagents may need to react.</event> <event id="audit.fail">An auditor run returned non-zero; the implementer or human needs to react.</event> <event id="friction.surfaced">A verification round has surfaced an item that needs classification.</event> <event id="drift.suspected">Something doesn't smell right relative to north-star intent; trigger a drift check.</event> <event id="schema.changed">A wire contract or DB schema has changed; downstream consumers need to react.</event> <event id="human.invocation">The human explicitly invoked this subagent (escape hatch; use sparingly).</event> </event_catalog>
<!-- ============================================================ --> <!-- Core fleet — present in every full-stack project --> <!-- ============================================================ -->
<subagent> <name>planner</name> <description> Sequences upcoming work. Owns MILESTONES.md edits. Classifies friction-round outputs into closed / in-budget / deferred. Does NOT write code. </description> <spawns_on> <event>phase.close</event> <event>friction.surfaced</event> <event>human.invocation</event> </spawns_on> <reads> <path>docs/north-star/intent.md</path> <path>MILESTONES.md</path> <path>docs/decisions/</path> <path>docs/findings/</path> </reads> <writes> <path>MILESTONES.md</path> <path>docs/roles/planner/outputs/</path> <path>docs/decisions/YYYY-MM-DD-phase-{N}-open.md</path> </writes> <handoff_to>implementer</handoff_to> <output_contract> <format>markdown</format> <required_sections>goal, dependencies, subphases, acceptance_gate, verification_trigger, handoff</required_sections> </output_contract> <refusal_conditions> <condition>The proposed phase has >5 deferred items from the prior phase. Stop and ask the human whether the prior phase should reopen.</condition> <condition>A phase would touch a frozen pilot bundle or superseded phase without a thaw decision doc.</condition> <condition>Anti-shape detected: the phase as scoped pulls toward a drift watch declared in north-star.</condition> </refusal_conditions> </subagent>
<subagent> <name>implementer</name> <description> Writes code. Default safe-window: 5–15 contiguous lines per edit unless the milestone explicitly waives it. Anchors every change in a milestone ID and an invariant. </description> <spawns_on> <event>milestone.start</event> <event>audit.fail</event> </spawns_on> <reads> <path>MILESTONES.md</path> <path>CLAUDE.md</path> <path>docs/spec/</path> <path>docs/roles/planner/outputs/</path> </reads> <writes> <path><source tree></path> <path>tests/</path> <path>docs/roles/implementer/outputs/</path> </writes> <handoff_to>auditor</handoff_to> <output_contract> <format>code-diff + session-log</format> <session_log_path>docs/roles/implementer/outputs/YYYY-MM-DD-M{N}.{n}.md</session_log_path> <session_log_required_fields>milestone_id, invariants_touched, files_changed, lines_changed, tests_added, open_questions</session_log_required_fields> </output_contract> <refusal_conditions> <condition>The change exceeds the safe window (15 contiguous lines) AND no waiver is in the milestone's note. Stop and propose a decomposition.</condition> <condition>The change would violate an invariant (I-N) declared in CLAUDE.md.</condition> <condition>The change requires editing a sibling file with its pair (e.g. wire contract sibling pair) without a justifying decision doc.</condition> <condition>The milestone's spec anchor doesn't actually contain the requirement being implemented.</condition> </refusal_conditions> </subagent>
<subagent> <name>auditor</name> <description> Runs acceptance checks. Does NOT modify code. Produces evidence docs that either certify a milestone passes or block it with reproducible failure cases. </description> <spawns_on> <event>milestone.acceptance_check</event> <event>decision_doc.created</event> </spawns_on> <reads> <path>MILESTONES.md</path> <path>docs/spec/</path> <path>docs/roles/implementer/outputs/</path> <path>tests/</path> </reads> <writes> <path>docs/roles/auditor/outputs/</path> <path>docs/findings/YYYYMMDD-HHMM-*.md</path> </writes> <handoff_to>reviewer</handoff_to> <output_contract> <format>markdown</format> <required_sections>milestone_id, checks_run, evidence, verdict, blocking_findings</required_sections> <verdict_values>pass, block, advisory</verdict_values> </output_contract> <refusal_conditions> <condition>The acceptance check is unfalsifiable as written. Stop and ask the planner to rewrite it.</condition> <condition>The implementer's session log claims tests were added but they're not in the diff.</condition> <condition>Verification cannot be performed without running code that requires production secrets.</condition> </refusal_conditions> </subagent>
<subagent> <name>reviewer</name> <description> Final sign-off before a phase or milestone closes. Cross-checks the auditor's evidence against the planner's acceptance gate. Can override audit verdicts only with explicit human confirmation. </description> <spawns_on> <event>phase.close</event> </spawns_on> <reads> <path>docs/roles/planner/outputs/</path> <path>docs/roles/auditor/outputs/</path> <path>docs/decisions/</path> <path>MILESTONES.md</path> </reads> <writes> <path>docs/roles/reviewer/outputs/</path> <path>docs/decisions/YYYY-MM-DD-phase-{N}-close.md</path> </writes> <handoff_to/> <!-- terminal --> <output_contract> <format>markdown</format> <required_sections>phase_id, acceptance_summary, friction_budget, deferred_milestones, invariants_status, handoff_to_next</required_sections> </output_contract> <refusal_conditions> <condition>The auditor verdict is `block` and no remediation has been recorded. Stop and route to the implementer.</condition> <condition>The verification round produced >3 deferred items. Stop and ask the human whether to reopen the phase.</condition> <condition>An invariant violation surfaced during review and was not addressed.</condition> </refusal_conditions> </subagent>
<!-- ============================================================ --> <!-- Full-stack-specific subagents --> <!-- ============================================================ -->
<subagent> <name>migrator</name> <description> Owns DB schema migrations. Produces forward + rollback pairs. Validates against the schema invariants. Coordinates with implementer when application code needs to change in lockstep. </description> <spawns_on> <event>schema.changed</event> <event>milestone.start</event> </spawns_on> <reads> <path>docs/spec/schema/</path> <path>migrations/</path> <path>MILESTONES.md</path> </reads> <writes> <path>migrations/</path> <path>docs/roles/implementer/outputs/migrations-YYYY-MM-DD-M{N}.{n}.md</path> </writes> <handoff_to>auditor</handoff_to> <output_contract> <format>sql + markdown</format> <required_sections>up_migration, down_migration, data_backfill_strategy, rls_impact, rollback_tested</required_sections> </output_contract> <refusal_conditions> <condition>The migration is forward-only with no rollback strategy AND the data is not trivially reproducible.</condition> <condition>The migration touches a table covered by RLS without an explicit RLS impact note.</condition> <condition>The forward migration was not tested against a copy of production-shaped data.</condition> </refusal_conditions> </subagent>
<subagent> <name>frontend-designer</name> <description> Owns design-system-adjacent work: component primitives, design tokens, accessibility, responsive behaviour. Does NOT own business-logic components — those go to the implementer. </description> <spawns_on> <event>milestone.start</event> <event>friction.surfaced</event> </spawns_on> <reads> <path>frontend/</path> <path>docs/spec/ui/</path> <path>docs/findings/</path> </reads> <writes> <path>frontend/src/components/</path> <path>frontend/src/styles/</path> <path>docs/roles/implementer/outputs/</path> </writes> <handoff_to>auditor</handoff_to> <output_contract> <format>code-diff + session-log</format> <required_sections>component_id, a11y_checks, responsive_breakpoints_tested, design_tokens_used</required_sections> </output_contract> <refusal_conditions> <condition>The design diverges from the established design tokens without a justifying decision doc.</condition> <condition>The component lacks keyboard-navigation support and the spec doesn't waive it.</condition> </refusal_conditions> </subagent>
<subagent> <name>drift-watcher</name> <description> Invoked when something feels off. Reads north-star and the proposed change, traces the rank graph, and either clears the change or surfaces the specific anti-shape it's drifting toward. </description> <spawns_on> <event>drift.suspected</event> <event>human.invocation</event> </spawns_on> <reads> <path>docs/north-star/</path> <path>MILESTONES.md</path> <path>docs/decisions/</path> <path><the proposed change></path> </reads> <writes> <path>docs/roles/auditor/outputs/drift-YYYY-MM-DD-*.md</path> </writes> <handoff_to>planner</handoff_to> <output_contract> <format>markdown</format> <required_sections>change_summary, anti_shapes_evaluated, verdict, recommended_action</required_sections> <verdict_values>aligned, drift-suspected, drift-confirmed</verdict_values> </output_contract> <refusal_conditions> <condition>The change touches surfaces in >3 phases at once. Stop and ask the planner to decompose.</condition> </refusal_conditions> </subagent>
<!-- ============================================================ --> <!-- Handoff graph (rendered for human readers) --> <!-- ============================================================ -->
<handoff_graph> <edge from="planner" to="implementer" trigger="milestone.start"/> <edge from="implementer" to="auditor" trigger="milestone.acceptance_check"/> <edge from="auditor" to="reviewer" trigger="phase.close"/> <edge from="auditor" to="implementer" trigger="audit.fail"/> <edge from="migrator" to="auditor" trigger="milestone.acceptance_check"/> <edge from="frontend-designer" to="auditor" trigger="milestone.acceptance_check"/> <edge from="drift-watcher" to="planner" trigger="drift.suspected"/> <edge from="reviewer" to="planner" trigger="phase.close"/> </handoff_graph>
<!-- ============================================================ --> <!-- Refusal escalation --> <!-- ============================================================ -->
<escalation_rules> <!-- When a subagent hits a refusal_condition, this is what it does NEXT. The chain terminates either at human review or at a re-scope. --> <rule> <when>any subagent refuses</when> <then>write the refusal reason to docs/roles/<role>/outputs/refusal-YYYYMMDD-HHMM-*.md</then> <then>do NOT proceed with the task</then> <then>surface to the human with: refusal reason, recommended re-scope, alternatives considered</then> </rule> <rule> <when>3 refusals in a single milestone</when> <then>route to drift-watcher</then> <then>drift-watcher reads the milestone scope and the refusals; either confirms drift (kick to planner) or escalates to human as architecturally ambiguous</then> </rule> <rule> <when>refusal mentions an invariant violation</when> <then>STOP. Do not proceed even with human approval until the invariant is either re-grounded (decision doc) or the change is dropped.</then> </rule> </escalation_rules>
<!-- ============================================================ --> <!-- Capabilities — petrova-act verb surface --> <!-- ============================================================ --> <!-- Capabilities are external action surfaces this fleet may invoke. Each capability is a typed, schema-validated, PR-emitting verb layer maintained outside the consumer repo. Adding a capability is an MR-6 event: open a decision doc justifying it, then declare here. -->
<capabilities> <capability id="petrova-act"> <description> Petrova control-plane write verbs. Every invocation is dry-run by default, schema-validated against spec/verbs/, and emits a reviewable PR with an idempotency key. Branch protection + CODEOWNERS remain the human gate; this capability propose-only. </description> <provider>petrova-hq (skills/petrova-act)</provider> <invocation> <method>Claude Code skill: petrova-act</method> <method>CLI: petrova <verb> <repo> --input <json> [--apply]</method> </invocation> <verbs> <verb name="open_decision" upholds="MR-4 MR-7"/> <verb name="update_milestone" upholds="MR-2 MR-7"/> <verb name="start_phase" upholds="MR-2 MR-7 MR-10"/> <verb name="close_phase" upholds="MR-2 MR-7 MR-10"/> <verb name="verify_round" upholds="MR-10"/> <verb name="request_review" upholds="MR-3 MR-12"/> <verb name="request_merge_when_green" upholds="MR-3 MR-12" profile_required="permissive | standard+fleets_allowed"/> <verb name="propose_fix" upholds="MR-7 MR-12" requires="recent diagnose run (≤24h)"/> </verbs> <preconditions> <precondition>Target repo's slug must appear in petrova-hq/registry.yaml.</precondition> <precondition>For fleet:* actors, registry entry's fleets_allowed must include the fleet ID.</precondition> <precondition>For --apply: PETROVA_GITHUB_TOKEN OR PETROVA_APP_* env vars must be set.</precondition> <precondition>Privileged paths (.github/workflows/, *.env, secrets/, deploy/credentials/) are refused unconditionally — humans edit those.</precondition> </preconditions> <refusal_recovery> <on code="REPO_NOT_IN_REGISTRY">Open onboarding PR against petrova-hq/registry.yaml first.</on> <on code="FLEETS_ALLOWED">Open registry update adding fleet ID to fleets_allowed.</on> <on code="DIAGNOSIS_EXISTS">Run diagnose first; cache lives at ~/.petrova/diagnoses.jsonl.</on> <on code="PROFILE_PERMITS_AUTOMERGE">Repo is strict-profile; use request_review and request human merge.</on> <on code="DEFERRED_HAS_TARGET">Add deferred_to_milestone in a later phase per MR-2.</on> <on code="NO_PRIVILEGED_PATHS">Route the change to a human; agent fleets do not edit privileged paths.</on> </refusal_recovery> <reference>docs/integrations/kahn-fleet.md (in petrova-hq) — full integration contract.</reference> </capability> </capabilities>
</agents>