Skip to content

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

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.

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.

  • open_decision live PR: https://github.com/eva-hq/eva/pull/17 (closed without merge — smoke artifact only).
  • open_decision idempotent replay returned same PR #17 with status: skipped_idempotent.
  • start_phase live PR: https://github.com/eva-hq/eva/pull/18 (closed without merge — smoke artifact only).
  • start_phase idempotent replay returned same PR #18 with status: skipped_idempotent.
  • cli test suite: 183/184 pass (one pre-existing validate.test.ts failure unrelated to TASKSET 5a — flagged separately).
  • host test suite: 66/66 pass, 8 new mutation-wrapper tests exercising every verb’s dry-run path.
  • host MCP tools/list exposes 19 tools (11 read + 8 petrova.act.* write).
┌────────────────────────────────────┐
│ 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 PR
  • 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 raw git PRs), 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.

For code:

  • cli/src/verbs/_helpers.ts enforces the auth gate in dry-run + apply.
  • host/ exports eight petrova.act.* MCP tools; total surface area is now 19 MCP tools.
  • host/package.json declares @petrova/cli as a file: dep.

For docs:

  • cli/.env.example documents 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_by ref and the audit trail is durable via PR history.
  • MR-3 (sibling-files) untouched.
  • The EMPTY_MEANS_DENY rule strengthens the implicit invariant that petrova-codes itself 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 diagnose extension 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_OVERRIDE in 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 in cli/.env.example.
  • 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-codes is human-PR-only.