2026-05-10 — TASKSET 5a — `petrova-act` activated
rank: decision outranks: [finding, runbook]
Section titled “rank: decision outranks: [finding, runbook]”2026-05-10 — TASKSET 5a — petrova-act activated
Section titled “2026-05-10 — TASKSET 5a — petrova-act activated”Date: 2026-05-10 Status: closed Supersedes: none Superseded-by: none — current
Context
Section titled “Context”spec/verbs/ has defined eight write verbs since 2026-04-30. The
cli/ package implemented all eight with PAT/App auth, idempotency,
schema validation, and live PR emission, but no live PR had been
emitted and no MCP transport surfaced the verbs. CLAUDE.md framed
“write verbs land in TASKSET 5” as if greenfield; in reality the gap
was activation, not construction.
TASKSET 5a closes that gap.
Decisions
Section titled “Decisions”D1 — Verbs stay in cli/; host imports them
Section titled “D1 — Verbs stay in cli/; host imports them”No greenfield rewrite. host/package.json declares @petrova/cli as
a file: dependency; host/src/tools/mutations.ts registers thin MCP
adapters that translate snake_case envelopes into the camelCase option
objects each verb accepts. One implementation, three transports
(MCP, CLI, HTTP/dashboard later).
D2 — Idempotency stays as PR-body scan; no audit JSONL
Section titled “D2 — Idempotency stays as PR-body scan; no audit JSONL”The implemented mechanism (open-PR list filtered by idempotency_key
substring in PR body, plus open-branch detection by branch-name
prefix) is the contract. Audit JSONL was considered and rejected
(extra merge-conflict surface; PR history already serves as audit
trail).
D3 — fleets_allowed:[] is a hard wall, with documented human override
Section titled “D3 — fleets_allowed:[] is a hard wall, with documented human override”Pre-5a, checkFleetsAllowed early-returned for any non-fleet:
actor. Effect: a human-CLI call against petrova-codes
(fleets_allowed:[]) silently passed, contradicting CLAUDE.md’s
intent. Rewritten with EMPTY_MEANS_DENY: empty list refuses all
actors except humans who set PETROVA_ALLOW_HUMAN_OVERRIDE=1.
Override path emits a stderr audit line on every use. Fleet actors
can never bypass.
D4 — GitHub App petrova-act registered and installed on eva-hq
Section titled “D4 — GitHub App petrova-act registered and installed on eva-hq”App ID <set in env> registered under Dev4rno; private key stored
at /home/devarno/code/env/petrova-act.private-key.pem
(chmod 600); installation 131038933 on eva-hq/eva. PAT path via
PETROVA_GITHUB_TOKEN remains supported for development.
D5 — Two live PRs ship as evidence; remaining six verbs stay dry-run-tested
Section titled “D5 — Two live PRs ship as evidence; remaining six verbs stay dry-run-tested”open_decision and start_phase exercised end-to-end against
eva-hq: real PR opened, idempotency replay returned skipped_idempotent
with the same PR URL, smoke PRs then closed without merging. The
other six verbs (close_phase, update_milestone, verify_round,
request_review, request_merge_when_green, propose_fix) are
MCP-exposed and dry-run-tested; live exercise lands as real workflow
demands.
Evidence
Section titled “Evidence”open_decisionlive PR: https://github.com/eva-hq/eva/pull/17 (closed without merge — smoke artifact only).open_decisionidempotent replay returned same PR #17 withstatus: skipped_idempotent.start_phaselive PR: https://github.com/eva-hq/eva/pull/18 (closed without merge — smoke artifact only).start_phaseidempotent replay returned same PR #18 withstatus: skipped_idempotent.clitest suite: 183/184 pass (one pre-existing validate.test.ts failure unrelated to TASKSET 5a — flagged separately).hosttest suite: 66/66 pass, 8 new mutation-wrapper tests exercising every verb’s dry-run path.hostMCPtools/listexposes 19 tools (11 read + 8petrova.act.*write).
Architecture
Section titled “Architecture” ┌────────────────────────────────────┐ │ Adapters (transport) │ │ host/src/tools/mutations.ts (MCP)│ │ cli/src/index.ts (commander) │ │ api/rpc.ts (deferred) │ └────────────────┬───────────────────┘ │ verb(opts) → OutputEnvelope ▼ ┌────────────────────────────────────┐ │ cli/src/verbs/* │ │ _helpers.ts (auth gate) │ │ pr-emitter.ts (live emit) │ │ github-app.ts (App+PAT auth) │ │ github-client.ts (Octokit) │ │ idempotency.ts (canonical SHA256)│ └────────────────┬───────────────────┘ │ Octokit (App-installed) ▼ Target *-hq repo PRAlternatives considered
Section titled “Alternatives considered”- Greenfield rewrite in
host/src/verbs/— rejected: throws away ~2.7k LOC of working code to relitigate decisions already made well. - Audit JSONL appended in every verb’s commit — rejected: conflict surface and noisy diffs; PR-body scan already provides the signal, and GitHub’s PR history is itself the durable audit.
fleets_allowed:[]with no override at all — rejected: would force control-plane edits to bypass the verb path entirely (back to rawgitPRs), defeating the point of having verbs.- Live exercise of all eight verbs before close — rejected: manufactures work in target repos for verbs that already pass dry-run tests; live exercise lands organically as real workflow demands.
Consequences
Section titled “Consequences”For code:
cli/src/verbs/_helpers.tsenforces the auth gate in dry-run + apply.host/exports eightpetrova.act.*MCP tools; total surface area is now 19 MCP tools.host/package.jsondeclares@petrova/clias afile:dep.
For docs:
cli/.env.exampledocuments PAT, App, and override env vars.
For in-flight phases:
- TASKSET 5a closed.
- Remaining six verbs ship MCP-exposed but dry-run-only; condition for declaring TASKSET 5b done is “each remaining verb has been exercised live at least once.”
For invariants:
- MR-7 still upheld: every action carries a
triggered_byref and the audit trail is durable via PR history. - MR-3 (sibling-files) untouched.
- The
EMPTY_MEANS_DENYrule strengthens the implicit invariant thatpetrova-codesitself is human-PR-only.
- App install drift — App can be uninstalled silently from any
consumer org; verb call returns 404 from Octokit. Mitigation
deferred to TASKSET 5b: a
petrova diagnoseextension that surfaces “App installed on this repo? y/n”. - Idempotency-replay window — PR-body scan only catches open PRs. Re-running an already-merged verb opens a new PR with the same idempotency key. Acceptable: re-merging an already-applied verb is a human-noticed event (the diff against current state will be empty).
PETROVA_ALLOW_HUMAN_OVERRIDEin CI — the override is gated on a process env var; if set in shared CI infra, the gate becomes silent. Mitigation: stderr audit line; documented as not-for-CI incli/.env.example.
References
Section titled “References”- Spec:
docs/superpowers/specs/2026-05-10-taskset-5a-petrova-act-design.md - Plan:
docs/superpowers/plans/2026-05-10-taskset-5a-petrova-act.md - Smoke PRs: eva-hq/eva#17 (open_decision), eva-hq/eva#18 (start_phase)
- CLAUDE.md “What this repo deliberately does not do” section —
intent that
petrova-codesis human-PR-only.
Sign-off
Section titled “Sign-off”- Subagent: claude:taskset-5a
- Human: alex@devarno.com 2026-05-10