Skip to content

onboard repo

The end-to-end flow for admitting a new repo to the petrova control plane. After a successful run, the repo is reachable via every verb, visible in petrova status / petrova dashboard, and (if the operator chose) governed by agent fleets.

  • Repo is reachable on GitHub (public or private with petrova auth).
  • Operator has push access to petrova-codes/petrova (the registry lives here).
  • PETROVA_GITHUB_TOKEN (or the GitHub App env triple) configured for the --apply step.
  • Repo is locally cloned at $PETROVA_WORKSPACE/<slug> (default: parent dir of petrova-codes). If not, clone it first — the readiness report needs filesystem access.
  • After the playbook PR merges, set a one-time PETROVA_DISPATCH_TOKEN secret on the consumer repo so .github/workflows/petrova-notify-state.yml can fire repository_dispatch at petrova-codes on phase verb merges. See PETROVA_DISPATCH_TOKEN setup at the end of this runbook. Optional — without it the workflow no-ops and the 30-min state-sweep cron is the safety net.
Terminal window
cd ~/code/workspace/petrova-codes
claude code

The prompt lives at core/playbook/prompts/05-petrova-onboard.md. Copy it verbatim into the Claude Code session. The prompt is interactive — it will:

  1. Ask which repo you’re onboarding.
  2. Read the consumer repo (read-only) and produce a readiness report covering petrova-shape state, branch protection, recommended role and profile.
  3. Confirm fleets_allowed (default: empty).
  4. Compose the registry-update PR payload as a JSON file.
  5. Run dry-run, show you the diff.
  6. After your go-ahead, run --apply, return the PR URL.

2a. Or: register the consumer in one command

Section titled “2a. Or: register the consumer in one command”

If the consumer already has .petrova/contract.yaml on its default branch (typical: petrova_install_playbook + petrova_onboard ran previously, just never registered upstream), use petrova_act_register_consumer directly. The verb fetches the contract, derives the entry, and opens the registry PR — no interactive prompt:

Terminal window
cd ~/code/workspace/petrova-codes
echo '{"repo":"petrova-fleet/newco-hq"}' | \
petrova petrova_act_register_consumer petrova-codes
# (review dry-run diff)
echo '{"repo":"petrova-fleet/newco-hq"}' | \
petrova petrova_act_register_consumer petrova-codes --apply

The PR is opened with labels registry-edit + awaiting-human-merge and never auto-merges (MR-12, control-plane firewall). Co-emits a dated ADR under docs/decisions/. Merge by hand once the entry looks right.

Pre-flight failures (each emits a failed envelope):

CodeMeaning
REPO_NOT_ACCESSIBLEgh api repos/<repo> returned 404/403. Verify the slug + petrova auth scope.
CONTRACT_MISSING.petrova/contract.yaml not on default branch. Run petrova_install_playbook and commit a contract first.
CONTRACT_INVALIDContract fails contracts/contract.schema.json. The error message lists the failing fields.
SLUG_TAKENAn entry with that slug already exists. Use petrova_act_registry_edit if the update is intentional.

The registry PR appears in petrova-codes/petrova. CODEOWNERS for registry.yaml (or any human approver) reviews it.

Things to check:

  • The new entry’s url matches the consumer’s actual git remote get-url origin.
  • profile is conservative — strict for production, standard for most, permissive only when intentional.
  • fleets_allowed is empty unless the operator named specific fleets (and they exist).

Merge.

Terminal window
git -C ~/code/workspace/petrova-codes pull
petrova status # new slug appears
petrova diagnose <slug> # returns structured context
petrova validate <slug> # MR / convention compliance

If petrova status doesn’t show the new slug, the local registry is stale — git pull and retry.

If the readiness report flagged the consumer as missing control-loop docs (CLAUDE.md, AGENTS.xml, MILESTONES.md):

Terminal window
cd ~/code/workspace/<slug>
claude code
# Paste core/playbook/prompts/00-bootstrap.md

Bootstrap is its own session — it needs the consumer’s spec docs in context, not the petrova-codes root.

6. (Optional) Add petrova-act capability declaration

Section titled “6. (Optional) Add petrova-act capability declaration”

If the consumer has an AGENTS.xml but no <capability id="petrova-act"> block, add it via petrova request_review:

Terminal window
# Read the new template's capability block from
# core/templates/AGENTS.xml.tmpl, splice it into the consumer's
# AGENTS.xml just before </agents>, then:
petrova request_review <slug> \
--input /tmp/capability-add.json \
--triggered-by-kind human_request \
--triggered-by-ref "post-onboarding capability declaration"

This is purely informational for the consumer’s orchestrator — verbs work without it (gating happens in registry.yaml’s fleets_allowed, not in the consumer’s XML).

ProblemFix
Slug already in registryThis is an update, not an onboard. Re-target as a request_review modifying the existing entry.
git remote get-url origin doesn’t match registry urlOperator typo or registry drift. Open a one-line request_review PR fixing the url.
Consumer not locally clonedgit clone it first; the prompt won’t proceed without filesystem access.
PETROVA_GITHUB_TOKEN unsetSet it. Onboarding cannot complete in dry-run only.
Concurrent registry editPull latest, recompose JSON with current registry contents, retry.
Branch protection on petrova-codes blocks the PR’s auto-mergeExpected. Petrova-hq is strict-profile; the registry PR needs human approval.
  • Does not push to the consumer repo’s main branch (everything is PR-based per the control-plane decision).
  • Does not auto-grant agent-fleet access. Empty fleets_allowed is the safe default; fleets are added in subsequent registry PRs as their identity gets defined.
  • Does not auto-bootstrap. The bootstrap session is run separately with the consumer’s spec docs in context.

The petrova_install_playbook verb deposits .github/workflows/petrova-notify-state.yml into the consumer repo. That workflow fires a repository_dispatch at petrova-codes/petrova whenever a phase verb PR merges, so the fleet-wide phases grid refreshes in <60s rather than waiting for the 30-min state-sweep cron.

The workflow needs a token with permission to call the dispatches endpoint on petrova-codes/petrova. Without the secret it no-ops (logged warning, no failure); the 30-min cron is the safety net.

The token is reused across all consumer repos; you only mint it once.

  1. https://github.com/settings/personal-access-tokens/new
  2. Token name: petrova-state-dispatch
  3. Resource owner: petrova-codes
  4. Repository access → Only select repositories → petrova-codes/petrova
  5. Permissions → Repository permissions → set both:
    • Actions: Read and write (for the dashboard’s ▶ trigger sweep button, which calls POST /actions/workflows/.../dispatches).
    • Contents: Read and write (for the consumer-side petrova-notify-state.yml workflow, which calls POST /dispatches — a separate, contents-scoped endpoint).
  6. Generate, copy the github_pat_… string.

Install per consumer (after the playbook PR merges)

Section titled “Install per consumer (after the playbook PR merges)”
Terminal window
gh secret set PETROVA_DISPATCH_TOKEN -R <owner>/<consumer-repo>
# paste token at the prompt

Verify by closing a phase in the consumer; the petrova-notify-state workflow run should show Dispatched petrova-state-stale for slug=<consumer-repo> and the petrova-codes state-sweep should fire within ~60s.