Derived from the canonical SHAMPOO tool documentation (the same source the implementation is gated against), so these pages and the software cannot drift apart.
10 tools
Tasks and notes
Everyday work: create, assign, query, digest and archive tasks and notes.
archive_done_tasks
Write · Advanced
Sweeps done tasks whose last update is older than N days into archived. A maintenance pass for keeping the live task lists quiet without losing history.
Optional: `older_than_days`.
- `older_than_days`.
Note: Non-negative age threshold in days (default 7). Only done tasks of type task older than this move.
Result
JSON object with archived (how many moved) and threshold_days (the applied threshold).
Boundaries
Only done tasks visible under the caller's project grants are candidates; notes, live statuses, and foreign projects are never touched. With no visible scope the sweep succeeds with archived 0.
Lifecycle
Each move travels through the canonical mutation path and records an audit/history event, so archived content keeps its full trail and can be distinguished from cancelled or live work.
Errors
Bad types are rejected (strict validator). A negative threshold is rejected. Failures move nothing (atomic).
Sets who should do a task or note. Assignment is a coordination hint only: the assignee value is stored verbatim and grants nothing, while the calling principal is recorded alongside as shared_by.
Required: `task_id`.
Optional: `assignee`.
- `task_id`: UUID of the task to assign (required).
- `assignee`: Who should do it (free-form).
Note: Free-form verbatim hint, never authority. Empty or omitted unassigns, clearing both assignee and shared_by.
Result
JSON object with task_id, assignee (string or null when unassigned), and shared_by (the calling principal, or null when unassigned).
Boundaries
The caller must reach the task under their project grants; anything else answers Task not found. The assignee value itself is never scope-checked.
Lifecycle
Applies the assignment through the canonical mutation path and records an audit/history event. Reassigning overwrites the previous hint; unassigning clears both coordination fields while history is preserved.
Errors
Missing task_id is rejected by validation. Unknown arguments are rejected (strict validator). Unknown or out-of-scope tasks answer not-found. Failures leave nothing half-applied (atomic).
Raises every past-due live task below the target priority up to that priority. A maintenance pass for making overdue work surface again in priority order.
Optional: `target_priority`.
- `target_priority`.
Note: Must be low, medium, high, or critical (default high). Only tasks strictly below it move. Targeting low is a no-op reporting bumped 0 with an explanatory message.
Result
JSON object with bumped (how many raised) and target_priority. Targeting the bottom rung returns bumped 0 with a message instead.
Boundaries
Only past-due live tasks (type task) visible under the caller's project grants are candidates; finished content and foreign projects are never touched. With no visible scope the sweep succeeds with bumped 0.
Lifecycle
Each raise travels through the canonical mutation path and records an audit/history event, so the escalation stays attributable.
Errors
Bad types are rejected (strict validator). An unknown priority name is rejected with Invalid priority. Failures raise nothing (atomic).
Creates one task or note. The everyday write entry point: a task is actionable work tracked to done, a note is durable long-form content. Call it when new work, findings, or decisions must persist beyond the current session.
Required: `title`.
Optional: `assignee`, `description`, `due_date`, `notes`, `parent_id`, `priority`, `project`, `recurring`, `reminder_at`, `section`, `type`.
- `title`: Task title (required).
- `assignee`.
Note: Accepted by the schema but intentionally not stored at creation (oracle parity). Use assign_task afterwards, which records shared_by as the calling principal.
- `description`: Primary task/note body and main long-form content.
Note: Primary long-form content. notes is only for auxiliary or machine-readable metadata.
- `due_date`: YYYY-MM-DD format or empty to skip.
- `notes`: Secondary/internal notes or machine-readable metadata.
- `parent_id`: UUID of parent task (for subtasks).
- `priority`: low | medium | high | critical.
- `project`: Project tag for grouping.
- `recurring`: JSON config for recurrence (e.g. '{"every":"week","day":"monday"}').
- `reminder_at`: ISO datetime for reminder (e.g. '2026-03-15T14:00:00').
- `section`: inbox | today | next | someday | waiting.
- `type`: task | note.
Result
JSON object with task_id (new UUID), title, type, and status (always not_started on creation).
Boundaries
Stored within the selected profile boundary only; visible only to principals whose project grants cover the given project. Untagged (no project) content lives in the untagged enclave and fails closed for principals without that grant. Creation never crosses profiles.
Lifecycle
Creates the task and records an audit/history event through the canonical mutation path. New tasks start not_started. No undo tool exists; later movement uses update_task (archive/cancel), which preserves history per the no-delete invariant.
Errors
Missing title is rejected by validation. Unknown arguments are rejected (strict validator). Out-of-scope project values deny generically. If the operation fails, nothing is partially created (atomic).
Example
```json
{
"project": "shared-ops",
"section": "next",
"title": "Draft Paul pack README",
"type": "task"
}
```
Result shape:
```json
{
"status": "not_started",
"task_id": "<uuid>",
"title": "Draft Paul pack README",
"type": "task"
}
```
task_id is assigned by the server; the placeholder stands for the returned UUID.
Finds tasks, notes, and entities from a remembered phrase when the exact title is unknown. Scores title, description, notes, project, entity names, observations, and entity types, ranking live work above finished work under the memory_lookup_v2 contract.
Required: `title_fragment`.
Optional: `limit`.
- `title_fragment`.
- `limit`.
Note: Clamped to 1..100 (default 20); candidate pools scale from it internally.
Result
JSON object with matches (task/note entries or entity entries, each carrying score, matched_in, surface_scores, primary_surface, confidence, and ranking_contract_version), count, query, hidden_low_confidence_count, ranking_contract_version, lookup_strategy (fts_prefilter or full_scan_fallback), markdown, and message. A blank fragment returns matches [] with the message Empty title fragment.
Boundaries
Only content inside the caller's project grants is loaded and scored; foreign projects never appear, even as low-confidence hints. Finished entries stay reachable but always rank below live work.
Lifecycle
Pure read; scoring and ranking leave no trace on the matched content.
Errors
title_fragment is enforced-required at call time (missing input is rejected by validation) even though the display schema carries no required list. Unknown arguments and bad types are rejected (strict validator).
Lists live tasks and notes with combined filters, full-text search, sorting, and paging. The default workhorse view: finished work stays out unless explicitly asked for, and every non-empty page ships with a markdown rendering.
Optional: `include_completed`, `limit`, `offset`, `overdue_only`, `parent_id`, `priority`, `project`, `search`, `section`, `sort_by`, `sort_order`, `status`, `summary_only`, `type`.
- `include_completed`.
- `limit`.
- `offset`.
- `overdue_only`.
Note: Shows only past-due content and always excludes finished content, even with include_completed.
- `parent_id`.
- `priority`.
- `project`.
- `search`.
Note: Re-ranks matches by relevance and adds a per-item rank; sort order has little visible effect on that path.
- `section`.
- `sort_by`.
Note: Allowlist: created_at, updated_at, due_date, priority, status, title, project, section, type. Anything else is rejected; each field has its own sensible default direction.
- `sort_order`.
- `status`.
Note: Passing an explicit status (or include_completed) opts back in to done, archived, and cancelled content; otherwise it is excluded.
- `summary_only`.
- `type`.
Result
JSON object with tasks (full entries, or summaries with summary_only), count, total, offset, limit, and markdown. Paging adds has_more and next_offset when content remains. Empty results return tasks [], count 0, total, and the message No tasks match.
Boundaries
Only content inside the caller's project grants is ever searched or counted; anything else reads as no match. Out-of-scope project filters yield the empty shape, never a denial.
Lifecycle
Pure read; nothing is created, moved, or annotated as a side effect.
Errors
Unknown arguments and bad types are rejected (strict validator). Unknown sort_by or sort_order values are rejected with an invalid_sort message naming the allowlist.
Example
```json
{
"limit": 5,
"section": "today"
}
```
Result shape:
```json
{
"count": 1,
"limit": 5,
"markdown": "| # | Title | Status | Priority | Section | Due | Project | Created | Notes |",
"offset": 0,
"tasks": [
{
"id": "<uuid>",
"priority": "high",
"section": "today",
"status": "not_started",
"title": "Draft Paul pack README"
}
],
"total": 1
}
```
Returns deterministic ready/prime context: live tasks scored into ready states with reasons, urgency, blockers, and provenance. The cross-project answer to what should be worked next, meant to be consulted before broad memory search.
Optional: `include_readings`, `limit`, `mode`.
- `include_readings`.
Note: Threaded into record building; off by default.
- `limit`.
Note: Clamped to 1..100 (default 12).
- `mode`.
Note: ready (scored records), suggested (task-shaped candidates), or prime (compact session boot pack). Anything else returns an error naming the valid modes.
Result
Versioned under contract_version ready_context.v1. ready returns mode, count, truncated, and items; suggested returns mode, count, and task-shaped items; prime returns the mandate, guidance, today_used, items_empty, top_ready_items, blocked_or_waiting, cleanup_candidates, explicit_exclusions, risk_or_escalation_items, evidence_refs, and mode. An invalid mode returns error plus valid_modes.
Boundaries
Only live content inside the caller's project grants is scored; finished content appears solely as review-only candidates or explicit exclusions. No visible scope reads as the empty shape for the requested mode, never a denial.
Lifecycle
Pure read; scoring, sorting, and sort_position stamping leave the underlying tasks untouched.
Errors
Unknown arguments and bad types are rejected (strict validator). An unknown mode is rejected with an error plus the valid mode list.
Renders the session-start digest: status counts plus the active today, inbox, and next sections, with past-due items highlighted first. A quick orientation read before choosing what to work on.
Optional: `include_overdue`, `limit`.
- `include_overdue`.
Note: When true (default), up to 10 past-due items lead the digest.
- `limit`.
Note: Caps the active section listing.
Result
JSON object with digest (markdown text starting with ## Task Digest), active_count, and overdue_count. An empty or out-of-scope view returns the bare header with both counts zero.
Boundaries
Only tasks inside the caller's project grants are counted and listed; notes are never part of the digest. No grants at all reads as an empty digest, never a denial.
Lifecycle
Pure read; rendering the digest changes nothing.
Errors
Unknown arguments and bad types are rejected (strict validator).
Updates one task or note in place. Only the supplied non-empty fields change, so it is the everyday tool for moving work through status and section, retitling, reprioritizing, rescheduling, or rehoming content across projects.
Required: `task_id`.
Optional: `description`, `due_date`, `notes`, `parent_id`, `priority`, `project`, `recurring`, `reminder_at`, `section`, `status`, `title`, `type`.
- `task_id`: UUID of the task to update (required).
- `description`: New main task/note body.
- `due_date`: YYYY-MM-DD or "CLEAR" to remove.
- `notes`: New auxiliary/internal notes or "CLEAR" to remove.
- `parent_id`: Parent UUID or "CLEAR" to remove.
Note: The named parent must be visible to the caller; CLEAR detaches.
- `priority`: low | medium | high | critical.
- `project`: Project tag or "CLEAR" to remove.
Note: Moving to a named project or to untagged (CLEAR) each needs the write grant for the destination.
- `recurring`: JSON config or "CLEAR" to remove.
- `reminder_at`: ISO datetime or "CLEAR" to remove.
- `section`: inbox | today | next | someday | waiting.
- `status`: not_started | in_progress | done | archived | cancelled.
- `title`: New title.
Note: Empty strings are ignored; only non-empty values are applied, except CLEAR which nulls the field.
- `type`: task | note.
Result
JSON object with updated (the task UUID) and fields (the applied field names plus updated_at).
Boundaries
The target task must be visible under the caller's project grants; anything else answers Task not found, never distinguishing missing from foreign. Project and parent moves are each grant-checked at the destination.
Lifecycle
Applies the field changes through the canonical mutation path and records an audit/history event. Status moves (including to archived or cancelled) preserve history per the no-delete invariant.
Errors
Missing task_id is rejected by validation. Unknown arguments are rejected (strict validator). Bad enum, date, or recurrence values are rejected. Unknown or out-of-scope tasks and parents answer not-found. A call with no effective fields answers No fields to update. Failures leave nothing half-applied (atomic).
Creates a note or updates the existing note with the same normalized title within the same project. The idempotent write surface for durable research and decision notes: repeated runs converge on one note instead of creating near-duplicates.
Required: `title`.
Optional: `description`, `notes`, `priority`, `project`, `section`, `update_if_found`.
- `title`: Note title (required).
- `description`: Primary long-form note body.
Note: Primary long-form content. notes is only for auxiliary or machine-readable metadata.
- `notes`: Optional auxiliary/internal metadata.
- `priority`: Priority for new notes; updates only when explicitly set.
- `project`: Project tag for matching and grouping.
Note: Empty or absent means untagged, which needs an explicit untagged grant.
- `section`: Section for new notes; updates only when explicitly set.
Note: Defaults to next and priority to medium on creation; on update they change only when explicitly set.
- `update_if_found`: When false, return the existing row without mutation.
Note: When false, a matched note is returned untouched with action existing.
Result
JSON object with task_id, title, type (always note), action (created, updated, or existing), and matched_on (always normalized_title_project). Creation also returns status not_started; updates return the changed fields list.
Boundaries
Matching and creation stay inside the selected profile boundary and the caller's project grants. Out-of-scope projects deny with a generic scope message; untagged writes fail closed without the untagged grant.
Lifecycle
Creates the note or applies the supplied fields through the canonical mutation path, recording an audit/history event. Matching is by normalized title plus project. No undo tool exists; later movement uses update_task, which preserves history per the no-delete invariant.
Errors
Blank title is rejected. Unknown arguments are rejected (strict validator). Out-of-scope projects deny generically. If the operation fails, nothing is partially created (atomic).
Example
```json
{
"description": "Chosen because WAL tolerates concurrent writers.",
"project": "shared-ops",
"title": "Bus Timeout Decision"
}
```
Result shape:
```json
{
"action": "created",
"matched_on": "normalized_title_project",
"status": "not_started",
"task_id": "<uuid>",
"title": "Bus Timeout Decision",
"type": "note"
}
```
task_id is assigned by the server; the placeholder stands for the returned UUID.
Required: `observations`.
- `observations`.
Note: Each entry needs entityName plus a contents string list. Non-string contents are skipped; absent contents still refresh the entity timestamp.
Result
JSON object with added (newly added plus revived count) and observation_ids.
Boundaries
Unknown entities and entities outside the caller's project grants are silently skipped with zero contribution, so callers cannot probe foreign projects by name. Operates only inside the caller's profile boundary.
Lifecycle
Each addition records an audit/history event. Duplicate text is skipped by content identity; re-adding retired text revives the same observation identity with its original creation evidence intact.
Errors
observations must be an array; entries need a string entityName. Unknown arguments are rejected (strict validator). Failures add nothing (atomic).
Creates entities in the knowledge graph, each with a name, type, and optional observations, project, aliases, and visibility. Names are unique: a repeated name resolves to the existing identity rather than duplicating it.
Required: `entities`.
- `entities`.
Note: Each entry needs name and entityType; observations is a string list, aliases a free-form list, project a tag. Untagged creation needs an explicit untagged grant.
Result
JSON object with created (newly created plus revived count), total_requested, entity_ids (one per resolved entity), and observation_ids (for observations added by this call).
Boundaries
Every entry needs the write grant for its project, and the whole batch fails closed on the first denied entry. Duplicate names belonging to foreign projects answer entity name already exists, never revealing their content.
Lifecycle
Creations record an audit/history event each. A repeated name revives the same identity when retired (void stamps stay as last-void evidence) or reuses the live one, still accepting new observations, aliases, and project backfill on this call. Duplicate observation text is skipped by content identity.
Errors
entities must be an array; entries need string name and entityType. Unknown arguments are rejected (strict validator). Denied projects fail the whole batch with nothing half-applied (atomic).
Required: `relations`.
- `relations`.
Note: Each entry needs from, to (entity names), and relationType. Entries with a missing endpoint are silently skipped.
Result
JSON object with created (newly created plus revived count), total_requested, and relation_ids.
Boundaries
Both endpoints must be live and visible under the caller's project grants; anything else is silently skipped with zero contribution, so callers cannot probe foreign content by name. Operates only inside the caller's profile boundary.
Lifecycle
Each creation records an audit/history event. Duplicate triples are skipped; re-creating a retired triple revives the same relation identity with its history preserved.
Errors
relations must be an array; entries need string from, to, and relationType. Unknown arguments are rejected (strict validator). Failures create nothing (atomic).
Retires named entities from the active knowledge graph. Despite the name, nothing is physically deleted: this is a lifecycle transition (active to voided) covered by the no-delete invariant. Re-creating the same name revives the same entity identity.
Required: `entityNames`.
- `entityNames`.
Note: Names that are unknown, already retired, or outside the caller's project scope are silently skipped and contribute zero to the deleted count; there is no error for unmatched names.
Result
JSON object with deleted (count of entities retired by this call).
Boundaries
Scope is enforced per entity: content outside the caller's project grants is silently skipped, never denied loudly, so callers cannot probe foreign projects by name. Operates only inside the caller's profile boundary.
Lifecycle
Records an audit/history event per entity, then marks it void with voided_at/voided_by stamps. Observations, relations, and aliases are NOT cascaded: they stay in place, resolve as absent while the entity is void, and revive independently. Re-creating an entity with the same name revives the original identity (void stamps retained as last-void evidence; history lives in the audit trail).
Errors
entityNames must be an array of strings (validation error otherwise). Unknown arguments rejected. The operation fails atomically (zero partial voids). deleted: 0 with no error means nothing matched or nothing was visible.
Example
```json
{
"entityNames": [
"Stale Vendor"
]
}
```
Result shape:
```json
{
"deleted": 1
}
```
A later create_entities with name Stale Vendor revives the same identity rather than duplicating it.
Retires exact observation texts from named entities. Despite the name, nothing is physically removed: this is a lifecycle transition (active to voided) covered by the no-delete invariant, and re-adding the text revives the same identity.
Required: `deletions`.
- `deletions`.
Note: Each entry needs entityName plus an observations string list matched by exact text. Unknown entities, unknown texts, and non-string items are silently skipped with zero contribution.
Result
JSON object with deleted (count of observations retired by this call).
Boundaries
Scope is enforced per entity: content outside the caller's project grants is silently skipped, never denied loudly, so callers cannot probe foreign projects by name. Operates only inside the caller's profile boundary.
Lifecycle
Records an audit/history event per retired observation, then marks it void with voided stamps. Re-adding the same text revives the original identity (creation evidence untouched; history lives in the audit trail).
Errors
deletions must be an array; entries need a string entityName. Unknown arguments are rejected (strict validator). The operation fails atomically (zero partial voids). deleted 0 with no error means nothing matched or nothing was visible.
Retires named relations from the knowledge graph. Despite the name, nothing is physically removed: this is a lifecycle transition (active to voided) covered by the no-delete invariant, and re-creating the triple revives the same identity.
Required: `relations`.
- `relations`.
Note: Each entry needs from, to (entity names), and relationType, matched exactly. Unknown endpoints and non-matching triples are silently skipped with zero contribution.
Result
JSON object with deleted (count of relations retired by this call).
Boundaries
Scope is enforced per endpoint: triples touching content outside the caller's project grants are silently skipped, never denied loudly, so callers cannot probe foreign projects by name. Operates only inside the caller's profile boundary.
Lifecycle
Records an audit/history event per retired relation, then marks it void with voided stamps. Re-creating the same triple revives the original identity (history lives in the audit trail).
Errors
relations must be an array; entries need string from, to, and relationType. Unknown arguments are rejected (strict validator). The operation fails atomically (zero partial voids). deleted 0 with no error means nothing matched or nothing was visible.
Opens named entities with their observations plus the relations that run directly between the opened set. The precise read when the names are already known.
Required: `names`.
- `names`.
Note: Entity names to open. Non-string entries are skipped; unknown or out-of-scope names are silently omitted.
Result
JSON object with entities (name, entityType, observations in order, project included only when set) and relations (from, to, relationType) limited to edges with both endpoints in the opened set (only computed when two or more open).
Boundaries
Names outside the caller's project grants read as missing and contribute nothing, so callers cannot probe foreign projects by name. Operates only inside the caller's profile boundary.
Lifecycle
Pure read of live content; retired entities and observations resolve as absent. A best-effort access note is recorded alongside the read and never affects the returned content.
Errors
names must be an array (validation error otherwise). Unknown arguments are rejected (strict validator). No match is an empty result, not an error.
Reads the visible knowledge graph as a paged snapshot: entities with their observations plus the relations between them. The broad orientation read when exploring what is known.
Optional: `limit`, `offset`.
- `limit`.
Note: Page size (default 500).
- `offset`.
Note: Starting position in name order.
Result
JSON object with entities (name, entityType, project which is always present and null when unset, observations), relations (from, to, relationType), total (visible live entity count), and has_more.
Boundaries
Only live entities and relations visible under the caller's project grants are included; both relation endpoints must be visible. No visible scope reads as an empty snapshot with total 0, never a denial.
Lifecycle
Pure read of live content; retired entries resolve as absent. A best-effort access note is recorded alongside the read and never affects the returned content.
Errors
Unknown arguments and bad types are rejected (strict validator).
Searches the knowledge graph strictly inside one named project. A substring candidate pool is re-ranked with multi-signal scoring, so every hit is guaranteed in-project rather than merely boosted toward it.
JSON object with entities (name, entityType, project echoing the request argument, observations, _score), the echoed query, and the echoed project. An out-of-scope project returns entities [] with the echoes intact.
Boundaries
The named project must be granted to the caller; anything else reads as an empty result, never a denial. Only live entities and live observations in that project are pooled and returned. Operates only inside the caller's profile boundary.
Lifecycle
Pure read of live content; ranking leaves no trace. Contrast search_nodes, whose project hint is affinity-only and currently unused on its path.
Errors
Missing or non-string query or project is rejected by validation. Unknown arguments are rejected (strict validator).
Searches the knowledge graph by free text and packs the best evidence per entity under a wire budget. Returns ranked entities with short observation windows plus an accounting block describing coverage and calibration.
Required: `query`.
Optional: `budget`, `project`.
- `query`.
Note: Free text; a blank query lists the first 50 entities by name instead of matching.
- `budget`.
Note: Wire-character budget for packing (default 16000); over-budget entities stop the pack and report truncated.
- `project`.
Note: Accepted but currently unused on this path: it does not narrow or boost the ranking.
Result
JSON object with entities (name, entityType, observations as evidence windows, project when set, _evidence_status), the echoed query, and _accounting (entities_considered, entities_returned, observations_returned, truncated, budget_wire_chars, wire_chars, status, degraded, jump when measurable, refills, query_status, meaningful_terms, stopword_list).
Boundaries
Candidates, one-hop neighbours, and observations are all gated to the caller's project grants; foreign content never enters the pool or the expansion. Operates only inside the caller's profile boundary.
Lifecycle
Pure read of live content with scope-gated one-hop expansion. A best-effort access note is recorded alongside the read and never affects the returned content.
Errors
Missing or non-string query is rejected by validation. Unknown arguments and bad types are rejected (strict validator).
Connections between tasks and the entities they concern.
get_entity_tasks
Read · Ordinary
Lists every live task linked to one knowledge-graph entity. The reverse view of get_task_links, useful for seeing all work touching a person, vendor, or topic.
JSON object with entity_name and tasks, each carrying id, title, status, priority, section, link_type, score (or null), and linked_at (or null).
Boundaries
The entity must be visible under the caller's project grants, and linked tasks are filtered to the caller's grants as well. An unknown or invisible entity answers Entity not found; an empty grant set reads as an empty task list.
Lifecycle
Pure read over live links; removed links resolve as absent.
Errors
Missing or non-string entity_name is rejected by validation. Unknown arguments are rejected (strict validator). Unlike get_task_links, an unknown name is an error, not an empty list.
JSON object with task_id and links, each carrying entity_id, entity_name, entity_type, link_type, score (or null), and created_at (or null).
Boundaries
The task and the linked entities must all be visible under the caller's project grants. An invisible task reads as an empty link list, never a denial; a malformed id answers Task not found.
Lifecycle
Pure read over live links and live entities; removed links and retired entities resolve as absent.
Errors
Missing or non-string task_id is rejected by validation. Unknown arguments are rejected (strict validator).
Creates a manual link between a task and a knowledge-graph entity. Manual links always win: an existing auto-discovered link for the pair is upgraded rather than duplicated.
JSON object with the decision record (decision_id, task_id, entity_id, entity_name, decision accepted, score, rank_at_decision, model_version, label_progress) plus link_type (manual) and created_at for the link.
Boundaries
Both the task and the entity must be visible under the caller's project grants. An invisible task answers Task not found; an unknown or invisible entity answers Entity not found; neither leaks the other side.
Lifecycle
Records an accepted manual decision and creates (or revives and upgrades) the link in one atomic unit, clearing any prior removal marker for the pair. Re-linking an existing manual link refreshes it rather than duplicating it.
Errors
Missing task_id or entity_name is rejected by validation. Unknown arguments are rejected (strict validator). Unknown or out-of-scope endpoints answer not-found. Failures link nothing (atomic).
Suggests entities that may relate to a task using one versioned pairwise scorer (exact name and alias, full-text, project, provenance, graph and meta-path, temporal, plus vector and community signals). Suggestions only: links are never created here.
Required: `task_id`.
Optional: `include_vector`, `limit`.
- `task_id`.
- `include_vector`.
Note: Accepted and echoed; the current output always carries a null vector rank and a null community run.
- `limit`.
Note: Caps the returned suggestions (default 5).
Result
JSON object with task_id, model_version (explainable-pairwise-v1), include_vector, community_run (null), suggestions (entity_id, entity_name, entity_type, score, signals with raw, weights, and contributions, reasons, matched_aliases, shared_keywords, fts_rank, vector_rank, existing_decision, rank), candidate_count, accept_tool (link_task_entity), and undo_auto_tool (unlink_task_entity).
Boundaries
The task must be visible under the caller's project grants; anything else answers task not found (lowercase shape). Candidates and their evidence are drawn only from content the caller may see.
Lifecycle
Pure read; scoring creates no links and records no decisions. Accepting a suggestion is a separate link_task_entity call.
Errors
Missing task_id is rejected by validation. Bad limit or include_vector types are rejected (strict validator). Unknown or out-of-scope tasks answer task not found.
Removes a task-entity link while keeping the removal itself as durable evidence. Removing a silent high-confidence link additionally records an explicit human rejection, so the pair stays suppressed and becomes a real evaluation label.
JSON object with removed (boolean). Removing a silent high-confidence link instead returns removed true with decision_recorded rejected and the decision_id. A missing link or an invisible task returns removed false, not an error.
Boundaries
The entity must be visible under the caller's project grants or the call answers Entity not found. An invisible task reads as removed false so task existence never leaks.
Lifecycle
Marks the link removed with removal stamps and preserves a removal marker carrying the prior link detail; re-linking later revives the edge and clears the marker with its own stamps. A rejected auto-discovered link cannot resurface silently. Nothing is ever physically removed.
Errors
Missing task_id or entity_name is rejected by validation. Unknown arguments are rejected (strict validator). An unknown or out-of-scope entity answers Entity not found.
Object with closed (echoed session_id) and matched (1 closed, 0 untouched).
Boundaries
Only entries owned by the caller's own principal flip; foreign or unknown ids report matched 0 with no existence oracle. Operates inside the selected profile boundary.
Lifecycle
Moves one owned live session entry to ended. No history is removed; the entry stays as renewal hygiene evidence.
Errors
Missing session_id rejected by validation. Foreign or already-closed sessions are not errors: matched 0.
Session continuity bundle: handoff pack plus unresolved questions, chunks awaiting human input, and recently changed facts. Call it to resume work where the last session left off.
Optional: `include_open_questions`, `session_id`.
- `include_open_questions`.
Note: Defaults true; false skips the open-question scan.
- `session_id`.
Note: Malformed ids fail with the session_save vocabulary; unowned ids read as session not found with no owner oracle. Absent means a general handoff with no session data.
Result
Object with session_id (null when absent), open_questions (up to 20 with text, type, priority, chunk title), changed_facts_since_last_session (up to 20), chunks_awaiting_human (up to 10), pack (pack_id, token_usage, freshness_score, body), and a summary string.
Boundaries
A supplied session must be owned by the caller (star bypasses). Open questions, awaiting chunks, and recent facts are global scans inside the selected profile boundary.
Lifecycle
Builds and persists a handoff pack with a run-history entry, then reads the top 20 open questions, top 10 awaiting chunks, and facts changed in the last 7 days. Read-shaped but persisted through the pack, hence write access.
Errors
Malformed session_id returns a normal session-must-be-UUID error. Unowned sessions return session not found. Backend failures surface as transport errors.
Optional: `last_n`.
- `last_n`.
Note: How many sessions to return (default 5). Integers, integral floats, and booleans are accepted; other types are rejected.
Result
JSON object with sessions (session_id, project, summary, active_files as a list or null, started_at, ended_at, each null where unset) and count. No visible scope returns sessions [] with count 0.
Boundaries
Only sessions inside the caller's project grants are returned, and callers without star scope see only sessions owned by their own principal. Anything else reads as an empty list, never a denial.
Lifecycle
Pure read; recalling changes nothing about the sessions returned.
Errors
Bad last_n types are rejected (strict validator). Unknown arguments are rejected.
Example
```json
{
"last_n": 5
}
```
Result shape:
```json
{
"count": 1,
"sessions": [
{
"active_files": null,
"ended_at": "<iso-8601>",
"project": "shared-ops",
"session_id": "<uuid>",
"started_at": "<iso-8601>",
"summary": "Pack README drafted; left proofing for next run."
}
]
}
```
Saves a session snapshot, creating it on first call and updating it on later calls with the same id. The durable handoff surface: project, summary, and active files persist beyond the current run.
Optional: `active_files`, `project`, `session_id`, `summary`.
- `active_files`.
Note: Empty, missing, or null stores as null; otherwise the file list is stored as JSON text.
- `project`.
Note: Supplied projects must be granted now. An omitted project on creation is an untagged write needing the untagged grant; on update, unmentioned fields are left untouched.
- `session_id`.
Note: Omit or blank for a fresh server-assigned UUID; a malformed explicit id is rejected. All forms the platform UUID parser accepts are honored.
- `summary`.
Result
JSON object with action (created or updated) and session_id. Denied updates answer with an error object reading session not found rather than revealing the session.
Boundaries
A session's current project must be writable by the caller, and callers without star scope may only touch sessions owned by their own principal; anything else reads as session not found. Operates only inside the caller's profile boundary.
Lifecycle
Create-or-update by session id through one atomic unit, recording the write with the caller's identity. ended_at is always stamped to the current time on both paths; updates keep prior project, summary, and file values wherever the call supplies nothing.
Errors
Bad session_id, project, summary, or active_files types are rejected (strict validator). A malformed session_id answers session_id must be a UUID returned by session_save. Out-of-scope projects and foreign sessions deny generically. Failures persist nothing (atomic).
Example
```json
{
"project": "shared-ops",
"summary": "Pack README drafted; left proofing for next run."
}
```
Result shape:
```json
{
"action": "created",
"session_id": "<uuid>"
}
```
session_id is assigned by the server when omitted; the placeholder stands for the returned UUID.
Assessing context, asking humans, promoting candidate facts and building context packs.
assess_context
Write · Advanced
Classifies one context chunk: scans signal markers, scores materiality and uncertainty, and advances its lifecycle state. Call it before extraction to make a chunk enrichable.
Required: `chunk_ref`.
Optional: `force`, `session_id`.
- `chunk_ref`.
- `force`.
Note: Re-assesses frozen chunks when true; without it frozen chunks return a blocked frozen payload.
- `session_id`.
Note: Attribution only; never authority.
Result
Object with chunk_id, state, previous_state, policy, materiality, uncertainty, should_skip, skip_reason, signals_detected, and annotations_created.
Boundaries
Operates inside the selected profile boundary. No conductor or project gate; unknown chunks answer as not-found with no existence oracle.
Lifecycle
Classifies signals, recomputes materiality/uncertainty, moves chunk state where the transition is legal, and records a run-history entry plus a history event on state change. Frozen and awaiting_human-skip paths still record the run entry.
Errors
Missing chunk_ref rejected by validation; unknown argument rejected. Unknown chunk returns a normal not-found error payload. Frozen chunks and unchanged awaiting_human sources return skip payloads, not errors.
Runs the persistent self-repair audit over facts, packs, provenance, and sync drift. Call it to detect and reconcile drift, then read list_memory_issues for what remains open.
Optional: `repair`, `stale_sync_minutes`.
- `repair`.
Note: Defaults true. When false the scan still records newly found issues but applies no reconciliations.
- `stale_sync_minutes`.
Note: Sync-drift threshold; defaults 120.
Result
Object with audit_version (memory_audit_v2), repair flag, emit_event, open_issue_count, resolved_issue_count, issues array, and a repairs breakdown of five counters.
Boundaries
Operates inside the selected profile boundary. No conductor gate; findings cover facts, packs, provenance, and task materialization visible to the caller.
Lifecycle
Backfills missing provenance, refreshes contradiction counts, materializes pack summaries, reconciles task materialization, records open issues, marks resolved ones resolved, and emits a run-history event whenever anything was found, resolved, or repaired. All-or-nothing per run.
Errors
Wrong-typed arguments rejected by validation. Backend failures surface as transport errors; scan findings are data, never errors.
Compiles a role-specific context pack under a token budget from facts, claims, questions, and chunks. Call it to assemble grounded context for planning, review, execution, or handoff.
Optional: `pack_type`, `session_id`, `target_ref`, `token_budget`.
- `pack_type`.
- `session_id`.
Note: Must be owned by the caller (star bypasses); unowned reads as session not found.
- `target_ref`.
Note: Task id scoping the pack; absent means unscoped.
- `token_budget`.
Note: Unset or 0 means 4000; negatives pass through and select nothing.
Result
Object with pack_id (null when suppressed), pack_type, token_budget, token_usage, items_included, preview_items_included, task_scoped, persisted, freshness/relevance/quality scores, previewable, contract_version, selection_policy, advanced_context, sections, and body.
Boundaries
Operates inside the selected profile boundary. Task-scoped relevance uses the caller's visible tasks and linked entities only.
Lifecycle
Selects facts, claims, questions, and chunks greedily under the token budget, persists the pack with its summary artifact and provenance when anything was visible, retires stale packs of the same kind, and records a run-history entry. Empty packs are not persisted and carry pack_id null.
Errors
Unknown pack_type rejected with a normal error payload. Unowned session_id answers session not found. pack_type values are planner, reviewer, executor, bridge_checker, handoff.
Compatibility wrapper that enriches context at increasing depth: assess, pack, extract and auto-promote claims, then impact analysis. Call it to advance the whole pipeline in one step.
Optional: `depth`.
- `depth`.
Note: quick, standard, or deep. quick assesses enrichable chunks and builds packs; standard adds claim extraction plus auto-promotion; deep adds impact analysis over recent facts.
Result
Object with depth, steps (per-stage counters), pack_body, and at standard/depth: claims_extracted, claims_promoted, promoted_facts; at deep: impacts_analyzed.
Boundaries
Operates inside the selected profile boundary across enrichable chunks, packs, claims, and recent facts. No conductor gate.
Lifecycle
Assesses every enrichable chunk, builds an executor pack plus warm task packs (all persisted with run-history entries); standard additionally extracts and auto-promotes high-confidence memory-scope claims; deep additionally walks impact for recent facts. All-or-nothing per run.
Errors
Wrong-typed depth rejected by validation. Per-chunk failures abort the run as a transport error; nothing is partially kept.
Optional: `depth`, `source_kind`, `source_ref`.
- `depth`.
Note: quick walks 1 hop, standard 3, deep 5; unknown values behave as standard.
- `source_kind`.
Note: One of chunk, claim, fact.
- `source_ref`.
Note: Id of the source chunk, claim, or fact; required.
Result
Object with source, depth, max_depth, total_impacts, impacts_by_kind grouped by session, snapshot, mapping, validation, export, and fact (each with edge, impact, and propagated scores plus depth and rationale), and a summary string.
Boundaries
Operates inside the selected profile boundary. Unknown sources answer as not-found with no further detail.
Lifecycle
Bounded breadth-first walk over recorded impact links, then a run-history entry. Read-shaped but persisted through the run log, hence write access.
Errors
Invalid source_kind rejected with a normal error payload. Empty source_ref rejected. Unknown source returns a not-found error payload.
Extracts typed subject-predicate-object-scope claims from a context chunk. Call it on enrichable chunks to stage governance candidates; nothing becomes canonical until promote_candidate.
Required: `chunk_ref`.
Optional: `scope_hint`.
- `chunk_ref`.
- `scope_hint`.
Note: Overrides scope detection; one of memory, bridge, mapping, validation, export.
Result
Object with chunk_id, claims_extracted count, scope, and claims array (claim_id, subject, predicate, object, scope, confidence, requires_human). Predicates come from uses, depends_on, is, requires, produces, validates, contains, replaces.
Boundaries
Operates inside the selected profile boundary. Only enrichable or uncertain chunks qualify; all other states answer blocked.
Lifecycle
Persists one candidate claim plus its source-chunk evidence and provenance per extracted triple, with a history event each, then a run-history entry. All-or-nothing per call.
Errors
Missing chunk_ref rejected by validation. Unknown chunk returns not-found. Non-enrichable states return an invalid-state error naming the current state.
Applies truth maintenance to a canonical fact: supersede, contradict, invalidate, or revalidate. Call it when knowledge changes rather than editing facts in place.
Required: `fact_id`, `action`.
Optional: `effective_at`, `rationale`, `target_fact_id`.
- `fact_id`.
- `action`.
Note: One of supersede, contradict, invalidate, revalidate.
- `effective_at`.
Note: ISO timestamp; defaults to now. Empty string behaves as absent.
- `rationale`.
Note: Recorded in the decision artifact and history; empty string behaves as absent.
- `target_fact_id`.
Note: Required for supersede and contradict; must differ from fact_id and exist. Empty string behaves as absent.
Result
Object with fact_id, action, target_fact_id (null unless supersede/contradict), effective_at, and changed (always true on success).
Boundaries
Operates inside the selected profile boundary. Truth maintenance only; entries are never physically removed.
Lifecycle
Applies the lifecycle change (supersede stamps validity end and successor links; contradict links both directions; invalidate ends validity; revalidate clears validity end and successor links), refreshes contradiction counts, records a fact history event, and persists a decision artifact. Always reports changed true on success; all-or-nothing.
Errors
Unsupported action, missing target, self-target, or unknown fact/target return normal error payloads. Validation rejects missing fact_id/action and unknown arguments.
Example
```json
{
"action": "invalidate",
"fact_id": "<uuid>",
"rationale": "superseded by field trial"
}
```
Result shape:
```json
{
"action": "invalidate",
"changed": true,
"effective_at": "<iso-8601>",
"fact_id": "<uuid>",
"target_fact_id": null
}
```
Required: `claim_id`.
Optional: `mode`.
- `claim_id`.
- `mode`.
Note: Defaults human_confirmed. multi_evidence and imported are policy-gated; auto_layer1 is pipeline-internal.
Result
Object with claim_id, promoted flag, reason when false, and on success fact_id, subject, predicate, object, scope, validation_mode, and reused_existing_fact when deduplicated.
Boundaries
Operates inside the selected profile boundary. Sensitive scopes (mapping, validation, bridge, export) accept only human_confirmed; multi_evidence additionally needs memory scope, three evidence entries, and confidence at least 0.7.
Lifecycle
Marks the claim promoted, creates the canonical fact (or reinforces an identical live fact, reporting reused_existing_fact), copies evidence into provenance, links contradictions with counts refreshed, and records history plus a run-history entry. Idempotent via the promoted claim status.
Errors
Unknown claim returns not-found. Non-candidate status and invalid modes return normal error payloads. Blocked promotions (human confirmation needed, insufficient evidence, low confidence, wrong scope) return promoted false with a reason, not an error.
Object with chunk_id, state (awaiting_human), questions array (id, text, type, priority), and the awaiting_human_block text. Question types are scope, time, semantics, action, downstream_use.
Boundaries
Operates inside the selected profile boundary. Frozen chunks are refused; all other states may be queued.
Lifecycle
Persists up to five typed open questions with history events, writes the AWAITING_HUMAN annotation block, moves the chunk toward awaiting_human where the transition is legal, and records a run-history entry. All-or-nothing.
Errors
Missing chunk_ref rejected by validation. Unknown chunk returns not-found. Frozen chunks return a cannot-queue error payload.
Required: `chunk_ref`, `answer_text`.
Optional: `question_id`.
- `chunk_ref`.
- `answer_text`.
Note: Appended to the chunk body, so the source hash changes and the chunk becomes assessable again.
- `question_id`.
Note: Answers one question when given; answers all open questions for the chunk when omitted. Omitted (not null) to answer all.
Result
Object with chunk_id, new_state, previous_state, questions_resolved count, and source_hash_updated flag.
Boundaries
Operates inside the selected profile boundary. Frozen chunks refuse answers.
Lifecycle
Marks addressed questions answered, appends the answer to the chunk body with a fresh source hash, moves awaiting_human or uncertain chunks back to enrichable, and records resolution provenance, a history event, and a run-history entry. All-or-nothing.
Errors
Missing chunk_ref or answer_text rejected by validation (multi-missing shape when both absent). Unknown chunk returns not-found. Frozen chunks return a cannot-record error payload.
Optional: `aggregate_id`, `aggregate_kind`, `limit`, `since_ts`.
- `aggregate_id`.
Note: Narrows to one aggregate; empty means all.
- `aggregate_kind`.
Note: Narrows by task, fact, chunk, and similar kinds; empty means all.
- `limit`.
Note: Defaults 100, clamped 1 to 500.
- `since_ts`.
Note: Lower timestamp bound; empty means all.
Result
Object with count, events array (20 history fields: identity, clock, timestamp, old/new values, payload, parent, and source span), and contract_version.
Boundaries
Reads the append-only history inside the selected profile boundary. Newest first; every entry present with nulls preserved.
Lifecycle
Pure read; records nothing. Stored values parse with strict whole-input semantics so dates stay strings.
Errors
Wrong-typed arguments rejected by validation. Filters that match nothing return count 0, not an error.
Reviewing and consolidating accumulated memory as a reviewed pipeline.
reflect_apply
Write · Advanced
Applies accepted candidates from a completed run as conservative task mutations with snapshots. Call it after deciding; reruns are safe because applied candidates skip.
Required: `run_id`.
Optional: `applied_by`, `candidate_ids_csv`.
- `run_id`: id of a run in `completed` status. Other statuses error.
- `applied_by`: actor recorded on each snapshot row.
Note: Actor recorded on each snapshot; defaults user.
- `candidate_ids_csv`: optional comma-separated subset of candidate
ids to apply. Empty string = apply all accepted.
Result
Object with run_id, considered and applied counts, skipped array (candidate_id plus reason), and failed array (candidate_id plus error).
Boundaries
Run must be completed and its input project granted (unfiltered runs are star-only; foreign runs read as not-found, never leaking status). Every task target re-checks caller scope before mutation; denied targets land in skipped as target_out_of_scope.
Lifecycle
Applies accepted candidates through the canonical task mutation path with before/after snapshots per candidate. Already-applied candidates skip idempotently as already_applied; entity targets skip (no archive primitive yet); vanished targets skip as target_not_found. All-or-nothing per call.
Errors
Unknown, discarded, or foreign runs read as not-found; non-completed runs fail as run_not_completed. Engine failures return normal error plus internal_error payloads, never transport errors.
Object with run_id, archived_at, and newly_archived flag.
Boundaries
Run access follows the input-project gate: foreign or unfiltered runs read as not-found for non-star callers.
Lifecycle
Stamps archived_at on a terminal run; history stays listed by default until include_archived filtering hides it. Idempotent: re-archiving reports newly_archived false with the same stamp.
Errors
Unknown, discarded, or foreign runs fail as run_not_found. Pending or running runs fail as cannot_archive_active_run. Shapes are normal invalid_state_transition payloads.
Optional: `abandoned_inbox_days`, `format`, `limit_per_category`, `project`, `stale_days`.
- `abandoned_inbox_days`: inbox items untouched this long are flagged (default 30)
Note: Inbox items untouched this long are flagged; defaults 30.
- `format`: "json" (default) or "markdown" (adds rendered report)
Note: json or markdown; markdown adds a rendered report.
- `limit_per_category`: cap candidates per category (default 20)
Note: Cap per category; defaults 20.
- `project`: filter to a single project (empty = all)
Note: Single-project filter; empty means all but then requires star scope.
- `stale_days`: due_date older than this many days counts as stale (default 60)
Note: Overdue thresholds older than this many days count as stale; defaults 60.
Result
Object with version, summary (total_candidates, by_category, applied_filters), candidates per category, and markdown when requested.
Boundaries
An explicit project must be granted; an unfiltered global audit requires star scope. Scans additionally constrain to grants, so candidates only come from visible projects.
Lifecycle
Read-only dry run; persists nothing. Six deterministic categories: exact duplicate titles, stale overdue not_started tasks, empty-description notes, orphan parent links, abandoned inbox items, and entities without observations, each with a suggested action.
Errors
Out-of-scope project or non-star global request returns a normal error payload. Engine failures return a normal error payload, never a transport error.
Run access follows the input-project gate: foreign or unfiltered runs read as not-found for non-star callers.
Lifecycle
Moves a pending or running entry to canceled with an end stamp. Terminal entries are rejected, never rewritten.
Errors
Unknown, discarded, or foreign runs fail as run_not_found. Terminal runs fail as cannot_cancel_terminal_run. Shapes are normal invalid_state_transition payloads.
Required: `candidate_id`, `decision`.
Optional: `decided_by`.
- `candidate_id`.
- `decision`.
Note: One of accept, reject, defer.
- `decided_by`.
Note: Actor recorded on the candidate; defaults user.
Result
Object with candidate_id, decision, and decided_by.
Boundaries
Unknown and out-of-scope candidates both read as candidate_not_found: run input project plus candidate evidence project must be granted (star bypasses).
Lifecycle
Stamps the candidate decision with decider and timestamp. Decisions are overwriteable by deciding again; only accept is picked up by apply.
Errors
Unknown decisions fail as invalid_argument. Unknown or foreign candidates fail as not_found. Validation rejects missing candidate_id/decision.
Discards a terminal run as a lifecycle transition: it resolves as absent while its candidates are preserved. Call it to retire runs without losing review evidence.
Object with run_id and rows_deleted (1 on success, kept for oracle wire shape).
Boundaries
Run access follows the input-project gate: foreign or unfiltered runs read as not-found for non-star callers.
Lifecycle
Lifecycle transition to discarded with timestamp, actor, and reason; inputs, candidates, and snapshots are preserved, so candidate-centric review and decide keep working while run-centric tools resolve the run as absent. The narrow sanctioned retirement path; nothing is physically removed.
Errors
Unknown, already-discarded, or foreign runs fail as not_found. Pending or running runs fail as cannot_discard_active_run: cancel first.
Required: `run_id`.
Optional: `candidate_type_filter`, `decision_filter`, `limit`, `offset`.
- `run_id`: parent run id.
- `candidate_type_filter`: optional category narrowing
(e.g. 'stale_overdue_tasks').
Note: Narrows to one of the six audit categories.
- `decision_filter`: empty | pending | accept | reject | defer.
Note: Empty, pending, accept, reject, or defer; anything else is an error.
- `limit`: max rows (clamped to 1000).
Note: Defaults 100, clamped 1 to 1000.
- `offset`: pagination cursor.
Note: Pagination cursor; negative behaves as 0.
Result
Object with candidates array (candidate_id, run_id, type, suggested_action, target_kind, target_ref, evidence, confidence, decision fields, timestamps, already_applied), total, limit, offset, and the two filters.
Boundaries
Foreign runs read as the empty shape. Candidates additionally filter by evidence project, so pre-gate runs with cross-project candidates stay contained; scoped callers get the visible total.
Lifecycle
Pure read over persisted candidates with parsed evidence and an already_applied flag derived from apply snapshots.
Errors
Unknown decision_filter fails as unknown_decision_filter. Missing run_id rejected by validation. Backend failures return normal invalid_argument payloads.
Optional: `abandoned_inbox_days`, `created_by`, `instructions`, `limit_per_category`, `model`, `project`, `stale_days`, `version`.
- `abandoned_inbox_days`.
- `created_by`: actor recorded in reflection_runs.created_by.
Note: Actor recorded on the run; defaults user.
- `instructions`: free-form guidance text (max 4096 chars per C14/Dreams).
Note: Free-form guidance, at most 4096 characters; longer fails as instructions_too_long.
- `limit_per_category`.
- `model`: optional model id for future LLM-based runs (Phase 2 uses).
Note: Optional model id for future runs; recorded only.
- `project`: optional project filter for the audit pass.
- `stale_days`.
- `version`: run schema version for forward-compat (default reflect_v1.0).
Note: Run schema version; defaults reflect_v1.0.
Result
Object with run_id, status (completed, or failed on cap exhaustion), candidates_persisted, summary, and on failure error_type.
Boundaries
An explicit project must be granted; unfiltered runs are star-only since their candidates would span all projects. The audit scan constrains to grants.
Lifecycle
Creates a pending entry, records the filter input, marks it running, reuses the Phase 0.5 audit to persist one candidate per finding (capped at 10000, beyond which the run fails as candidate_limit_exceeded), then marks it completed. All-or-nothing per run.
Errors
Out-of-scope project or non-star global request fails as invalid_argument. Overlong instructions fail as instructions_too_long. Engine failures return normal internal_error payloads with a best-effort failed marking.
Structured disagreement: roles, posts, verdicts and protocol maintenance.
debate_add_role
Write · Conductor only
Appends a NEW role to a live topic and installs its active binding in one atomic unit. The mid-debate way to grow the roster, including numbered EXECUTOR_n workers.
Required: `topic_id`, `role`, `session_id`.
Optional: `bound_by_msg_id`, `bound_by_role`, `conductor_override_msg_id`, `reason`, `replace_active`, `runtime`.
- `topic_id`: existing debate topic.
- `role`.
- `session_id`.
Note: The session owning the new role; must be owned by the caller.
- `bound_by_msg_id`.
- `bound_by_role`.
- `conductor_override_msg_id`.
Note: Validated when supplied; ordinary roster growth does not need it.
- `reason`.
Note: Defaults to a flexible-roster marker when omitted.
- `replace_active`.
Note: Covers the same-owner replay path; idempotent replays report added_role false.
- `runtime`.
Result
Object with topic_id, role, session_id, runtime, state, generation, added_role, retired_sessions, and retired_worker_claims.
Boundaries
Restricted to conductor or star callers, plus ownership of the named session by the caller: knowing a foreign session id never enrolls it. Shape validation runs before ownership so precedence is unchanged for privileged callers.
Lifecycle
Roster append plus binding install succeed or fail together with an audit/history event. Same-owner replays are idempotent; a role owned elsewhere falls through to the ordinary binding path.
Errors
Unknown topics, malformed ids, unowned sessions, and callers without conductor or star scope are rejected uniformly.
Advances the (topic, role) watermark cursor to a specific message: looks up its timestamp, posts the canonical WATERMARK marker, and reconciles the active primary signal cursor in one atomic unit.
Required: `topic_id`, `role`, `processed_up_to_msg_id`.
- `topic_id`: existing debate topic.
- `role`.
Note: Role-addressed only (no session argument): the caller must own the active binding for (topic, role).
- `processed_up_to_msg_id`.
Note: Must name a message already preserved in the topic.
Result
Same post shape: msg_id, ts, topic_state, and vehicle, for the INFO/WATERMARK marker.
Boundaries
Ownership without exception: star and conductor callers naming a role they hold no binding for are denied as not-found. All shape checks run before ownership.
Lifecycle
Marker plus cursor progress persist atomically with an audit/history event. The marker carries no recipients, so on its own it reconciles no addressed inbox.
Errors
Unknown message ids for the topic, malformed ids, and unowned roles are rejected; failures preserve nothing.
Installs, retires, or diagnoses one role/session binding. Enforces single active ownership per role; retiring an active owner needs conductor sanction.
Required: `topic_id`, `role`, `session_id`.
Optional: `bound_by_msg_id`, `bound_by_role`, `conductor_override_msg_id`, `reason`, `replace_active`, `runtime`, `state`.
- `topic_id`: existing debate topic.
- `role`.
- `session_id`.
Note: The session being bound; must be owned by the caller.
- `bound_by_msg_id`.
- `bound_by_role`.
- `conductor_override_msg_id`.
Note: Conductor or star callers only; cites the sanctioning CONDUCTOR decision and is validated twice, read-only first and authoritatively inside the unit.
- `reason`.
- `replace_active`.
Note: Atomic swap onto a new session; without it a duplicate active owner is rejected.
- `runtime`.
- `state`.
Note: Defaults to active when omitted; an explicit empty value fails validation.
Result
Object with topic_id, role, session_id, runtime, state, generation, plus retired_sessions and retired_worker_claims counts for the swap path.
Boundaries
Binding a session to a role the caller does not hold is treated as takeover and denied as not-found unless a valid conductor override is supplied. Topic existence is checked before ownership so missing topics never become an existence oracle. Without an override, star and conductor callers meet the same ownership rule as everyone else.
Lifecycle
Binding changes persist atomically with an audit/history event; replacing an active owner retires its worker claims. Retired bindings stay visible as history and never grant further authority.
Errors
Unknown topics, malformed ids, unowned sessions, duplicate actives, and invalid or missing overrides are rejected with uniform vocabulary.
Object with topic_id, topic_state, and bindings: each entry carries role, session_id, runtime, state, generation, timestamps, reason, bound_by attribution, and the last processed cursor triple.
Boundaries
Gated by project scope AND active participation with an indistinguishable missing shape for outsiders. Per-entry session ids are visible only to star callers or the owner of that session; all other fields stay identical and all other entries show null.
Lifecycle
Pure read: preserves nothing. Retired bindings remain listed as history alongside active owners.
Errors
Malformed ids fail shape validation; unknown or unreachable topics answer the native missing shape.
Closes a topic through the same authoritative transition path as debate_state, with identical gating, Q/A checks, and retirement. Kept so existing close call sites keep working.
Required: `topic_id`, `role`, `new_state`.
Optional: `reason`.
- `topic_id`: existing debate topic.
- `role`.
Note: Must be a declared role whose binding the caller owns; no star or conductor exception.
- `new_state`.
Note: One of INIT, ACTIVE, RESOLVED, ARCHIVED; only forward lifecycle moves are valid.
- `reason`.
Note: Recorded inside the synthetic STATE message body.
Result
Same shape as debate_state: old_state, new_state, ts, blocking_questions, transition_msg_id, body, retired_bindings, and retired_worker_claims.
Boundaries
Identical authority to debate_state: owned binding on the named role, uniform denial for unknown or unowned positions, and the same [DEFERRED: resolution-equivalence.
Lifecycle
Shares the transition unit with debate_state: STATE message plus binding retirement succeed or fail together with an audit/history event. No separate close semantics exist.
Errors
Same vocabulary as debate_state: uniform not-found for unknown/unowned, illegal-transition rejection, and open-question blocks carrying the blocking list.
Writes a COMPACTION snapshot that later reads can resume from, keeping long topics bounded. The body must carry OBSERVE / ORIENT / DECIDE / ACT sections.
Required: `topic_id`, `role`, `body`.
Optional: `since_ts`, `until_ts`.
- `topic_id`: existing debate topic.
- `role`.
Note: Posts as this declared role, so the caller must own its binding.
- `body`.
Note: Must contain OBSERVE / ORIENT / DECIDE / ACT sections; anything else is rejected before anything is preserved.
- `since_ts`.
Note: Optional ISO-8601 UTC lower bound recorded in the composed snapshot header.
- `until_ts`.
Note: Optional ISO-8601 UTC upper bound recorded in the composed snapshot header.
Result
Same post shape: msg_id, ts, topic_state, and vehicle, for the INFO/COMPACTION message.
Boundaries
Ownership authority matches debate_post: the named role must be owned by one of the caller's authenticated bindings, with uniform denial otherwise. Timestamp bounds are validated before ownership is consulted.
Lifecycle
Preserved as an ordinary INFO/COMPACTION message with an audit/history event. debate_read with since_latest_compaction resumes after the newest snapshot.
Errors
Section-gate failures, malformed timestamps, unknown topics, and unowned roles are rejected; failures preserve nothing.
Force-writes a high-priority PING shaped [ESCALATE:reason] tagged for a target role (default HUMAN). A convenience over hand-formatting escalation messages.
Required: `topic_id`, `role`, `reason`.
Optional: `target_role`.
- `topic_id`: existing debate topic.
- `role`.
Note: Posts as this declared role, so the caller must own its binding.
- `reason`.
Note: Must be non-empty; carried inside the composed PING body.
- `target_role`.
Note: Defaults to HUMAN; validated as a role shape.
Result
Same post shape: msg_id, ts, topic_state, and vehicle, for the H/PING message.
Boundaries
Ownership authority matches debate_post with uniform denial for unknown or unowned positions. On debate/v1 topics the structured ESCALATE kind via debate_post is the packet-writing path; this legacy PING form coexists with it.
Lifecycle
Preserved as an H/PING message with an audit/history event. Nothing about human-packet close-out lives in this surface.
Errors
Empty reasons, malformed ids or roles, and unowned roles are rejected; failures preserve nothing.
Bootstraps a new debate topic: idempotent on (topic_id, roles), declares the roster, seeds active bindings, and optionally configures debate/v1 micro-state. Call it once per topic before any posts.
Required: `title`, `created_by_role`.
Optional: `blind_roles_csv`, `max_rounds`, `metadata_json`, `phase_timeout_seconds`, `project`, `protocol_version`, `resolve_by`, `roles_json`, `topic_id`.
- `title`: non-empty.
- `created_by_role`: role posting the init.
- `blind_roles_csv`: exactly two declared semantic roles when using debate/v1.
- `max_rounds`.
Note: 1..10; executor roles must be numbered EXECUTOR_1, EXECUTOR_2, ...
- `metadata_json`: JSON object.
- `phase_timeout_seconds`.
- `project`: topic-owned immutable project; defaults from a singleton-grant creator, untagged for star, required otherwise.
Note: Topic-owned immutable project. Explicit value wins when writable; otherwise derived from a singleton-grant creator, untagged for star/conductor, and required for multi-grant creators.
- `protocol_version`: empty for legacy behavior, or debate/v1.
Note: Empty selects legacy behaviour; debate/v1 enables blind roles, phases, and rounds.
- `resolve_by`: optional ISO 8601 UTC deadline.
- `roles_json`: JSON array of unique {role, session_id} dicts.
Note: JSON array of {role, session_id} entries. Entries naming an existing session must be owned by the caller and visible in caller scope; omitted session ids are minted by the service as caller-owned identity.
- `topic_id`: optional previously returned UUID; omitted on CREATE.
Result
Object with topic_id, title, state (INIT), created_at, created_by_role, resolve_by, archived_at (null), roles, metadata, seeded_bindings, plus protocol_state when the topic runs under debate/v1.
Boundaries
Creation stays inside the selected profile boundary and the resolved topic project. Named sessions are never taken over by knowledge of an identifier: each supplied session must exist, be owned by the caller, and sit in caller scope (star never substitutes for ownership). A repeat call with identical shape returns the existing topic; differing shape is rejected.
Lifecycle
Creates the topic and its seeded bindings atomically with an audit/history event. Priority lane plus reason is required at creation. Topics live INIT to ACTIVE to RESOLVED to ARCHIVED; later movement uses debate_state or debate_close_topic.
Errors
Missing title or created_by_role is rejected. Malformed topic ids, role shapes, blind sets, round bounds, timeout floors, and out-of-scope projects are rejected by validation. Unknown arguments are rejected. Nothing is partially created on failure.
Example
```json
{
"created_by_role": "CONDUCTOR",
"project": "shared-ops",
"roles_json": "[{\"role\":\"CONDUCTOR\",\"session_id\":\"<uuid>\"}]",
"title": "Musl-first packaging"
}
```
Result shape:
```json
{
"seeded_bindings": [],
"state": "INIT",
"title": "Musl-first packaging",
"topic_id": "<uuid>"
}
```
topic_id is assigned by the server when omitted; placeholders stand for returned values.
Creates the immutable AB and BA order-swap projections for adjudication from two CLAIM or REBUT positions held by the two opposing blind roles. Agreement later stops the debate.
Required: `topic_id`, `left_msg_id`, `right_msg_id`.
- `topic_id`: existing debate topic.
- `left_msg_id`.
Note: First position; must sit in the topic and carry CLAIM or REBUT.
- `right_msg_id`.
Note: Second position; must be distinct from the left and come from the opposing blind role.
Result
Object with topic_id and projections: two entries (order AB and BA), each with projection_id, order_key, protocol_version, topic_id, round_no, and positions.
Boundaries
Conductor callers only; the call takes no judge role so it cannot be role-gated any other way. Both positions must come from the two opposing blind roles while the topic sits in ADJUDICATE. Rewrites conflict: identical repeats are idempotent, differing repeats are rejected.
Lifecycle
Projections persist atomically with an audit/history event and are immutable afterwards. Verdicts arrive separately via debate_judge_verdict.
Errors
Wrong kinds, non-opposing roles, wrong phases, unknown messages, and projection conflicts are rejected uniformly.
Required: `projection_id`, `judge_role`, `verdict_json`.
- `projection_id`.
Note: Target projection from debate_judge_prepare.
- `judge_role`.
Note: Must be active, declared, non-blind, and non-human; one judge role per pair.
- `verdict_json`.
Note: Object with non-empty winner_msg_id and decision; stringified JSON is re-parsed like the reference client.
Result
Object with projection_id, topic_id, complete, stable (null until both sides speak), and the current protocol_state.
Boundaries
Judge independence is enforced: the judge role must be an active declared participant outside the blind pair, and each pair admits a single judge role. Terminal phase moves compare-and-swap on ADJUDICATE, so concurrent verdicts cannot double-apply.
Lifecycle
Each verdict persists atomically with an audit/history event and is immutable. Full agreement moves the topic to STOPPED; split verdicts move it to STALEMATE with an order-swap disagreement reason.
Errors
Unknown projections, malformed verdicts, ineligible judges, duplicate judge roles, and lost phase races are rejected uniformly.
Reclaims stale active standing=false DECISION claims past a cutoff: late terminal answers complete them, the rest move to reclaimed or expired. Keeps a crashed one-shot owner from blocking a decision forever.
Required: `topic_id`, `older_than_ts`.
Optional: `minimum_age_seconds`.
- `topic_id`: existing debate topic.
Note: Topic whose one-shot DECISION claims are swept; gated before the sweep so non-participants cannot enumerate claim metadata.
- `older_than_ts`.
Note: Strict ISO-8601 UTC cutoff.
- `minimum_age_seconds`.
Note: Floor for reclaim age, defaults to 60.
Result
Object with topic_id, topic_state, reclaimed entries, and reclaimed_count. Completed claims report done; the rest report reclaimed or expired.
Boundaries
Only caller-owned claims complete or reclaim; the sweep never exposes foreign claim state. Claims exist implicitly from the one-shot DECISION post path; no dedicated claim tool creates them.
Lifecycle
Each outcome is a lifecycle transition with one audit/history event. Late terminal replies may still move a claim to done after reclaim.
Errors
Malformed ids, bad cutoffs, below-floor ages, and unreachable topics are rejected uniformly; failures change nothing.
Appends one broadcast message to a debate topic after atomic pre-store validation. Use it for contributions the whole topic may see; for named recipients use debate_post_with_recipients.
Required: `topic_id`, `role`, `priority`, `kind`, `body`.
Optional: `author_session_id`, `body_mode`, `payload_json`, `protocol_version`, `reply_to`, `standing`, `vehicle`.
- `topic_id`: existing debate topic.
- `role`: must appear in declared roles.
Note: Must be a declared topic role owned by one of the caller's authenticated bindings.
- `priority`: H | M | L | INFO.
- `kind`.
- `body`: non-empty.
- `author_session_id`.
Note: When supplied it must be a session the caller owns; star is no exception. Empty keeps the unattributed legacy shape.
- `body_mode`.
- `payload_json`.
Note: debate/v1 structured payloads require non-empty summary, assumptions[], and evidence_refs[].
- `protocol_version`.
- `reply_to`: optional msg_id in same topic.
- `standing`.
- `vehicle`.
Note: Empty defaults to analysis; implementation-tagged work fails closed downstream rather than here.
Result
Object with msg_id, ts (authoritative timestamp), topic_state, and vehicle (defaults to analysis), plus debate/v1 fields (protocol_version, round_no, body_mode, protocol_state) when the topic runs under debate/v1.
Boundaries
Authorship authority comes from the caller's authenticated binding, never from the role string (star scope covers project visibility only, never role ownership). Unknown topic, undeclared role, or unowned role answer with uniform not-found/denied vocabulary, revealing nothing about membership.
Lifecycle
Validates fully before anything is preserved; a rejection preserves nothing. Under debate/v1 the post also advances phase/round state and may complete worker-claim side effects. Messages are never edited or physically removed.
Errors
Empty body, unknown kinds, bad reply targets, malformed ids, and payload/phase/kind gate violations are rejected. Closed topics block posts per lifecycle rules. Conductor action without an owned binding must travel via debate_add_role or the override machinery, never by asserting a foreign role.
Example
```json
{
"body": "Adopt musl-first packaging.",
"kind": "DECISION",
"priority": "H",
"role": "CONDUCTOR",
"topic_id": "<uuid>"
}
```
Result shape:
```json
{
"msg_id": "<uuid>",
"topic_state": "ACTIVE",
"ts": "<iso-8601>",
"vehicle": "analysis"
}
```
msg_id and ts are assigned by the server; placeholders stand for the returned values.
Posts one addressed message to a debate topic: the message is delivered to explicitly named roles/sessions in a single atomic delivery. Use it when a contribution must reach specific participants (e.g. a verdict, a challenge, a conductor decision) rather than the whole topic.
Required: `topic_id`, `role`, `priority`, `kind`, `body`, `addressed_to_csv`.
Optional: `author_session_id`, `body_mode`, `conductor_override_msg_id`, `diagnostic_to_csv`, `payload_json`, `protocol_version`, `reply_to`, `standing`, `vehicle`.
- `topic_id`: existing debate topic.
- `role`: must appear in declared roles.
- `priority`: H | M | L | INFO.
- `kind`.
- `body`: non-empty.
- `addressed_to_csv`: comma-separated recipients.
Note: Recipients must be declared topic roles or live session ids; broadcasts are not supported (empty list rejected).
- `author_session_id`.
- `body_mode`.
- `conductor_override_msg_id`.
Note: Conductor/roster mediation for acting without an owned binding. Without it the uniform ownership rule applies: the posting role must be owned by one of the caller's authenticated bindings.
- `diagnostic_to_csv`.
- `payload_json`.
- `protocol_version`.
- `reply_to`: optional msg_id in same topic.
- `standing`.
- `vehicle`.
Result
JSON object with msg_id, ts (authoritative timestamp), topic_state, and vehicle (defaults to analysis), plus debate/v1 protocol fields (protocol_version, round_no, body_mode, protocol_state) when the topic runs under debate/v1 semantics.
Boundaries
Authorship authority comes from the caller's authenticated binding, never from the role string (star scope covers project visibility only, never role ownership). Reads of the topic follow the separate access rule (project scope AND active participation); debate_read shows the topic-wide transcript, while genuinely private delivery is only via signal_check.
Lifecycle
Delivers the message and its recipient entries atomically; under debate/v1 the post also advances phase/round state and may complete worker-claim side effects. Messages are never edited or physically removed; stale DECISION claims are reclaimed through debate_message_claim_reclaim, never by removal.
Errors
Unknown topic, undeclared role, or role not owned by the caller resolve as not-found/denied with uniform vocabulary (no membership oracle: undeclared and unavailable roles answer identically). Empty body, empty recipient list, and unknown kinds are rejected. Conductor intervention without ownership must travel via debate_add_role (own session) or the override machinery, never by asserting a foreign role.
Example
```json
{
"addressed_to_csv": "PARTICIPANT",
"body": "Adopt musl-first packaging.",
"kind": "DECISION",
"priority": "H",
"role": "CONDUCTOR",
"topic_id": "<uuid>"
}
```
Result shape:
```json
{
"msg_id": "<uuid>",
"topic_state": "ACTIVE",
"ts": "<iso-8601>",
"vehicle": "analysis"
}
```
msg_id and ts are assigned by the server; placeholders stand for the returned values.
Runs the deterministic liveness sweeps manually: expired debate/v1 phases move to STALEMATE, and roles missing an active owner gain a recovery binding with cursor carry. Manual invocation only; no scheduler calls it.
Optional: `topic_ids_csv`.
- `topic_ids_csv`.
Note: Scopes ONLY the missing-role recovery sweep; the phase-timeout sweep is global by design in every run.
Result
Object with timed_out (each with topic_id and reason) and role_recoveries (each with topic_id, role, session_id, generation, and reason), in deterministic topic order.
Boundaries
Conductor callers only. Every listed id validates before anything runs. Recovery skips HUMAN and OPERATOR roles and carries the newest primary-or-completed-worker cursor onto the fresh binding.
Lifecycle
Timeout moves and recovery bindings persist atomically with audit/history events. Phase moves compare-and-swap so concurrent runs report only true winners; recovery repeats observe the winner and stand down.
Errors
Malformed ids are rejected before any sweep starts. Malformed roster or metadata content fails the whole run rather than sweeping partially.
Returns the deterministic debate/v1 micro-state for one topic: phase, round, blind barrier, deadlines, and transition version. Agents must read control state here, never infer it from prose.
Object with topic_id, protocol_version, phase, round_no, max_rounds, blind_barrier_state, stalemate_reason, transition_version, phase_deadline_at, phase_timeout_seconds, and updated_at.
Boundaries
Gated like other topic metadata: project scope AND active participation, conductor/star otherwise allow, sentinel denies all. Denial echoes the native not-configured shape exactly so unauthorized callers cannot distinguish it from a topic without protocol state.
Lifecycle
Pure read: preserves nothing. Phases move only through posts, verdicts, and debate_protocol_maintain.
Errors
Malformed ids fail shape validation. Missing, unreachable, and genuinely unconfigured topics share one not-configured shape.
Reads the topic-wide transcript with a compound (ts, msg_id) cursor plus kind/priority filters. This is the broadcast view every participant shares; genuinely private delivery is only via debate_signal_check.
Required: `topic_id`, `role`.
Optional: `kind_filter_csv`, `limit`, `priority_filter_csv`, `since_latest_compaction`, `since_msg_id`, `since_ts`.
- `topic_id`: existing debate topic.
- `role`.
- `kind_filter_csv`.
- `limit`.
Note: Bounded page size; over-range pages report truncation with follow-on cursors.
- `priority_filter_csv`.
- `since_latest_compaction`.
Note: When true, resume after the latest COMPACTION snapshot instead of replaying from the start.
- `since_msg_id`.
Note: Takes precedence over since_ts; both take precedence over the stored role cursor.
- `since_ts`.
Result
Object with messages, topic_state, last_msg_id_returned, last_ts_returned, count, truncated, next_msg_id_cursor, next_ts_cursor, limit, and bootstrap_compaction_msg_id (null when no snapshot exists).
Boundaries
Gated by project scope AND active participation; the sentinel project denies everyone including star and conductor, whose only path is debate_reconcile_project. Missing, foreign, and non-participant topics answer an identical unknown_topic shape. Blind-claim hiding applies inside for non-privileged viewers.
Lifecycle
Pure read: preserves nothing and advances no cursor. Bounded resume depends on snapshots written by debate_compact.
Errors
Malformed ids fail shape validation. Unknown topics answer unknown_topic regardless of caller. Unknown cursor ids are rejected without revealing transcript content.
Example
```json
{
"limit": 200,
"role": "CONDUCTOR",
"topic_id": "<uuid>"
}
```
Result shape:
```json
{
"bootstrap_compaction_msg_id": null,
"count": 0,
"messages": [],
"topic_state": "ACTIVE",
"truncated": false
}
```
Filters and cursors narrow the window; the transcript itself is never altered by reading.
Sets a legacy topic's project exactly once, one way. The sole escape hatch for sentinel topics that deny everyone, and it never exposes topic contents.
Required: `topic_id`, `project`.
- `topic_id`.
Note: Must name a topic whose project is still unset or sentinel.
- `project`.
Note: Explicit project value; sentinel and wildcard values are rejected.
Result
Object with topic_id, project, and matched (always 1 on success). Identifiers only, never transcript content.
Boundaries
Conductor callers only. All other principals, including star, are denied on sentinel topics across every other path until this call completes. Second calls on an already-set topic fail.
Lifecycle
One-way and final: the write persists atomically with an audit/history event and cannot be repeated or reversed through this surface.
Errors
Unknown topics, already-set topics, and invalid project values are rejected uniformly without exposing contents.
Atomically swaps a role owner from an old session to a new one with an explicit cursor mode, so exhausted sessions hand off without losing or replaying the inbox.
Required: `topic_id`, `role`, `old_session_id`, `new_session_id`, `cursor_mode`.
Optional: `bound_by_msg_id`, `bound_by_role`, `reason`, `runtime`.
- `topic_id`: existing debate topic.
- `role`.
- `old_session_id`.
Note: Must currently hold the role actively and be owned by the caller.
- `new_session_id`.
Note: The incoming owner; must also be owned by the caller.
- `cursor_mode`.
Note: head starts at the tip, copy carries the newest primary-or-completed-worker cursor, replay restarts from the beginning.
- `bound_by_msg_id`.
- `bound_by_role`.
- `reason`.
- `runtime`.
Note: Carried onto the new binding when supplied.
Result
Binding object plus old_session_id, new_session_id, cursor_mode, cursor_source (primary or completed_worker, null when none), and warning (copy_source_cursor_missing when copy found nothing to carry).
Boundaries
Both sides must be caller-owned; any other combination denies with one shared not-found shape so neither side is revealed. The predecessor binding must be active. No override parameter exists on this path: both-owned is the fail-closed rule.
Lifecycle
Swap plus cursor carry persist atomically with an audit/history event. Copy mode supersedes the incoming cursor when no source exists; replay mode marks prior cursors superseded.
Errors
Missing cursor modes, malformed sessions, inactive predecessors, and unowned sides are rejected; failures move nothing.
Searches one topic's messages by literal substring of the body, newest first, with blind-hiding applied. Use it to locate passages without replaying the full transcript.
Required: `topic_id`, `query`.
Optional: `limit`, `viewer_role`.
- `topic_id`: existing debate topic.
- `query`.
Note: Matched literally; wildcard characters are escaped so input can never widen the match.
- `limit`.
Note: Capped page size; non-positive values collapse to a single hit.
- `viewer_role`.
Note: Shapes blind-claim visibility only, never ranking scope.
Result
Object with topic_id, query, count, limit, and messages (newest first, same message shape as debate_read).
Boundaries
Same topic gate as debate_read: project scope AND active participation, sentinel denies all, conductor/star otherwise allow. The viewer_role parameter never grants visibility beyond that gate.
Lifecycle
Pure read: preserves nothing. Server tries a full-text path first and falls back to literal match with identical membership and ordering.
Errors
Malformed topic ids fail shape validation; unknown or unreachable topics answer unknown_topic. No hit content ever leaks through an error.
Sets the conductor-owned P0..P7 priority lane in topic metadata, with reason, next action, and blockers. The cross-topic triage authority behind debate_work_queue ordering.
Required: `topic_id`, `role`, `lane`, `reason`.
Optional: `blocked_by`, `next_action`.
- `topic_id`: existing debate topic.
- `role`.
Note: Must name the CONDUCTOR role verbatim and be declared in the topic.
- `lane`.
Note: P0..P7, case-insensitive on input and stored uppercased with a derived rank.
- `reason`.
Note: Required and non-blank; stored and echoed back.
- `blocked_by`.
Note: Optional blocker surfaced by the work queue.
- `next_action`.
Note: Optional explicit next step surfaced by the work queue.
Result
Object with topic_id, lane, rank, reason, next_action, blocked_by, and updated_at.
Boundaries
Two gates in order: the role argument must read CONDUCTOR, and the caller must hold the conductor role (star scope alone never suffices). The topic must declare the role. Shape validation runs before either gate.
Lifecycle
Lane write persists atomically with an audit/history event and immediately reshapes work-queue ordering for the topic.
Errors
Bad lanes, missing reasons, undeclared roles, non-conductor callers, and unknown topics are rejected; failures change nothing.
Records a durable consumption receipt: advances the (session, role, topic) compound cursor to a specific message. The target must be addressed to the caller, so nothing can be skipped over.
Required: `session_id`, `role`, `topic_id`, `last_processed_msg_id`.
- `session_id`.
Note: Cursor owner; must be caller-owned.
- `role`.
- `topic_id`: existing debate topic.
- `last_processed_msg_id`.
Note: Must name a message addressed to the caller; the timestamp is derived from the preserved message, never caller-supplied.
Result
Object with session_id, role, topic_id, last_processed_msg_id, last_processed_ts, last_check_at, and worker_claim when a worker claim completes on this receipt.
Boundaries
Advancing requires the target to be addressed to the caller's role or session; unaddressed targets are rejected rather than skipped. Cursors move monotonically and must not precede delivery on debate/v1 topics. Completing a recipient's delivery affects only that recipient.
Lifecycle
Persists the new cursor with an audit/history event, completes that recipient's delivery marker, acknowledges TASK_ALARM reminders, and completes the matching worker claim on terminal replies. At-most-once consumption per recipient rests on this receipt.
Errors
Unaddressed, regressive, or unknown message ids are rejected. Missing claims and inactive parent bindings fail without moving any cursor.
Returns the recipient-scoped inbox: messages addressed to the caller's role or session past the compound cursor. This is the sole private-delivery path; debate_read shows the topic-wide transcript instead.
Required: `session_id`, `role`, `topic_id`.
Optional: `limit`, `since_msg_id`, `since_ts`.
- `session_id`.
Note: Caller session; UUID sessions without a worker claim resolve through the parent binding claim.
- `role`.
Note: Must be declared in the topic; delivery matches role OR session.
- `topic_id`: existing debate topic.
- `limit`.
Note: Defaults to 200, capped at 1000.
- `since_msg_id`.
Note: Explicit cursor, takes precedence over since_ts and any persisted cursor.
- `since_ts`.
Result
Object with pending, count, truncated, next_cursor ({ts, msg_id} or null), max_priority, topic_state, and limit. Empty inboxes return zero counts with null cursors rather than errors.
Boundaries
Only messages addressed to the given role or session are ever returned. Cursor precedence is explicit ids, then timestamps, then persisted signal plus watermark self-heal, then the start of the topic. Unknown roles and missing topics answer with uniform vocabulary.
Lifecycle
Read-shaped but not side-effect free: checking persists cursor, delivery-progress, and one-shot DECISION claim state atomically, so later advances observe current data.
Errors
Malformed session, role, or topic ids fail shape validation. Undeclared roles, missing worker claims for UUID sessions, and unknown topics are rejected without disclosing foreign traffic.
Moves a topic along INIT to ACTIVE to RESOLVED to ARCHIVED, posting a synthetic STATE message and retiring bindings in the same atomic unit. RESOLVED requires every Q to hold a matching A reply.
Required: `topic_id`, `role`, `new_state`.
Optional: `reason`.
- `topic_id`: existing debate topic.
- `role`.
Note: Must be a declared role whose binding the caller owns; no star or conductor exception.
- `new_state`.
Note: One of INIT, ACTIVE, RESOLVED, ARCHIVED; only forward lifecycle moves are valid.
- `reason`.
Note: Recorded inside the synthetic STATE message body.
Result
Object with old_state, new_state, ts, blocking_questions, transition_msg_id, body, retired_bindings, and retired_worker_claims. When the Q/A gate blocks, old and new states equal the current state and blocking_questions lists the open questions.
Boundaries
Lifecycle authority comes from the caller's owned binding on the named role. A body starting with [DEFERRED: counts as resolution-equivalent for the gate. A direct kind=STATE post bypasses the Q/A gate (pinned behaviour); this tool never bypasses it.
Lifecycle
Transition, STATE message, and binding retirement succeed or fail together with an audit/history event. RESOLVED retires active bindings; ARCHIVED retires active plus diagnostic bindings. Nothing is ever physically removed.
Errors
Unknown topic or undeclared role answers with uniform vocabulary. Illegal transitions and open-question blocks are rejected with the blocking list. Unowned roles deny as not-found.
Resolves wake targets for a trigger response and preserves a history event without waking anyone or posting anything. The dry-run lever for delivery bring-up only.
Required: `tool_response_json`.
Optional: `action`.
- `tool_response_json`: Trigger tool-response object as JSON.
Note: Trigger response object as JSON; must carry the expected schema version, msg_id, and topic_id.
- `action`: Wake action namespace.
Note: Wake action namespace, defaults to dry_run_wake.
Result
Object with targets, logs, and suppressed counts, plus notify_targets for the implementation-vehicle notify-only branch. Unknown schemas still return this envelope alongside a mismatch history event.
Boundaries
The trigger's preserved message is authority, never the caller's JSON. Callers must reach the trigger's topic or the call answers unknown_topic with no recipient disclosure. Per-recipient suppression, redispatch, and singleton rules apply inside.
Lifecycle
Signal-only by design: every path preserves its history event atomically, including schema mismatches, unknown triggers, and blind-commit waiting. Real wake actions are out of scope.
Errors
Non-object responses fail closed. Unknown triggers and blind-barrier waits resolve to history events rather than targets.
Lists open topics in deterministic conductor priority order from lane rank, deadline urgency, open questions, claims, bindings, and message signals. The triage view for what needs attention next.
Optional: `limit`, `states_csv`, `topics_csv`.
- `limit`.
Note: Defaults to 50, capped at 1000.
- `states_csv`.
Note: Defaults to INIT,ACTIVE; empty falls back to the same default.
- `topics_csv`.
Note: Optional explicit topic filter; malformed ids are rejected.
Result
Object with items (each with topic_id, title, state, lane, priority_score, reason_codes, next_action, blocked_by, resolve_by, open/blocked counts, missing roles, message peaks, and latest_message), count, total, limit, skipped_invalid_topic_ids, and ordering.
Boundaries
Participation-filtered: unreachable topics vanish silently rather than appearing as skipped (malformed ids only ever populate the skipped list). Conductor callers additionally see sentinel topics as metadata-only reconciliation candidates with no message queries run for them.
Lifecycle
Pure read: preserves nothing. Explicit conductor lanes outrank derived urgency; without any lane the score degrades deterministically through deadline, question, claim, binding, and message signals.
Errors
Bad limits, unknown states, and malformed topic ids are rejected. Malformed preserved roster or metadata content fails the run rather than silently dropping topics.
Idempotently allocates or reuses the derived worker for one trigger (topic, role, parent, trigger). The worker-side take primitive for exactly-once execution.
Required: `topic_id`, `role`, `parent_session_id`, `trigger_msg_id`.
Optional: `details_json`.
- `topic_id`: existing debate topic.
- `role`.
- `parent_session_id`.
Note: Owning parent session of the trigger.
- `trigger_msg_id`.
Note: The message being claimed.
- `details_json`.
Note: Optional object; malformed JSON is rejected with an envelope error.
Result
Claim object: topic_id, role, parent_session_id, trigger_msg_id, worker_session_id, state, parent cursor pair, claimed_at, heartbeat_at, completed_at, ack_msg_id, details, plus duplicate and no_action markers. Active claims heartbeat; retired claims requeue at most twice before reporting exhausted.
Boundaries
Only caller-visible triggers resolve; foreign triggers answer without disclosure. Triggers on the implementation vehicle fail closed here and belong to the conductor-approved vehicle out of band. Worker session ids are UUIDs; the counter is history only.
Lifecycle
Allocate, heartbeat, bounded requeue, and completion persist atomically with an audit/history event. Terminal replies complete the claim through the post and advance paths.
Errors
Unknown triggers, inactive parents, malformed JSON, and exhausted requeues are reported without leaking foreign claim state.
Retires dead workers' claims past a cutoff without hiding the parent trigger. Crash reconciliation: a terminal A/STATUS reply completes the claim instead of retiring it.
Required: `topic_id`, `older_than_ts`.
Optional: `minimum_age_seconds`.
- `topic_id`: existing debate topic.
Note: Topic whose stale claims are swept.
- `older_than_ts`.
Note: Strict ISO-8601 UTC cutoff.
- `minimum_age_seconds`.
Note: Floor for staleness, defaults to 120.
Result
Object with topic_id, topic_state, the cutoff echo, recovered entries (each with recovered_at, prior heartbeat, and parent_trigger_still_pending), and counts.
Boundaries
Only caller-visible claims recover; the parent cursor never moves on this path, so parent work stays discoverable. Liveness is judged from preserved heartbeats, which use a strict timestamp shape that naive values never satisfy.
Lifecycle
Retirements and terminal completions persist atomically, each with an audit/history event. Recovery never invents progress: it only closes what the evidence already shows.
Errors
Malformed ids, bad cutoffs, and below-floor minimum ages are rejected uniformly.
Cancels one open coordination quorum. A Quorum is what was agreed in Mind Quorum layering (Debate holds why, Quorum holds what was agreed). Call it when the agreement is abandoned rather than resolved.
JSON object with quorum_id and status (always cancelled on success).
Boundaries
Requires the conductor role. Unlike resolve, no additional participation gate is applied beyond the conductor check. Stored within the selected profile boundary only.
Lifecycle
Marks an open quorum cancelled with a resolution stamp and records an audit/history event. Cancelled is terminal; lanes beneath keep their own standing.
Errors
Missing conductor role is denied with requires-conductor vocabulary. Missing or malformed identity reads as unknown_quorum with no oracle. Non-open quorums are rejected as quorum not open. Unknown arguments are rejected.
Creates one coordination quorum: the agreed-what in Mind Quorum layering (Debate holds why, Quorum holds what was agreed, Lane carries the coordinated stream, Job is one executable unit). Call it when a decision needs named participants and later lanes and jobs under one agreement.
Required: `project`.
Optional: `participants`, `primary_topic_id`.
- `project`.
- `participants`.
Note: Optional JSON array of {kind, ref} entries, at most 64. kind is client or role; client refs must name a live principal. Entries start pending.
- `primary_topic_id`.
Note: Optional debate topic the quorum coordinates around; when given it must exist.
Result
JSON object with quorum_id (new UUID), status (always open on creation), and project.
Boundaries
Requires the conductor role AND an explicit project grant together; star scope never confers origination and literal * plus the unreconciled sentinel are rejected as projects. Stored within the selected profile boundary only.
Lifecycle
Creates the quorum in open standing with its participant entries in one atomic operation and records an audit/history event. New quorums start open with participants pending.
Errors
Missing conductor role or missing explicit grant is denied. Missing or reserved project is rejected as invalid project. Unknown topic reads as unknown_topic. Malformed participant payload, too many participants, and unknown client refs are rejected. Unknown arguments are rejected. Failures create nothing.
Example
```json
{
"participants": "[{\"kind\":\"role\",\"ref\":\"EXECUTOR\"}]",
"primary_topic_id": "<uuid>",
"project": "shared-ops"
}
```
Result shape:
```json
{
"project": "shared-ops",
"quorum_id": "<uuid>",
"status": "open"
}
```
quorum_id is assigned by the server; the placeholder stands for the returned UUID.
Lists quorums the caller may see, optionally narrowed by project or standing. Call it to survey agreements before resolving, cancelling, or attaching lanes.
JSON object with quorums (entries carrying quorum_id, project, status, primary_topic_id, created_by, created and resolved stamps) and count.
Boundaries
Project scope AND participation filter silently; inaccessible quorums are omitted with no marker (conductor and star principals see all). Operates only inside the caller's profile boundary.
Lifecycle
Read-only. No standing changes and no history is recorded.
Errors
Unknown status values match nothing (empty result, no error). Unknown arguments are rejected. Limit is clamped to 1..100.
Marks one quorum participant's standing. Call it when a required client or role joins, fulfils, or withdraws from the agreed-what so quorum visibility and later resolution reflect reality.
Required: `quorum_id`, `kind`, `ref`, `state`.
- `quorum_id`.
- `kind`.
Note: client or role. Client refs must name a live principal.
- `ref`.
- `state`.
Note: pending, joined, fulfilled, or withdrawn. pending is the birth standing; the other three are forward marks set here.
Result
JSON object with quorum_id, kind, ref, and state (the new participant standing).
Boundaries
Requires the conductor role in the current implementation. Applies only to open quorums inside the caller's profile boundary. Role refs are names; live debate binding is checked at read time, not at this call.
Lifecycle
Adds the participant or moves its standing (creation-or-change semantics) and records an audit/history event. Only open quorums accept marks.
Errors
Missing conductor role is denied. Missing or malformed identity reads as unknown_quorum. Non-open quorums are rejected as quorum not open. Bad kind or ref is rejected as invalid participant; bad standing as invalid state; dangling client refs as unknown client.
Resolves one open coordination quorum and records its decision and result references. A Quorum is what was agreed (Debate holds why). Call it when all lanes are terminal and the agreement must become durable.
Required: `quorum_id`.
Optional: `decision_ref`, `result_ref`.
- `quorum_id`.
- `decision_ref`.
Note: Optional pointer at the decisive debate message or verdict; reasoning is never duplicated into the quorum.
- `result_ref`.
Note: Optional pointer at the agreed artifact or outcome reference.
Result
JSON object with quorum_id and status (always resolved on success).
Boundaries
Requires the conductor role; the caller must also satisfy quorum visibility (project scope AND participation, with conductor and star override). Only open quorums resolve. Stored within the selected profile boundary only.
Lifecycle
Verifies every lane is terminal (done, failed, or cancelled), then marks the quorum resolved with a resolution stamp plus the two refs, and records an audit/history event. Resolved is terminal.
Errors
Missing conductor role is denied. Missing or inaccessible identity reads as unknown_quorum with no oracle. Non-open quorums are rejected as quorum not open. Resolving over non-terminal lanes is rejected as quorum has open lanes. Overlong refs are rejected as invalid refs.
Claimable work units and reviewable streams of work.
job_claim
Write · Advanced
Claims one queued or lease-expired job under an atomic single-winner lease. A Job is one executable unit in Mind Quorum layering (Quorum holds what was agreed, Lane carries the coordinated stream, Job is the unit an executor runs). Call it when an executor is ready for work.
JSON object with claimed (bool). When true, also job_id, kind, payload (empty string when absent), lease_until, and attempts. When false, no other keys.
Boundaries
Only entries whose project grants cover the caller are visible. Nothing available and nothing authorized answer identically, so callers cannot probe foreign projects. Attempts are bounded at 5; exhausted entries are skipped. Operates only inside the caller's profile boundary.
Lifecycle
Moves one queued or lease-expired entry to claimed with the caller as owner, a lease window, and an incremented attempt count. Expired-lease takeovers also record a reclaimed marker alongside the claim. Lost races emit nothing. The whole move fails atomically on error.
Errors
lease_seconds outside 1..3600 is rejected. Overlong kind is rejected. Unknown arguments are rejected. When nothing can be claimed the answer is claimed:false, never an error.
Example
```json
{
"kind": "delivery",
"lease_seconds": 300
}
```
Result shape:
```json
{
"attempts": 1,
"claimed": true,
"job_id": "<uuid>",
"kind": "delivery",
"lease_until": "<iso-8601>",
"payload": "{}"
}
```
lease_until is assigned by the server; the placeholder stands for the returned timestamp.
Completes a claimed job as completed or failed. Call it when execution of one Job unit has finished and its outcome plus result reference must become durable.
Required: `job_id`, `outcome`.
Optional: `result_ref`.
- `job_id`.
- `outcome`.
Note: completed or failed only.
- `result_ref`.
Note: Optional pointer at the produced artifact or verdict; kept as an opaque reference.
Result
JSON object with job_id and state (completed or failed). Idempotent replays add idempotent:true.
Boundaries
Owner path needs a live lease held by the caller; the conductor path may complete any claimed entry and skips the lease check. Out-of-scope entries read as missing with job not found vocabulary, so callers cannot probe foreign projects. Operates only inside the caller's profile boundary.
Lifecycle
Moves a claimed entry to completed or failed and records an audit/history event. Re-completing with the same outcome succeeds without change and without a further event (idempotent:true in that answer). Association with a lane never moves the claim gate. The delivery pump writes only job delivery, lease, and result facts, never lane decision or review standing.
Errors
job not found covers missing and out-of-scope identically. job is not claimed covers wrong standing. lease expired tells an owner to reclaim first. Re-completing a terminal entry with a different outcome is rejected as job already terminal. Bad outcome values are rejected.
Creates one durable executable job. A Job is one executable unit in Mind Quorum layering (Debate holds why, Quorum holds what was agreed, Lane carries the coordinated stream, Job is one executable unit). Call it when agreed work needs a durable unit an executor or the pump can claim.
Required: `kind`.
Optional: `lane_id`, `payload`, `project`.
- `kind`.
- `lane_id`.
Note: Optional. When given, the job inherits the lane-quorum project; an explicitly different project is rejected and an untagged job inherits the lane tag.
- `payload`.
Note: Opaque executor payload, carried verbatim.
- `project`.
Note: Optional. Untagged creation needs the explicit empty-string grant; star scope alone never suffices.
Result
JSON object with job_id (new UUID) and state (always queued on creation).
Boundaries
Requires the conductor role AND an explicit project grant together; star scope never confers origination and literal * is rejected as a project. Lane-bound jobs must match or inherit the lane-quorum project. Stored within the selected profile boundary only.
Lifecycle
Creates the job in queued standing and records an audit/history event through one atomic operation. No executor is assigned at creation; claim assigns the owner later.
Errors
Missing conductor role or missing explicit grant is denied with outside client scope vocabulary. Missing or overlong kind, overlong project or lane_id, unknown lane (lane not found), and project/lane mismatch are rejected. Unknown arguments are rejected. Failures create nothing.
Example
```json
{
"kind": "delivery",
"payload": "{\"uri\":\"example\"}",
"project": "shared-ops"
}
```
Result shape:
```json
{
"job_id": "<uuid>",
"state": "queued"
}
```
job_id is assigned by the server; the placeholder stands for the returned UUID.
Releases a claimed job back to queued for another executor to claim. Call it when the current holder cannot finish the unit and it must return to the pool without losing its attempt history.
JSON object with job_id and state (always queued after release).
Boundaries
Owner or conductor only. Out-of-scope entries read as missing with job not found vocabulary. Attempts are kept, so a released entry remains closer to its retry bound. Operates only inside the caller's profile boundary.
Lifecycle
Returns a claimed entry to queued, clearing owner and lease while keeping attempts, and records an audit/history event. The entry becomes claimable again. The whole move fails atomically on error.
Errors
job not found covers missing and out-of-scope identically. Entries that are not claimed are rejected as job is not claimed. Unknown arguments are rejected.
Creates one coordinated work lane under an open quorum. A Lane is the coordinated stream in Mind Quorum layering (Debate holds why, Quorum holds what was agreed, Lane carries the stream, Job is one executable unit). Call it when agreed work needs a named stream that later jobs follow.
Required: `quorum_id`.
Optional: `assignee_client_id`, `base_ref`, `branch`, `depends_on`, `repo`, `task_id`, `worktree`.
- `quorum_id`.
Note: Must name an open quorum the caller may access.
- `assignee_client_id`.
Note: Optional; when given must name a live principal.
- `base_ref`.
- `branch`.
- `depends_on`.
Note: Optional; when given must name a lane of the same quorum.
- `repo`.
- `task_id`.
Note: Optional; when given must name a live task.
- `worktree`.
Result
JSON object with lane_id (new UUID), state (always open on creation), and project (inherited from the quorum).
Boundaries
Requires the conductor role plus an explicit grant on the quorum project in the current implementation; star scope never confers it. The lane inherits the quorum project immutably (no project argument exists by design). Stored within the selected profile boundary only.
Lifecycle
Creates the lane in open standing under the named quorum and records an audit/history event in one atomic operation. Outward links are validated now, at creation.
Errors
Missing conductor role or missing explicit grant is denied. Missing, malformed, or inaccessible quorum reads as unknown_lane with no existence oracle. Non-open quorum, unknown client, missing task (task not found), cross-quorum depends_on, and overlong fields are rejected.
Example
```json
{
"assignee_client_id": "worker-1",
"branch": "feat-x",
"quorum_id": "<uuid>",
"repo": "saphira-memory"
}
```
Result shape:
```json
{
"lane_id": "<uuid>",
"project": "shared-ops",
"state": "open"
}
```
lane_id is assigned by the server; project in the answer is the inherited quorum project.
Lists lanes the caller may see, optionally narrowed by quorum, project, or execution standing. Call it to survey coordinated streams before moving, reviewing, or merging them.
JSON object with lanes (entries carrying lane_id, quorum_id, project, state, review_state, merge_state, assignee, task_id, result_commit, created and changed stamps) and count.
Boundaries
Project scope AND parent-quorum participation filter silently; inaccessible lanes are omitted with no marker (conductor and star principals see all). Operates only inside the caller's profile boundary.
Lifecycle
Read-only. No standing changes and no history is recorded.
Errors
Unknown state values match nothing (empty result, no error). Unknown arguments are rejected. Limit is clamped to 1..100.
Records one lane merge outcome. Call it when a lane's branch result must be memorialized as merged, conflicted, or skipped after execution and review truth exist.
Required: `lane_id`, `outcome`.
Optional: `result_commit`.
- `lane_id`.
- `outcome`.
Note: merged, conflicted, or skipped. merged needs execution done plus review approved plus a non-empty result_commit; conflicted needs in_progress or done; skipped needs a non-done lane.
- `result_commit`.
Note: Commit pointer recorded with the merge; required for merged.
Result
JSON object with lane_id and merge_state (the recorded outcome).
Boundaries
Requires the conductor role; the caller must also satisfy parent-quorum visibility (project scope AND participation, with conductor and star override). The pump never writes merge standing; it writes only job delivery, lease, and result facts. Operates only inside the caller's profile boundary.
Lifecycle
Records the merge outcome plus the commit pointer and records an audit/history event. Merge records; it never executes and never moves execution or review standing.
Errors
Missing conductor role is denied. Missing, malformed, or inaccessible lane reads as unknown_lane with no oracle. Gates that are not met are rejected as lane not mergeable. Unknown arguments are rejected.
Reviews one lane for coordination only. Call it to submit finished lane work for approval, or to approve or request changes, without moving the lane's execution standing.
Required: `lane_id`, `decision`.
- `lane_id`.
- `decision`.
Note: submit, approve, or request-changes. submit is by conductor or the lane assignee; approve and request-changes are conductor-only.
Result
JSON object with lane_id and review_state (pending, approved, or changes_requested).
Boundaries
Verdict privilege is checked before entry access, so denied verdicts reveal nothing about the entry. Submit loads the entry first (parent-quorum visibility applies) and then needs conductor standing or assignee match. Review records authority and never moves execution standing. The pump never writes review standing.
Lifecycle
Moves review standing (empty or changes_requested to pending on submit; pending to approved or changes_requested on verdict) and records an audit/history event. Completing a job never moves review standing.
Errors
Missing, malformed, or inaccessible lane reads as unknown_lane. Submit by a caller who is neither conductor nor assignee is denied. Re-submit over a decided review is rejected as review closed; verdicts with nothing pending are rejected as nothing under review.
Moves one lane along its execution axis. Call it when coordination must advance a Lane stream (the coordinated-work layer between agreed Quorum and executable Jobs) and record that move durably.
Required: `lane_id`, `state`.
- `lane_id`.
- `state`.
Note: open, in_progress, done, failed, or cancelled. Legal moves: open to in_progress; in_progress to done or failed; any non-terminal standing to cancelled; terminal standings are immutable.
Result
JSON object with lane_id and state (the new execution standing).
Boundaries
Requires the conductor role (manual conductor path; the future pump writer shares the same move validator). The caller must also satisfy parent-quorum visibility. Operates only inside the caller's profile boundary.
Lifecycle
Moves execution standing under a guarded compare-and-hold and records an audit/history event. Review and merge standings are untouched.
Errors
Missing conductor role is denied. Missing, malformed, inaccessible, or badly named target standing reads as unknown_lane with no oracle. Illegal moves are rejected as illegal lane transition, including races where the standing moved underneath.
Reviewing inbound material before it becomes agreed state.
inbound_approve
Write · Conductor only
Approves staged inbound items into agreed state. Tasks insert as fresh entries through real creation; knowledge merges additively with type conflicts skipped and relations gated on both endpoints.
Required: `kind`, `item_ids`.
Optional: `reason`.
- `kind`.
Note: tasks or knowledge.
- `item_ids`.
Note: JSON array string of namespaced ids (t:N tasks, e:N entities, r:N relations); must match kind. Every id must exist and be pending or the whole call fails with nothing applied.
- `reason`.
Note: Recorded as the decide reason on each item.
Result
Object with approved array (id plus task_id for tasks) and skipped array (id plus reason).
Boundaries
Conductor role required. Approved content is created under the approver identity within the selected profile boundary, so approver scope and validation apply exactly as for direct creation.
Lifecycle
Strict eligibility pre-check first, then entities before relations: tasks are created fresh through the real creation path; entities merge additively (type conflicts skip, only new observations merge); relations need both endpoints pre-existing or approved in the same batch. Each item is marked decided with a decision history event.
Errors
Non-conductor callers get a requires-conductor error payload. Invalid kind, malformed ids, kind/id mismatch, or any ineligible item fails the whole call with nothing applied. Type conflicts and missing relation endpoints land in skipped, not errors.
Optional: `kind`, `limit`, `project`, `quorum_id`, `state`.
- `kind`.
Note: Empty means both; tasks or knowledge narrows. Anything else returns the empty shape.
- `limit`.
Note: Defaults 20, clamped 1 to 100.
- `project`.
- `quorum_id`.
Note: Narrows to one quorum; participation still applies.
- `state`.
Note: Defaults pending; approved and rejected are queryable. Anything else returns the empty shape.
Result
Object with tasks, entities (each with observations plus a set-difference diff: new_entity, merge with new_observations, or type_conflict), relations (each with from_present/to_present endpoint flags), and count.
Boundaries
Project scope AND quorum participation filter silently: conductor and star see all, others see only entries whose quorum they participate in or whose target project is granted. Untargeted entries stay override-only.
Lifecycle
Pure staged-content read; records nothing. The Relay is the only writer and nothing auto-imports.
Errors
Wrong-typed arguments rejected by validation. Unknown kind or state values return the empty shape, not an error.
Required: `kind`, `item_ids`.
Optional: `reason`.
- `kind`.
Note: tasks or knowledge.
- `item_ids`.
Note: JSON array string of namespaced ids (t:/e:/r:) matching kind; every id must exist and be pending or the whole call fails.
- `reason`.
Note: Recorded as the decide reason on each item.
Result
Object with rejected array of id entries.
Boundaries
Conductor role required. Operates inside the selected profile boundary; rejection changes only item state, never content.
Lifecycle
Marks each item rejected with decider, timestamp, and reason plus a decision history event. Content entries are untouched.
Errors
Non-conductor callers get a requires-conductor error payload. Invalid kind, malformed ids, kind/id mismatch, or any ineligible item fails the whole call with nothing applied.
Status and health checks for a session and the service.
doctor
Read · Ordinary
SHAMPOO-wide health diagnostics in nine grouped sections with typed checks. Call it to distinguish ok, warn, fail, and unknown before investigating or repairing.
Optional: `detail`, `sections`.
- `detail`: summary | full.
Note: summary omits per-check detail text; full includes it.
- `sections`: CSV section filter; empty runs all.
Note: CSV filter using exact section names (config, database, auth, jobs, pump, wake, ledger, readiness, invariants); empty runs all.
Result
Object with sections array; each section has a name and checks with id, status (ok, warn, fail, unknown), severity, summary, and detail (full mode only).
Boundaries
Read-only across the selected profile; secret material is never disclosed (presence and mode only). UNKNOWN means unobservable, never unhealthy.
Lifecycle
Pure diagnostic read; records nothing and changes nothing.
Errors
Wrong-typed arguments rejected by validation. Unknown section names are silently skipped. Backend failures surface as transport errors.
Licence compliance information for this SHAMPOO copy. Call it to see whether the copy presents FREE or a valid paid artefact; the answer never changes what the software can do.
Object with licence (FREE or VALID PERPETUAL LICENCE), artefact_present, and legacy_preset (display-only guess for older artefacts).
Boundaries
Compliance and display information only: the artefact never unlocks code and its absence never refuses. The legacy preset guess is display compatibility, never authority.
Lifecycle
Pure read; zero writes by construction.
Errors
Takes no arguments; backend failures surface as transport errors.
Object with product, version, backend, database, profile, caller, role, visibility (self or global), caller-filtered counts, tools_total, and tools_debate.
Boundaries
Counts are filtered to caller visibility: star sees global with an explicit global marker, others see only granted projects; debates count via owned bindings and inbox/wake numbers are star-only, so no metadata leaks across scopes.
Lifecycle
Pure read; zero writes by construction.
Errors
Takes no arguments; backend failures surface as transport errors.
Enrolment, handshakes, revocation and session administration.
handshake_challenge
Write · Internal
Begins a hybrid session handshake and returns a one-time challenge with the server's ephemeral key material. Spoke machinery only; completion must run on the same connection.
Required: `ref_kind`, `ref_id`.
Optional: `client_instance_id`, `client_kind`, `client_name`, `client_version`, `transport`, `user_agent`.
- `ref_kind`.
Note: One of bootstrap, ticket.
- `ref_id`.
Note: Shape-checked only here; validity is never decided until complete, so refs cannot be probed.
- `client_instance_id`.
Note: Optional client metadata stored with the challenge slot; never authority.
- `client_kind`.
Note: Optional session kind (client or spoke), bound into the session row at complete; absent means legacy direct. Invalid values fail the challenge.
- `client_name`.
Note: Optional client metadata stored with the challenge slot; never authority.
- `client_version`.
Note: Optional client metadata stored with the challenge slot; never authority.
- `transport`.
Note: Optional client metadata stored with the challenge slot; never authority.
- `user_agent`.
Note: Optional client metadata stored with the challenge slot; never authority.
Result
Object with challenge_id, server_ecdh and server_kem public material, suite, and expires_in seconds.
Boundaries
Pre-authentication call on the same connection that must later complete. Challenge slots are single-process, bounded (64), and expire after 300 seconds. Ordinary users never call this directly; spokes do.
Lifecycle
Holds one single-use challenge with fresh ephemeral P-256 plus ML-KEM-768 public material. The slot is consumed by exactly one complete attempt, success or failure.
Errors
Bad ref shape, invalid client kind, overlong metadata, or a full challenge heap deny generically as a normal handshake failed payload. No failure leaks whether a ref exists.
Completes a hybrid handshake: derives the shared secret, verifies the credential envelope, and returns a sealed session response. Single use per challenge; any failure denies generically.
Required: `challenge_id`, `client_ecdh`.
Optional: `envelope_ct`, `envelope_iv`, `envelope_tag`, `kem_ct`, `profile`.
- `challenge_id`.
Note: Single-use; unknown, reused, or expired ids deny generically.
- `client_ecdh`.
Note: Caller ephemeral P-256 public material; exact length required.
- `envelope_ct`.
Note: Credential envelope ciphertext carrying a bootstrap or ticket credential.
- `envelope_iv`.
Note: Envelope nonce; exact length required.
- `envelope_tag`.
Note: Envelope tag; exact length required.
- `kem_ct`.
Note: ML-KEM-768 ciphertext; exact length required.
- `profile`.
Note: Requested profile boundary only, never authority: unknown names deny like bad credentials, and the credential check runs against the selected profile's own grants.
Result
Sealed response envelope object with iv, ct, and tag; inside are session_id, client_id, expires_at, session credential, bound profile, and (ticket path only) the new bootstrap secret.
Boundaries
Pre-authentication call bound to the challenge's connection. Ticket enrolment burns the ticket, provisions the principal, and opens the session atomically; grants come from server-held entries only, never caller claims. Ordinary users never call this directly.
Lifecycle
Derives the hybrid secret over the full transcript, opens the credential envelope, verifies a bootstrap or ticket credential, opens a 1-hour registry session, mints its credential, and seals the session response. Post-commit failures revoke the just-made session rather than leaving it orphaned.
Errors
Every failure shape (bad challenge, bad key lengths, transcript mismatch, envelope failure, unknown credential, full key heap) denies generically as handshake failed with no oracle.
Example
```json
{
"challenge_id": "<uuid>",
"client_ecdh": "<b64u>"
}
```
Result shape:
```json
{
"ct": "<b64u>",
"iv": "<b64u>",
"tag": "<b64u>"
}
```
Real calls also carry kem_ct and the envelope_iv/ct/tag triplet; the sealed session response is an iv/ct/tag envelope.
Required: `user`, `projects`.
Optional: `reason`, `role`, `ticket_kind`, `ttl_seconds`.
- `user`.
Note: Server-side grant subject, required.
- `projects`.
Note: Server-side grant, required; bound into the ticket and never taken from the later handshake caller.
- `reason`.
- `role`.
Note: agent or conductor; defaults agent.
- `ticket_kind`.
Note: Optional designation for spoke enrolment (spoke or standard, default standard); stored on the ticket. Installations that disallow spokes refuse spoke-kind issuance.
- `ttl_seconds`.
Note: Defaults 3600.
Result
Object with ticket_id, secret (shown once; deliver out-of-band), and a note.
Boundaries
Conductor role required. The grant (user, role, projects) is fixed server-side at issue; the secret travels only inside the later encrypted bootstrap envelope.
Lifecycle
Persists a one-time ticket bound to the grant. The secret is shown once at issue; redemption burns the ticket and provisions the principal.
Errors
Non-conductor callers get a requires-conductor error payload. Missing user/projects, bad role/TTL/kind, or spoke-kind issuance where spokes are disallowed fail as a ticket issue failed payload.
Optional: `client_id`, `limit`, `status`.
- `client_id`.
Note: Narrows to one principal; empty means all.
- `limit`.
Note: Defaults 20, clamped 1 to 100.
- `status`.
Note: Narrows by active, revoked, expired, or ended; unknown values match nothing.
Result
Object with sessions array (principal, status, generation, timestamps, transport peer, client application/version/instance, revocation state, profile) and count.
Boundaries
Conductor role required. Returns audit metadata only; no key material exists in the registry by construction.
Lifecycle
Pure registry read; records nothing.
Errors
Non-conductor callers get a requires-conductor error payload. Unknown status values return the empty shape, fail-closed.