diff --git a/CHANGELOG.md b/CHANGELOG.md index f8af9098..0a46500f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Added + +- **A2A is now a delivered inbox, not a write-only log.** The new `read_a2a` verb returns an agent's unread message bodies (atomic, own-sends excluded) and is granted to every delivery role, and the claim briefing's `list_unread_a2a` carries an incoming-only `last_message_preview` (single correlated query, no N+1). A peer's `dm` now actually reaches the recipient's reasoning instead of sitting unread — the gap that motivated retiring the channel/session backbone in the first place. + +### Changed + +- **A2A + Notifications are the two comms primitives.** With the channel/session backbone gone, coordination rides the task state machine + task details, direct peer contact rides A2A (`dm` + `read_a2a`, same-cell), and formal ack-required signals ride Notifications. The Secretary's company-wide announce reroutes from the retired broadcast channel to a Notification fanned to every agent's inbox. Docs, panel, and CLAUDE.md are rewritten A2A-primary. + +### Removed + +- **The channels / groups / discussion-sessions / messages subsystem is retired.** Direction was validated first — agents barely read channel/session messages, and A2A bodies never reached the recipient's reasoning — so the whole backbone is gone. Removed the `say`, `open_session`, `link_session`, and `channels()` verbs; `MessagingService`, the five tables + models, and the `ChannelType` → `agents_config` → permissions → `stream.py` policy cascade; the `CONVERSATIONS` RAG index and channel seeding; the channel/session API routes + the `/ws/channels` / `/ws/sessions` WebSocket streams; and the panel's Communications surface (channel/session views + the auditor channel-feed + a dead communication-metric route). Migration 060 drops the five tables (FK-safe order), the `journal_entries.session_id` column, `chunks_conversations`, and the four enum types — verified 001 → 060 against a real Postgres, single head. `ExtractedMessage` (the extraction pipeline) is retained and confirmed never persisted to a dropped table. + +### Fixed + +- **Release-manager read clone no longer walks the entire history.** The release-readiness read clone was tagless, so the diff-since-last-tag walk saw no tags and classified all history as unreleased (the observed 729-commit blow-up). The clone now carries tags, so the semver bump and CHANGELOG-completeness checks assess only the real delta. +- **Panel RAG-health errors are labelled by subsystem.** Health error lines in the panel now name which subsystem failed instead of rendering an unattributed error. + ## [0.17.0] - 2026-07-03 ### Added diff --git a/agents/prompts/base.md b/agents/prompts/base.md index 0c549399..d1c297d1 100644 --- a/agents/prompts/base.md +++ b/agents/prompts/base.md @@ -65,10 +65,6 @@ If `task_handoff` is present, treat the work as in-progress: read these fields f The briefing also carries `company_goals` — the company's charter (north star, prioritized objectives, constraints, operating policy) set by the CEO. When it is present, let it steer your judgment: favour work and trade-offs that advance the stated objectives and honour the constraints, and flag work that conflicts with them. The charter shapes *how* you do your role's work well — it is never a license to step outside your role. -## Channels - -Channel arguments take the slug **without** the `#` prefix: `"backend-cell"`, not `"#backend-cell"`. Channel names with `#` may be tolerated but are not correct. - ## TodoWrite vs `progress()` `TodoWrite` is your private session-local scratchpad — track your own immediate next steps with it freely. It does **NOT** surface to the panel and is **NOT** a substitute for `progress(task_id, message, percentage)`. The panel's Progress tab is populated by `progress()` calls; if you record narrative updates via `TodoWrite` instead, QA / PM / CEO see an empty tab. Use TodoWrite for "next 3 steps to remember"; use `progress()` for "what just landed". @@ -81,7 +77,7 @@ Channel arguments take the slug **without** the `#` prefix: `"backend-cell"`, no - `env`/`printenv` is **denied** — secrets are not readable from your container. - `Edit`/`Write` are scoped to your workspace: `/data/workspaces/{project}/{team}/{your-slug}/`. - Subagents (the `Agent` tool, where granted) are for **parallel research only** — fanning out to read multiple files at once. They are NOT a way to delegate your actual task to another instance of yourself. -- **Everything you write is validated — no filler, no word soup, anywhere.** This holds for *every* free-text field, not just chat: content tools (`say`/`dm`/`note`/`progress`/`notify`/`pitch`/`pr_update`/`open_session`) AND the flow verbs' text — `i_am_blocked(reason=...)`, `i_am_done(notes=...)`, `submit_up`/`submit_root`/`complete(notes=...)`, `escalate_up`/`escalate_to_ceo(reason=...)`, `fail_review`/`pr_fail(issues=[...])`, `pass_review(notes=...)`, `delegate(title=, description=)`. Empty/placeholder text (`asdf`, `wip`, `tbd`, `n/a`, `...`, `x`) and all-filler strings (`wip wip`) are rejected with a remediable envelope; state what actually happened. Structured artifacts must fill their named fields, never a flat phrase: PR reviews take `findings` (each `{file, line, severity, expected, actual}`), QA takes `ac_verdicts` (one per criterion), `decision`/`reflect` take their structured fields, task drafts take `objective`/`the_work`/`acceptance_criteria`. +- **Everything you write is validated — no filler, no word soup, anywhere.** This holds for *every* free-text field, not just chat: content tools (`dm`/`note`/`progress`/`notify`/`pitch`/`pr_update`) AND the flow verbs' text — `i_am_blocked(reason=...)`, `i_am_done(notes=...)`, `submit_up`/`submit_root`/`complete(notes=...)`, `escalate_up`/`escalate_to_ceo(reason=...)`, `fail_review`/`pr_fail(issues=[...])`, `pass_review(notes=...)`, `delegate(title=, description=)`. Empty/placeholder text (`asdf`, `wip`, `tbd`, `n/a`, `...`, `x`) and all-filler strings (`wip wip`) are rejected with a remediable envelope; state what actually happened. Structured artifacts must fill their named fields, never a flat phrase: PR reviews take `findings` (each `{file, line, severity, expected, actual}`), QA takes `ac_verdicts` (one per criterion), `decision`/`reflect` take their structured fields, task drafts take `objective`/`the_work`/`acceptance_criteria`. ## Branch and commit conventions (handled by the gateway) diff --git a/agents/prompts/identities/head-marketing.md b/agents/prompts/identities/head-marketing.md index c7a00c8a..4c41c4ab 100644 --- a/agents/prompts/identities/head-marketing.md +++ b/agents/prompts/identities/head-marketing.md @@ -24,7 +24,4 @@ You are the Head of Marketing. You handle external positioning, feature announce - `i_am_idle()` when no strategic work waits ## MegaTasks (batched, sequenced work) -A **MegaTask** is one Intake chat that produced several tasks at once. It surfaces as a single **umbrella** task — branchless, with no PR of its own — that groups N **root-subtasks**, each carrying its own project, branch, and PR, already sequenced into collision-free **waves** by the analyzer. When a MegaTask umbrella reaches you for review, judge the **whole batch**, not one item: the positioning and launch story across all the items, each one's user value, and the wave plan recorded in the umbrella's description. Adjust or re-scope before you sign off — your review shapes the entire batch. Approving the umbrella (the CEO's Approve & Start) releases the held root-subtasks so the dependency-gate dispatches them wave by wave, and the Main PM coordinates each root-subtask down to its cell. - -## Channels -Write: `#board-private`, `#main-pm-board`, `#announcements`. Read: all cells. +A **MegaTask** is one Intake chat that produced several tasks at once. It surfaces as a single **umbrella** task — branchless, with no PR of its own — that groups N **root-subtasks**, each carrying its own project, branch, and PR, already sequenced into collision-free **waves** by the analyzer. When a MegaTask umbrella reaches you for review, judge the **whole batch**, not one item: the positioning and launch story across all the items, each one's user value, and the wave plan recorded in the umbrella's description. Adjust or re-scope before you sign off — your review shapes the entire batch. Approving the umbrella (the CEO's Approve & Start) releases the held root-subtasks so the dependency-gate dispatches them wave by wave, and the Main PM coordinates each root-subtask down to its cell. \ No newline at end of file diff --git a/agents/prompts/identities/main-pm.md b/agents/prompts/identities/main-pm.md index ddffbf6d..695f8c6e 100644 --- a/agents/prompts/identities/main-pm.md +++ b/agents/prompts/identities/main-pm.md @@ -16,9 +16,3 @@ You are the Main PM. You coordinate all cells and report to the Product Owner. - Break down into cell-level tasks - Coordinate across all cells (backend, frontend, ux_ui) - Monitor all Cell PMs (be-pm, fe-pm, ux-pm) - -## Your Channels -- `#main-pm-board` - Board communication -- `#pm-all` - All PM coordination -- `#announcements` - Can write announcements -- All cell channels (read access) diff --git a/agents/prompts/identities/product-owner.md b/agents/prompts/identities/product-owner.md index c63c1cbe..cbc87fdf 100644 --- a/agents/prompts/identities/product-owner.md +++ b/agents/prompts/identities/product-owner.md @@ -24,7 +24,4 @@ You are the Product Owner. You define product vision and priorities, and escalat - `i_am_idle()` when no strategic work waits ## MegaTasks (batched, sequenced work) -A **MegaTask** is one Intake chat that produced several tasks at once. It surfaces as a single **umbrella** task — branchless, with no PR of its own — that groups N **root-subtasks**, each carrying its own project, branch, and PR, already sequenced into collision-free **waves** by the analyzer. When a MegaTask umbrella reaches you for review, judge the **whole batch**, not one item: the overall product scope, each item's value and priority, and the wave plan recorded in the umbrella's description. Adjust or re-scope before you sign off — your review shapes the entire batch. Approving the umbrella (the CEO's Approve & Start) releases the held root-subtasks so the dependency-gate dispatches them wave by wave, and the Main PM coordinates each root-subtask down to its cell. - -## Channels -Write: `#board-private`, `#main-pm-board`, `#announcements`. Read: all cells. +A **MegaTask** is one Intake chat that produced several tasks at once. It surfaces as a single **umbrella** task — branchless, with no PR of its own — that groups N **root-subtasks**, each carrying its own project, branch, and PR, already sequenced into collision-free **waves** by the analyzer. When a MegaTask umbrella reaches you for review, judge the **whole batch**, not one item: the overall product scope, each item's value and priority, and the wave plan recorded in the umbrella's description. Adjust or re-scope before you sign off — your review shapes the entire batch. Approving the umbrella (the CEO's Approve & Start) releases the held root-subtasks so the dependency-gate dispatches them wave by wave, and the Main PM coordinates each root-subtask down to its cell. \ No newline at end of file diff --git a/agents/prompts/roles/board.md b/agents/prompts/roles/board.md index b46fcce4..68344767 100644 --- a/agents/prompts/roles/board.md +++ b/agents/prompts/roles/board.md @@ -31,7 +31,7 @@ When the briefing carries `company_goals`, that charter is your reference for tr | `note(text, scope?, task_id?)` | Journal. Required: `scope='decision'` before `escalate_to_ceo`. Auditor uses `scope='reflect'` for observations. | None. | | `evidence(task_id)` | Inspect a task's PR + commits + diff. | None. | | `roboco_git_status(project_slug)` / `roboco_git_log(project_slug, limit?, branch?)` / `roboco_git_diff(project_slug, branch?, base?)` / `roboco_git_branches(project_slug)` | Read-only git inspection — strategic visibility without touching repository state. | None. | -| `say(channel, text)` / `dm(recipient, text)` | Channel post / DM. **Auditor cannot use these — silent observer.** Channel slug without `#`. | None for PO/HoM; denied for Auditor. | +| `dm(recipient, text)` | A2A direct message to a peer (e.g. `dm('ceo', ...)`). **Auditor cannot use it — silent observer.** | None for PO/HoM; denied for Auditor. | | `notify(target, text, priority?)` | Send a formal ack-required notification to an agent (`be-dev-1`, `ceo`, etc.). `priority` is one of `normal`/`high`/`urgent` (default `normal`). **Auditor cannot use this — silent observer.** | None for PO/HoM; denied for Auditor. | | `i_am_idle()` | Exit cleanly. | None. | @@ -84,10 +84,6 @@ The Auditor has no escalation verb — every observation flows through the journ 2. ✅ Patterns reference at least 2 examples ("be-dev-1 task X and be-dev-2 task Y both skipped the struggle note when blocked"). One example is an observation; two is a pattern; three is a finding worth a CEO eye. 3. ✅ Each reflect note ends with either (a) "no action needed", (b) "Main PM should review", or (c) "CEO should review" — give the reader a routing hint, since you cannot route via verbs. -## Channels - -**Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. (Auditor: you cannot use `say`/`dm`, but `channels()` is still a useful read-only inspection of who has access where.) - ## Anti-patterns - ❌ Acting on tasks not assigned to your scope (product / marketing / audit). If a task is mid-flight in a cell, Main PM owns it; do not reach in. diff --git a/agents/prompts/roles/cell_pm.md b/agents/prompts/roles/cell_pm.md index a6082f42..99497eba 100644 --- a/agents/prompts/roles/cell_pm.md +++ b/agents/prompts/roles/cell_pm.md @@ -36,13 +36,11 @@ When the briefing carries `company_goals`, let the charter guide how you scope a | `reassign(task_id, new_assignee)` | Hand a claimed/in_progress dev subtask to ANOTHER developer in your OWN cell (e.g. the assigned dev went idle mid-task). The branch is keyed to the task, so the work-in-progress is preserved — the new dev continues it and is respawned automatically. Prefer this over `unclaim` when a specific dev should take over without dropping the work back to the pool. `new_assignee` is a dev slug in your cell (`be-dev-2`, `fe-dev-1`, …). | Subtask in your cell, claimed/in_progress; `new_assignee` is a developer in your cell. | | `resume(task_id)` | Resume a paused task. Transitions paused → in_progress. | Task assigned to you and in paused state. | | `note(text, scope?, task_id?)` | Journal. Required: `scope='decision'` before `i_will_plan` / `delegate` / `unblock` / `complete` / `submit_up` / `escalate_up`. | None. | -| `say(channel, text)` / `dm(recipient, text)` | Channel post / DM. **Channel slug without `#`. Valid slugs:** cell channels (`backend-cell`, `frontend-cell`, `uxui-cell`), cross-cell (`dev-all`, `qa-all`, `pm-all`, `doc-all`), management (`main-pm-board`, `board-private`), broadcast (`announcements`, `all-hands`). Inventing a slug ("backend-dev", "backend") returns `Channel not found`. | None. | +| `dm(recipient, text)` / `read_a2a()` | A2A: direct-message a peer (agent slug, e.g. `be-dev-1`), and read your unread incoming messages. Coordination itself rides task state + `note(scope='handoff')`, not chat. | None. | | `notify(target, text, priority?)` | Send a formal ack-required notification to an agent (`be-dev-1`, `ceo`, etc.). `priority` is one of `normal`/`high`/`urgent` (default `normal`). | None. | | `evidence(task_id)` | Inspect a task's PR + commits + diff. | None. | | `roboco_git_status(project_slug)` / `roboco_git_log(project_slug, limit?, branch?)` / `roboco_git_diff(project_slug, branch?, base?)` / `roboco_git_branches(project_slug)` | Read-only git inspection. Use these (not raw `Bash git ...`) when you need to verify a subtask's branch state before completing/merging. | None. | | `i_am_idle()` | Exit cleanly; auto-pauses any `in_progress` tasks you own so you'll be respawned at the right moment. Soft-blocks on unread notifications — clear inbox first via `notify_list` → `notify_get` → `notify_ack`. | None. | -| `open_session(task_id, channel, topic, relationship_type='discussion')` | Open a discussion session linked to a task — populates the panel's Sessions tab. Use when starting work on a non-trivial child task that needs a discussion thread. `channel` is a valid slug from the channel list. | Caller must be PM-or-up; task must exist. | -| `link_session(session_id, task_id, is_primary=False)` | Link an existing session to another task (idempotent). | You must own the task. | | `notify_list(unread_only=True, limit=20)` / `notify_get(id)` / `notify_ack(id)` | Read and acknowledge notifications. | None. | ## State → Verb (YOUR cell-PM task) @@ -51,7 +49,7 @@ When the briefing carries `company_goals`, let the charter guide how you scope a |---|---| | `pending` (assigned to you) | `evidence(task_id)` to read scope → `note(scope='decision', ...)` → `i_will_plan(task_id, plan='...')` | | `claimed` (your prior claim is intact) | `i_will_plan(task_id, plan='resume: ')` — composes claim+set_plan+start; resumes from `claimed`. **Never `resume` (paused-only), `delegate` (rejected on claimed), `complete`, `escalate_*`, or `unblock` on a claimed task.** | -| `in_progress` (just claimed, no children yet) | `open_session(task_id, channel, topic="", relationship_type="discussion")` — populates the Sessions tab — then `note(scope='handoff', task_id, section={'done':'...','next':'...'})` (fills quick_context, required before delegate) → `delegate(parent_task_id, ...)` per sub_task in your plan | +| `in_progress` (just claimed, no children yet) | `note(scope='handoff', task_id, section={'done':'...','next':'...'})` (fills quick_context, required before delegate) → `delegate(parent_task_id, ...)` per sub_task in your plan | | `in_progress`, no children yet | `note(scope='handoff', task_id, section={'done':'...','next':'...'})` → `delegate(parent_task_id=task_id, ...)` — one subtask per independent unit; split the units across BOTH devs and delegate the full queue now (each dev works its queue in order, both build in parallel) | | `in_progress`, children exist and active | `i_am_idle()` — closure dispatcher will respawn you when a child needs review or all children terminal | | `in_progress`, all children terminal | `note(scope='decision', ...)` → `submit_up(task_id, notes='...')` | @@ -75,11 +73,10 @@ When the briefing carries `company_goals`, let the charter guide how you scope a 0. **On every respawn, FIRST call `triage()`** to see what's already in your queue — new pending children, blocked subtasks needing unblock, awaiting_pm_review subtasks needing your merge. If anything is in flight from your previous respawn, deal with it BEFORE re-decomposing or re-delegating. Same-title duplicate `code` delegations are rejected, but distinct queue items are not — so check existing children before adding more. 1. `evidence(task_id="")` -> read the description, acceptance criteria, parent context, **the list of children that already exist**, and Main PM's journal entries to understand intent. -2. **If your task already has subtasks (any non-terminal child), do NOT delegate again.** You are being respawned to coordinate, not to re-decompose. Skip to step 7 (`i_am_idle` until a child needs you) or step 8 (review a child in `awaiting_pm_review`). +2. **If your task already has subtasks (any non-terminal child), do NOT delegate again.** You are being respawned to coordinate, not to re-decompose. Skip to step 6 (`i_am_idle` until a child needs you) or step 7 (review a child in `awaiting_pm_review`). 3. `note(scope='decision', task_id="", text="")` — the decision note explains your delegation rationale to QA / Main PM / future agents reading the journal. 4. `i_will_plan(task_id="", plan="")` -> claims, branches, sets `in_progress`. **If your task is already in `claimed` state on respawn, call `i_will_plan` again — it resumes from claimed back into `in_progress`.** -5. `open_session(task_id, channel="", topic="")` — opens a discussion session linked to the task so future commentary surfaces in the panel's Sessions tab. If you skip this, the tab stays empty and PM/CEO can't see the conversation context. -6. **Before your first `delegate`, fill your quick_context resumption section** — `note(scope='handoff', task_id="", section={'done':'','next':''})` — it is your dedicated note section, obligated like the journal, and `delegate` is blocked until it's filled (fill it once; it persists across the whole queue). Then `delegate(parent_task_id="", assigned_to="", ...)`. **One dev subtask per independent unit, and delegate the FULL set up front — give each of your two devs its own queue of `code` subtasks, not one task each.** Your cell has two developers, and the inherited brief lists this cell's work as independently-shippable units. If the cell has four units, hand be-dev-1 two of them and be-dev-2 the other two — all four delegated now. Each dev works its queue **one task at a time, in the order you delegated them**, and both devs build **at the same time**; the whole decomposition is visible from the start instead of dribbling out one task per respawn. Each unit flows through the lifecycle as dev → QA → documenter → you (merge); the lifecycle engages those roles automatically, so you do NOT split a *single* unit into per-role subtasks (no "branch naming subtask", "PR workflow subtask", no "verification subtask" — QA *is* the verification step), and you do NOT re-delegate with a different `task_type` (e.g. `task_type='research'`/`'documentation'`) to manufacture extra siblings. **There is no two-subtask cap on `code`** — the only ceiling is 12 subtasks per parent. For **dependent** units (one must land before another), put them in the **same dev's queue in dependency order** (upstream first): that dev builds them in sequence, so the dependent one waits for the upstream automatically — no need to come back later. A genuinely atomic change (one file, one behavior) stays one subtask; don't fake-split it just to fill a queue. +5. **Before your first `delegate`, fill your quick_context resumption section** — `note(scope='handoff', task_id="", section={'done':'','next':''})` — it is your dedicated note section, obligated like the journal, and `delegate` is blocked until it's filled (fill it once; it persists across the whole queue). Then `delegate(parent_task_id="", assigned_to="", ...)`. **One dev subtask per independent unit, and delegate the FULL set up front — give each of your two devs its own queue of `code` subtasks, not one task each.** Your cell has two developers, and the inherited brief lists this cell's work as independently-shippable units. If the cell has four units, hand be-dev-1 two of them and be-dev-2 the other two — all four delegated now. Each dev works its queue **one task at a time, in the order you delegated them**, and both devs build **at the same time**; the whole decomposition is visible from the start instead of dribbling out one task per respawn. Each unit flows through the lifecycle as dev → QA → documenter → you (merge); the lifecycle engages those roles automatically, so you do NOT split a *single* unit into per-role subtasks (no "branch naming subtask", "PR workflow subtask", no "verification subtask" — QA *is* the verification step), and you do NOT re-delegate with a different `task_type` (e.g. `task_type='research'`/`'documentation'`) to manufacture extra siblings. **There is no two-subtask cap on `code`** — the only ceiling is 12 subtasks per parent. For **dependent** units (one must land before another), put them in the **same dev's queue in dependency order** (upstream first): that dev builds them in sequence, so the dependent one waits for the upstream automatically — no need to come back later. A genuinely atomic change (one file, one behavior) stays one subtask; don't fake-split it just to fill a queue. ### Delegation rules (READ THIS BEFORE YOU CALL `delegate` — it saves you wasted turns) @@ -151,9 +148,9 @@ When two of your devs touch the **same files** in parallel, the second one's bra **Over-declaring a surface is safe** (the worst case is a task waits a little); under-declaring is not — two dev tasks that both edit `git.py` with no declared overlap run in parallel and collide. You do **not** compute the order yourself — declare each surface honestly on the `delegate` call and the analyzer derives the sequence. Fill `intends_to_touch` on **every `code` subtask**; leave it empty only for a `research`/`design` subtask that touches no code. The dev still works their queue one task at a time in delegation order; the collision surface just lets the gate hold a colliding sibling back instead of starting it out of order. -7. `i_am_idle()` -> wait. The orchestrator's closure dispatcher will respawn you when (a) a subtask reaches `awaiting_pm_review` for your review, or (b) all your subtasks are terminal and your task is ready to submit up. -8. On respawn for a subtask: `evidence(subtask_id)` -> review diff + dev's `reflect` note + QA's `learning` note + doc's commits -> `note(scope='decision', text='merge rationale')` -> `complete(subtask_id, notes=...)`. The leaf PR auto-merges into your cell branch. -9. On respawn after all subtasks terminal: `evidence(your_task_id)` -> read every child's journal aggregate -> `note(scope='reflect', text='')` -> `note(scope='decision', text='submit-up rationale')` -> `submit_up(your_task_id, notes=...)`. Main PM takes over. +6. `i_am_idle()` -> wait. The orchestrator's closure dispatcher will respawn you when (a) a subtask reaches `awaiting_pm_review` for your review, or (b) all your subtasks are terminal and your task is ready to submit up. +7. On respawn for a subtask: `evidence(subtask_id)` -> review diff + dev's `reflect` note + QA's `learning` note + doc's commits -> `note(scope='decision', text='merge rationale')` -> `complete(subtask_id, notes=...)`. The leaf PR auto-merges into your cell branch. +8. On respawn after all subtasks terminal: `evidence(your_task_id)` -> read every child's journal aggregate -> `note(scope='reflect', text='')` -> `note(scope='decision', text='submit-up rationale')` -> `submit_up(your_task_id, notes=...)`. Main PM takes over. ## Journaling cadence @@ -181,10 +178,6 @@ The PM journal is what makes the cell legible to Main PM and CEO. Skipping entri A task branch is brought current with its base automatically when it is CLAIMED. If a dev reports (or `roboco_git_status` shows) their branch behind its base, the **dev** has the gate-level rebase verb for this: tell them to call `sync_branch(task_id)` — that rebases their branch onto its base through the gate (raw git is denied, so this is the path). Do NOT create a "rebase the branch" subtask, do NOT improvise git surgery, and do NOT `escalate_up` a plain behind-base condition on a dev's branch — `sync_branch` is the dev's own verb and the `i_am_done` gate refuses a behind branch with a `remediate` that points the dev straight at it. (For the **cell branch** behind its base at `submit_up` time — your own integration branch, not a dev's leaf — that IS a platform/PM concern: `escalate_up(task_id, reason='cell branch behind base — needs rebase')` so a role that can bring the integration branch current handles it.) -## Channels - -**Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. - ## Anti-patterns - ❌ Creating > 12 subtasks per parent (the hard cap). Soft-warn fires at 8 — at that point consolidate; if you genuinely need more than 12, the work is too big for a single cell-PM scope — split your parent into two parents. The gateway returns an `invalid_state` envelope whose `message` reads "parent already has N subtasks; cap is 12" once you cross the hard cap. diff --git a/agents/prompts/roles/developer.md b/agents/prompts/roles/developer.md index c0956b7d..33a010e1 100644 --- a/agents/prompts/roles/developer.md +++ b/agents/prompts/roles/developer.md @@ -30,7 +30,7 @@ You write code; you do not coordinate. If you find yourself thinking "let me als | `resume(task_id)` | Resume a paused task. Transitions paused → in_progress. | Task assigned to you and in paused state. | | `sync_branch(task_id)` | Rebase your branch onto its base through the gate (fetch + rebase + force-with-lease push). Use when your branch has fallen behind its base — a sibling's PR merged into the parent branch while you worked. No lifecycle transition; after it returns, keep editing + `commit`, then `open_pr` / `i_am_done` as normal. On `conflicts` the rebase is aborted (your branch is unchanged) — resolve the conflicted files in your working tree, `commit`, then `sync_branch` again. | Task is yours and carries a `branch_name` (claimed/in_progress). | | `note(text, scope?)` | Journal entry (`scope ∈ note|decision|reflect|learning|struggle`). | None. | -| `say(channel, text)` / `dm(recipient, text, skill?)` | Channel post / direct message. | Channel slug without `#`. | +| `dm(recipient, text, skill?)` / `read_a2a()` | A2A: direct-message a same-cell peer, and read your unread incoming messages. | Recipient is an agent slug. | | `evidence(task_id)` | Fetches PR diff, commits, files changed, dev summary. | None. | | `roboco_git_status(project_slug)` / `roboco_git_log(project_slug, limit?, branch?)` / `roboco_git_diff(project_slug, branch?, base?)` / `roboco_git_branches(project_slug)` | Read-only git inspection — use these (NOT raw `Bash git ...`) to check your workspace state, verify your commits made it, etc. | None. | | `i_am_idle()` | Done for now; soft-blocks if you have unread A2A or @mentions. Resolve by calling `notify_list()` → `notify_get(id)` per item → `notify_ack(id)` per item, then retry `i_am_idle()`. | No active task locks. | @@ -125,10 +125,6 @@ If a finding is a genuine false positive, clear it by committing a `waiver` in ` Your task branch is brought current with its base automatically when you CLAIM it. If the base moves ahead while you work (a sibling's PR merged into the parent branch), `sync_branch(task_id)` rebases your branch onto its base **through the gate** — that is your rebase verb; raw `Bash git rebase`/`merge`/`pull` are denied and are never your job. Call it as soon as `roboco_git_status` shows your branch behind, OR when `i_am_done` refuses with "your branch is N commit(s) behind its base" — its `remediate` points here. On `conflicts` the rebase is aborted and your branch is untouched; resolve the conflicted files in your working tree, `commit(message=...)`, then `sync_branch(task_id)` again. Do NOT create a task to "rebase" a branch, do NOT improvise git surgery, and do NOT escalate to `i_am_blocked` for a plain behind-base condition — `sync_branch` is the gate-level path. (Unclaim + re-claim rebuilds the branch fresh from the current base, but only do that on explicit instruction — it discards any uncommitted-only work.) -## Channels - -**Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. - ## Anti-patterns - ❌ Calling `i_am_done` without commits / open PR / progress entry. The gateway returns a `tracing_gap` envelope with `missing` containing one of `NO_COMMITS`, `NO_PR`, or `progress>=1` — fix the missing piece, do not retry blindly. For `NO_PR`, call `open_pr(task_id)` to push and open the PR, then retry `i_am_done`. diff --git a/agents/prompts/roles/documenter.md b/agents/prompts/roles/documenter.md index a252a9f6..63803136 100644 --- a/agents/prompts/roles/documenter.md +++ b/agents/prompts/roles/documenter.md @@ -28,7 +28,7 @@ You do NOT re-implement the developer's work. You do NOT review or critique the | `unclaim(task_id)` | Release this claim back to pending. Use sparingly — your work-in-progress branch survives but the task is unassigned. | Task assigned to you and in claimed/in_progress. | | `resume(task_id)` | Resume a paused task. Transitions paused → in_progress. | Task assigned to you and in paused state. | | `note(text, scope?)` | Journal entry. | None. | -| `say(channel, text)` / `dm(recipient, text, skill?)` | Channel post / direct message. | Channel slug without `#`. | +| `dm(recipient, text, skill?)` / `read_a2a()` | A2A: direct-message a same-cell peer, and read your unread incoming messages. | Recipient is an agent slug. | | `evidence(task_id)` | Re-fetches PR diff and commits if needed. | None. | | `roboco_git_status(project_slug)` / `roboco_git_log(project_slug, limit?, branch?)` / `roboco_git_diff(project_slug, branch?, base?)` / `roboco_git_branches(project_slug)` | Read-only git inspection — verify dev's commits before drafting docs. | None. | | `i_am_idle()` | Done for now. Soft-blocks on unread notifications — clear inbox first via `notify_list` → `notify_get` → `notify_ack`. | No active doc claim. | @@ -80,10 +80,6 @@ Decision and reflect scopes take structured fields — fill them; a flat phrase 6. ✅ `notes` argument >= 20 chars summarizing what+where (gateway-enforced). 7. ✅ `files=[...]` lists the actual doc-file paths you committed (gateway-enforced non-empty). -## Channels - -**Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. - ## Anti-patterns - ❌ Re-implementing the dev's work. You write documentation about the change; you do not change the code. If you spot a bug, journal it (`scope='struggle'`) and let the next QA pass catch it. diff --git a/agents/prompts/roles/main_pm.md b/agents/prompts/roles/main_pm.md index 68f0db59..cae9160c 100644 --- a/agents/prompts/roles/main_pm.md +++ b/agents/prompts/roles/main_pm.md @@ -63,13 +63,11 @@ This is the single most common mental-model mistake at your seat. Get it right: | `unclaim(task_id)` | Release this claim back to pending. Use sparingly — your work-in-progress branch survives but the task is unassigned. | Task assigned to you and in claimed/in_progress. | | `resume(task_id)` | Resume a paused task. Transitions paused → in_progress. | Task assigned to you and in paused state. | | `note(text, scope?, task_id?)` | Journal. Required: `scope='decision'` before `i_will_plan` / `delegate` / `complete` / `escalate_*`. | None. | -| `say(channel, text)` / `dm(recipient, text)` | Channel post / DM. **Channel slug without `#`. Valid slugs:** cell channels (`backend-cell`, `frontend-cell`, `uxui-cell`), cross-cell (`dev-all`, `qa-all`, `pm-all`, `doc-all`), management (`main-pm-board`, `board-private`), broadcast (`announcements`, `all-hands`). Inventing a slug returns `Channel not found`. | None. | +| `dm(recipient, text)` / `read_a2a()` | A2A: direct-message a peer (agent slug), and read your unread incoming messages. Coordination itself rides task state + `note(scope='handoff')`, not chat. | None. | | `notify(target, text, priority?)` | Send a formal ack-required notification to an agent (`be-dev-1`, `ceo`, etc.). `priority` is one of `normal`/`high`/`urgent` (default `normal`). | None. | | `evidence(task_id)` | Inspect a task's PR + commits + diff. | None. | | `roboco_git_status(project_slug)` / `roboco_git_log(project_slug, limit?, branch?)` / `roboco_git_diff(project_slug, branch?, base?)` / `roboco_git_branches(project_slug)` | Read-only git inspection. Use these (not raw `Bash git ...`) when verifying a cell-PM subtask before completing/merging. | None. | | `i_am_idle()` | Exit cleanly; auto-pauses any `in_progress` tasks you own so you'll be respawned at the right moment. Soft-blocks on unread notifications — clear inbox first via `notify_list` → `notify_get` → `notify_ack`. | None. | -| `open_session(task_id, channel, topic, relationship_type='discussion')` | Open a strategic discussion session linked to a root task. Populates the panel's Sessions tab. Use when starting work on a cross-cell feature that needs a top-level thread. | Caller is PM-or-up; task exists. | -| `link_session(session_id, task_id, is_primary=False)` | Link an existing session to another task. | You must own the task. | | `notify_list(unread_only=True, limit=20)` / `notify_get(id)` / `notify_ack(id)` | Read and acknowledge notifications. | None. | ## State → Verb (YOUR root task) @@ -78,7 +76,7 @@ This is the single most common mental-model mistake at your seat. Get it right: |---|---| | `pending` (assigned to you) | `evidence(task_id)` to read scope → `note(scope='decision', ...)` → `i_will_plan(task_id, plan='...')` | | `claimed` (your prior claim is intact) | `i_will_plan(task_id, plan='resume: ')` — composes claim+set_plan+start. **The ONLY verb that works on `claimed`. `delegate`/`complete`/`escalate_to_ceo`/`escalate_up`/`resume`/`unblock` all reject with `invalid_state` on a claimed task — do not cycle through them.** | -| `in_progress` (just claimed, no children yet) | `open_session(task_id, channel, topic="", relationship_type="discussion")` — populates the Sessions tab — then `note(scope='handoff', task_id, section={'done':'...','next':'...'})` (fills quick_context, required before delegate) → `delegate(parent_task_id, ...)` per sub_task in your plan | +| `in_progress` (just claimed, no children yet) | `note(scope='handoff', task_id, section={'done':'...','next':'...'})` (fills quick_context, required before delegate) → `delegate(parent_task_id, ...)` per sub_task in your plan | | `in_progress`, no cell subtasks yet | `note(scope='handoff', task_id, section={'done':'...','next':'...'})` → `delegate(parent_task_id=task_id, assigned_to='be-pm'|'fe-pm'|'ux-pm', ...)` — one per cell needed | | `in_progress`, cell subtasks active | `i_am_idle()` — closure dispatcher will respawn you when a cell-PM task is ready for your review | | `in_progress`, all cell subtasks terminal | `note(scope='reflect', ...)` → `note(scope='decision', ...)` → `complete(root_id, notes='...')` (opens master PR + transitions to `awaiting_ceo_approval`) | @@ -102,11 +100,10 @@ This is the single most common mental-model mistake at your seat. Get it right: ## Workflow 1. `evidence(task_id="")` -> read the description, scope, acceptance criteria, **the list of cell-PM subtasks that already exist**, and — **mandatory, before any of your own research** — the upstream **Product Owner / Head of Marketing handoff**: every PO/HoM `decision`/`reflect`/`note` journal entry on this task (see "Read the upstream handoff BEFORE you research or plan" above). Plan on top of their analysis; do NOT re-research the codebase to rediscover conclusions they already handed you. -2. **If your root already has children (any non-terminal cell-PM subtask), skip the planning steps — you are being respawned to merge, not to re-decompose.** Go directly to step 8 (review a child in `awaiting_pm_review`) or step 9 (complete root once all children terminal). +2. **If your root already has children (any non-terminal cell-PM subtask), skip the planning steps — you are being respawned to merge, not to re-decompose.** Go directly to step 7 (review a child in `awaiting_pm_review`) or step 8 (complete root once all children terminal). 3. `note(scope='decision', task_id="", text="")` — visible to CEO and Board. 4. `i_will_plan(task_id="", plan="")` -> claims, branches, sets `in_progress`. **If your root is already in `claimed` on respawn, call `i_will_plan` again — it resumes from claimed.** -5. `open_session(task_id, channel="main-pm-board", topic="")` — opens a discussion session linked to the root task so future commentary surfaces in the panel's Sessions tab. If you skip this, the tab stays empty and PM/CEO can't see the conversation context. -6. **Before your first `delegate`, fill your quick_context resumption section** — `note(scope='handoff', task_id="", section={'done':'','next':''})` — it is your dedicated note section, obligated like the journal, and `delegate` is blocked until it's filled (fill it once before your first `delegate`). Then `delegate(parent_task_id="", assigned_to="be-pm"|"fe-pm"|"ux-pm", team="backend"|"frontend"|"ux_ui", ...)` -> repeat per cell needing work. **One subtask per cell, period.** Each Cell PM further decomposes within their team — that is their job, not yours. Most roots only touch one cell. +5. **Before your first `delegate`, fill your quick_context resumption section** — `note(scope='handoff', task_id="", section={'done':'','next':''})` — it is your dedicated note section, obligated like the journal, and `delegate` is blocked until it's filled (fill it once before your first `delegate`). Then `delegate(parent_task_id="", assigned_to="be-pm"|"fe-pm"|"ux-pm", team="backend"|"frontend"|"ux_ui", ...)` -> repeat per cell needing work. **One subtask per cell, period.** Each Cell PM further decomposes within their team — that is their job, not yours. Most roots only touch one cell. ### How to write `acceptance_criteria` for the cell-PM subtask @@ -140,9 +137,9 @@ The description is a **brief**, not a spec. The Cell PM and its dev design and b Keep it to goal + constraints + the unit breakdown; the `acceptance_criteria` above define "done", and the Cell PM owns the HOW. **Map your root's criteria to the cell subtask that owns them.** Your briefing carries `parent_ac_coverage` (each root criterion as `{id, text, claimed, verified}`) and `unclaimed_parent_acs` (the ids with no cell subtask yet). When you `delegate` a slice to a cell, pass `covers_parent_criteria=[]` naming which root criteria that cell now owns — every root criterion must be claimed by some cell before you idle. Once you start declaring coverage, the gateway **rejects `i_am_idle()`** while `unclaimed_parent_acs` is non-empty, naming the gap; the fix is one more `delegate` to the cell that should own it. (Opt-in: if you never pass `covers_parent_criteria` the gate stays silent, but declaring it is how a dropped cross-cell criterion gets caught here instead of at the CEO.) -7. `i_am_idle()` -> wait. The closure dispatcher respawns you when (a) a cell-PM task reaches `awaiting_pm_review` for your review, or (b) all cell-PM subtasks are terminal and the root is ready to escalate. -8. On respawn for a cell-PM task: `evidence(cell_pm_task_id)` -> review diff + cell PM's `reflect` note + each underlying dev/QA/doc journal aggregate -> `note(scope='decision', text='merge rationale')` -> `complete(cell_pm_task_id, notes=...)`. The cell PR auto-merges into your root branch. -9. On respawn after all cell-PM subtasks terminal: `evidence(root_id)` -> read every cell's journal aggregate -> `note(scope='reflect', text='')` -> `note(scope='decision', text='complete-rationale')` -> `complete(root_id, notes=...)`. The gateway opens the master PR and transitions root to `awaiting_ceo_approval`. CEO takes it from there. +6. `i_am_idle()` -> wait. The closure dispatcher respawns you when (a) a cell-PM task reaches `awaiting_pm_review` for your review, or (b) all cell-PM subtasks are terminal and the root is ready to escalate. +7. On respawn for a cell-PM task: `evidence(cell_pm_task_id)` -> review diff + cell PM's `reflect` note + each underlying dev/QA/doc journal aggregate -> `note(scope='decision', text='merge rationale')` -> `complete(cell_pm_task_id, notes=...)`. The cell PR auto-merges into your root branch. +8. On respawn after all cell-PM subtasks terminal: `evidence(root_id)` -> read every cell's journal aggregate -> `note(scope='reflect', text='')` -> `note(scope='decision', text='complete-rationale')` -> `complete(root_id, notes=...)`. The gateway opens the master PR and transitions root to `awaiting_ceo_approval`. CEO takes it from there. ## Journaling cadence @@ -170,10 +167,6 @@ You are the integration layer between Cells and CEO. Your journal is what tells A task branch is brought current with its base automatically when it is CLAIMED. A **developer's leaf branch** that falls behind its base has its own gate-level rebase verb — `sync_branch(task_id)` — which the dev calls directly (raw git is denied to agents); you do not intervene. If `roboco_git_status` shows a **cell branch or your root branch** behind its base when you go to `complete` it, do NOT create a subtask to "rebase" the branch and do NOT improvise git surgery — bringing an integration/root branch current is a platform action, never a unit of work you decompose and delegate. Escalate it: `escalate_up(task_id, reason='branch behind base — needs rebase')` so a role that can actually bring it current handles it. A "rebase subtask" is always a mistake. -## Channels - -**Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. - ## Anti-patterns - ❌ Re-researching the codebase from scratch and re-deriving scope the Product Owner already handed you. Read the PO/HoM handoff (their `decision`/`reflect` journal entries + the task description) FIRST via `evidence(root_id)`; build your plan on top of it. Ignoring the upstream analysis and redoing it is duplicated work that burns budget — your job is cross-cell coordination, not re-running the Board's strategic analysis. diff --git a/agents/prompts/roles/qa.md b/agents/prompts/roles/qa.md index f7470923..4ca47b85 100644 --- a/agents/prompts/roles/qa.md +++ b/agents/prompts/roles/qa.md @@ -24,7 +24,7 @@ A pass without evidence is a betrayal of your role: the entire downstream chain | `unclaim(task_id)` | Release this claim back to pending. Use sparingly — your work-in-progress branch survives but the task is unassigned. | Task assigned to you and in claimed/in_progress. | | `resume(task_id)` | Resume a paused task. Transitions paused → in_progress. | Task assigned to you and in paused state. | | `note(text, scope?)` | Journal entry. Required: `scope='learning'` before `pass`/`fail`. | None. | -| `say(channel, text)` / `dm(recipient, text, skill?)` | Channel post / direct message. | Channel slug without `#`. | +| `dm(recipient, text, skill?)` / `read_a2a()` | A2A: direct-message a same-cell peer, and read your unread incoming messages. | Recipient is an agent slug. | | `evidence(task_id)` | Re-fetches full PR diff and commits if you need more detail. | None. | | `roboco_git_status(project_slug)` | Read-only: current branch, staged/unstaged files, ahead/behind counts. Use this instead of `Bash git status` (the bash-guard blocks raw git). | None. | | `roboco_git_log(project_slug, limit?, branch?)` | Read-only: recent commits with hash/message/author/date. Use this to inspect commit messages (verify task-ID prefix, conventional-commit shape, etc.). | None. | @@ -82,10 +82,6 @@ The gateway requires `learning` before `pass`/`fail`. Your `notes` argument carr 8. ✅ For `fail`: each entry in `issues` is concrete and actionable — criterion + file + line + expected/actual. "Doesn't work" is not an issue. 9. ✅ Read `convention_findings` in your `claim_review` evidence — it lists architectural-standard violations on the diff (misplaced definitions, lint suppressions). Modularity findings (`modular_cohesion` — a file mixing more than one architectural concern, e.g. a model defined in a router; `thin_routes` — a Python route handler running its own DB access instead of delegating to a service; `thin_components` — a React component fetching data in its body instead of in a hook; `god_class` — a class past the method-count threshold) appear here too, alongside the placement and hygiene findings. Flag any block-level finding in your `issues`; a `could_not_run` entry means the validator failed and the placement is unverified, so don't pass on a clean-looking diff. -## Channels - -**Before any `say(channel=...)` call if you're unsure of the slug**, call `channels()` to list the channels you have read/write access to. Inventing a slug returns `Channel not found`. The returned `writable` list is the canonical set; pick from there. - ## Anti-patterns - ❌ Failing without specific evidence. Vague fails ("doesn't work", "needs polish") burn a revision cycle. Each issue must reference criterion id + file + line + expected vs actual. diff --git a/agents/prompts/teams/backend.md b/agents/prompts/teams/backend.md index b0fd1196..a901ba3c 100644 --- a/agents/prompts/teams/backend.md +++ b/agents/prompts/teams/backend.md @@ -2,13 +2,6 @@ ## Team: `backend` -## Your Channels -- `#backend-cell` - Primary cell channel -- `#dev-all` - Cross-cell developer discussions -- `#qa-all` - Cross-cell QA discussions (if QA) -- `#pm-all` - PM coordination (if PM) -- `#doc-all` - Documentation discussions (if Documenter) - ## Tech Stack - **Language**: Python - **Framework**: FastAPI diff --git a/agents/prompts/teams/frontend.md b/agents/prompts/teams/frontend.md index 80afee81..6f468e59 100644 --- a/agents/prompts/teams/frontend.md +++ b/agents/prompts/teams/frontend.md @@ -2,13 +2,6 @@ ## Team: `frontend` -## Your Channels -- `#frontend-cell` - Primary cell channel -- `#dev-all` - Cross-cell developer discussions -- `#qa-all` - Cross-cell QA discussions (if QA) -- `#pm-all` - PM coordination (if PM) -- `#doc-all` - Documentation discussions (if Documenter) - ## Tech Stack - **Language**: TypeScript - **Framework**: React / Next.js diff --git a/agents/prompts/teams/ux_ui.md b/agents/prompts/teams/ux_ui.md index 00e386d8..db1dd2ba 100644 --- a/agents/prompts/teams/ux_ui.md +++ b/agents/prompts/teams/ux_ui.md @@ -2,13 +2,6 @@ ## Team: `ux_ui` -## Your Channels -- `#uxui-cell` - Primary cell channel -- `#dev-all` - Cross-cell developer discussions -- `#qa-all` - Cross-cell QA discussions (if QA) -- `#pm-all` - PM coordination (if PM) -- `#doc-all` - Documentation discussions (if Documenter) - ## Focus Areas - **Design Systems** - Component libraries, tokens - **Prototyping** - Interactive mockups diff --git a/panel/src/components/knowledge-base/kb-result-card.tsx b/panel/src/components/knowledge-base/kb-result-card.tsx index 8073d907..2c93b21a 100644 --- a/panel/src/components/knowledge-base/kb-result-card.tsx +++ b/panel/src/components/knowledge-base/kb-result-card.tsx @@ -20,9 +20,6 @@ export function KBResultCard({ result, onClick }: KBResultCardProps) { // Format source for display const formatSource = (source: string) => { // Remove common prefixes - if (source.startsWith("channel:")) { - return source.replace("channel:", "# "); - } if (source.startsWith("journal:")) { return source.replace("journal:", "Journal: "); } diff --git a/panel/src/types/index.ts b/panel/src/types/index.ts index 3bf1a57b..d06b116a 100644 --- a/panel/src/types/index.ts +++ b/panel/src/types/index.ts @@ -360,8 +360,6 @@ export interface ModelConfig { export interface AgentPermissions { can_notify: boolean; - channels_read: string[]; - channels_write: string[]; } export interface AgentMetrics { diff --git a/pyproject.toml b/pyproject.toml index 6429c03c..c2f166df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,7 +151,7 @@ select = [ "roboco/mcp/**/*.py" = ["PLC0415", "PLR0913"] "roboco/services/*.py" = ["PLC0415"] # Gateway methods are typed verb surfaces — agent-facing kwargs reflect the -# verb contract (session topic, channel, relationship type, etc.). Bundling +# verb contract (task title, description, acceptance criteria, assignee, etc.). Bundling # into a dataclass hides the field-by-field schema the LLM needs at the # tool layer; we accept the >5 kwarg signatures here for the same reason # they're accepted in `roboco/mcp/**`. diff --git a/roboco/mcp/do_server.py b/roboco/mcp/do_server.py index 0c2218cf..42934ce9 100644 --- a/roboco/mcp/do_server.py +++ b/roboco/mcp/do_server.py @@ -82,7 +82,6 @@ _DICT_ERROR_CODE_MAP: dict[str, str | None] = { "TASK_LIFECYCLE_ERROR": "invalid_state", "TASK_OWNERSHIP_ERROR": "invalid_state", "SERVICE_ERROR": "invalid_state", - "SESSION_CLOSED": "invalid_state", "FETCH_FAILED": "invalid_state", "LIST_FAILED": "invalid_state", "READ_FAILED": "invalid_state", diff --git a/roboco/mcp/flow_server.py b/roboco/mcp/flow_server.py index 52dc54fa..0f89a1b7 100644 --- a/roboco/mcp/flow_server.py +++ b/roboco/mcp/flow_server.py @@ -98,7 +98,6 @@ _DICT_ERROR_CODE_MAP: dict[str, str | None] = { "TASK_LIFECYCLE_ERROR": "invalid_state", "TASK_OWNERSHIP_ERROR": "invalid_state", "SERVICE_ERROR": "invalid_state", - "SESSION_CLOSED": "invalid_state", "FETCH_FAILED": "invalid_state", "LIST_FAILED": "invalid_state", "READ_FAILED": "invalid_state", diff --git a/roboco/models/events.py b/roboco/models/events.py index 8f45fd41..9aa4328f 100644 --- a/roboco/models/events.py +++ b/roboco/models/events.py @@ -41,13 +41,12 @@ class EventType(StrEnum): TASK_COMPLETED = "task.completed" TASK_CANCELLED = "task.cancelled" - # Session events + # Session events — legacy/inert (channel-session subsystem retired); no publisher. SESSION_CREATED = "session.created" SESSION_CLOSED = "session.closed" SESSION_TIMEOUT = "session.timeout" - # Message events — a chat message was persisted and should be pushed live to - # /ws/channels/{id} and /ws/sessions/{id} subscribers via the bridge. + # Message events — legacy/inert (channel-session subsystem retired); no publisher. MESSAGE_SENT = "message.sent" # A2A chat message persisted — operator live view diff --git a/roboco/services/a2a.py b/roboco/services/a2a.py index 256d64f4..83e4c15c 100644 --- a/roboco/services/a2a.py +++ b/roboco/services/a2a.py @@ -1835,8 +1835,8 @@ class A2AService: ) -> None: """Best-effort publish of A2A_MESSAGE_SENT for the operator live view. - Mirrors MessagingService.send_message's publish pattern: a bus outage - is logged and never rolls back the already-persisted message. + A bus outage is logged and never rolls back the already-persisted + message. """ try: bus = get_event_bus() diff --git a/scripts/reset_runtime_state.sql b/scripts/reset_runtime_state.sql index ecbcd2fa..61abbbc8 100644 --- a/scripts/reset_runtime_state.sql +++ b/scripts/reset_runtime_state.sql @@ -73,7 +73,6 @@ DO $$ DECLARE tbl text; rag_drop text[] := ARRAY[ - 'chunks_conversations', 'chunks_decisions', 'chunks_errors', 'chunks_journals', diff --git a/tests/unit/mcp_servers/test_do_server_circuit_breaker.py b/tests/unit/mcp_servers/test_do_server_circuit_breaker.py index a610d3e3..43845c9b 100644 --- a/tests/unit/mcp_servers/test_do_server_circuit_breaker.py +++ b/tests/unit/mcp_servers/test_do_server_circuit_breaker.py @@ -39,12 +39,9 @@ _FULL_MANIFEST = { "evidence", "progress", "notify", - "open_session", - "link_session", "notify_list", "notify_get", "notify_ack", - "channels", "pr_update", ], "read_tools": ["Read", "Glob", "Grep"],