{
  "schema_version": 1,
  "trace_date": "2026-05-08",
  "follow_up_trace_date": "2026-05-09",
  "synthesis_note": "The original audit-log SQL dump from /tmp/audit-trace.txt on the NAS was wiped during cleanup before the spec-migration session. This fixture is a documented synthesis of the bug shapes captured in the prior session's commit messages and analysis, NOT a verbatim event-by-event replay. Each record below is faithful to one bug observed in the trace and pins (verb, role, task setup, expected post-fix envelope shape) so the spec can never silently re-introduce the bug.",
  "known_bugs": [
    {
      "id": "bug-1-pm-cannot-execute-code-on-i-will-plan",
      "trace_date": "2026-05-08",
      "title": "pm_cannot_execute_code fired on i_will_plan, deadlocking code-typed parents",
      "summary": "Pre-fix the pm_cannot_execute_code guard rejected i_will_plan against code-typed parent tasks. Cell PMs PLAN code-typed parents and DELEGATE the code execution; rejecting the planning verb deadlocked the slice. Fix: scope the guard to i_will_work_on (the EXECUTION verb) only.",
      "verb": "i_will_plan",
      "role": "cell_pm",
      "task_status": "pending",
      "task_type": "code",
      "context": {"plan": "plan the backend slice"},
      "expected_post_fix_decision": "allow",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "504b553",
      "spec_invariant": "i_will_plan.allowed_roles must include cell_pm and main_pm regardless of task_type; per-role-vs-task_type narrowing only applies to i_will_work_on (the execution verb).",
      "replay_kind": "spec_decision"
    },
    {
      "id": "bug-2-delegate-magic-task-type-default",
      "trace_date": "2026-05-08",
      "title": "delegate.task_type silently defaulted to 'code' when caller omitted it",
      "summary": "main-pm called delegate without task_type; the schema's default of 'code' silently changed the semantics. The downstream cell PM ended up with a code-typed task they couldn't legitimately own. Fix: task_type is REQUIRED on DelegateRequest at the HTTP boundary (HTTP 422) and on the choreographer DelegateInputs dataclass.",
      "verb": "delegate",
      "role": "main_pm",
      "task_status": "in_progress",
      "task_type": null,
      "context": {},
      "expected_post_fix_decision": "rejected_at_http_boundary",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "504b553",
      "spec_invariant": "DelegateRequest.task_type is required (HTTP 422 at boundary); choreographer never receives a defaulted value. The spec itself doesn't gate task_type at delegate; it's enforced at the schema layer.",
      "replay_kind": "schema_only_skip",
      "skip_reason": "Bug is enforced at the Pydantic/HTTP schema boundary, not at the choreographer or spec layer. There is no spec-level Decision to assert. Documented for completeness; replay test skips."
    },
    {
      "id": "bug-3-silent-partial-submit-for-qa",
      "trace_date": "2026-05-08",
      "title": "submit_for_qa returned OK after opening PR but agent never called i_am_done",
      "summary": "The verb name 'submit_for_qa' implied the QA handoff was complete after the PR opened. Agents read the verb name, never called i_am_done, and PRs ended up orphaned (PR #12 in the trace). Fix: rename submit_for_qa -> open_pr; the actual QA handoff happens at i_am_done. Atomic preconditions (assignee, commits, no-prior-PR) checked BEFORE git.create_pr/push_branch.",
      "verb": "open_pr",
      "role": "developer",
      "task_status": "in_progress",
      "task_type": "code",
      "context": {"owns_task": true, "commits": 1, "pr_number": null},
      "expected_post_fix_decision": "allow",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "5a10ae9",
      "spec_invariant": "open_pr's IntentSpec exists in _INTENT_VERBS (the rename landed); composes=() with side_effects=('push_branch','create_pr'); extra_preconditions=(PRECONDITION_OWNERSHIP, PRECONDITION_COMMITS, PRECONDITION_NO_PR). submit_for_qa is NOT in the verb set.",
      "replay_kind": "spec_introspection"
    },
    {
      "id": "bug-4-scattered-role-checks-disagreed",
      "trace_date": "2026-05-08",
      "title": "Three places held role x state tables that could disagree silently",
      "summary": "Pre-fix, role checks lived in role_config.py, claim_guards.py, and Choreographer string constants in _impl.py / qa.py / doc.py / content_actions.py. They could (and did) drift. Fix: collapse into a single declarative table — verb_gates.py initially (Task 81ffc16), now lifecycle.spec (the canonical migration). Every consumer derives behavior from spec.intents_for_role / spec.can_invoke_intent / spec.valid_next_verbs.",
      "verb": "*",
      "role": "*",
      "task_status": "*",
      "task_type": "*",
      "context": {},
      "expected_post_fix_decision": "single_source_of_truth",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "81ffc16",
      "spec_invariant": "spec.intents_for_role(role) returns the same verb set as role_config.FLOW_TOOLS_BY_ROLE; verb_gates.py is deleted (commit bdeedd8); choreographer verbs all gate via spec.can_invoke_intent (no scattered role-string lists).",
      "replay_kind": "spec_introspection"
    },
    {
      "id": "bug-5-i-will-plan-not-idempotent-after-restart",
      "trace_date": "2026-05-08",
      "title": "i_will_plan rejected re-entry after orchestrator restart, agent had no recovery path",
      "summary": "Spawn 1: i_will_plan succeeds, task pending -> claimed -> in_progress. Container exits, agent respawns. Spawn 2: i_will_plan called again on a task already in in_progress; pre-fix preflight rejected with 'task in in_progress, expected pending'. Agent had no recovery; reaper eventually dropped the claim, spawn 3 fired, infinite loop. Fix: idempotent re-entry — if respawned PM/dev re-enters on a task they already own in claimed/in_progress, return OK with current state and refresh heartbeat.",
      "verb": "i_will_plan",
      "role": "cell_pm",
      "task_status": "in_progress",
      "task_type": "planning",
      "context": {"owns_task": true, "plan": "still my plan"},
      "expected_post_fix_decision": "verb_body_idempotent_returns_ok",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "e51ba30",
      "spec_invariant": "Idempotent re-entry is a verb-body concern (Choreographer-level guard the spec does not model). Spec-level: i_will_plan with composes=('claim','set_plan','start') would reject from in_progress because claim's source_statuses don't include in_progress. The verb body intentionally bypasses the spec gate when the caller is the current owner.",
      "replay_kind": "behavioral_skip",
      "skip_reason": "Idempotent re-entry is a Choreographer-level behavioral guard, not a spec-level Decision. Pinned by separate unit tests in test_choreographer_pm_extras. Spec gate rejection is the EXPECTED first-line behavior; the verb body's owner-shortcut is what makes the verb idempotent."
    },
    {
      "id": "bug-6-not-your-claim-after-restart",
      "trace_date": "2026-05-08",
      "title": "'not your claim' rejection on unclaim/resume gave no current_owner hint",
      "summary": "main-pm hit 'not your claim' on unclaim/resume at 02:51:22 / 02:52:48 because an upstream verb (cell_pm_complete propagating up via _maybe_advance_parent_to_pm_review, or main_pm_complete clearing assigned_to to None, or unblock with restore=True) reassigned the task. Pre-fix the rejection said only 'not your claim'; agents couldn't tell if it was a transient race or a legitimate move. Fix: surface current_owner UUID and hint that an upstream verb did this.",
      "verb": "unclaim",
      "role": "main_pm",
      "task_status": "in_progress",
      "task_type": "planning",
      "context": {"actor_is_owner": false, "task_was_reassigned": true},
      "expected_post_fix_decision": "verb_body_includes_current_owner_hint",
      "expected_post_fix_envelope_error": "invalid_state",
      "fix_commit": "a5d358d",
      "spec_invariant": "unclaim's IntentSpec has composes=() and no extra_preconditions; the spec gate is role-only. The reassignment-rejection branch with current_owner hint is a Choreographer-level guard the spec does not model. Spec invariant: role gate must allow main_pm to call unclaim regardless of actual ownership; the verb body's current_owner-hinted rejection is then the load-bearing UX fix.",
      "replay_kind": "spec_decision_role_only"
    },
    {
      "id": "bug-7-plan-sdk-vs-gate-disagreement",
      "trace_date": "2026-05-08",
      "title": "Agent SDK / verb-gate disagreement on what to do after submit_for_qa",
      "summary": "Pre-fix the agent_sdk post-tool guidance map said one thing about next= after submit_for_qa, while the gateway envelope's next-hint said another. The verb's name ('submit_for_qa') reinforced the wrong mental model. Coupled with the rename of submit_for_qa -> open_pr, the SDK guidance, role_config flow manifest, runtime orchestrator prompts, and generated agent prompts were all updated in lockstep. Fix: single rename across all surfaces; the spec is now the source of truth via spec.intents_for_role + spec generators.",
      "verb": "open_pr",
      "role": "developer",
      "task_status": "in_progress",
      "task_type": "code",
      "context": {},
      "expected_post_fix_decision": "single_source_of_truth",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "5a10ae9",
      "spec_invariant": "open_pr is in _INTENT_VERBS and submit_for_qa is NOT. spec.intents_for_role(Role.DEVELOPER) returns open_pr (and not submit_for_qa). All agent-facing surfaces (SDK guidance map, role_config flow lists, agent prompts) are derived from this spec.",
      "replay_kind": "spec_introspection"
    },
    {
      "id": "bug-8-audit-role-wrong",
      "trace_date": "2026-05-08",
      "title": "audit_log.agent_role recorded the verb's expected role, not the actor's actual role",
      "summary": "The trace caught an audit row with actor=main-pm but agent_role=cell_pm. The caller had supplied the verb's *expected* role rather than the actor's actual role. Forensics joining audit_log on agent_role would silently miscategorize. Fix: AuditService now reads the actor's role directly from agents.role at write time via _resolve_actor_role_from_db.",
      "verb": "*",
      "role": "*",
      "task_status": "*",
      "task_type": "*",
      "context": {},
      "expected_post_fix_decision": "audit_layer_concern",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "7a2d4e3",
      "spec_invariant": "Spec layer does not model audit. Bug is a side-channel data integrity concern in roboco.services.audit; fix lives below the spec.",
      "replay_kind": "audit_only_skip",
      "skip_reason": "Audit-layer concern below the lifecycle spec. The fix is in roboco.services.audit; pinned by tests in tests/unit/services/test_audit.py and test_audit_real_query.py."
    },
    {
      "id": "bug-9-notfounderror-500s",
      "trace_date": "2026-05-08",
      "title": "agent_id null on task.* and agent.* audit events",
      "summary": "task.awaiting_qa fired AFTER submit_qa cleared claimed_by; the audit row therefore had agent_id=null. agent.* events stored slug-only. Forensics by agent_id missed these rows. Fix: capture claimed_by BEFORE mutation and add slug->UUID resolver in orchestrator audit path. (Categorized as 'NotFoundError 500s' colloquially because the missing-id-then-lookup pattern surfaced as 500s in some downstream consumers.)",
      "verb": "*",
      "role": "*",
      "task_status": "*",
      "task_type": "*",
      "context": {},
      "expected_post_fix_decision": "audit_layer_concern",
      "expected_post_fix_envelope_error": null,
      "fix_commit": "e9d53fb",
      "spec_invariant": "Spec layer does not model audit-event field population. Bug is a data-capture ordering concern in roboco.services.audit + roboco.services.task; fix lives below the spec.",
      "replay_kind": "audit_only_skip",
      "skip_reason": "Audit-layer concern below the lifecycle spec. The fix is in roboco.services.audit + roboco.services.task; pinned by tests in tests/unit/services/test_audit_agent_id.py."
    },
    {
      "id": "bug-a-claim-before-plan-check",
      "trace_date": "2026-05-09",
      "title": "i_will_work_on without plan partially claimed (claim ran before plan check)",
      "summary": "be-dev-1 called i_will_work_on without `plan` on a pending task. claim() ran first (transitioned to claimed), then the plan check failed with tracing_gap. The natural retry path then dispatched to _i_will_work_on_claimed, which had no plan-recovery logic and called start() against a still-plan-less task, returning 'start failed' forever. Dev kept looping; parent escalated up; whole slice ended blocked. Fix (Task-5 atomicity pattern): plan precondition runs BEFORE claim(); a missing-plan first call now returns tracing_gap with the task untouched in pending.",
      "verb": "i_will_work_on",
      "role": "developer",
      "task_status": "pending",
      "task_type": "code",
      "context": {"plan": null},
      "expected_post_fix_decision": "tracing_gap",
      "expected_post_fix_envelope_error": "tracing_gap",
      "expected_missing": ["plan"],
      "fix_commit": "1e4c7a8",
      "spec_invariant": "i_will_work_on's IntentSpec has extra_preconditions=(PRECONDITION_PLAN,). spec.can_invoke_intent rejects with rejection_kind='tracing_gap' and missing=['plan'] BEFORE any composed action is attempted. The verb body must run the spec gate before any DB mutation (claim+set_plan+start are atomic per Task 10's VerbRunner).",
      "replay_kind": "spec_decision"
    },
    {
      "id": "bug-b-cell-pm-gets-code-typed-task",
      "trace_date": "2026-05-09",
      "title": "main-pm delegated a code-typed task to a Cell PM (mis-typed subtask)",
      "summary": "main-pm called delegate(assigned_to='be-pm', task_type='code'). The chain validator let it through (be-pm IS in main-pm's allowed targets), the schema let it through (task_type='code' is a valid enum value), and the subtask was created mis-typed. Task 0 made it cosmetically work (PMs can plan code-typed parents) but the model is wrong: a Cell PM owns the PLANNING of the slice; code execution is delegated to devs. Fix: new gate in _delegate_static_guards — when assignee is a Cell PM (be-pm/fe-pm/ux-pm), task_type MUST be 'planning'. Returns invalid_state with a remediate hint pointing at the right type. Devs are unrestricted.",
      "verb": "delegate",
      "role": "main_pm",
      "task_status": "in_progress",
      "task_type": "planning",
      "delegate_inputs": {
        "assigned_to": "be-pm",
        "team": "backend",
        "task_type": "code"
      },
      "context": {},
      "expected_post_fix_decision": "verb_body_invalid_state",
      "expected_post_fix_envelope_error": "invalid_state",
      "fix_commit": "dfbcb3e",
      "spec_invariant": "delegate's IntentSpec gates role+state at the spec layer (_PM_ROLES, parent task_status==in_progress). The Cell-PM-vs-task-type narrowing is a verb-body guard (_delegate_static_guards) the spec does not directly model; documented as a spec-vs-canon diff in the design doc. The role+state gate must STILL allow the verb to enter so the verb-body gate can fire and surface invalid_state with the right remediate.",
      "replay_kind": "spec_decision_then_verb_body"
    }
  ]
}
