Commit Graph
137 Commits
Author SHA1 Message Date
Renn F 38246050d6 feat(gateway): audit-log every gate rejection
Choreographer takes an audit dep but never used it. Now every rejection
Envelope (invalid_state, not_authorized, tracing_gap, not_found) writes a
gateway.rejected row with verb + reason + missing fields. Forensic
signal for stuck flows; no behaviour change for happy path.

Adds AuditService.log_event() — generic free-form event_type write so
the gateway doesn't have to extend AuditEventType for every new surface.
Audit writes are best-effort: a failure logs a warning but never
propagates, since the agent's response Envelope is the contract.
2026-05-03 06:00:36 +02:00
Renn F 8b43ed98be fix(messaging): close fail-open when agent_slug lookup returns None
I1: post_to_channel raised no error if get_agent_slug returned None
(unknown/deleted agent), and send_message's 'if agent_slug' skipped
the validate_channel_access call. Same fail-open class the prior
commit was fixing, just narrower window. Now post_to_channel raises
ChannelAccessDeniedError directly when the slug lookup fails — say()
already converts that to a clean not_authorized Envelope.

I2: 'writable channels for your role' wording was misleading because
get_agent_channels resolves by slug, not role. Replaced with
'channels you may write to' for clarity.
2026-05-03 05:46:02 +02:00
Renn F 7907988e52 fix(messaging): restore channel-access RBAC in gateway say()
post_to_channel was calling send_message without agent_slug, which
disabled the validate_channel_access check. Forward the slug; convert
ChannelAccessDeniedError to a friendly not_authorized Envelope with
the agent's writable-channel list (pre-gateway behaviour).
2026-05-03 05:37:12 +02:00
Renn F 92294f90fb feat(api/v2): enforce X-Agent-Role on every flow router
Route layer now rejects 403 if the role doesn't match the router's
allowed set. Choreographer still re-checks role per verb where needed,
but defense in depth means a future verb that forgets the role check
doesn't leak. Auditor router also gated.
2026-05-03 05:27:31 +02:00
Renn F 3d5f14815d feat(gateway): add submit_for_qa verb so devs can open PRs
Gate E made i_am_done strict (requires pr_number set), but the only
verb that opened PRs was i_am_done_with_catchup which lives off the
dev manifest. Devs hit NO_PR with no escape. Adds submit_for_qa as
the explicit push+PR step, leaving i_am_done to do the strict submit.
2026-05-03 05:14:07 +02:00
Renn F 6643b4c375 feat(gateway): heartbeat on every hot verb
i_will_work_on, i_have_committed, i_am_done, i_am_blocked, i_will_plan,
and pm_give_me_work now call task.heartbeat() so the reaper has fresh
data. Closes the loop opened by tasks 2a + 2b.
2026-05-03 05:01:50 +02:00
Renn F c12aad3005 fix(orchestrator): consolidate stale-heartbeat config + drop dead _task_svc slot
I1: claim_heartbeat_ttl_seconds (300s) overlapped semantically with the
pre-existing claim_stale_seconds (180s). Between 180-300s of silence,
trigger_filter queued duplicate spawns while the reaper hadn't yet
released the claim — exactly the dispatcher churn the reaper was
supposed to close. Collapse to one field (claim_stale_seconds, 180s);
reaper now consumes the same setting trigger_filter uses, so both
agree on 'stale' on the same tick and the reaper runs first.

I2: _task_svc injection slot on AgentOrchestrator.__init__ was
production-dead (always None) and only used by __new__-based test
instances. Drop the __init__ slot + the production branch in
_reap_stale_claims that read it. Tests still pre-bind on __new__
instances; the attribute exists per-instance, not per-class.
2026-05-03 04:57:25 +02:00
Renn F b301020398 feat(orchestrator): reap stale claims via last_heartbeat_at
Dispatch loop now releases tasks whose holder has gone silent past
ROBOCO_CLAIM_HEARTBEAT_TTL_SECONDS (default 300s). Closes the
'dead container squats task forever' failure mode that the schema
hinted at but no code enforced.
2026-05-03 04:47:51 +02:00
Renn F 37bc4e58ed feat(task): add heartbeat() to touch last_heartbeat_at
Foundation for stale-claim recovery. Schema column existed since
migration 006; no writer until now. Idempotent UPDATE — no select
roundtrip — so callable from any hot verb without extra DB cost.
2026-05-03 04:37:19 +02:00
Renn F c33340bda1 fix(gateway): correct claim/start argument order in choreographer
Service signatures are (task_id, agent_id, ...) but choreographer was
calling (agent_id, task_id). Production claim path silently returned
None; unit tests pinned the buggy order so the bug was invisible. Swap
all 7 call sites and update test assertions. Add a regression pin
that locks in the correct order.
2026-05-03 04:27:14 +02:00
Renn FandClaude Opus 4.7 4c9b7c4210 feat(gateway): restore Gate Set F completion-time guards
cell_pm_complete, main_pm_complete, and submit_up already had subtask-
terminality gates inherited from the pre-gateway closure check at
roboco/services/task.py. This commit verifies the gate is preserved
and improves the remediation hint to actually NAME the non-terminal
subtasks instead of telling the PM to "call triage()".

The improvement uses a new private helper
``Choreographer._non_terminal_subtask_ids`` that queries get_subtasks
and filters to non-terminal statuses, returning a comma-separated
list of "<id> (<status>)" pairs. The PM now sees exactly which
subtasks are blocking the parent's completion.

Pre-gateway reference: roboco/services/task.py closure check
(documented in PRE_GATEWAY_LIFECYCLE.md §4.3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:37:17 +02:00
Renn FandClaude Opus 4.7 4cb47afdb2 feat(gateway): restore Gate Set E submit-qa field-level gates
i_am_done now strictly enforces the four pre-gateway field-level gates
restored from roboco/api/routes/tasks.py:903-940 at commit 254cc93:

- NOT_SELF_VERIFIED: task.self_verified must be true.
- NO_COMMITS: task.commits must be non-empty.
- NO_PR: task.pr_number must be set.
- NO_PROGRESS: task.progress_updates must have at least one entry.

Each missing field surfaces as a tracing_gap with the matching pre-
gateway error code in the missing list, and a remediation hint that
tells the dev exactly what to do.

The previous silent-catch-up behavior is preserved as a separate
opt-in verb i_am_done_with_catchup. Existing tests that asserted the
catch-up behavior have been migrated to the new verb.

This fixes the failure mode where a dev could call i_am_done with no
commits and the gateway would silently try to push nothing, open an
empty PR, etc. — now the dev sees an explicit error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:34:57 +02:00
Renn FandClaude Opus 4.7 855cd24477 feat(gateway): restore Gate Set D content-tool ownership guards
When a caller passes an explicit task_id to commit / note / say / dm /
evidence, ContentActions now verifies task.assigned_to == caller_agent_id
before allowing the side effect. Auto-fill from get_active_task_for_agent
is implicitly self-owned and does not need a re-check.

evidence() additionally allows assigned_to=None (post-handoff transient
state) so QA / documenter can inspect tasks between reassignments.

Pre-gateway, agents could not even see tasks they didn't own because the
MCP handlers resolved task from session context. The gateway exposes
task_id parameters across multiple verbs, so the explicit ownership
gate is required.

Exception: say() and dm() with NO task_id are exempt — used for channel
announcements and off-task A2A. The strict guard only applies when the
agent supplies a task_id parameter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:31:10 +02:00
Renn FandClaude Opus 4.7 197b0f22dc feat(gateway): restore Gate Set C exit-time guards
Choreographer.i_am_idle now refuses with INVALID_STATE when the caller
has any pending (assigned but never claimed) task. Pre-gateway this
was implicit because the orchestrator's auto-respawn would re-spawn
the agent for the assignment, leading to a tight respawn loop. The
explicit refusal lets the agent fix the state via i_will_work_on
(dev/qa/doc) or i_will_plan (pm) first.

Existing auto-pause for in_progress tasks is preserved (Gate Set C
spec calls this out as still required) — it runs AFTER the pending
guard, so an agent with a mix of pending+in_progress is told about
the pending task first instead of silently pausing in_progress and
then looping on the pending one.

Pre-gateway reference: roboco/runtime/orchestrator.py
auto-respawn loop guards (already preserved at HEAD); the explicit
agent-facing gate is new.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:27:54 +02:00
Renn FandClaude Opus 4.7 466cc8d8f7 feat(gateway): restore Gate Set B delegation-time guards
PARENT_NOT_CLAIMED: Choreographer.delegate now enforces that the parent
task is in_progress AND assigned to the calling PM before allowing
subtask creation. Pre-gateway this was implicit (orchestrator only
spawned PMs after they claimed their parent); the gateway exposes
delegate as a first-class verb so the gate must be explicit.

SUBTASK_CAP: hard-blocks delegation when the parent already has 12
subtasks. Pre-gateway never had this cap because PMs naturally never
created more than a handful per spawn cycle; with delegate as a verb
agents can loop, so a cap is needed.

The _delegate_guard helper was split into _delegate_role_guards,
_delegate_static_guards, and _delegate_lifecycle_guards to keep each
piece below the PLR0911 return-count threshold and make the layered
gating explicit.

Pre-gateway reference: implicit in roboco/runtime/orchestrator.py
spawn flow; restored here as explicit server-side enforcement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:26:00 +02:00
Renn FandClaude Opus 4.7 5c0011c90b feat(gateway): restore Gate Set A claim-time guards
Ports five pre-gateway predicates that were dropped when the gateway
displaced the MCP claim handler. Predicates restored from
roboco/mcp/tasks/handlers/_helpers.py:124-204 and
roboco/mcp/tasks/handlers/claim.py:121-180 at commit 254cc93:

- SEQUENCE_ORDER_VIOLATION: a sibling task with sequence < N must be
  in completed/cancelled before sibling N can be claimed.
- ALREADY_ACTIVE: agent cannot claim while owning an in_progress /
  claimed / verifying task other than the one being resumed.
- PAUSED_TASKS_EXIST: agent cannot claim while paused tasks exist.
- PM_CANNOT_EXECUTE_CODE: cell_pm/main_pm cannot claim task_type=code.
- ROLE_TYPED_CLAIM: developer claim is restricted to
  code/research/design; qa/documenter must use claim_review /
  claim_doc_task.

All five guards run inside Choreographer._run_claim_guards before
i_will_work_on / i_will_plan / claim_review / claim_doc_task mutate
state. Skip flags isolate guards that don't apply to a verb (e.g.,
PM-code skipped on QA verb, role-typed skipped on PM verb).

The guards live in roboco/services/gateway/claim_guards.py so
choreographer.py stays focused on orchestration.

Existing tests updated to provide the new mock primings; the
permissive auto-mock behavior they relied on no longer applies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:23:04 +02:00
Renn FandClaude Opus 4.7 1da4ac4b2e chore(prompts): restore role identity framing + anti-patterns
Each role file now has a six-section structure (Identity / Inputs / Your
verbs / Workflow / Anti-patterns / When the gateway returns an error).
The Identity section gives a hard role prior with explicit negative space
("you do NOT write code", "you do NOT merge"), recovering the framing
that was lost in the gateway slim-down. The Anti-patterns section names
the gateway error codes the role will hit if they step out
(PARENT_NOT_CLAIMED, SUBTASK_CAP, PM_CANNOT_EXECUTE_CODE, NO_COMMITS,
NO_PR, NOT_SELF_VERIFIED, etc.), so agents expect the system to catch
them. base.md now centralizes the envelope contract, ground rules, and
channel-slug convention so per-role files stay focused.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:17:11 +02:00
Renn FandClaude Opus 4.7 27dccc7215 fix(gateway): four PM-lifecycle smoke regressions
* choreographer.escalate_up: AttributeError when target lookup returned
  None. Switched from role-based escalate_up_to_role (which mishandled
  slug-shaped escalation_target like "main-pm" because AgentRole only
  accepts underscore form) to slug-based task.escalate, with explicit
  None-handling that returns invalid_state instead of 500.

* prompts/roles/cell_pm.md + main_pm.md: enumerate the new lifecycle
  verbs (i_will_plan, delegate, submit_up, give_me_work, i_am_idle).
  Without this, PM agents fell back to calling i_will_work_on (the dev
  verb) and 404'd at /api/v2/flow/cell_pm/i_will_work_on. Workflow
  walkthroughs included.

* messaging.get_channel_by_slug + get_or_create_channel_by_slug: strip
  leading "#" so "#main-pm-board" resolves to the row stored as
  "main-pm-board". Agents follow Slack convention; gateway must accept
  it.

* agent_sdk session-end post-mortem hook: corrected payload shape from
  {content, kind:"reflect"} to {type:"task_reflection", title, content}
  so /api/journals/me/entries validates. Added pad-to-min-length so the
  50-char content gate doesn't reject thin post-mortems.

* test_choreographer_pm: updated escalate_up test to assert task.escalate
  is awaited, plus regression test for the None-target invalid_state path.

380 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:54:05 +02:00
Renn F 4520293def feat(gateway): wire PM lifecycle verbs through API + MCP + tests
* api/schemas/v2/flow.py: IWillPlanRequest, DelegateRequest,
  SubmitUpRequest with min_length=1 validators where appropriate.

* api/routes/v2/flow_cell_pm.py: give_me_work routes to
  pm_give_me_work; new endpoints i_will_plan, delegate, submit_up.
* api/routes/v2/flow_main_pm.py: new endpoints give_me_work,
  i_will_plan, delegate.

* mcp/flow_server.py: Python wrappers for i_will_plan, delegate,
  submit_up registered in _TOOLS so manifest-scoped agents can call
  them.

* tests/unit/gateway/test_choreographer_pm_extras.py: 22 tests
  covering happy + reject paths for each new verb plus i_am_idle's
  auto-pause behavior.
* tests/unit/api/routes/v2/test_flow_cell_pm.py +
  test_flow_main_pm.py: route-level tests for the new endpoints.

Test count: 352 → 381 (+29). make quality-fast green.
2026-05-03 00:06:50 +02:00
Renn F 4e5cdd0891 feat(gateway): restore PM lifecycle verbs (i_will_plan/delegate/submit_up)
Adds the missing gateway verbs that PMs need to drive their parent task
through the lifecycle. Pre-Phase-4, PMs could only triage/unblock/
complete/escalate/idle — they had no way to claim+start their parent
task, no way to create subtasks (told to curl raw), no way to bubble
cell-level PRs up to the Main PM. The lifecycle stalled at pending.

This commit lands the choreographer + service layer:

* TaskService gains: list_in_progress_for_agent, pause_for_agent,
  submit_pm_review (gateway alias of submit_for_pm_review),
  create_subtask (TaskCreateRequest-based, infers status from
  assigned_to), and main_pm_agent.

* Choreographer gains:
  - i_will_plan(task_id, plan): claim + set_plan + start for PMs.
    Mirrors i_will_work_on, scoped to cell_pm/main_pm.
  - delegate(parent_task_id, DelegateInputs): create a subtask with
    delegation-chain validation (Main PM → cell PMs; Cell PM → its
    own team's devs). Resolves slug → UUID via AGENT_UUIDS.
  - submit_up(task_id, notes): cell PM bubbles a finished cell-scope
    task up to Main PM. Opens a cell-level PR into the parent (Main
    PM) branch, transitions to awaiting_pm_review, reassigns to
    Main PM. Tracing gates: notes>=20, journal:decision, all
    subtasks terminal, branch present.
  - pm_give_me_work: returns the PM's first assigned task in any
    active status (not just dev-active states).
  - i_am_idle: now auto-pauses every in_progress task this agent
    owns before marking idle. Restores the pre-Phase-4 behavior the
    closure dispatcher relies on.

* role_config: cell_pm gains give_me_work + i_will_plan + delegate
  + submit_up; main_pm gains give_me_work + i_will_plan + delegate.
2026-05-03 00:06:29 +02:00
Renn F 736bd57f30 fix(orchestrator/prompts): rewrite stale spawn prompts to use gateway verbs
5 spawn prompt builders (_build_dev_prompt + _get_workflow_instructions,
_build_qa_prompt, _build_doc_prompt, _build_pm_review_prompt,
_build_pm_closure_prompt) plus the recently-rewritten triage prompts
referenced MCP tools deleted in Phase 4 T9. Replace with gateway verbs
(give_me_work, i_will_work_on, claim_review, claim_doc_task,
i_will_plan, delegate, complete, submit_up, etc). Drops the curl-POST
recipe in favor of the new delegate verb. Surfaced live during NAS
smoke.
2026-05-02 23:53:02 +02:00
Renn F 249e9c2c59 fix(gateway): align content_actions with actual service method names
content_actions.note/dm/say/commit/evidence called write_entry/send/
post_to_channel/git.commit/git.diff(base=)/fetch_branch_for_inspection,
none of which existed on JournalService/A2AService/MessagingService/
GitService/WorkspaceService. Live smoke threw AttributeError on every
content tool. Add the matching gateway-shaped adapters on each service
(scope-string -> JournalEntryType for note; channel-by-slug -> default
group -> active session for say; UUID-or-slug recipient resolution for
dm; branch-name commit + diff(base=) for commit/evidence; project-aware
fetch_branch_for_inspection on workspace). Surfaced live.
2026-05-02 21:48:07 +02:00
Renn F 54e19f88e6 fix(orchestrator): rewrite PM spawn prompts with gateway verbs + curl for delegation
Both _build_main_pm_triage_prompt and _build_pm_triage_prompt referenced MCP tools deleted in Phase 4 T9 (roboco_task_get/plan/start/create/activate/journal_decision/agent_idle). Main_pm + cell_pm spawned with these and tried the dead tools — minimax-m2.7 then improvised, ending up implementing the task itself instead of breaking it down.

New prompts: gateway verbs (evidence, note, say, dm, complete, escalate_*, i_am_idle) for transitions/journal/comms; explicit POST /api/tasks curl recipe for breakdown + delegation (gateway has no create_subtask verb yet). Both prompts now reinforce: PMs do not implement, do not run git, must delegate. Main PM hands to Cell PMs; Cell PMs hand to devs.

Surfaced live during NAS smoke.
2026-05-02 19:04:57 +02:00
Renn F 7089d78428 Revert "fix(orchestrator): default code tasks route to dev, not cell_pm"
This reverts commit 8d689e3eec.
2026-05-02 18:59:49 +02:00
Renn F 8d689e3eec fix(orchestrator): default code tasks route to dev, not cell_pm
_classify_code_task routed every default-complexity (medium) code task to cell_pm even when the description named no coordination work. The PM then re-delegated back to a developer, adding a useless hop and producing the smoke-test pattern where main_pm/cell_pm tried to do every lifecycle stage themselves.

Drop the complexity==medium → cell_pm branch. Cell PM now lights up only when the description carries an actual coordination keyword (coordinate, integration, cross-team, sync, planning, milestone, dependencies, review). High/critical complexity, cross-cell keywords, missing team, and team=all still route to main_pm. Adds 11 unit tests; full suite 363 passing.

Surfaced live during NAS smoke.
2026-05-02 18:57:47 +02:00
Renn F 4a3da479d1 test(gateway): cover per-transition reassignment
Adds:
- TaskService.reassign unit tests (set, clear, missing-task)
- New test_choreographer_reassignment.py covering:
    i_am_done -> qa
    pass_review -> documenter
    i_documented -> cell_pm
    main_pm_complete -> None (CEO via UI)
    escalate_to_ceo (board) -> None
    cell_pm_complete -> walks up to parent and reassigns when all
                        siblings terminal, skips otherwise
    fail_review -> does NOT issue an explicit reassign (qa_fail
                   already restores the original developer via
                   quick_context)
2026-05-02 05:25:07 +02:00
Renn F 3b02a72e05 feat(gateway): reassign task.assigned_to on every lifecycle transition
The orchestrator polls per-agent for actionable tasks. Without
updating assigned_to as the lifecycle hands a task to the next role,
the orchestrator kept respawning the previous-stage agent (often
main-pm) for every stage, hitting role-permission rejections and
looping. Each Choreographer transition now writes the new assignee
alongside the existing A2A notification.

- Adds TaskService.reassign(task_id, new_assignee | None)
- _notify_qa: dev → qa
- pass_review: qa → documenter
- i_documented: doc → cell_pm
- main_pm_complete + escalate_to_ceo: clear assignment (CEO acts via UI)
- cell_pm_complete: walks up to parent; if all subtasks terminal,
  hands the parent off to the cell PM for that team via a new
  _maybe_advance_parent_to_pm_review helper

fail_review still leans on qa_fail's existing original_developer
recovery via quick_context, and unblock_with_restore restores
pre_block_assignee — both already correct, no changes needed.
2026-05-02 05:24:54 +02:00
Renn F 55d2fae564 fix(errors): replace stale roboco_task_* MCP refs in error messages with gateway verbs
10 stale remediation hints across api/routes/tasks.py + exceptions.py pointed at MCP tools deleted in Phase 4 T9 (roboco_task_start, roboco_task_qa_pass, roboco_task_qa_fail, roboco_task_progress, roboco_task_unblock, roboco_task_submit_verification, roboco_task_submit_qa, roboco_task_complete, roboco_task_claim, roboco_task_activate). Each now mentions both the gateway verb (i_will_work_on, pass, fail, complete, unblock, etc.) and the panel REST equivalent. Surfaced live during NAS smoke.
2026-05-02 05:13:28 +02:00
Renn F 453a7ae22a fix(orchestrator): forward agent UUID (not slug) as ROBOCO_AGENT_ID to MCP env
Gateway v2 endpoints declare X-Agent-ID as Annotated[UUID, Header(...)]. The MCP servers (flow_server, do_server) read os.environ['ROBOCO_AGENT_ID'] verbatim and put it in the header. We were exporting the slug ('main-pm', 'be-dev-1', ...) so every gateway call from a containerized agent 422'd on header UUID parse. Resolve to UUID via AGENT_UUIDS lookup in seeds/initial_data.py. Surfaced by the new 422 logger (de54c3b) during NAS smoke.
2026-05-02 05:06:32 +02:00
Renn F de54c3b52d feat(api/middleware): log request body + per-field errors on 422 validation failures
FastAPI's default RequestValidationError returns details to the client but nothing to server logs. Smoke test hit a 422 on /api/v2/flow/main_pm/complete with no way to tell which field failed. Add a handler that logs path/method/body/errors on every 422 so the next failure is debuggable in one log scan.
2026-05-02 04:55:21 +02:00
Renn F 33464a207a docs(prompts): forbid Bash curl/git for gateway-covered ops; PMs don't implement
Live smoke runs showed agents reaching for `Bash curl /api/...` and
`Bash git ...` even though the slim role prompts named the gateway
verbs, and main_pm took ownership of an implementation task and tried
to commit code from the PM seat.

- base.md and every role prompt: explicit ground rule that direct
  curl-to-orchestrator and raw-git invocations are forbidden — every
  commit/push/PR/transition/journal/comms call goes through the gateway
  verbs. The deny-list line in base.md now also covers curl/wget to the
  orchestrator's /api/... (was GitHub-only before).
- main_pm.md and cell_pm.md: explicit "you do not implement tasks
  yourself" rule. Implementation belongs to developers; PMs delegate.
2026-05-02 04:45:23 +02:00
Renn F f2211a15a9 refactor(mcp): scope do_server tool registration to per-agent manifest
Same change as flow_server: read the spawn manifest's do_tools list and
register only those names on this MCP server, so e.g. a Cell PM agent
no longer sees commit() in its tool palette (Cell PMs don't write code,
they only coordinate). Falls back to registering all do tools when the
manifest is missing, with a warning, so test runs without the bind
mount keep working.
2026-05-02 04:45:11 +02:00
Renn F 3d6028b036 refactor(mcp): scope flow_server tool registration to per-agent manifest
The MCP server previously registered every flow verb unconditionally, so
Claude Code surfaced off-role verbs (e.g. main_pm seeing i_am_blocked) in
the model's tool palette and agents called them, only to get 404s back
from the role-scoped orchestrator API.

Refactor each verb into a plain function and register them through a
manifest-driven loop: read /app/tool-manifest.json, register only the
verbs in flow_tools. When the manifest is absent or malformed, register
the full set as a failsafe (covers local test runs without the bind
mount) and log a warning. Drop _validate_role_compatibility — it's
superseded by the new registration loop.
2026-05-02 04:45:04 +02:00
Renn F 6bae4ad69f fix(gateway): add escalate_to_ceo endpoint + unblock to main_pm role
Smoke surfaced: main-pm hit 404 on /api/v2/flow/main_pm/escalate_to_ceo and /i_am_blocked. role_config promised neither escalate_to_ceo nor unblock for main_pm; the router exposed unblock but not escalate_to_ceo. Aligned both: role_config now lists triage_all/unblock/complete/escalate_up/escalate_to_ceo/i_am_idle, and the router has the matching escalate_to_ceo endpoint.

Note: i_am_blocked stays absent (PMs don't get blocked, they unblock others) — agent hallucinated it from the unscoped MCP tool list. Tightening MCP visibility to the manifest is a separate followup.
2026-05-02 04:37:14 +02:00
Renn F 25c51941f5 fix(api/tasks): coerce malformed sub_task ids in convert_plan response
Agents (esp. minimax-m2.7) PUT plans with sub_tasks like {id: '1', ...}. The write succeeds (DB stores raw JSON), but the response model SubTaskResponse.id requires a UUID — first read crashes the endpoint. Coerce non-UUID ids to a fresh UUID at serialization time so a single bad write doesn't brick the read path. Surfaced during NAS smoke.
2026-05-02 04:17:06 +02:00
Renn F dc147f41db fix(api/tasks): NO_PLAN remediation hint references gateway, not deleted MCP
Old hint pointed agents at roboco_task_plan() / roboco_task_start() — both deleted in Phase 4 T9. New hint covers both callers: panel via PATCH, agents via gateway i_will_work_on(plan=...).
2026-05-02 04:15:00 +02:00
Renn F 0ecf47ad77 style(notification_delivery): re-format after subagent dead-code cleanup 2026-05-02 03:51:52 +02:00
Renn F 2c7b1aed27 refactor(tasks): remove dead /tasks routes + service code
Phase 4 left these task-route endpoints with no callers:
- POST /tasks/{id}/unclaim — no panel button, no orchestrator call,
  no agent_sdk path. CLAIMED→PENDING transition stays defined in
  enforcement/task_lifecycle.py for any future re-introduction.
- POST /tasks/{id}/pm-reject — orchestrator's PM-closure briefing
  prompt references roboco_task_pm_reject (an MCP tool that does not
  exist). Real PM rework path is escalate or cancel-and-recreate.

Cascading service cleanup (now-unreferenced):
- TaskService.unclaim, TaskService.pm_reject
- NotificationDeliveryService.notify_developer_of_pm_reject
- PMRejectDetails dataclass
- EventType.TASK_PM_REJECTED enum value (never emitted)

Panel and orchestrator paths preserved:
- claim, start, block/unblock, pause/resume, verify, submit-qa,
  pass-qa, fail-qa, complete, cancel, activate, docs-complete,
  ceo-approve/reject, escalate, escalate-to-ceo, substitute,
  progress, checkpoint, commit, soft-block, submit-pm-review.

Tests: 336 unit tests still passing.
2026-05-02 03:50:13 +02:00
Renn F 27e0b2c689 refactor(notifications): remove dead /notifications routes + service code
Phase 4 left these endpoints with no callers:
- POST /notifications (send_notification)
- GET /notifications/pending-a2a (check_pending_a2a)
- POST /notifications/ack-a2a (ack_a2a_notifications)

Panel only uses GET (list), GET/{id}, POST/{id}/read, POST/{id}/ack.
Orchestrator only calls GET /notifications. Agent_sdk does not call
notifications at all (uses A2A through different paths).

Cascading service cleanup (now-unreferenced):
- ApiNotificationCreate dataclass
- send_from_api, _assert_content
- has_pending_a2a, auto_ack_a2a
- NotificationCreateRequest schema + __init__ re-export

Also resolves smoketest issue #9 (Missing X-Agent-ID on
/notifications/pending-a2a) — endpoint no longer exists.
2026-05-02 03:47:20 +02:00
Renn F 702c14eb2f fix(alembic/009): make enum reconcile dynamic — handles ALL postgres enums, not just 3
Original 009 only reconciled agentrole/team/taskstatus. Production hit LookupError: 'CELL' is not among defined channeltype values during seed bootstrap — channeltype (and ~16 other enums declared in 001) had uppercase members from the original create_all bootstrap that 009 never touched.

Rewrite queries pg_enum at migration time to find every enum with uppercase members, then for each: read current members, lowercase them, add desired_additions for the 3 enums with new ORM members, find every (table, column) referencing the enum via pg_attribute, RENAME-old/CREATE-new/ALTER-USING-lower/DROP-old.
2026-05-02 03:38:58 +02:00
Renn F 1d688a302c refactor(mcp): trim dead exports from utils + schemas
After Phase 4 T9 deleted task/journal/notify/a2a/message/project MCP
servers, only optimal_server and docs_server consume mcp/utils + mcp/schemas.

mcp/utils.py: removed 7 dead exports — get_agent_headers (made private as
_get_agent_headers, only kept as ApiClient internal helper),
format_success_response, resolve_agent_uuid, resolve_agent_uuid_cached,
clear_agent_uuid_cache, get_cached_agent_uuid, _agent_uuid_cache, plus
unused _UUID_LENGTH/_UUID_HYPHEN_COUNT/_HTTP_OK constants. Live exports
(format_error_response, ApiClient, ApiResponse) preserved.

mcp/schemas/__init__.py: removed 21 dead Pydantic schemas (JournalEntryInput,
TaskReflectionInput, DecisionOption, DecisionLogInput, LearningInput,
StruggleInput, SendMessageInput, AskQuestionInput, ReportBlockerInput,
SendNotificationInput, TaskCreateInput, TaskAssignInput, TaskEscalateInput,
TaskBlockInput, TaskPauseInput, SessionCreateForTasksInput,
SessionLinkTaskInput, GroupCreateInput, UpdateDocInput, ProjectCreateInput,
ProjectUpdateInput). Only WriteDocInput (used by docs_server) preserved.

Net: -598 LOC, +22 LOC across the two files.
2026-05-02 03:29:35 +02:00
Renn F c3d325fe4b refactor: remove dead Test/CI route stack
Trashed roboco/api/routes/test.py, roboco/api/schemas/test.py, and roboco/services/test_runner.py — 744 LOC across 3 files. The routes (/test/run, /test/lint, /test/format, /test/typecheck, /test/build) were the API surface for the deleted roboco/mcp/test/test_server.py MCP. With that MCP server gone (Phase 4 T9) no caller remains: panel never used /test/*, and no Python service imports TestRunnerService outside the deleted MCP.
2026-05-02 03:25:02 +02:00
Renn F a82a4f9fd4 fix(.gitignore): anchor Python build artifacts; recover panel/src/lib (28 files)
The 'lib/' rule (intended for Python virtualenv at repo root) was matching panel/src/lib/, hiding the entire panel API client + utility tree from git. Anchored Python build-artifact rules to the repo root with a leading slash so they only match at the top level. Adds 28 panel/src/lib files that should have been tracked from day one.
2026-05-02 03:18:47 +02:00
62bda0c497 Gateway/full (#9)
* chore(gateway): scaffold gateway package and test layout

* feat(config): add gateway feature flags, coordination thresholds, commit-validator settings

* feat(gateway): add standardized response envelope with ok/error variants

* feat(gateway): add remediation hint catalog for tracing-gap and invalid-state errors

* feat(gateway): add per-role flow/do tool catalog with developer, qa, doc, pm, board configs

* feat(db): add gateway columns — active_claimant_id, heartbeat, pre_block snapshot, acceptance_criteria_status, qa_evidence_inspected

* feat(db): create gateway_triggers table for dispatcher decision logging

* feat(db): align canonical skill set; substitute qa_review -> code_review across agent seeds

* fix(db/008): make skill alignment in-place + idempotent; preserve column and existing custom skills

* feat(gateway): add claimant_lock for single-active-agent invariant with heartbeat staleness

* feat(gateway): add trigger_filter with stale-cleanup, claimant-queue, and cooldown rules

* feat(gateway): add tracing_gate with plan, progress, journal, acceptance_criteria, qa requirements

* refactor(gateway): drop per-file ruff ignores; refactor tracing_gate with dispatch table + GateContext

* feat(gateway): add merge_chain to resolve PR target by branch hierarchy depth

* feat(gateway): add commit_validator with min-length, banned-words, and conventional-shape hints

* feat(gateway): add evidence_builder for verb-response evidence and capped context_briefing

* feat(gateway): add Choreographer skeleton with per-phase verb signatures and DI protocols

* feat(runtime): add spawn_manifest builder for per-role pre-loaded tool registration

Introduces SpawnInputs dataclass + build_for_role(inputs) + write_manifest()
in roboco/runtime/spawn_manifest.py; reads role_config for allowed verbs/tools,
emits JSON manifest that SDK shim reads at container startup to eliminate ToolSearch.

* feat(runtime): wire gateway pre-spawn check (trigger_filter + claimant_lock) into orchestrator behind ROBOCO_GATEWAY_ENABLED flag

- Add GatewayTriggerTable SQLAlchemy ORM model to roboco/db/tables.py
  (matches existing table from migration 007_gateway_triggers_table)
- Add module-level gateway_pre_spawn_check() + helpers to orchestrator.py
  (gated: returns ("spawn", "gateway disabled") immediately when flag is False)
- Wire gateway check into _safe_spawn() — the single dispatcher choke-point
  for all agent spawns; QUEUE or DROP outcome logs and returns None (no spawn)
- ROBOCO_GATEWAY_ENABLED defaults to False; legacy behaviour is unchanged

* feat(agent_sdk): load tool-manifest.json at startup behind ROBOCO_GATEWAY_ENABLED flag (no agent-visible change yet)

Adds load_tool_manifest() to the SDK server that reads env at call-time
so gateway-enabled agents can obtain their pre-registered tool list at
startup; returns None when the flag is off, leaving the legacy briefing
path completely unchanged.

* fix(optimal_brain): skip indexing when source ID is None to eliminate roboco://journals/None spam

- Add `build_doc_source(kind, id_)` module-level helper in indexes/base.py that
  returns None when id_ is None instead of producing a "roboco://journals/None" URI
- Update abstract `build_source_uri` return type to `str | None` so subclasses
  can legitimately signal a missing ID
- Short-circuit `ingest()` and `_prepare_docs_for_batch()` in BaseIndexPlugin
  when `build_source_uri` returns None (debug log, no push to vector store)
- Fix JournalsIndexPlugin.build_source_uri: `kwargs.get("entry_id")` returns the
  kwarg value even when it is None, so fall back to doc_id before calling
  build_doc_source
- Fix ConversationsIndexPlugin.build_source_uri: return None when session_id is
  None rather than producing "roboco://conversations/None-unknown"
- Add 9 unit tests with a piragi-free conftest that stubs sys.modules

* fix(agent_sdk): inject X-Agent-ID header on notification-poller requests

Both `_check_pending_a2a` and `_auto_ack_a2a_notifications` in
`roboco/mcp/a2a_server.py` were calling the main API without identity
headers, causing orchestrator `Missing X-Agent-ID header` warnings on
`GET /api/v1/notifications/pending-a2a` and the ack-a2a POST.

Add module-level `AGENT_ROLE` constant (mirrors the existing `AGENT_ID`
pattern) and pass `{"X-Agent-ID": AGENT_ID, "X-Agent-Role": AGENT_ROLE}`
on both requests.

* fix(git): use ROBOCO_PUBLIC_BASE_URL for commit-trailer Links instead of hardcoded localhost

* fix(test_runner): call uv run pytest/ruff directly; add make to orchestrator Dockerfile as backstop

FileNotFoundError was propagating as a raw 500 when a project had `make test`
configured but make was not installed in the orchestrator container.

Two fixes:
1. Catch FileNotFoundError in _run_command and re-raise as ValidationError (400)
   with a clear message telling the operator to reconfigure the project command
   (e.g. replace 'make test' with 'uv run pytest').
2. Add `make` to the orchestrator Dockerfile runner-stage apt-get so projects
   that legitimately use make targets continue to work without reconfiguration.

* fix(api/git): resolve project by slug or UUID in git_log endpoint

Add _resolve_project_slug() helper to git routes that tries UUID
lookup first and falls back to slug, matching the pattern already
used in project routes. Apply to all four read-only git endpoints:
status, log, branches, diff.

* fix(a2a): auto-create conversation when conversation_id absent; reject empty IDs in URL builder

* fix(agent_sdk): default subagent model to parent agent's model from spawn manifest, not hardcoded haiku

Inject CLAUDE_CODE_SUBAGENT_MODEL env var into every agent container at
spawn time.  Claude Code ≥2.1.x reads this variable to override the
default Task (Agent) subagent model, which otherwise hard-codes
claude-haiku-4-5-20251001.  When the parent runs on a non-Anthropic
provider (e.g. Ollama Cloud / minimax-m2.7:cloud) that Anthropic model
is unreachable, so subagent dispatch fails.

The value follows the same provider-aware translation already used for
the --model CLI flag: Anthropic short names go through MODEL_MAP, and
non-Anthropic identifiers are passed verbatim.  To avoid calling the
class by name inside a @staticmethod, the shared translation logic is
extracted to the module-level _resolve_agent_cli_model() helper;
_resolve_cli_model() now delegates to it.

Verified: CLAUDE_CODE_SUBAGENT_MODEL is present and honoured in the
Claude Code 2.1.123 binary (grep confirmed the env-var lookup pattern
`if(process.env.CLAUDE_CODE_SUBAGENT_MODEL) return KK(…)`).

* chore(makefile): add quality and quality-fast targets composing every PR gate

* chore(quality): add import-linter dependency and gateway boundary contract

* test(property): scaffold tracing-completeness assertion (filled in Phase 4)

* Format test file to pass ruff check

* fix(gateway): drop Protocol scaffolding from choreographer skeleton; del-statements on unused stub args; clear vulture whitelist

* linting

* feat(gateway): Phase 1 dev cutover — ChoreographerDeps + give_me_work

Add ChoreographerDeps frozen dataclass (7 deps: task, work_session, git,
a2a, journal, audit, evidence_repo), refactor Choreographer.__init__ to
accept the bundle, implement give_me_work + _briefing_for via
evidence_builder.build_context_briefing, and add property accessors for
all deps. All Phase 2-4 stubs gain del-statements and still raise
NotImplementedError. 3 tests added and passing; mypy/ruff/vulture clean.

* feat(gateway): implement i_will_work_on handling pending, claimed, and needs_revision recovery

* feat(gateway): implement i_have_committed with plan-required precondition

Replaces the NotImplementedError stub with the real implementation: looks up
the agent's active task, enforces plan presence before recording, calls
task.add_progress, and returns a structured Envelope. Adds 3 unit tests
(records progress, no active task → invalid_state, no plan → tracing_gap).

* feat(gateway): implement i_am_done with smart catch-up and skill resolution

* feat(gateway): implement i_am_blocked (struggle + escalate) and i_am_idle (with unread soft-block)

* feat(gateway): add ContentActions for commit, note, say, dm, evidence with auto-inject and validation

* feat(api/v2): add /api/v2/flow/dev/* endpoints delegating to Choreographer

Six intent-verb endpoints (give_me_work, i_will_work_on, i_have_committed,
i_am_done, i_am_blocked, i_am_idle) under /api/v2/flow/dev/, each a thin
handler that delegates to Choreographer. Includes Pydantic request schemas,
EvidenceRepo Phase 1 stub (all methods return []), get_choreographer FastAPI
dep wired with all 7 service deps, and 8 unit tests (all passing).

* feat(api/v2): add /api/v2/do/* endpoints for commit, note, say, dm, evidence

* feat(mcp): add roboco-flow MCP server for intent verbs (Phase 1: dev verbs implemented)

* feat(mcp): add roboco-do MCP server for smart-wrapped content tools

* feat(runtime): mount per-agent tool-manifest.json on developer-container spawn; gateway flag enabled for devs only

* docs(prompts): rewrite developer role prompt for gateway-only verbs (~15 lines vs 49)

* chore(mcp): confirm dev manifest excludes legacy task/journal/notify/a2a tools (Phase 1 cutover; servers retired in Phase 4)

* feat(gateway): implement claim_review with inline evidence (kills #15) and qa_evidence_inspected tracking

* feat(gateway): implement pass_review with qa_notes/learning/evidence tracing gates

* feat(gateway): implement fail_review with issue list, tracing gates, and dev A2A handoff

* feat(api/v2): add /api/v2/flow/qa/* endpoints (claim_review, pass, fail, give_me_work, i_am_idle)

* feat(mcp): add QA verbs (claim_review, pass, fail) to roboco-flow MCP server

* docs(prompts): rewrite QA role prompt for gateway verbs; explicitly warn against grep-the-commit anti-pattern

* feat(runtime): enable gateway flag for QA-role spawns (Phase 2 cutover)

* feat(gateway): implement claim_doc_task and i_documented with file-list and notes-min-chars gates

* feat(gateway): implement triage (cell PM) and triage_all (main PM) with priority order

* feat(gateway): implement unblock with pre_block_state restoration (kills #23)

* feat(gateway): implement cell_pm_complete with auto-merge to parent branch (kills #22 for cell scope)

* feat(gateway): implement main_pm_complete (open master PR + escalate to CEO)

* feat(gateway): add complete() dispatcher routing to cell_pm_complete or main_pm_complete by role

* feat(gateway): implement escalate_up routing by role.escalation_target

* feat(api/v2): add /api/v2/flow/{documenter,cell_pm,main_pm}/* endpoints

* feat(mcp): add Doc + PM verbs to roboco-flow MCP server (claim_doc_task, i_documented, triage, triage_all, unblock, complete, escalate_up)

* docs(prompts): rewrite Doc, Cell PM, and Main PM role prompts for gateway verbs

* feat(runtime): enable gateway flag for Doc, Cell PM, and Main PM roles (Phase 3 cutover)

* test(integration): full pending->awaiting_ceo_approval test through dev/QA/doc/cell-PM/main-PM gateway path

* chore(tests): rename unused args to _args in flow_server tests

Cleared RUF059 lint blocker for Phase 3 closeout. The destructured args was only consumed in URL-asserting tests; one variant only checks kwargs["json"], so its args is now _args.

* chore: untrack docs/superpowers/ + add to .gitignore

Plans + spec were inadvertently swept into commits 5d41a4b and de0c5b5 by subagent 'git add -A' calls. Removed from index and gitignored going forward; files remain on disk for ongoing reference. They still exist in history of those two commits — invoke a follow-up filter-repo if a full purge is desired.

* feat(gateway): implement Board escalate_to_ceo with role allow-list

Allows main_pm, product_owner, and head_marketing to escalate tasks to
CEO. Enforces awaiting_pm_review state and journal:decision tracing gate.
Closes Phase 4 Task 1.

* feat(gateway): implement board_triage prioritizing strategic root tasks

Adds Choreographer.board_triage and TaskService.list_strategic_for_board.
PO and Head Marketing get curated lists of strategic-nature root tasks
in awaiting_pm_review. Closes Phase 4 Task 2.

* feat(gateway): implement auditor_triage surfacing long-running blocked-task anomalies

Adds Choreographer.auditor_triage and TaskService.list_long_running_blocked.
The Auditor surfaces tasks blocked >30min as anomalies for reflect-note
observation. Closes Phase 4 Task 3.

* chore(tests): add return + arg type annotations to gateway tests

All gateway test functions now have -> None and parameter annotations. Cleared 63 mypy [no-untyped-def] errors that pre-existed since Phase 1. Mypy now clean across tests/unit/gateway/.

* feat(api/v2): add /api/v2/flow/{board,auditor}/* endpoints

Board: triage, escalate_to_ceo, i_am_idle.
Auditor: triage, i_am_idle (read-only role).
Adds EscalateToCeoRequest schema with reason min_length validation.
Closes Phase 4 Task 4.

* feat(mcp): add Board + Auditor verbs to roboco-flow MCP server

Adds escalate_to_ceo MCP tool used by Board (PO + Head Marketing) and Main PM. Updates the implemented set in _validate_role_compatibility. Auditor uses the existing triage tool with role-routing in URL.

Closes Phase 4 Task 5.

* docs(prompts): rewrite Board (PO, Head-Marketing, Auditor) prompts for gateway verbs

All 3 board identity files + roles/board.md now use the slim, gateway-aware shape (no ToolSearch directive, no state-tool table). Auditor is explicit about its read-only scope. Closes Phase 4 Task 6.

* feat(runtime): enable gateway manifest for ALL roles (Phase 4 cutover)

Adds product_owner, head_marketing, auditor to GATEWAY_ENABLED_ROLES. Every spawned agent now gets a gateway manifest mounted at /app/tool-manifest.json. The legacy briefing path is dead. Closes Phase 4 Task 8.

* test(property): implement tracing-completeness assertion across smoke-test batch

Replaces Phase 0 stub. Asserts the 6 tracing-contract requirements on every
completed task: audit_log agent_id non-null per state-transition row,
DEVELOPER:TASK_REFLECTION journal entry, QA:LEARNING journal entry,
CELL_PM/MAIN_PM:DECISION_LOG journal entry, acceptance_criteria_status
covering every criterion with a referencing_artifact_id, and
qa_evidence_inspected = true.

Uses an in-memory ephemeral Postgres test DB (`roboco_test_<pid>_<rand>`)
provisioned per pytest session, not SQLite — the production schema relies
on Postgres-only types (UUID, ARRAY) the SQLite dialect cannot compile.
Tests requesting db_session/smoke_test_batch are auto-skipped when no
Postgres is reachable on localhost:5432; ROBOCO_TEST_DB_HOST/PORT/USER
override the endpoint.

Schema is built via Base.metadata.create_all + manual ALTER for the
acceptance_criteria_status / qa_evidence_inspected columns, NOT via
`alembic upgrade head`. This sidesteps two pre-existing layer-drift items
that block any fresh migration run today:

  1. Migration 001 declares the agentrole Postgres enum with lowercase
     values (qa, developer, ...) but the SQLAlchemy ORM binds
     Enum(AgentRole) to the StrEnum's uppercase NAMES — production DBs
     mask this by being bootstrapped via create_all and stamped at 001.
  2. Migration 008 runs UPDATE agents SET skills WHERE id over an
     agents.skills column that no migration in this chain ever creates.

Documented in conftest.py so a future migrations cleanup can find them.
Also notes that acceptance_criteria_status/qa_evidence_inspected are in
the DB schema (per migration 006) but are NOT mapped on the ORM TaskTable
nor on the Pydantic Task model — services that read them via
`task.qa_evidence_inspected` rely on those values being set on raw rows.
The property test uses raw SQL to read the columns directly, matching the
DB-level contract.

Closes Phase 4 Task 11.

Side change: pyproject.toml — adds asyncpg.* to the existing
[[tool.mypy.overrides]] ignore_missing_imports list (asyncpg ships no
py.typed marker), matching the convention used for redis, anthropic,
piragi, etc.

Test count: 1; backend: Postgres (localhost test DB).

* style(mcp/flow_server): single-line _post call after format pass

* fix(db): map 7 gateway columns from migration 006 to TaskTable + Task model

active_claimant_id, last_heartbeat_at, pre_block_state, pre_block_assignee, pre_block_metadata, acceptance_criteria_status, qa_evidence_inspected: present in DB since migration 006 but absent from the ORM mapping. Gateway code (tracing_gate, choreographer, claimant_lock) reads these via task.<attr>; without the mapping, runtime would AttributeError. Closes PHASE4-BUG-A.

* fix(db): repair alembic chain — neutralize 008, add 009 enum reconcile, ORM uses values_callable

Three coordinated changes that close PHASE4-BUG-B:

1. roboco/db/tables.py — introduce _str_enum() helper that wraps Enum() with values_callable=lambda obj: [m.value for m in obj]. Apply to all 23 StrEnum-typed mapped columns. ORM now serializes by .value (lowercase) to match alembic 001's declared enum values; default Enum() was using .name (uppercase) which never matched.

2. alembic/versions/008_align_skills.py — replace with documented no-op. The original migration referenced agents.skills, a column that has never existed in any migration (the agents table has capabilities, not skills). The substitution intent (qa_review -> code_review) was already satisfied statically in roboco/agents_config.py.

3. alembic/versions/009_enum_reconcile.py — new migration that:
   - Adds missing enum values: agentrole.system, team.fullstack, taskstatus.quarantined.
   - Detects uppercase drift from a Base.metadata.create_all bootstrap and rebuilds agentrole/team/taskstatus enums with lowercase members + USING lower(col::text)::enum on every column referenced. No-op if already lowercase.

Tests stay green: 281 passed.

* feat(services): backfill 36 gateway-shaped methods for Choreographer

The gateway Choreographer was wired to call methods that the underlying
services did not expose. This adds them as thin wrappers + queries (most
alias canonical methods; a handful are gateway-specific variants).

TaskService — 26 methods: aliases (submit_verification, submit_qa,
list_blocked_for_team, list_blocked_all_teams,
list_awaiting_pm_review_for_team, list_assigned_for_agent), agent
queries (agent_for, qa_agent_for_team, documenter_for_team,
cell_pm_for_team, get_active_task_for_agent, list_paused_for_agent),
triage queries (list_awaiting_main_pm_all, all_subtasks_terminal),
state setters (set_plan, mark_evidence_inspected, mark_agent_idle),
QA/Doc claim variants (qa_claim, doc_claim, qa_pass, qa_fail),
PM completion (cell_pm_complete with merge_commit), unblock with
state restore (unblock_with_restore), and escalation
(escalate, escalate_up_to_role). Also adds GatewayAgentView
dataclass that unifies DB and config-derived agent attributes.

JournalService — 4 methods: existence checks (has_decision_for_task,
has_learning_for_task, has_reflect_for_task) + write_struggle.

GitService — 4 methods: branch-keyed entry points (create_pr,
pr_merge, pr_target, diff) plus push_branch helper. Each derives
project + workspace from the task that owns the branch / PR.

WorkSessionService — 2 methods: files_changed + has_unpushed_commits.
PR existence is the proxy for pushed (no per-commit push column).

Choreographer: switched git.push(branch_name) call to push_branch()
to dispatch to the new gateway-shaped helper.

* test(services): unit tests for 36 gateway-backfill methods

Adds happy-path + edge tests for every method added in the prior
backfill commit. Total 61 new tests across:
  - tests/unit/services/test_task.py (36)
  - tests/unit/services/test_journal.py (8)
  - tests/unit/services/test_git.py (10)
  - tests/unit/services/test_work_session.py (7)

Each test mocks at the session boundary (no DB) and stubs adjacent
service methods via a dynamic _bind helper to avoid mypy
[method-assign] noise without resorting to type:ignore comments.

* test(gateway): switch dev catch-up assertion to push_branch

The Choreographer's catch-up sequence was renamed from git.push(branch)
to git.push_branch(branch) when GitService got a gateway-shaped helper
in the prior commit. This updates the existing assertion to match.

* feat(mcp): add roboco-git-readonly server with status/log/diff/branches

Slim FastMCP server exposing the four read-only git tools every role
needs (status, log, diff, branch_list) by forwarding to /api/v1/git/*
on the orchestrator. Replaces the read-only half of the legacy
roboco-git server; write operations now go through gateway verbs in
roboco-flow / roboco-do.

The endpoint shapes mirror the panel-facing API (project_slug,
include_remote, staged/file_path) so the same backend handlers serve
both human and agent traffic.

* refactor(mcp): delete legacy task/journal/notify/a2a/message/project servers

Phase 4 cutover: agents now reach every state-changing surface through
the gateway (roboco-flow intent verbs + roboco-do content tools), with
roboco-git-readonly + roboco-optimal + roboco-docs covering reads. The
seven legacy MCP servers + their handler trees are dead code from the
agent side, so they're removed:

  roboco/mcp/task_server.py            (1020 LOC)
  roboco/mcp/journal_server.py         (512 LOC)
  roboco/mcp/notify_server.py          (440 LOC)
  roboco/mcp/a2a_server.py             (790 LOC)
  roboco/mcp/message_server.py         (682 LOC)
  roboco/mcp/project_server.py         (667 LOC)
  roboco/mcp/tasks/  (handlers+utils)  (~4300 LOC)
  roboco/mcp/test/   (in-container runner; replaced by gateway evidence
                      + manual smoke)
  roboco/mcp/git/    (full server; read-only half migrates to the new
                      slim roboco-git-readonly module, write half is
                      owned by gateway verbs)

Orchestrator updates:
  - _generate_mcp_config registers only roboco-flow, roboco-do,
    roboco-git-readonly, roboco-optimal, and (for docs roles) roboco-docs.
    No more per-role legacy fan-out.
  - base_allow flips to mcp__roboco-flow__*, mcp__roboco-do__*,
    mcp__roboco-optimal__*, mcp__roboco-git-readonly__*. Role-specific
    allow lists are reduced to file IO scoping, since gateway verbs
    enforce role policy server-side.
  - TRACEABILITY_TRIGGER_TOOLS rewritten in terms of the gateway servers
    (mcp__roboco-flow__* / mcp__roboco-do__*) instead of the now-deleted
    per-tool list.

Test fix: tests/unit/services/test_a2a.py imported _handle_send_chat_message
from the deleted a2a_server. The four MCP-layer URL-builder tests (empty
conversation_id guard) are dropped — the equivalent boundary now lives
in /api/v2/do/* which has its own integration coverage. The two
service-layer nil-UUID guard tests are kept; they exercise A2AService
directly and remain meaningful (the panel still uses the v1 chat surface,
where a buggy caller could pass the nil UUID).

Net: ~9600 LOC removed from roboco/mcp/. quality-fast green:
338 tests pass, mypy clean, ruff clean. No /api/v1/* router changes —
those endpoints stay live for the panel UI which still uses every
lifecycle action; agents have no prompts that name them so the path is
dead code from the agent side.

* docs(claude.md): replace legacy MCP listing with gateway/verb-surface section

Phase 4 cutover: agents go through roboco-flow + roboco-do (gateway), not the deleted task/journal/notify/a2a/message/project servers. Document the verb surface per role + the Envelope response shape so future Claude Code sessions land in the correct mental model. Closes Phase 4 Task 13.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-05-02 03:11:49 +02:00
Renn F 254cc93fd5 It gets to documenter but QA fails false positive then it gets blocked because it needs to be awaiting_pm_review to be able to merge. It's almost there. 2026-05-01 05:41:10 +02:00
Renn F 7970020021 AI Providers 2026-05-01 03:09:11 +02:00
Renn F d15b7ae561 Enforcements, hooks and code quality 2026-04-21 17:48:45 +02:00
Renn F e4b4ac6d33 Fixed some ggit operations and that. Still needs work. PR problem 2026-04-21 04:21:08 +02:00
Renn F 8e201901c0 I mean, it's at a good place rn... 2026-04-20 15:10:54 +02:00
Renn F 0023c25d60 Added git workflow + fixing some issues 2026-04-19 16:13:42 +02:00