mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
chore(comms): purge dead say()/channel refs the first sweep missed
The #306 teardown finalize (7cb00611) grepped only `say(` / `channels()`
(paren-anchored) and missed bare mentions, docstrings, error strings, and
inline prompt templates baked into .py. This catches them:
- content_actions.py: error remediations no longer tell agents to "use say()"
(a removed verb) when a notify is rejected
- orchestrator.py: the inline PM/board/escalation/audit prompt templates no
longer teach say(channel); the now-dead channel local is removed
- secretary directive descriptions: relay_message payload is (text), not
(channel, text) — the handler was already rerouted to a broadcast notification
- say/channel bare mentions purged from do_server, flow_server, role_config,
task, security, notification, a2a_access, permissions + board/main_pm/
auditor/base/secretary/head-marketing/product-owner prompts
- dead CHANNEL_ACCESS_DENIED error-map keys removed (missed next to SESSION_CLOSED)
Pending (dead-vs-live pass, not in this commit): panel stream.ts channel-perms
client (unimported), agents.channels unused field, CHANNEL_ACCESS_DENIED enum,
and channel-perm bits tangled in the live /stream extraction router.
This commit is contained in:
@@ -19,8 +19,8 @@ The envelope's top-level `error` is one of four categories:
|
||||
|
||||
- `tracing_gap` — a precondition (commit, PR, journal entry, plan, etc.) is missing. Look at `missing` for the literal field key. See the cheatsheet below.
|
||||
- `invalid_state` — task is in a status that doesn't allow this verb (e.g. cannot `start` a `cancelled` task). The `message` names the actual status. Common phrasings: "task X is in <status>; cannot start work", "task X is in <status>, expected awaiting_qa for review", "parent task X is in pending; must be in_progress to accept subtasks", "claim failed", "start failed for task X", "fail_review requires at least one issue", "no commits on this task yet", "parent already has N subtasks; cap is 12", "N architectural-convention violation(s) must be fixed" (a definition is in the wrong module per `.roboco/conventions.yml`, or a lint/type suppression slipped in — `remediate` lists each `file:line` + fix; move it, or for a genuine false positive add a `waiver` to `.roboco/conventions.yml` in your branch).
|
||||
- `not_authorized` — your role / assignment / channel-access doesn't permit this. The `message` names the rule. Common phrasings: "not assigned to you", "role 'cell_pm' may not commit code; only developers and documenters write commits", "Cell PM cannot claim code tasks. PMs coordinate, never execute code.", "you are not the assignee of {task_id}; cannot post content to it", "agent '{X}' may not write to channel '{Y}'", "role X cannot send formal notifications".
|
||||
- `not_found` — task / agent / channel id doesn't exist.
|
||||
- `not_authorized` — your role / assignment doesn't permit this. The `message` names the rule. Common phrasings: "not assigned to you", "role 'cell_pm' may not commit code; only developers and documenters write commits", "Cell PM cannot claim code tasks. PMs coordinate, never execute code.", "you are not the assignee of {task_id}; cannot post content to it", "role X cannot send formal notifications".
|
||||
- `not_found` — task / agent id doesn't exist.
|
||||
|
||||
The fix is always in `remediate`, never in working around the gate.
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ You silently observe org activity and log anomalies. You do **not** communicate
|
||||
- `i_am_idle()` when no anomalies remain — **but you must have recorded at least one observation this session first.** Recording observations is your entire output and is obligated like everyone else's notes: if you have not noted anything recently, `i_am_idle()` is blocked. Always `note(scope='reflect', ...)` what you observed (even "scanned X, no anomalies") before going idle.
|
||||
|
||||
## Access
|
||||
- **Read-only** to ALL channels and tasks.
|
||||
- You have **no** `say` or `dm` verbs. Your output is your journal.
|
||||
- **Read-only** to ALL tasks.
|
||||
- You have **no** `dm` verb. Your output is your journal.
|
||||
- Errors include a `remediate` field — follow it.
|
||||
|
||||
## Principle
|
||||
|
||||
@@ -20,7 +20,7 @@ You are the Head of Marketing. You handle external positioning, feature announce
|
||||
- `triage()` returns the next strategic root task awaiting review
|
||||
- `escalate_to_ceo(task_id, reason)` for marketing decisions that need CEO sign-off (after `note(scope='decision', ...)`)
|
||||
- `evidence(task_id)` to inspect before deciding
|
||||
- `say` / `dm` for board + main-pm coordination
|
||||
- `dm` for board + main-pm coordination
|
||||
- `i_am_idle()` when no strategic work waits
|
||||
|
||||
## MegaTasks (batched, sequenced work)
|
||||
|
||||
@@ -20,7 +20,7 @@ You are the Product Owner. You define product vision and priorities, and escalat
|
||||
- `triage()` returns the next strategic task awaiting review
|
||||
- `escalate_to_ceo(task_id, reason)` for that task once you've logged a `note(scope='decision', task_id, text)`
|
||||
- `evidence(task_id)` to inspect a task before deciding
|
||||
- `say` / `dm` for board + main-pm coordination
|
||||
- `dm` for board + main-pm coordination
|
||||
- `i_am_idle()` when no strategic work waits
|
||||
|
||||
## MegaTasks (batched, sequenced work)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
You are a strategic overseer (Product Owner, Head of Marketing, or Auditor). You triage tasks at the org level, escalate strategic decisions to the CEO, and stay out of execution. The Board sits *above* Main PM — you do NOT communicate directly with Cell PMs, and you do NOT execute tasks yourself. You do NOT write code. You do NOT merge. You do NOT delegate (Main PM does that).
|
||||
|
||||
The Auditor is silent: read-only across every channel, no `say` or `dm`, observations recorded as journal entries. Product Owner and Head of Marketing can post in board channels and DM, but only escalate up to CEO — never down to Cell PMs. If you have feedback for a cell, you write it to the CEO or to Main PM and let Main PM relay it.
|
||||
The Auditor is silent: read-only, no `dm`, observations recorded as journal entries. Product Owner and Head of Marketing can `dm`, but only escalate up to CEO — never down to Cell PMs. If you have feedback for a cell, you write it to the CEO or to Main PM and let Main PM relay it.
|
||||
|
||||
If you find yourself reaching for `Bash git`, `Edit`, or any execution tool, stop — you are about to step out of role. The right move at the Board level is `escalate_to_ceo` for strategic decisions, or `note` for observations.
|
||||
|
||||
@@ -15,7 +15,7 @@ When the briefing carries `company_goals`, that charter is your reference for tr
|
||||
## Inputs you start with
|
||||
|
||||
- Your `task_id` (if you were spawned to triage a specific task) and `agent_id` are pre-baked.
|
||||
- Your team: `board`. Your channels: `board-private`, `main-pm-board`, `announcements`. Read access to all cells.
|
||||
- Your team: `board`. Read access to all cells.
|
||||
- Your role-specific scope:
|
||||
- **Product Owner**: product vision, feature priorities, accept/reject delivered work.
|
||||
- **Head of Marketing**: positioning, announcements, user feedback.
|
||||
@@ -45,7 +45,7 @@ When the briefing carries `company_goals`, that charter is your reference for tr
|
||||
| `blocked` | `note(scope='reflect')` capturing what the blocker reveals at the strategic level; escalate if it indicates a systemic issue |
|
||||
| `completed` / `cancelled` | strategic post-mortem via `note(scope='reflect')` if there's a lesson worth recording |
|
||||
|
||||
**Auditor**: every row above ends in `note(scope='reflect')` and `i_am_idle()`. You have no `say`/`dm`/`escalate_*` — your only output is the journal, which the CEO reads.
|
||||
**Auditor**: every row above ends in `note(scope='reflect')` and `i_am_idle()`. You have no `dm`/`escalate_*` — your only output is the journal, which the CEO reads.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -89,7 +89,7 @@ The Auditor has no escalation verb — every observation flows through the journ
|
||||
- ❌ 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.
|
||||
- ❌ Communicating directly with Cell PMs. The chain is Board -> CEO -> Main PM -> Cell PMs. Use `escalate_to_ceo` or message `main-pm-board`.
|
||||
- ❌ Running `Bash git ...`, `Edit`, or `Write`. The Board does not execute — every action is a triage call, an escalation, or a journal entry.
|
||||
- ❌ (Auditor only) Calling `say` or `dm`. The Auditor is silent; record observations with `note(scope='reflect')` and let the journal layer surface them.
|
||||
- ❌ (Auditor only) Calling `dm`. The Auditor is silent; record observations with `note(scope='reflect')` and let the journal layer surface them.
|
||||
- ❌ Skipping the `journal:decision` entry before `escalate_to_ceo`. The gateway rejects with a tracing-gap envelope.
|
||||
- ❌ Trying to merge or complete tasks. PMs and CEO own merge/complete; the Board does not have those verbs.
|
||||
|
||||
|
||||
@@ -43,8 +43,7 @@ This is the single most common mental-model mistake at your seat. Get it right:
|
||||
## Inputs you start with
|
||||
|
||||
- Your `task_id` (your root coordination task) and `agent_id` are pre-baked into the gateway session.
|
||||
- Your cell-PM slugs: `be-pm`, `fe-pm`, `ux-pm`. Your team: `board`. Your channel: `main-pm-board`.
|
||||
- Your verb manifest is loaded — MCP verbs are registered. Built-in tools (`Read`, `Bash`, `Task`, etc.) are loaded and ready — use them directly. Do NOT call `ToolSearch` (it does not gate built-in tools and is not available here).
|
||||
- Your cell-PM slugs: `be-pm`, `fe-pm`, `ux-pm`. Your team: `board`.- Your verb manifest is loaded — MCP verbs are registered. Built-in tools (`Read`, `Bash`, `Task`, etc.) are loaded and ready — use them directly. Do NOT call `ToolSearch` (it does not gate built-in tools and is not available here).
|
||||
- Workspace: `/data/workspaces/{project}/board/main-pm/` — but you have no `Edit`/`Write` permission; this is just where merge operations resolve.
|
||||
|
||||
## Your verbs
|
||||
|
||||
@@ -42,7 +42,7 @@ Before your first question, use `Read` / `Grep` / `Glob` and the read-only git v
|
||||
|
||||
## Your tools
|
||||
|
||||
You have the built-in read tools `Read`, `Grep`, `Glob`, and `Task` (research subagents for a large codebase), plus **two** action tools: **`propose_draft`** (one task) and **`propose_batch`** (a MegaTask — several tasks at once). That's everything you have and everything you need — you read the code, you talk to the human, and when the spec is ready you call `propose_draft` (or `propose_batch`). You have **no** `say`, `dm`, `notify`, git, or lifecycle verbs, no `Write`/`Edit`/`Bash`, **no plan mode / `ExitPlanMode`**, **no `ToolSearch`**, and **no `AskUserQuestion`** or any structured question/prompt tool — you never speak to another agent, never write code, never create or route a task. **You ask the human by simply writing your questions as plain text in this chat** — they read every message you send live, so the chat itself is your question channel. None of those Claude Code built-ins exist for you; reaching for one only stalls the turn. **You do not "plan" and wait** — when the spec is ready you call `propose_draft` (or `propose_batch`) directly; never announce that a plan is written and ask whether to proceed. **Your replies in this conversation are your entire output to the human, and `propose_draft` / `propose_batch` is the only way a draft leaves this chat.**
|
||||
You have the built-in read tools `Read`, `Grep`, `Glob`, and `Task` (research subagents for a large codebase), plus **two** action tools: **`propose_draft`** (one task) and **`propose_batch`** (a MegaTask — several tasks at once). That's everything you have and everything you need — you read the code, you talk to the human, and when the spec is ready you call `propose_draft` (or `propose_batch`). You have **no** `dm`, `notify`, git, or lifecycle verbs, no `Write`/`Edit`/`Bash`, **no plan mode / `ExitPlanMode`**, **no `ToolSearch`**, and **no `AskUserQuestion`** or any structured question/prompt tool — you never speak to another agent, never write code, never create or route a task. **You ask the human by simply writing your questions as plain text in this chat** — they read every message you send live, so the chat itself is your question channel. None of those Claude Code built-ins exist for you; reaching for one only stalls the turn. **You do not "plan" and wait** — when the spec is ready you call `propose_draft` (or `propose_batch`) directly; never announce that a plan is written and ask whether to proceed. **Your replies in this conversation are your entire output to the human, and `propose_draft` / `propose_batch` is the only way a draft leaves this chat.**
|
||||
|
||||
## Presenting the draft
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ When you carry out a directive, you act with the CEO's authority — but that au
|
||||
- Keep replies tight and decision-oriented. The CEO is busy; lead with the answer, then the supporting detail.
|
||||
- When you need information, read it — don't guess. Ground every claim about company state in what you actually read.
|
||||
- When the CEO is vague, ask a short clarifying question rather than assuming.
|
||||
- Never invent agents, channels, tasks, or numbers. If you don't know, say so and offer to look it up.
|
||||
- Never invent agents, tasks, or numbers. If you don't know, say so and offer to look it up.
|
||||
- You do not write code, open PRs, or merge. You coordinate and inform; the cells and PMs execute, and the CEO decides.
|
||||
|
||||
## Your tools
|
||||
@@ -42,7 +42,7 @@ You have read-only file tools to inspect the repos, plus three read tools and on
|
||||
- **`read_task`** — one task's detail by its id (get the id from `search_tasks`).
|
||||
- **`submit_directive`** — act on the CEO's command. `kind` is one of `relay_message`, `update_charter`, `control_task`, `approve_pitch`, `announce`; `payload` carries that kind's fields. The high-impact kinds (`update_charter`, `control_task`, `approve_pitch`, `announce`) are gated server-side and queued for the CEO's explicit confirmation — so restate the action and wait for a clear "yes" before you call `submit_directive` for any of them. `relay_message` runs directly.
|
||||
|
||||
You have no `say`/`dm`/`notify` and no lifecycle verbs — you never talk to other agents or run the delivery lifecycle. You inform the CEO by writing in this chat, and you act only through `submit_directive`.
|
||||
You have no `dm`/`notify` and no lifecycle verbs — you never talk to other agents or run the delivery lifecycle. You inform the CEO by writing in this chat, and you act only through `submit_directive`.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ export function useRateLimitWebSocket(
|
||||
const prevStateRef = useRef<string | null>(null);
|
||||
|
||||
// getWebSocketUrl() already supplies the "/ws" base, so the endpoint is just
|
||||
// the path (matching the agents/channels/notifications hooks). Passing
|
||||
// the path (matching the agents/notifications hooks). Passing
|
||||
// "/ws/system" here produced the doubled "/ws/ws/system" URL.
|
||||
const { state, lastMessage } = useWebSocket<SystemWsMessage>(
|
||||
"/system",
|
||||
|
||||
@@ -186,7 +186,7 @@ def build_secretary_options(
|
||||
@tool(
|
||||
"submit_directive",
|
||||
"Act on the CEO's command. 'kind' is one of: relay_message "
|
||||
"(payload: channel, text), update_charter (payload: charter), "
|
||||
"(payload: text), update_charter (payload: charter), "
|
||||
"control_task (payload: task_id, action[start|cancel|override|edit], "
|
||||
"status? for override, fields? for edit — edit accepts title/"
|
||||
"description/acceptance_criteria/priority/team/estimated_complexity/"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
A2A Access Enforcement
|
||||
|
||||
Validates agent-to-agent communication permissions.
|
||||
Uses the same communication matrix as channels/notifications.
|
||||
Uses the same communication matrix as notifications.
|
||||
"""
|
||||
|
||||
from roboco.agents_config import can_a2a_direct, get_a2a_route_hint
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Forwards to /api/v1/do/* on the orchestrator. Tools are role-scoped at *spawn*
|
||||
time: the orchestrator writes ``do_tools`` into the per-agent manifest and we
|
||||
register only those names on this server. The orchestrator's API is not
|
||||
role-scoped here (any allowed role can call commit/note/say/dm/notify/evidence),
|
||||
role-scoped here (any allowed role can call commit/note/dm/notify/evidence),
|
||||
so the path is fixed (no role segment). Per-tool role gates (e.g., notify
|
||||
restricting to PMs/Board) live inside the gateway verbs.
|
||||
|
||||
@@ -72,7 +72,6 @@ _CIRCUIT_REJECTION_KINDS: frozenset[str] = frozenset(
|
||||
# counted kind without a map update. Mirrors flow_server.
|
||||
_DICT_ERROR_CODE_MAP: dict[str, str | None] = {
|
||||
"AUTHENTICATION_REQUIRED": "not_authorized",
|
||||
"CHANNEL_ACCESS_DENIED": "not_authorized",
|
||||
"JOURNAL_ACCESS_DENIED": "not_authorized",
|
||||
"PERMISSION_DENIED": "not_authorized",
|
||||
"INVALID_INPUT": "incomplete_input",
|
||||
@@ -576,7 +575,7 @@ def notify(
|
||||
) -> dict[str, Any]:
|
||||
"""Send a formal ack-required notification (PMs and Board only).
|
||||
|
||||
Distinct from say (channel post) and dm (informal A2A): notify creates
|
||||
Distinct from dm (informal A2A): notify creates
|
||||
a notification the recipient must acknowledge. priority in
|
||||
normal|high|urgent. task_id auto-injected from active task when omitted.
|
||||
"""
|
||||
|
||||
@@ -88,7 +88,6 @@ _CIRCUIT_REJECTION_KINDS: frozenset[str] = frozenset(
|
||||
# counted kind without a map update.
|
||||
_DICT_ERROR_CODE_MAP: dict[str, str | None] = {
|
||||
"AUTHENTICATION_REQUIRED": "not_authorized",
|
||||
"CHANNEL_ACCESS_DENIED": "not_authorized",
|
||||
"JOURNAL_ACCESS_DENIED": "not_authorized",
|
||||
"PERMISSION_DENIED": "not_authorized",
|
||||
"INVALID_INPUT": "incomplete_input",
|
||||
|
||||
@@ -65,7 +65,7 @@ async def search_tasks(q: str, limit: int = 20) -> str:
|
||||
async def submit_directive(kind: str, payload: dict[str, Any]) -> str:
|
||||
"""Act on the CEO's command.
|
||||
|
||||
'kind' is one of: relay_message (payload: channel, text), update_charter
|
||||
'kind' is one of: relay_message (payload: text), update_charter
|
||||
(payload: charter), control_task (payload: task_id, action[start|cancel|
|
||||
override|edit], status? for override, fields? for edit — edit accepts
|
||||
title/description/acceptance_criteria/priority/team/estimated_complexity/
|
||||
|
||||
@@ -18,7 +18,7 @@ class PermissionLevel(IntEnum):
|
||||
CEO = 0 # Full access
|
||||
BOARD = 1 # Cross-org access
|
||||
MAIN_PM = 2 # All cells access
|
||||
CELL_PM = 3 # Own cell + PM channel
|
||||
CELL_PM = 3 # Own cell + PM scope
|
||||
CELL_MEMBER = 4 # Own cell only
|
||||
AUDITOR = 99 # Special: silent read all
|
||||
|
||||
|
||||
@@ -9107,7 +9107,7 @@ Gateway verbs (already loaded):
|
||||
- unblock(task_id, restore=True)
|
||||
- complete(task_id="{task_id}", notes=...) for root awaiting_pm_review
|
||||
- escalate_to_ceo(task_id="{task_id}", reason=...) for root tasks
|
||||
- say(channel, text), dm(recipient, text)
|
||||
- dm(recipient, text), read_a2a()
|
||||
- i_am_idle() — when delegated and waiting
|
||||
|
||||
== WORKFLOW ==
|
||||
@@ -9123,8 +9123,7 @@ Gateway verbs (already loaded):
|
||||
acceptance_criteria=["c1", "c2"], estimated_complexity="medium")
|
||||
— repeat per cell that needs work. ONE subtask per cell; the Cell PM
|
||||
breaks it down further.
|
||||
5. say("#main-pm-board", "Delegated <root> to be-pm/fe-pm — see subtasks")
|
||||
6. i_am_idle() — you'll be respawned once subtasks are terminal so you can
|
||||
5. i_am_idle() — you'll be respawned once subtasks are terminal so you can
|
||||
complete(task_id="{task_id}", notes=...) or escalate_to_ceo on the root.
|
||||
|
||||
== RULES ==
|
||||
@@ -9145,7 +9144,6 @@ Start now: evidence(task_id="{task_id}")
|
||||
team = task.get("team", "unknown")
|
||||
|
||||
# Build team-specific info
|
||||
channel = f"{team}-cell" if team != "ux_ui" else "uxui-cell"
|
||||
dev_map = {
|
||||
"backend": ("be-dev-1", "be-dev-2"),
|
||||
"frontend": ("fe-dev-1", "fe-dev-2"),
|
||||
@@ -9195,7 +9193,7 @@ Gateway verbs (already loaded):
|
||||
when YOUR OWN cell-PM task's subtasks are all terminal: opens cell-level
|
||||
PR up to Main PM's branch and transitions to awaiting_pm_review.
|
||||
- escalate_up(task_id, reason) — to Main PM
|
||||
- say("{channel}", text), dm(recipient, text)
|
||||
- dm(recipient, text), read_a2a()
|
||||
- i_am_idle() — when delegated and waiting
|
||||
|
||||
== WORKFLOW ==
|
||||
@@ -9210,8 +9208,7 @@ Gateway verbs (already loaded):
|
||||
assigned_to="{primary_dev}", team="{team}", task_type="code",
|
||||
acceptance_criteria=["c1", "c2"], estimated_complexity="medium")
|
||||
— repeat 2 to 5 times for focused subtasks under your cell-PM task.
|
||||
5. say("{channel}", "Broke down <task>: subtasks created and assigned")
|
||||
6. i_am_idle() — you'll be respawned for two reasons:
|
||||
5. i_am_idle() — you'll be respawned for two reasons:
|
||||
- a SUBTASK enters awaiting_pm_review → review + complete(subtask_id, ...)
|
||||
- all subtasks terminal → submit_up(task_id="{task_id}", notes=...) on YOUR task
|
||||
|
||||
@@ -10329,7 +10326,7 @@ Start now: evidence(task_id="{task_id}")
|
||||
button appear — it never shows on a board task the board hasn't
|
||||
finished reviewing.
|
||||
2. Emit an ack-required APPROVAL notification to the CEO. Board agents
|
||||
only post channel dialogue + journal notes during review, which
|
||||
only record journal notes during review, which
|
||||
left the CEO with no actionable signal; this is that signal.
|
||||
|
||||
Fires at most once per task; a failure clears the guard so a later tick
|
||||
@@ -12610,8 +12607,8 @@ delegate — those verbs are not yours. Your deliverable is a recorded review.
|
||||
the UX, user-facing impact, and how the feature is positioned>",
|
||||
scope='decision', task_id="{task_id}")
|
||||
— this recorded review is how the CEO and Main PM act on your input.
|
||||
3. say(...) in your board channel to flag UX, positioning, or risk concerns and
|
||||
to coordinate with your fellow board reviewer.
|
||||
3. dm(...) your fellow board reviewer to flag UX, positioning, or risk concerns
|
||||
and coordinate (optional; PO/HoM only).
|
||||
4. i_am_idle()
|
||||
— when your review is recorded. Once both board reviewers are done, the
|
||||
CEO is notified the task is ready for Approve & Start, then routes it to
|
||||
@@ -12732,7 +12729,7 @@ Your job:
|
||||
|
||||
1. Acknowledge the notification with notify_ack("{notif_id}")
|
||||
2. Assess the escalation and determine action needed
|
||||
3. Communicate decisions via appropriate channels
|
||||
3. Communicate decisions via dm / notify
|
||||
4. If this requires further escalation, use escalate_up()
|
||||
5. When resolved, call triage() for other work
|
||||
6. If no more work, call i_am_idle() to shutdown gracefully
|
||||
@@ -12779,9 +12776,9 @@ DETAILS: {body}
|
||||
Your job:
|
||||
|
||||
1. Investigate the quality issue
|
||||
2. Review relevant channels and task history (you have read access to all)
|
||||
2. Review relevant tasks and history (you have read access to all)
|
||||
3. Compile your findings
|
||||
4. Report to CEO via appropriate channel
|
||||
4. Report to CEO via your journal (note scope='reflect')
|
||||
5. Call i_am_idle() when complete
|
||||
"""
|
||||
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@ async def prompt_injection_validator(request: GuardRequest) -> GuardResponse | N
|
||||
"""Block prompt-injection / role-override phrasing in free-text bodies.
|
||||
|
||||
Attach to human/agent free-text ingress (intake + secretary chat, task
|
||||
descriptions, agent note/say). Not for code/structured bodies.
|
||||
descriptions, agent note/dm). Not for code/structured bodies.
|
||||
"""
|
||||
body = await _scan_body(request)
|
||||
if body and any(p.search(body) for p in _PROMPT_INJECTION_PATTERNS):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Smart-wrapped content tools — commit, note, say, dm, evidence.
|
||||
"""Smart-wrapped content tools — commit, note, dm, read_a2a, evidence.
|
||||
|
||||
Each method:
|
||||
1. Validates input (e.g., commit_validator for commit messages).
|
||||
@@ -89,9 +89,9 @@ _NOTIFY_ALLOWED_ROLES: frozenset[str] = frozenset(
|
||||
)
|
||||
|
||||
# Roles with NO agent-comms surface (CLAUDE.md): auditor (silent observer),
|
||||
# pr_reviewer (posts review findings on the PR itself — no say/dm), prompter
|
||||
# and secretary (human-only, restricted to note + evidence — no say/dm/notify).
|
||||
# The spawn manifest already omits say/dm from these roles' tool surfaces, but
|
||||
# pr_reviewer (posts review findings on the PR itself — no dm), prompter
|
||||
# and secretary (human-only, restricted to note + evidence — no dm/notify).
|
||||
# The spawn manifest already omits dm from these roles' tool surfaces, but
|
||||
# that is convention-only — this frozenset is the handler-level defence-in-depth
|
||||
# that refuses any call that bypassed the manifest (direct verb dispatch, test
|
||||
# harness, future routing change), so the no-comms invariant holds regardless of
|
||||
@@ -103,7 +103,7 @@ _NO_COMMS_ROLES: frozenset[str] = frozenset(
|
||||
|
||||
|
||||
def _no_comms_remediate(role: str) -> str:
|
||||
"""Role-appropriate remediation for a no-comms role blocked at say/dm."""
|
||||
"""Role-appropriate remediation for a no-comms role blocked at dm."""
|
||||
if role == "auditor":
|
||||
return "record observations via note(scope='reflect') instead"
|
||||
if role == "pr_reviewer":
|
||||
@@ -249,9 +249,9 @@ def _ownership_violation(task_id: UUID) -> Envelope:
|
||||
return Envelope.not_authorized(
|
||||
message=(f"you are not the assignee of {task_id}; cannot post content to it"),
|
||||
remediate=(
|
||||
"only the task's assignee may attach content (commit/note/say/"
|
||||
"only the task's assignee may attach content (commit/note/"
|
||||
"dm/evidence) to it. Use a different task_id or omit task_id "
|
||||
"for off-task channel posts (say/dm only)."
|
||||
"for off-task messages (dm only)."
|
||||
),
|
||||
context_briefing={},
|
||||
)
|
||||
@@ -1219,8 +1219,8 @@ class ContentActions:
|
||||
if rej := self._reject_soup(text, field="message", min_chars=2):
|
||||
return rej
|
||||
# Spec §5.5: silent / no-comms roles — defense-in-depth runtime guard.
|
||||
# See say() for rationale. Mirrored here because dm() is the other
|
||||
# channel through which a no-comms role could "speak". Covers auditor,
|
||||
# Defense-in-depth: dm() is the channel through which a no-comms role
|
||||
# could "speak". Covers auditor,
|
||||
# pr_reviewer, and the human-only prompter / secretary.
|
||||
agent = await self.task.agent_for(agent_id)
|
||||
caller_role = str(agent.role) if agent is not None else ""
|
||||
@@ -1287,7 +1287,7 @@ class ContentActions:
|
||||
) -> Envelope:
|
||||
"""Send a formal ack-required notification (PMs and Board only).
|
||||
|
||||
Distinct from `say` (channel post, no ack) and `dm` (informal A2A):
|
||||
Distinct from `dm` (informal A2A, no ack):
|
||||
a notification is a formal signal that the recipient must
|
||||
acknowledge. Pre-gateway, NotificationService restricted senders
|
||||
to PMs/Board; the gateway re-asserts that gate here because the
|
||||
@@ -1321,7 +1321,7 @@ class ContentActions:
|
||||
"only PMs and Board may issue ack-required signals"
|
||||
),
|
||||
remediate=(
|
||||
"use say() for channel posts or dm() for informal A2A. "
|
||||
"use dm() for informal A2A. "
|
||||
"notify() is reserved for cell_pm, main_pm, "
|
||||
"product_owner, and head_marketing."
|
||||
),
|
||||
@@ -1386,7 +1386,7 @@ class ContentActions:
|
||||
" same-purpose notifications via the dedup query"
|
||||
),
|
||||
remediate=(
|
||||
"use say() to a channel the human reads, or escalate via the"
|
||||
"escalate via the"
|
||||
" CEO route. ack-required notify() targets must be agents"
|
||||
" (or the CEO, who acks via the panel)"
|
||||
),
|
||||
|
||||
@@ -121,7 +121,7 @@ _AUDITOR_FLOW = spec.intents_for_role(spec.Role.AUDITOR)
|
||||
# no ack (silent observer — wouldn't ack notifications).
|
||||
# The Auditor is the playbook quality gate — a deliberate, bounded expansion of
|
||||
# its surface (approve/reject/archive are KB curation actions, not agent comms,
|
||||
# so the no-say/no-dm restriction is preserved).
|
||||
# so the no-dm restriction is preserved).
|
||||
_AUDITOR_DO = (
|
||||
"note",
|
||||
"evidence",
|
||||
@@ -135,7 +135,7 @@ _AUDITOR_DO = (
|
||||
# PR reviewer: a read-only reviewer of inbound external/fork PRs. Flow verbs come
|
||||
# from the lifecycle spec (a dedicated review trio, not QA's). It reads diffs and
|
||||
# records findings (note/evidence); the change-request is posted server-side, so
|
||||
# it has no outward agent comms (no say/dm).
|
||||
# it has no outward agent comms (no dm).
|
||||
_PR_REVIEWER_FLOW = spec.intents_for_role(spec.Role.PR_REVIEWER)
|
||||
_PR_REVIEWER_DO = ("note", "evidence", "notify_list", "notify_get")
|
||||
|
||||
@@ -143,8 +143,8 @@ _PROMPTER_FLOW = spec.intents_for_role(
|
||||
spec.Role.PROMPTER
|
||||
) # none — not a lifecycle role
|
||||
# Intake interviewer: human-only. It journals (note) and cites sources
|
||||
# (evidence) but has NO outward agent comms — no say (channels), no dm/notify
|
||||
# (agents), no channel discovery. Its conversation with the human runs over the
|
||||
# (evidence) but has NO outward agent comms — no dm/notify (agents).
|
||||
# Its conversation with the human runs over the
|
||||
# live-session bridge, not these gateway tools.
|
||||
_PROMPTER_DO = ("note", "evidence")
|
||||
|
||||
|
||||
@@ -254,11 +254,11 @@ class NotificationService:
|
||||
|
||||
Board-reviewed coordination tasks stay ``pending`` and wait for the
|
||||
CEO's Approve & Start gate (``TaskService.approve_and_start``). The
|
||||
Product Owner + Head of Marketing record their review via channel
|
||||
dialogue and journal notes, but that left the CEO with no actionable
|
||||
Product Owner + Head of Marketing record their review via journal
|
||||
notes, but that left the CEO with no actionable
|
||||
signal — only buried chatter. This emits a
|
||||
formal APPROVAL notification (ack-required) carrying ``related_task_id``
|
||||
so the handoff is a real signal the panel can surface, not channel
|
||||
so the handoff is a real signal the panel can surface, not buried
|
||||
noise. Board roles are exactly the senders permitted to notify, so the
|
||||
orchestrator emits it as ``system`` on their behalf once BOTH board
|
||||
reviewers (PO + Head of Marketing) have finished.
|
||||
@@ -346,7 +346,7 @@ class NotificationService:
|
||||
treat it as a high-attention formal signal rather than
|
||||
conflating with task-state-driven notifications. The subject
|
||||
is derived from the first line of `body` (truncated), matching
|
||||
how `say`/`dm` derive a subject from free text.
|
||||
how `dm` derives a subject from free text.
|
||||
"""
|
||||
subject = body.split("\n", 1)[0][:200] or "Notification"
|
||||
related_task_id = str(task_id) if task_id is not None else None
|
||||
|
||||
@@ -41,7 +41,7 @@ MAX_CONCURRENT_BATCHES = 4 # Number of batches to process in parallel
|
||||
DEFAULT_BATCH_SIZE = 32 # default batch size for Ollama embedding requests
|
||||
|
||||
# Keep the embedding model resident in Ollama. It runs on CPU and Ollama's
|
||||
# default 5-min idle unload means a `say` after an idle window pays a cold 2.4 GB
|
||||
# default 5-min idle unload means a `dm` after an idle window pays a cold 2.4 GB
|
||||
# reload before embedding; under contention with glm-5.2:cloud that overran the
|
||||
# embed retry window and dropped the background conversation ingest. -1 = never
|
||||
# unload (sent as `keep_alive` on every /api/embed request).
|
||||
|
||||
@@ -7481,7 +7481,7 @@ class TaskService(BaseService):
|
||||
}
|
||||
|
||||
# States in which the agent still owns the task for content / journal
|
||||
# context, even if it isn't progressing. Used by note / say / dm /
|
||||
# context, even if it isn't progressing. Used by note / dm /
|
||||
# evidence so journal entries written from blocked or paused get the
|
||||
# task_id auto-attached (otherwise the C8 + tracing gates never see
|
||||
# the agent's decisions and the agent spirals).
|
||||
|
||||
Reference in New Issue
Block a user