Skip to content

propose_fix

Draft a fix PR grounded in a preceding diagnose run. Composes request_review under the hood, but adds a hard binding to a diagnosis_id and requires explicit MR-12 grounding for the proposed change. The verb fleets reach for when implementing fixes.

Upholds: MR-7 · MR-12 Side effects: Same as request_review: branch + PR. Plus: PR body includes the diagnosis_id and links to the diagnose run’s recent_findings entries.

  • REPO_IN_REGISTRY — target_repo must appear in registry.yaml.
  • DIAGNOSIS_EXISTS — diagnosis_id must reference a diagnose run completed within the last 24 hours against the same target_repo.
  • DIAGNOSIS_REPO_MATCH — The diagnose run’s result.repo must equal envelope.target_repo.
  • FILES_NONEMPTY — proposed_changes must contain ≥1 entry.
  • TEST_PLAN_NONEMPTY — test_plan must list ≥1 verification step. Fixes without test plans are forbidden.
  • GROUNDING_NONEMPTY — mr_grounding must cite at least one MR or one decision doc / spec section explaining why the change is correct.
  • NO_PRIVILEGED_PATHS — Same privileged-path rejection as request_review.
  • diagnosis_id string (required)
  • title string (required)
  • rationale string (required)
  • proposed_changes array (required)
    • items:
      • path string (required)
      • operation string (required) enum: create, modify, delete
      • contents string
      • patch string
      • edit_rationale string — Per-file rationale; reviewer-readable.
  • test_plan array (required)
  • mr_grounding array (required)
    • items:
      • kind string (required) enum: meta_rule, decision_doc, spec, north_star, finding
      • ref string (required)
      • claim string — What this grounding source claims that justifies the change.
  • merge_strategy string enum: request_review, request_merge_when_green — Whether to compose request_review (default, human merges) or request_merge_when_green (profile-permitting, auto-merge).
  • diagnosis_id string
  • prPRRef
  • diff_previewDiffPreview
  • composed_verb string enum: request_review, request_merge_when_green

Input:

{
"envelope": {
"verb": "propose_fix",
"target_repo": "kahn-hq",
"idempotency_key": "2b7ffa28f53ee7791c25d4e8f9012b3c4d5e6f7a8b9c0d1e2f30415263748596",
"dry_run": true,
"actor": "fleet:kahn-implementer",
"triggered_by": {
"kind": "audit_fail",
"ref": "diag-a3f29c4b1e8d7602"
}
},
"params": {
"diagnosis_id": "diag-a3f29c4b1e8d7602",
"title": "fix(scope): live-dot for sub-second runs (M7.2.1)",
"rationale": "Diagnose surfaced live-dot regression on runs <1s; M7.2.1 acceptance criterion not met.",
"proposed_changes": [
{
"path": "frontend/src/components/live-dot.ts",
"operation": "modify",
"contents": "...",
"edit_rationale": "Lower threshold from 1000ms to 250ms."
}
],
"test_plan": [
"vitest live-dot.spec.ts (existing + new sub-1s case)",
"manual: scripts/seed-subsec-run.sh && curl /runs/<id> shows dot"
],
"mr_grounding": [
{
"kind": "meta_rule",
"ref": "MR-2",
"claim": "M6.5 carry-over rule: fix surfaced friction in next phase, not retrofit."
},
{
"kind": "decision_doc",
"ref": "docs/decisions/2026-04-28-phase-6-friction-round.md",
"claim": "Q1 surfaced live-dot subsec gap."
}
]
}
}

Output (output_dry_run):

{
"envelope": {
"verb": "propose_fix",
"status": "dry_run",
"idempotency_key": "2b7ffa28f53ee7791c25d4e8f9012b3c4d5e6f7a8b9c0d1e2f30415263748596",
"mr_citations": [
"MR-7",
"MR-12"
]
},
"result": {
"diagnosis_id": "diag-a3f29c4b1e8d7602",
"composed_verb": "request_review",
"diff_preview": {
"files": [
{
"path": "frontend/src/components/live-dot.ts",
"operation": "modify"
}
],
"branch": "petrova/propose-fix/2b7ffa28",
"commit_message": "fix(scope): live-dot for sub-second runs (M7.2.1)"
}
}
}

Schema: spec/verbs/propose_fix.schema.json Upholds: MR-7, MR-12. Composes: request_review (default) or request_merge_when_green.

propose_fix requires a fresh diagnose run (≤24h) for the same repo. The diagnosis cache lives at ~/.petrova/diagnoses.jsonl and persists diagnosis_id values across sessions.

If you don’t have a recent diagnose, run one first:

Terminal window
petrova diagnose <slug>
# Capture the diagnosis_id from the output: diag-<16 hex>

Implementing a fix where the rationale grounds in something diagnose surfaced — a recent finding, a milestone gap, a CI failure. The verb encodes the diagnosis → fix link explicitly so reviewers can see the chain.

{
"diagnosis_id": "diag-<16 hex>",
"title": "<conventional PR title>",
"rationale": "<why; lands in PR body>",
"proposed_changes": [
{"path": "<file>", "operation": "create|modify|delete", "contents": "<full contents>", "edit_rationale": "<per-file why>"}
],
"test_plan": ["<verification step 1>", "<step 2>"],
"mr_grounding": [
{"kind": "meta_rule|decision_doc|spec|north_star|finding", "ref": "<path or MR-N>", "claim": "<what this source claims that justifies the change>"}
]
}

Optional: merge_strategyrequest_review (default, human merges) or request_merge_when_green (profile-permitting, auto-merge).

  • diagnosis_id must exist in the local cache and be ≤24h old.
  • Diagnosis’s repo must equal the target repo (no cross-repo binds).
  • test_plan must contain ≥1 step. Fixes without test plans are forbidden.
  • mr_grounding must contain ≥1 entry. The “claim” field is what makes this MR-12-compliant — every fix names its source.
  • No privileged paths (.github/workflows/, *.env, etc.).
Terminal window
# 1. Diagnose
petrova diagnose kahn-hq --json | jq -r '.result.diagnosis_id'
# → diag-a3f29c4b1e8d7602
# 2. Compose fix
cat > /tmp/petrova-input.json <<'JSON'
{
"diagnosis_id": "diag-a3f29c4b1e8d7602",
"title": "fix(scope): live-dot for sub-second runs (M7.2.1)",
"rationale": "Diagnose surfaced live-dot regression on runs <1s; M7.2.1 acceptance criterion not met.",
"proposed_changes": [
{"path": "frontend/src/components/live-dot.ts", "operation": "modify", "contents": "...", "edit_rationale": "Lower threshold from 1000ms to 250ms."}
],
"test_plan": [
"vitest live-dot.spec.ts (existing + new sub-1s case)",
"manual: scripts/seed-subsec-run.sh && curl /runs/<id> shows dot"
],
"mr_grounding": [
{"kind": "meta_rule", "ref": "MR-2", "claim": "M6.5 carry-over rule: fix surfaced friction in next phase, not retrofit."},
{"kind": "decision_doc", "ref": "docs/decisions/2026-04-28-phase-6-friction-round.md", "claim": "Q1 surfaced live-dot subsec gap."}
]
}
JSON
# 3. Dry-run
petrova propose_fix kahn-hq --input /tmp/petrova-input.json
# 4. Show diff to human, get go-ahead, then apply
petrova propose_fix kahn-hq --input /tmp/petrova-input.json --apply
  • DIAGNOSIS_EXISTS — diagnose cache empty or older than 24h. Re-run.
  • DIAGNOSIS_REPO_MATCH — diagnose was for a different repo. Run diagnose for the right repo first.
  • NO_PRIVILEGED_PATHS — proposed change touches a privileged path. Refuse and route to a human.