Stele
Server Details
Shared project memory for AI coding agents: decisions, lessons, risks and tasks in one graph.
- Status
- Healthy
- Uptime
- 93.7% over 25 days
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- Repository
- Stele-Dev/stele-mcp-connector
- GitHub Stars
- 0
TDQS
Scored across 20 tools
Descriptions work hard to draw boundaries (fetch vs show_node vs inspect vs recall vs search vs list), and those overlaps are explicitly cross-referenced. However, the composite tools (`destructive`, `organize`, `inspect`) bundle loosely related actions whose placement is arbitrary: cancel_task sits in `destructive` while complete/reopen sit in `task`, unlink is in `destructive` while link is in `node`, retire_objective is in `destructive` while achieve is in `objective`. An agent must memorize where each action lives, and `destructive` mixes a read-only dedupe scan with irreversible operations.
There is a readable implicit convention (bare resource noun = multi-action tool, verb_noun = single-purpose tool such as list_tasks, show_node, open_stele), but it is applied unevenly. `destructive` names a risk category rather than a resource, `organize` is an odd verb-noun hybrid holding component creation plus feedback and pause_rag, and `list` vs `list_tasks` splits one concept across two naming styles.
Twenty top-level tools is heavy for a project-graph server, and the effective surface is far larger once the action parameters are counted (destructive alone exposes 14 actions). The domain is genuinely broad (tasks, knowledge, docs, objectives, workspaces, timeline, comments), so most tools are justified, but consolidation is warranted.
Coverage is strong: full task lifecycle, knowledge capture/review/dedupe, documents and diagrams, objectives and milestones, components, topics, workspaces, edges, versions, comments, timeline, and provenance. Gaps are modest — no hard node deletion (only soft tombstoning and version restore), no paging on search, and `pause_rag` is a kept-for-compatibility no-op.
Available Tools
20 toolscommentComment on a nodeADestructiveInspect
Discuss on a node. action = add | update | resolve (mark a thread settled) | delete (soft tombstone; replies are preserved). Replies are one level deep: reply to a top-level comment only. Read a node's comments with list(what="comments").
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, and the description adds real behavioral detail beyond them: delete is a soft tombstone with replies preserved, and replies are one level deep so replying to a reply is rejected. It does not mention the privacy-bypass write path, which lives only in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler, and the core purpose plus action list is front-loaded before the two constraints. Every clause carries information the agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and the annotations cover the safety profile. For a multi-action tool the description covers the action semantics, the reply-depth restriction, and the read alternative — enough to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description genuinely adds meaning to the action enum values ('resolve' = mark thread settled, 'delete' = soft tombstone), whereas the schema only says 'Run the delete action.' That extra semantic content lifts it above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a concrete resource (comments on a node) and enumerates the four action modes (add/update/resolve/delete), each with a short gloss. Combined with the pointer to list(what="comments") for reading, an agent can distinguish this from sibling tools without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear per-action guidance ('resolve (mark a thread settled)', 'delete (soft tombstone)') and routes reading to the alternative list(what="comments"). It also states the one-level-deep reply constraint, which is a usage rule. It stops short of saying when to comment versus other node-mutation tools, so it is clear context rather than full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
destructiveConsequential actionsADestructiveInspect
Operations that require extra caution or confirmation. dedupe_knowledge is a read-only candidate scan; the remaining actions are consequential. Action names carry their resource because this tool spans resources. action = cancel_task | force_claim_task | dedupe_knowledge | dedupe_pair | unlink | restore_node_version | retire_objective | merge_workspace | cherry_pick_workspace | archive_workspace | archive_component | merge_topics | run_loop_attempt | doctor. run_loop_attempt executes the exact declared shell command supplied in the approval arguments with a scrubbed environment when trusted local repository execution is available; hosted MCP fails closed.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations already declaring destructiveHint=true, readOnlyHint=false, and openWorldHint=true, the description adds meaningful context: confirmation/caution expectations, the read-only nature of dedupe_knowledge, and run_loop_attempt's execution with a scrubbed environment under trusted local execution, failing closed on hosted MCP. It does not detail what each destructive action destroys or any auth/rate-limit behavior, keeping it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the safety/confirmation framing and then supplies the action enumeration, which is necessary for a multiplexer spanning many resources. It is dense but not padded, and the run_loop_attempt caveat is appropriately included. Slightly more structure could improve scanability across 14 actions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists and annotations cover the safety profile, the description provides enough context to understand the tool's overall purpose, the read-only dedupe_knowledge exception, and the special run_loop_attempt execution model. It does not enumerate what each destructive action does beyond its name, but the resource-bearing action names and rich schema make this reasonably complete for selection.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the detailed anyOf schema already documents per-action parameters thoroughly. The description adds only limited parameter-level meaning, mainly clarifying that action names carry resource context and that run_loop_attempt uses the exact declared shell command. That is a baseline 3 given the schema already does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly frames the tool as a collection of consequential actions requiring caution or confirmation, and lists every action name. It distinguishes the tool from siblings by noting that action names carry their resource because the tool spans resources, and it calls out dedupe_knowledge as a read-only exception. The only slight gap is that the opening phrase is abstract rather than a direct verb+resource statement, but the action list resolves that.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when the tool is relevant: operations requiring extra caution or confirmation. It also gives a specific exception for dedupe_knowledge (read-only candidate scan) and a condition for run_loop_attempt (trusted local repository execution; hosted MCP fails closed). It does not name alternative tools or provide broader when-not-to-use guidance, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
documentCreate documents and diagramsAInspect
Create long-form artifacts. action = create (a document — a plan, spec, or imported file) | create_diagram (a d2 diagram node). For create, pass the body inline or a repo-relative file_path for the server to read. file_path works only on a local connection; over a hosted connection pass the body inline. Recall and search surface a document by title only; read its body with fetch.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare write (readOnlyHint=false) but non-destructive, so the bar for safety disclosure is partly met; the description adds substantive behavioral context beyond them — the console-only limitation of file_path, that hosted connections require inline bodies, and that branch-scoped writes are invisible to collaborators. It does not mention the privacy-flag/blocking flow, which is a meaningful write-path behavior covered only by the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the purpose and action split before the file_path caveat and the read-with-fetch pointer. Dense but every clause carries information; only the trailing sentence about recall/search sits slightly apart from the create-focused opening.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and annotations cover the safety profile. The description covers body sourcing, connection constraints, and the read-back path. The notable omission is the privacy-flag interception workflow (privacy_bypass/privacy_judgement_id), which affects whether a write succeeds and is only discoverable in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; every parameter including file_path, scope, and the privacy_* fields is already documented in schema. The description adds the mutually-exclusive framing of body vs file_path and the local-vs-hosted execution constraint, but little else beyond what the schema states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ("Create long-form artifacts") and immediately enumerates the two actions with what each produces (a document — plan/spec/imported file — vs a d2 diagram node). It also distinguishes the creation surface from the read surfaces (recall, search, fetch), so the agent can tell it apart from siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: use create for documents, create_diagram for d2 nodes, and use `fetch` to read a body since recall/search only surface titles. It also states the file_path-vs-inline condition tied to local vs hosted connections. Missing an explicit statement of when NOT to use this tool versus knowledge/task/node creation siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetchFetch a node in fullARead-onlyInspect
Fetch one node in full by its shareable id (TASK-N/KNOW-N/COMP-N/DOC-N/DIAG-N/EPIC-N/NODE-N), with its edges — this is how you read a full body (recall/search return summaries). Pass version to fetch a historical snapshot instead of the current one.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any). | |
| version | No | Fetch this historical version instead of current. | |
| project_slug | Yes | Project slug, e.g. "acme-app" — list(what="projects") returns yours. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The fetched Stele node and its related metadata. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this a safe, non-destructive, non-open-world read. The description adds real behavioral context beyond that: it returns the node with its edges, and version switches retrieval to a historical snapshot rather than the live record. It stops short of mentioning auth or permission requirements, which keeps it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly packed sentences with zero filler; the core action and its id forms come first, and the read-full-body guidance and version behavior follow in priority order.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read tool with an output schema (so return values need no explanation), annotations covering safety, and full schema coverage, the description supplies everything an agent needs: what it returns, how to call it, and when to prefer it over siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema lacks: it lists additional id prefixes (DIAG-N, EPIC-N) not present in the id property description, and clarifies that version retrieves a snapshot 'instead of the current one.'
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Fetch one node in full by its shareable id ... with its edges') and enumerates the accepted id prefixes. It also differentiates itself from siblings by contrasting with recall/search, which return summaries only.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes the agent: use this (not recall/search) when you need a full body, and use the version parameter when you want a historical snapshot instead of the current one. Both the when and the alternative are named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
inspectInspect a node in depthARead-onlyInspect
Look at what surrounds one node or the whole project: its connections, its history, and the project's state. Read-only. Use fetch for a node's own content, search to find nodes by their words, and recall to gather context for a question; use inspect when you need more than the node itself. Actions: context: a scored neighborhood of decisions, risks and tasks connected to a node, query or topic; read it before changing something it touches. provenance: the cited chain of why a node exists and what it superseded; check it before relying on or overriding a decision. briefing: the project's working rules, root components and suggested first calls; call it once at the start of a session. claim_history: who claimed and released a task, and when; check it before force-claiming a task that looks abandoned. workspace: one workspace overlay's details. stats: project totals, overdue commitments, likely-abandoned tasks and recent activity, in one call. loop: verify a task's protected check paths against local Git state; local only, and it fails closed. rag_status: what automatic topic recall is focused on now; local only.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/destructiveHint/openWorldHint, so the 'Read-only' sentence is mostly redundant. The description does add real behavior beyond the annotations: loop 'fails closed' and is local-only, rag_status is local-only, node_id 'Overrides query', and each action carries an implied risk profile. Strong additions, though a couple of lines simply restate the read-only hint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and read-only status, then a routing sentence, then a scannable action-by-action list. It is long, but nearly every clause in the action list carries routing or safety value. Slightly dense with eight actions in one paragraph, which costs a point.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an eight-action tool, the description covers each action's purpose, when to invoke it, and its local/fail-closed constraints. An output schema exists, so return-value explanation is correctly omitted. Nothing an agent needs to select an action is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (depth, seed_limit, max_entries, hub_edge_limit, max_hops, intent, environment, base/branch, consult_session) is already documented in place. The description adds action-level intent, which helps choose which params apply, but contributes no syntax or format meaning beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource up front ('Look at what surrounds one node or the whole project: its connections, its history, and the project's state') and then enumerates eight named actions. It explicitly distinguishes itself from siblings by name: fetch for a node's own content, search to find nodes, recall to gather question context, inspect when you need more than the node itself. An agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives per-action when-to-use guidance and even sequencing: 'read it before changing something it touches' (context), 'check it before relying on or overriding a decision' (provenance), 'call it once at the start of a session' (briefing), 'check it before force-claiming a task that looks abandoned' (claim_history). Alternatives are named explicitly. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
knowledgeCapture knowledgeAInspect
Capture or curate knowledge. action = create (file a typed fact — decision, lesson, risk, …) | review (mark a node re-verified against current reality). create files one fact under a component; it requires a lifecycle choice (expires_on_task, expires_at, due_at — pass null to skip each), and durable decisions and architecture usually skip all three. Merging duplicate knowledge lives in the destructive tool.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=false, destructiveHint=false, openWorldHint=true), and the description adds substantive behavior: that durable knowledge retires by supersession rather than dates, and that merging lives elsewhere. It does not restate branch-scope invisibility in the description itself, but that is covered in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The action split is front-loaded and every clause carries information, but the description is dense and packs three distinct ideas into one paragraph, making it slightly harder to scan than a cleanly separated version.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema and rich annotations, the description need not explain return values, and it covers the two-action branching plus the supersession rule. It is complete enough to call the tool correctly, with only minor room to note reading alternatives.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter thoroughly. The description only frames the lifecycle trio as a group ('pass null to skip each'), adding no format or semantic detail beyond what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names both actions with specific verbs and resources: create files a typed fact, review marks a node re-verified. It also routes duplicate-merging to the `destructive` sibling, so an agent can distinguish this tool from neighbors without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly frames when each action applies (create vs review) and gives a decision rule for lifecycle fields (durable decisions/architecture skip all three). It does not, however, state when not to use this tool at all, e.g. routing reads to recall/search.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
listList a collectionARead-onlyInspect
List a collection: what = nodes | components | topics | documents | diagrams | objectives | workspaces | comments | node_versions | projects, with per-what filters. what:"projects" is the project-less bootstrap (discover your slugs). Most collections page with limit/offset; a few return everything up to limit with no further paging.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, destructiveHint=false), so the bar is lower; the description still adds useful behavioral context about pagination: most collections use limit/offset while a few return everything up to a limit. That pagination asymmetry is not derivable from the annotations and materially affects how an agent should call it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, front-loaded with the `what` enumeration and followed by the two cross-cutting behavioral facts (bootstrap, pagination variance). Every sentence carries information, though the long inline `what` list is slightly run-on.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema and full annotation coverage, the description needn't document return values or safety; it covers the what-enumeration, filters, pagination variance, and bootstrap case. It is nearly complete, with only the absence of sibling routing keeping it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the description adds the cross-cutting list of valid `what` values (not exposed as a single top-level enum in the schema) and clarifies that `projects` is the project-less bootstrap. This meaningfully aids selection of the right sub-action beyond what the branch schemas alone convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Starts with a specific verb and resource ('List a collection') and then enumerates every valid value of `what`, so an agent knows exactly what this tool enumerates. It stops short of distinguishing itself from siblings like search, fetch, or list_tasks, so it is clear but not sibling-differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives one concrete routing hint ('what:"projects" is the project-less bootstrap (discover your slugs)'), which is real usage guidance, and notes the paging variance per collection. However, it never says when to use this versus search/fetch/show_node, so alternatives are left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tasksList tasksARead-onlyInspect
List tasks, most-recently-updated first, filterable by status, component, assignee, and kind — the roadmap/board view of work. It returns task cards without bodies unless include_body is true; read one task in full with fetch. In clients that display apps, the result also renders as a task board for the user, so summarize it rather than repeating every task.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Filter by task kind (e.g. 'epic' to list only containers). | |
| limit | No | Page size (default 25, max 100). | |
| offset | No | Skip N results (default 0). | |
| status | No | Filter to one task status. | |
| mine_only | No | Only tasks assigned to the signed-in user. | |
| component_id | No | Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any). | |
| include_body | No | Include body text (default false). | |
| project_slug | Yes | Project slug, e.g. "acme-app" — list(what="projects") returns yours. | |
| assignee_user_id | No | User uuid (NOT a shareable id). |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover safety (readOnlyHint=true, destructiveHint=false, openWorldHint=false), and the description adds real behavioral context beyond them: default sort order, that cards omit bodies unless include_body is true, and that clients render an app view. It doesn't mention pagination defaults or auth needs, but the added detail is substantive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, front-loaded with the core action and ordering, then the body-default caveat, then client rendering. Each sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value structure needn't be explained, yet the description usefully clarifies cards-vs-body behavior. Combined with annotations covering safety and a fully documented schema, nothing needed to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all nine parameters in detail. The description only restates the filter dimensions (status, component, assignee, kind) at a high level and adds no syntax or format detail beyond the schema, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource ('List tasks') with scope details: ordering (most-recently-updated first), filter dimensions, and the framing 'the roadmap/board view of work'. It explicitly distinguishes itself from the sibling `fetch`, which handles single-task reads.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: use this for listing filtered tasks, use `fetch` to read one task in full. It also gives client-specific guidance — summarize the rendered board rather than repeating every task — which is genuinely actionable when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
nodeEdit a nodeADestructiveInspect
Edit a node or its structure. action = update (edit fields/body) | move (re-file under a component or workspace) | link (add a typed edge between two nodes). Removing an edge (unlink) lives in the destructive tool.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, readOnlyHint=false, and openWorldHint=true, so the safety profile is carried by structured data. The description adds a useful boundary disclosure (edge removal is elsewhere), but says nothing about the approval pause, concurrency rejection, or the fact that `body` is a full replace — all of which live only in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero waste, and the action taxonomy is front-loaded before the sibling hand-off. The pipe-delimited action list is scannable and the exclusion clause closes the sentence usefully.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and a very rich input schema, the description only needed to explain the action dispatch — which it does. It is slightly thin on cross-action prerequisites (e.g., that lifecycle status changes must go through claim/complete/cancel), but that is documented per-parameter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema fully documents parameters and the baseline is 3. The description contributes only the action-level grouping (update/move/link) and does not add syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Edit a node or its structure') and enumerates the three action modes with one-line semantics each. It cleanly separates update/move/link, though it does not distinguish this edit surface from type-specific siblings like task, knowledge, or document.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives per-action routing ('move = re-file under a component or workspace') and one explicit exclusion: unlink lives in the `destructive` tool. It stops short of stating when to reach for this tool versus the type-specific siblings or the task lifecycle actions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
objectiveShape the roadmapADestructiveInspect
Shape the roadmap. action = create | add_milestone | attach (task → milestone) | move (task → objective) | detach | reorder | set_target | achieve. Retiring an objective lives in the destructive tool.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds real routing context by naming the sibling that owns the destructive retirement path, which the annotations alone would not convey. However, annotations declare destructiveHint=true, and the description never explains which of these eight actions is actually destructive (detach presumably breaks the task→milestone link) or whether it is reversible — leaving the agent with a tension between 'destructive lives elsewhere' and destructiveHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, both front-loaded with the highest-value information: the action vocabulary first, the destructive exclusion second. No filler and nothing an agent must scroll past to find the decision-relevant content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An eight-action tool with a rich discriminated-union schema and an output schema is reasonably served by an action list, and the schema carries body/scope/privacy semantics. But the description gives no sense of sequencing (create before add_milestone before attach), no confirmation of what achieve or reorder side effects are, and no warning that detach is the destructive path implied by the annotation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description earns extra credit by mapping operands the schema only implies: 'attach (task → milestone)' and 'move (task → objective)' clarify the direction of each relation, which is the key disambiguation between two otherwise similar actions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The title 'Shape the roadmap' alone is vague, but the description immediately enumerates the eight concrete actions (create, add_milestone, attach, move, detach, reorder, set_target, achieve) with parenthetical operand mappings, which pins the resource down as objectives/milestones. It clearly distinguishes itself from the `destructive` sibling by routing retirement there.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is one explicit exclusion — 'Retiring an objective lives in the `destructive` tool' — which is genuinely useful routing. But nothing says when to use create vs add_milestone, whether milestones must exist before attach, or how this relates to siblings like `task` and `organize` that also manipulate tasks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_project_boardProject boardARead-onlyInspect
Open the Stele project board beside this conversation, so the user can watch tasks while you work on them.
| Name | Required | Description | Default |
|---|---|---|---|
| project_slug | No | Project to open. Omit to open the user's most recently active project. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, openWorldHint=false and destructiveHint=false, so safety is covered. The description adds real context beyond that: this is a UI side-effect that opens a panel 'beside this conversation', which is a non-obvious behavior an agent should know before invoking it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with the action first and the rationale second. No filler, nothing wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-optional-parameter tool with no output schema and clear annotations, the description is nearly sufficient. It could mention what happens when the user has no projects or no board exists, but that is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single optional parameter already documents the 'omit to open most recently active project' fallback in the schema itself. The description adds no additional parameter meaning, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Open the Stele project board') and adds the intent of the action. It is distinguishable from the similarly-named open_stele sibling by naming the project board specifically, though it never explicitly contrasts the two.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The clause 'so the user can watch tasks while you work on them' implies the usage context (during task work, when the user wants visibility), but there is no explicit when-to-use guidance, no when-not, and no mention of an alternative such as open_stele.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_steleSteleARead-onlyInspect
Open the Stele app for the user: browse their projects, open tasks, recent activity, and search. Use it when the user wants to see or browse their project, not to answer a question.
| Name | Required | Description | Default |
|---|---|---|---|
| project_slug | No | Project to open. Omit to open the user's most recently active project. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description adds that this opens a client-facing UI for the user rather than returning data to the model, which is genuinely useful context, but it says nothing about focus behavior, state changes, or side effects on the user's session.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: the capability is front-loaded and the usage constraint follows immediately. No filler or repetition of the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-required-parameter, read-only UI opener with full schema coverage and no output schema, the description covers what the tool does and when to use it. The only real gap is sibling disambiguation against open_project_board.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single optional project_slug parameter is fully documented in the schema, including the omit-for-default behavior. The description mentions browsing "their projects" but adds no syntax, format, or default detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ("Open") and resource ("the Stele app") plus the surfaces it exposes (projects, tasks, activity, search), so the agent knows this launches a browsing experience. It does not, however, differentiate itself from the sibling open_project_board, leaving some ambiguity about which opener to pick.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear positive condition ("when the user wants to see or browse their project") and an explicit exclusion ("not to answer a question"). It stops short of naming the alternative tool to use for question-answering, but the routing intent is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
organizeProject housekeepingADestructiveInspect
Housekeeping: change how the project is organized. It never edits a node's content; use node, knowledge or task for that. Actions: add_component: create a durable area of ownership that knowledge and tasks attach to; list the existing components first to avoid a duplicate. rename_topic: rename a topic tag on every node that carries it, for typos and casing. feedback: report how well the memory helped this conversation: a 0–10 score, one strength, one hindrance, one improvement. pause_rag: kept for older clients; automatic topic recall runs only where the memory runs locally, so here it reports that recall is unavailable and changes nothing. Call recall with your question whenever you need context from the graph.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare destructiveHint=true, so the description's added disclosure that rename_topic rewrites the tag 'on every node that carries it' and that pause_rag 'changes nothing' is genuinely useful blast-radius information beyond the flags. The main gap is the privacy gating: the schema carries privacy_bypass/privacy_judgement_id/privacy_false_positive parameters, but the description never mentions that writes can be blocked by a privacy flag or how to proceed when they are.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core scope, then the negative boundary, then a compact per-action list — a sensible order with almost no wasted text. It is dense to the point of being run-on, and the closing recall sentence is a routing aid rather than part of this tool's contract, but neither costs the agent real effort.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-action dispatcher with ten parameters, the description covers all actions and an output schema exists so return values need no explanation. What's missing is the privacy-flag workflow — an agent hitting a privacy block on add_component gets that context only from the schema, not from the narrative.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and every parameter already carries an inline description, including required markers per action. The description's parameter content ('a 0–10 score, one strength, one hindrance, one improvement', 'rename a topic tag on every node') largely restates what the schema provides, so the baseline 3 applies with no meaningful added syntax or format detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource ('how the project is organized') and then enumerates the four actions with concrete verbs and objects: create a component, rename a topic tag across nodes, submit session feedback, pause recall. It also explicitly distinguishes itself from siblings ('It never edits a node's content; use `node`, `knowledge` or `task` for that'), so an agent can route correctly without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Every action carries a when-to-use cue: add_component says to list existing components first to avoid duplicates, rename_topic is scoped to typos and casing, feedback is for reporting how the memory helped, and pause_rag is flagged as a legacy no-op. It even routes the agent outward ('Call `recall` with your question whenever you need context'), which is exactly the alternative-selection guidance this dimension rewards.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
projectCreate a projectAInspect
Manage projects. action = create (bootstrap a new project and its root component, owned by you). This is the first thing to call when you have no project yet; discover existing ones with list what:"projects". New projects are private unless you pass visibility:"public"; plans allow a limited number of private projects and unlimited public ones.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Short display name for the project, at most 80 characters — a title, not a sentence. The project slug is derived from it and is hard to change later. | |
| action | Yes | Run the create action. | |
| repo_url | No | URL of the project's repository, if it has one. | |
| visibility | No | Who can read this project: "private" (default) — only you and members you invite; "public" — anyone can read its graph, including everything written to it later. Plans include a limited number of private projects but unlimited public ones, so where creation fails on a plan limit, the same call as public succeeds. Which one fits is the user's call, since it decides who can read their graph. | |
| description | No | One-line description of what the project is. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=false, destructiveHint=false, openWorldHint=true), and the description adds genuinely new behavior: default privacy, ownership by the caller, plan quotas on private vs unlimited public projects, and the retry-as-public workaround. It does not describe what else creation provisions beyond the root component, but that is minor given annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action and then layers usage, alternative tool, and privacy/quota behavior without padding. The only wasted phrase is the generic "Manage projects" lead-in.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and annotations carry the mutation/safety story. The description covers when to call it, the alternative, and the main failure mode (plan limits), leaving only deeper post-creation side effects unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the `visibility` parameter's schema entry is itself highly detailed about default and plan semantics, so the description largely restates it. Baseline 3 is appropriate — the description adds no format or constraint detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description pins the specific action (create) and resource (a project plus its root component), and explicitly contrasts with the sibling `list` for discovering existing projects. The vague opener "Manage projects" is slightly at odds with a tool whose action is a const of "create", but the operative sentence is precise.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"This is the first thing to call when you have no project yet; discover existing ones with list what:\"projects\"" gives an explicit trigger condition plus the named alternative tool. It also states the fallback condition — retry as public when a private-project plan limit fails.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recallRecall project contextARead-onlyInspect
One-call semantic + full-text recall over the graph — a ranked bundle of the decisions, tasks, risks, and documents most relevant to a question or topic. The default way to load context before acting; reach for search only for an exact-term or document-scoped lookup.
| Name | Required | Description | Default |
|---|---|---|---|
| depth | No | BFS depth from seeds (default 1, max 3). | |
| question | Yes | Natural-language question or topic — e.g. 'help me with monetization' or 'what's the state of auth?'. Embedded for semantic (meaning) search AND tokenized for keyword search; both seed sets are unioned, walked, and returned as one ranked neighborhood. | |
| seed_limit | No | Cap on seed nodes from each channel (default 10). | |
| max_entries | No | Cap on returned nodes (default 30, max 100). | |
| project_slug | Yes | Project slug, e.g. "acme-app" — list(what="projects") returns yours. | |
| hub_edge_limit | No | Per-hub edge follow cap (default 12). |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, destructiveHint=false and openWorldHint=false, so safety is covered. The description adds real behavioral context: it is a one-call union of semantic and keyword seeding followed by a graph walk returning one ranked neighborhood, which tells the agent what to expect in the result without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler, and the primary purpose is front-loaded before the alternative-tool routing. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and complete parameter coverage in the schema, the description only needs to convey purpose and tool selection, which it does fully. Nothing required to invoke the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description alludes to the seeding/walking behavior that the `question` parameter drives but adds no parameter-level syntax, defaults, or trade-offs beyond what the schema already documents for depth, seed_limit, max_entries and hub_edge_limit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('semantic + full-text recall over the graph') and enumerates the returned content ('decisions, tasks, risks, and documents'). It also explicitly distinguishes itself from the sibling `search`, so an agent can pick between them without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('the default way to load context before acting') and names the alternative with its selecting condition ('reach for `search` only for an exact-term or document-scoped lookup'). Both sides of the routing decision are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchSearch nodes and documentsARead-onlyInspect
Find nodes or documents by the words they contain. Nothing here matches by meaning, so use recall for a natural-language question. method "keyword" (default): ranked full-text search over active nodes; terms are AND-combined, and a multi-word query that matches nothing is retried once as an OR of its words. It returns cards, not bodies (default limit 5). method "exact": the matching line per node for a literal substring, or a POSIX regex with regex:true, across every status including done and archived (default limit 50). scope "documents": a line search of document bodies, a literal substring or, with regex:true, a JavaScript regex; it ignores method and type, and limit caps the number of documents unless max_docs is set. Every hit carries id, title, and url; limit is at most 50, with no paging. Read a full body with fetch.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | Limit node results to one type. | |
| limit | No | Max results. | |
| query | Yes | What to search for. | |
| regex | No | For method "exact": treat query as a POSIX regex. For scope "documents": treat query as a JavaScript regex. | |
| scope | No | "nodes" (default) or "documents". | |
| method | No | "keyword" (default, ranked relevance) or "exact" (literal term/pattern). | |
| max_docs | No | For scope "documents" only. Max docs in the response. Default 10. | |
| component_id | No | For scope "documents" only. Optional parent-component filter. Pass a COMP-N to scope the search to docs under that component. | |
| project_slug | Yes | Project slug, e.g. "acme-app" — list(what="projects") returns yours. | |
| context_lines | No | For scope "documents" only. Lines of surrounding context returned before and after each match. Default 1. | |
| case_sensitive | No | For scope "documents" only. Default false (case-insensitive). | |
| max_matches_per_doc | No | For scope "documents" only. Max matches surfaced per doc. Default 5. |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | Ranked search results with ChatGPT-compatible citation fields. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, destructiveHint false), and the description adds substantial context beyond them: hits return cards not bodies, default limits of 5/50/10, no paging, the AND-then-single-OR retry behavior, that exact search spans all statuses including done/archived, and that scope "documents" ignores method/type.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose sentence, and each subsequent sentence earns its place by disambiguating a mode or constraint. It is dense and packs several concepts per sentence, but for a 12-parameter tool with three modes it stays proportionate rather than padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 12 params, three enums, and an output schema, the description supplies everything the agent needs: mode selection, defaults, result shape (id/title/url), limit/paging behavior, and a cross-reference to `fetch`. Return-value detail is optional given the output schema, yet it is still summarized usefully.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents every parameter; however the description adds real meaning beyond it, notably the interaction semantics ("limit caps the number of documents unless max_docs is set", scope documents ignores method and type, limit max 50 with no paging). This exceeds the baseline-3 case where the schema does all the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource ("Find nodes or documents by the words they contain") and immediately names the sibling alternative `recall` for semantic queries, so an agent can route keywords vs. meaning without opening either schema. The method/scope breakdown further pins down what each invocation actually does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when NOT to use it ("Nothing here matches by meaning, so use `recall`") and names the alternative. It then gives selection conditions for each mode: keyword vs. exact, nodes vs. documents, and points to `fetch` for full bodies.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
show_nodeShow in SteleARead-onlyInspect
Show one Stele node (a task, knowledge entry, or document) to the user as a card in the conversation: its title, status, body, links, and provenance. Use it when the user wants to see a node. To read a node yourself, use fetch.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Shareable id of the node, e.g. TASK-42 or KNOW-7. | |
| project_slug | Yes | Project slug, e.g. "acme-app". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds valuable non-annotation behavior: the node is rendered as a card in the conversation and includes specific fields. It does not cover error cases or permission needs, but for a simple read tool this is largely sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences that front-load the core purpose and then immediately handle the sibling distinction. Every phrase earns its place with no redundant restatement of the tool name or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter read tool with full schema coverage and safety annotations, the description is complete: it states what is shown, how it is shown, and when to use it versus an alternative. No output schema exists, but the description already enumerates the card's visible fields, so nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with both parameters (id and project_slug) fully documented in the schema, including format examples. The description adds no additional parameter meaning beyond the structured field descriptions, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Show') and resource ('one Stele node'), enumerates node types (task, knowledge entry, document), and specifies what is displayed (title, status, body, links, provenance). It explicitly distinguishes itself from the sibling tool fetch, so an agent can select it correctly without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit usage condition ('Use it when the user wants to see a node') and names the alternative for a different use case ('To read a node yourself, use fetch'). This cleanly routes the agent between showing to the user versus reading for itself.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
taskTask lifecycleAInspect
Task lifecycle. action = create | claim | complete | release | reopen | force_release | loop_finalize. Claim before you start and complete when done — these route through the atomic claim path that records accountability; never flip a task's status by hand. loop_finalize keeps correctness, independent review, and intent/scope verdicts separate. Cancelling a task and force-claiming one live in the destructive tool.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false, destructiveHint=false, openWorldHint=true; the description adds real context beyond them — that claim/complete 'route through the atomic claim path that records accountability' and that status must not be flipped by hand, plus that loop_finalize keeps correctness, review, and intent/scope verdicts separate. It stops short of failure modes (e.g. what happens when a claim is already held), and it correctly does not contradict the destructiveHint=false annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four dense sentences, front-loaded with the action enum so the reader immediately sees the tool's surface. Every sentence carries information (workflow order, prohibition, loop_finalize semantics, sibling routing), though the loop_finalize clause is compact enough to read as cryptic without the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a seven-modal tool with an output schema present and 100% schema coverage, the description supplies the workflow policy the schema cannot: claim-before-start, complete-with-summary, and sibling routing for destructive actions. Remaining gaps (release/reopen semantics, error behavior) are secondary and partly inferable from the per-action schema titles.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the per-field semantics are already fully documented and the baseline would be 3. The description adds action-level meaning on top: it clarifies the ordering discipline for claim/complete and the non-obvious separation of concerns in loop_finalize, which is the meaning an agent most needs when choosing an action.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the specific resource (task) and enumerates all seven lifecycle operations verbatim: 'create | claim | complete | release | reopen | force_release | loop_finalize'. It also distinguishes itself from the closest sibling by explicitly routing 'Cancelling a task and force-claiming one' to the `destructive` tool. An agent can tell this is the write-side task lifecycle tool without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit workflow guidance: 'Claim before you start and complete when done', a prohibition ('never flip a task's status by hand'), and names the alternative tool for cancel/force-claim. It does not explain the conditions that select release vs reopen vs force_release vs loop_finalize, so the guidance is strong but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
timelineRead and journal the TimelineAInspect
The project's Timeline: automatic events captured on graph writes and task lifecycle actions, plus one-line custom entries. action = list (read it, newest first, keyset-paginated — not offset-based) | log (append a one-line custom entry). A logged entry is a short journal line, never reasoning or decisions — link the graph nodes that hold those instead. Logging can be turned off for a project; when it is, the call still succeeds but writes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, destructiveHint=false, openWorldHint=true, so the write-capable, non-destructive profile is already known. The description still adds genuine behavioral context the annotations cannot: keyset (not offset) pagination, the silent no-op when logging is disabled (call succeeds but writes nothing), and the privacy_bypass flow. Given the annotations carry the safety profile, a 3 is appropriate, though the silent-no-op disclosure pushes toward the upper end.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the resource and both actions with a compact pipe-separated structure, and every sentence carries information. There is mild duplication of the journal-vs-reasoning guidance between the description and the `message` param, which keeps it just short of 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be described. For a two-action tool the description covers the read path (ordering, pagination style), the write path (entry content constraints, silent no-op when disabled), and the privacy bypass flow — nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the schema already documents filtering fields. The description adds meaning beyond it by framing pagination as keyset rather than offset and by constraining what a logged `message`/`link` should contain (a journal line, never reasoning), which shapes how the write action's payload should be used.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names the resource (the project's Timeline) and both operations with specific verbs: `list` reads newest-first keyset-paginated entries, `log` appends a one-line custom journal entry. It also characterizes the content (automatic events from graph writes/task lifecycle plus custom entries), so an agent can distinguish it from siblings like `search` or `recall` without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when/when-not guidance for both actions: use it to read or journal, but never for reasoning or decisions — link graph nodes instead — and use `search`/`recall` for ranked pulls (echoed in the `query` param). It also warns that logging may be disabled at the project level, which changes what the agent should expect from the call.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
workspaceManage a workspaceADestructiveInspect
Manage a workspace overlay. action = create | update. Hosted MCP is stateless, so checkout-local activate/deactivate actions are not exposed; promoting or archiving a workspace lives in the destructive tool.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The action-specific result returned by Stele. |
| action | Yes | The action that produced this result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds non-obvious context beyond the annotations: the hosted server is stateless so activate/deactivate do not exist here. Annotations already flag destructiveHint=true, and the description's pointer to the `destructive` tool for promote/archive explains part of that. It falls short of clarifying why create/update themselves are marked destructive or what permission preconditions apply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight clauses: purpose, action set, then scope exclusions with the owning sibling tool. Front-loaded and nothing redundant — every sentence carries information the agent cannot get elsewhere.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a fully documented schema, an output schema, and annotations, the description covers the key gaps: which actions exist here and which do not. Minor omissions remain — permission requirements, and whether update requires an existing workspace — but nothing that blocks correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, including per-field notes for kind, branches, workspace, and the privacy_bypass flow, so the schema carries parameter meaning entirely. The description adds no parameter-level detail such as required fields per action. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the resource (workspace overlay) and enumerates the supported actions (create | update) explicitly. It also differentiates scope from the sibling `destructive` tool, which owns promote/archive. The verb 'manage' is generic, but the action enumeration removes ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names out-of-scope actions and routes them elsewhere: activate/deactivate is not exposed because hosted MCP is stateless, and promoting/archiving lives in the `destructive` tool. It does not spell out when to pick create vs update, but the action enum makes that self-evident.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
- Changed
project3 fields changed- added
Input schema / properties / description / maxLengthAdded value: +500 - changed
Input schema / properties / name / descriptionPrevious value: -"Project name. The project slug is derived from it and is hard to change later."New value: +"Short display name for the project, at most 80 characters — a title, not a sentence. The project slug is derived from it and is hard to change later." - added
Input schema / properties / name / maxLengthAdded value: +80
13 tool updates
- Changed
comment1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add\": Leave a comment on any node — the human↔agent steering channel; the next agent that reads the node sees it flagged unseen. Reply by passing parent_comment_id (replies are one level deep). Pass `anchor` to attach to a quoted span (inline comment); omit for node-level. Commenting watches the node, so future comments on it show as unread to you.", - "properties": { - "action": { - "const": "add", - "type": "string" - }, - "anchor": { - "additionalProperties": false, - "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", - "properties": { - "node_version": { - "description": "The node `version` the quote was taken from, as returned when you fetched the node.", - "exclusiveMinimum": 0, - "type": "integer" - }, - "offset": { - "description": "Rough char offset to disambiguate a repeated quote.", - "minimum": 0, - "type": "integer" - }, - "quote": { - "description": "The exact quoted substring to attach the comment to.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "node_version", - "quote" - ], - "type": "object" - }, - "body": { - "description": "REQUIRED for add/update — comment text (markdown).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "REQUIRED for add — the node to comment on (any node type).", - "type": "string" - }, - "parent_comment_id": { - "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", - "format": "uuid", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"update\": Edit the body of a comment you authored. AUTHOR-ONLY. Sets the 'edited' marker and does not re-flag the comment unread for others.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "body": { - "description": "New comment text.", - "minLength": 1, - "type": "string" - }, - "comment_id": { - "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", - "format": "uuid", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"resolve\": Resolve (or reopen) a comment thread. ANY project member may do this, not just the author. Resolve a steering comment once addressed so it stops nagging the next agent — resolved threads drop out of the [N comments, M unread] marker. Applies to the whole thread (resolving a reply resolves its parent).", - "properties": { - "action": { - "const": "resolve", - "type": "string" - }, - "comment_id": { - "description": "A comment_id in the thread (top-level or a reply).", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "resolved": { - "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", - "type": "boolean" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "resolved" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"delete\": Soft-delete a comment you authored — the row stays as a tombstone and replies remain intact (never orphaned). AUTHOR-ONLY; there is no hard delete.", - "properties": { - "action": { - "const": "delete", - "type": "string" - }, - "comment_id": { - "description": "The comment_id to delete.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "comment action \"add\".", + "properties": { + "action": { + "const": "add", + "description": "Run the add action.", + "type": "string" + }, + "anchor": { + "additionalProperties": false, + "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", + "properties": { + "node_version": { + "description": "The node `version` the quote was taken from, as returned when you fetched the node.", + "exclusiveMinimum": 0, + "type": "integer" + }, + "offset": { + "description": "Rough char offset to disambiguate a repeated quote.", + "minimum": 0, + "type": "integer" + }, + "quote": { + "description": "The exact quoted substring to attach the comment to.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "node_version", + "quote" + ], + "type": "object" + }, + "body": { + "description": "REQUIRED for add/update — comment text (markdown).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "REQUIRED for add — the node to comment on (any node type).", + "type": "string" + }, + "parent_comment_id": { + "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", + "format": "uuid", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "comment action \"update\".", + "properties": { + "action": { + "const": "update", + "description": "Run the update action.", + "type": "string" + }, + "body": { + "description": "New comment text.", + "minLength": 1, + "type": "string" + }, + "comment_id": { + "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", + "format": "uuid", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "comment action \"resolve\".", + "properties": { + "action": { + "const": "resolve", + "description": "Run the resolve action.", + "type": "string" + }, + "comment_id": { + "description": "A comment_id in the thread (top-level or a reply).", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "resolved": { + "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", + "type": "boolean" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "resolved" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "comment action \"delete\".", + "properties": { + "action": { + "const": "delete", + "description": "Run the delete action.", + "type": "string" + }, + "comment_id": { + "description": "The comment_id to delete.", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id" + ], + "type": "object" + } +]
- Changed
destructive1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"cancel_task\": Cancel a task (→ cancelled) with a required `reason` (appended to the body). For wrong/duplicate/obsolete tasks — not ones you merely couldn't finish (use release). Like complete, may surface nearby nodes to re-verify.", - "properties": { - "action": { - "const": "cancel_task", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for cancel_task/force_claim_task/restore_node_version/archive_component — shareable id of the target (TASK-N / KNOW-N / COMP-N / …).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reason": { - "description": "REQUIRED for cancel_task — why this task is being cancelled.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "reason" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"force_claim_task\": Emergency override: take over a task in any state. Use only when the prior claimer is abandoned (check destructive(action=\"doctor\") first) or the user asks. Returns the prior claimer's identity so you can notify them; concurrent force-claims are last-write-wins.", - "properties": { - "action": { - "const": "force_claim_task", - "type": "string" - }, - "dispatched_with": { - "description": "(force_claim_task only) Operator's full briefing prompt when this force-claim is from a dispatched sub-agent. Up to 16 KB.", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "parent_task_id": { - "description": "(force_claim_task only) Operator's claimed task id when this force-claim is from a dispatched sub-agent. Leave null for direct claims.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"dedupe_knowledge\": Find near-duplicate knowledge nodes by fuzzy title similarity (pg_trgm). Read-only — never deletes or merges; returns candidate pairs sorted by similarity for the caller to act on (use node(action=\"update\") to archive the dup side).", - "properties": { - "action": { - "const": "dedupe_knowledge", - "type": "string" - }, - "category": { - "description": "(dedupe_knowledge only) Restrict the scan to one knowledge category. Default: all categories.", - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - "limit": { - "description": "(dedupe_knowledge only) Max pairs to return. Default 20.", - "maximum": 200, - "minimum": 1, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "threshold": { - "description": "(dedupe_knowledge only) Similarity threshold (0–1). Default 0.7. Raise toward 1 for stricter matches.", - "maximum": 1, - "minimum": 0, - "type": "number" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"dedupe_pair\": Mark `drop_id` as a duplicate of `keep_id`. Atomically rewires every edge that touches the drop onto the keep (skipping conflicts on the unique (src,dst,relation) constraint), deletes any stragglers, archives the drop, and appends a `## Deduped` pointer to its body — all in one transaction. Returns the kept node and the pre-conflict count of edges that touched the drop. DESTRUCTIVE (the drop is archived). Find the candidate pair first (a duplicate scan or a search); this action merges one.", - "properties": { - "action": { - "const": "dedupe_pair", - "type": "string" - }, - "drop_id": { - "description": "REQUIRED for dedupe_pair — the duplicate to archive; its edges are rewired onto keep_id.", - "type": "string" - }, - "keep_id": { - "description": "REQUIRED for dedupe_pair — the node to keep.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "keep_id", - "drop_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"unlink\": Remove the (src, dst, relation) edge if it exists; no-op if it doesn't. To preserve what the edge USED to mean, prefer link with `supersedes` over removing it.", - "properties": { - "action": { - "const": "unlink", - "type": "string" - }, - "dst_id": { - "description": "REQUIRED for unlink — destination node shareable id.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "relation": { - "description": "REQUIRED for unlink — edge type to remove.", - "enum": [ - "relates-to", - "blocks", - "fulfills", - "contradicts", - "supersedes", - "caused-by", - "expires-on" - ], - "type": "string" - }, - "src_id": { - "description": "REQUIRED for unlink — source node shareable id.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "src_id", - "dst_id", - "relation" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"restore_node_version\": Restore a prior version's content as the new live content. APPEND, NEVER DESTRUCTIVE: restoring v2 over a live v3 writes v2 as a new live v4, snapshotting v3 into history first — both stay recoverable, so a bad restore is just another restore. Recorded with restored_from_version so history shows it was a restore, not a fresh edit.", - "properties": { - "action": { - "const": "restore_node_version", - "type": "string" - }, - "node_id": { - "description": "Node shareable id (e.g. KNOW-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "version": { - "description": "REQUIRED for restore_node_version — the version number to restore as the new live content.", - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "version" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"retire_objective\": Retire an objective: cancels it (its status reads as 'superseded') and, when a replacement objective is named, records a `supersedes` edge so the chain stays navigable.", - "properties": { - "action": { - "const": "retire_objective", - "type": "string" - }, - "objective_id": { - "description": "REQUIRED for retire_objective — the objective being retired (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "replaced_by": { - "description": "(retire_objective only) Optional replacement objective (TASK-N) — records a `supersedes` edge.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"merge_workspace\": Promote ALL of a workspace's scoped knowledge to trunk (shared truth), then mark the workspace 'merged'. Each promoted node flips to trunk and is stamped with provenance (which workspace + when). To graduate only SOME items, use destructive(action=\"cherry_pick_workspace\") instead. Permanent (domain) workspaces cannot be merged — they never graduate.", - "properties": { - "action": { - "const": "merge_workspace", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"cherry_pick_workspace\": Promote ONLY the named knowledge/tasks from a workspace to trunk, leaving everything else scoped and the workspace active — the partial-merge / salvage case. Each promoted node flips to trunk and is stamped with provenance (which workspace + when). The workspace itself is NOT changed (archive or merge it separately). Ids not scoped to this workspace are ignored.", - "properties": { - "action": { - "const": "cherry_pick_workspace", - "type": "string" - }, - "node_ids": { - "description": "REQUIRED for cherry_pick_workspace — short-ids of the nodes to promote (KNOW-N / TASK-N / DOC-N / …). Only items currently scoped to this workspace are promoted.", - "items": { - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "The workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace", - "node_ids" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"archive_workspace\": Archive a workspace (abandon it). RECOVERABLE — the workspace and ALL its scoped knowledge are kept as-is and can be reactivated; never deletes. Salvage anything worth keeping with destructive(action=\"cherry_pick_workspace\") FIRST — those items graduate to trunk and survive the archive.", - "properties": { - "action": { - "const": "archive_workspace", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"archive_component\": Archive a component (soft-delete). If it has any ACTIVE children (sub-components, knowledge, or tasks), `reparent_children_to` is REQUIRED — they move to that target. We never silently orphan or cascade-delete.", - "properties": { - "action": { - "const": "archive_component", - "type": "string" - }, - "node_id": { - "description": "Component shareable id to archive.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reparent_children_to": { - "description": "(archive_component only) Component to reparent active children under. Required if any active children exist.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"merge_topics\": Merge multiple source topics into a single target across every knowledge AND task node in the active project — like rename_topic once per source, in one transaction with the same dedup semantics. Sources equal to the target (or empty) are silently skipped. Members-only.", - "properties": { - "action": { - "const": "merge_topics", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "sources": { - "description": "REQUIRED for merge_topics — list of source topics to merge into target. Must be non-empty.", - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "target": { - "description": "REQUIRED for merge_topics — target topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "sources", - "target" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"run_loop_attempt\": Locally execute the task's declared Check in the worker branch's registered worktree, challenge its protected wrapper with a fresh nonce, accept only a versioned STELE_LOOP_EVIDENCE JSON line, persist exact counts/output on the task, and halt on the same failure twice or five failed attempts. Hosted MCP fails closed because it cannot execute the repository.", - "properties": { - "action": { - "const": "run_loop_attempt", - "type": "string" - }, - "base": { - "description": "Base git ref (default main).", - "minLength": 1, - "type": "string" - }, - "branch": { - "description": "Worker branch git ref (default HEAD).", - "minLength": 1, - "type": "string" - }, - "command": { - "description": "Exact declared check command. It is shown at the destructive approval boundary and must match the task contract.", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "Task whose declared Check and Check paths form the loop contract.", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "command" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"doctor\": Run health checks on the active project, returning grouped issue buckets (stale/archivable/expired/superseded knowledge, stale claims, oversized/dangling/orphaned components, overdue or unbound risks, unverified load-bearing decisions, duplicates, empty bodies, over-fulfilled tasks). Run periodically — especially before destructive(action=\"force_claim_task\") on a task you suspect is abandoned. fix=true applies safe auto-fixes (archives archivable/expired/superseded knowledge and expire-on-close risks); all other buckets are reported only. expensive=true (alias deep=true) also runs costly fuzzy/trigram near-duplicate detection, folded into the duplicates bucket.", - "properties": { - "action": { - "const": "doctor", - "type": "string" - }, - "deep": { - "description": "(doctor only) Alias for `expensive`. Default false.", - "type": "boolean" - }, - "expensive": { - "description": "(doctor only) Run costly checks too (fuzzy near-duplicate detection, folded into the duplicates bucket). Default false.", - "type": "boolean" - }, - "fix": { - "description": "Apply safe auto-fixes: archive stale, expired and superseded knowledge, and risks bound to closed tasks. Default false.", - "type": "boolean" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "destructive action \"cancel_task\".", + "properties": { + "action": { + "const": "cancel_task", + "description": "Run the cancel_task action.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for cancel_task/force_claim_task/restore_node_version/archive_component — shareable id of the target (TASK-N / KNOW-N / COMP-N / …).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reason": { + "description": "REQUIRED for cancel_task — why this task is being cancelled.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "reason" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"force_claim_task\".", + "properties": { + "action": { + "const": "force_claim_task", + "description": "Run the force_claim_task action.", + "type": "string" + }, + "dispatched_with": { + "description": "(force_claim_task only) Operator's full briefing prompt when this force-claim is from a dispatched sub-agent. Up to 16 KB.", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "parent_task_id": { + "description": "(force_claim_task only) Operator's claimed task id when this force-claim is from a dispatched sub-agent. Leave null for direct claims.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"dedupe_knowledge\".", + "properties": { + "action": { + "const": "dedupe_knowledge", + "description": "Run the dedupe_knowledge action.", + "type": "string" + }, + "category": { + "description": "(dedupe_knowledge only) Restrict the scan to one knowledge category. Default: all categories.", + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + "limit": { + "description": "(dedupe_knowledge only) Max pairs to return. Default 20.", + "maximum": 200, + "minimum": 1, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "threshold": { + "description": "(dedupe_knowledge only) Similarity threshold (0–1). Default 0.7. Raise toward 1 for stricter matches.", + "maximum": 1, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"dedupe_pair\".", + "properties": { + "action": { + "const": "dedupe_pair", + "description": "Run the dedupe_pair action.", + "type": "string" + }, + "drop_id": { + "description": "REQUIRED for dedupe_pair — the duplicate to archive; its edges are rewired onto keep_id.", + "type": "string" + }, + "keep_id": { + "description": "REQUIRED for dedupe_pair — the node to keep.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "keep_id", + "drop_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"unlink\".", + "properties": { + "action": { + "const": "unlink", + "description": "Run the unlink action.", + "type": "string" + }, + "dst_id": { + "description": "REQUIRED for unlink — destination node shareable id.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "relation": { + "description": "REQUIRED for unlink — edge type to remove.", + "enum": [ + "relates-to", + "blocks", + "fulfills", + "contradicts", + "supersedes", + "caused-by", + "expires-on" + ], + "type": "string" + }, + "src_id": { + "description": "REQUIRED for unlink — source node shareable id.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "src_id", + "dst_id", + "relation" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"restore_node_version\".", + "properties": { + "action": { + "const": "restore_node_version", + "description": "Run the restore_node_version action.", + "type": "string" + }, + "node_id": { + "description": "Node shareable id (e.g. KNOW-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "version": { + "description": "REQUIRED for restore_node_version — the version number to restore as the new live content.", + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "version" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"retire_objective\".", + "properties": { + "action": { + "const": "retire_objective", + "description": "Run the retire_objective action.", + "type": "string" + }, + "objective_id": { + "description": "REQUIRED for retire_objective — the objective being retired (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "replaced_by": { + "description": "(retire_objective only) Optional replacement objective (TASK-N) — records a `supersedes` edge.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"merge_workspace\".", + "properties": { + "action": { + "const": "merge_workspace", + "description": "Run the merge_workspace action.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"cherry_pick_workspace\".", + "properties": { + "action": { + "const": "cherry_pick_workspace", + "description": "Run the cherry_pick_workspace action.", + "type": "string" + }, + "node_ids": { + "description": "REQUIRED for cherry_pick_workspace — short-ids of the nodes to promote (KNOW-N / TASK-N / DOC-N / …). Only items currently scoped to this workspace are promoted.", + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "The workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace", + "node_ids" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"archive_workspace\".", + "properties": { + "action": { + "const": "archive_workspace", + "description": "Run the archive_workspace action.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"archive_component\".", + "properties": { + "action": { + "const": "archive_component", + "description": "Run the archive_component action.", + "type": "string" + }, + "node_id": { + "description": "Component shareable id to archive.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reparent_children_to": { + "description": "(archive_component only) Component to reparent active children under. Required if any active children exist.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"merge_topics\".", + "properties": { + "action": { + "const": "merge_topics", + "description": "Run the merge_topics action.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "sources": { + "description": "REQUIRED for merge_topics — list of source topics to merge into target. Must be non-empty.", + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "target": { + "description": "REQUIRED for merge_topics — target topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "sources", + "target" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"run_loop_attempt\".", + "properties": { + "action": { + "const": "run_loop_attempt", + "description": "Run the run_loop_attempt action.", + "type": "string" + }, + "base": { + "description": "Base git ref (default main).", + "minLength": 1, + "type": "string" + }, + "branch": { + "description": "Worker branch git ref (default HEAD).", + "minLength": 1, + "type": "string" + }, + "command": { + "description": "Exact declared check command. It is shown at the destructive approval boundary and must match the task contract.", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "Task whose declared Check and Check paths form the loop contract.", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "command" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "destructive action \"doctor\".", + "properties": { + "action": { + "const": "doctor", + "description": "Run the doctor action.", + "type": "string" + }, + "deep": { + "description": "(doctor only) Alias for `expensive`. Default false.", + "type": "boolean" + }, + "expensive": { + "description": "(doctor only) Run costly checks too (fuzzy near-duplicate detection, folded into the duplicates bucket). Default false.", + "type": "boolean" + }, + "fix": { + "description": "Apply safe auto-fixes: archive stale, expired and superseded knowledge, and risks bound to closed tasks. Default false.", + "type": "boolean" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + } +]
- Changed
document1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Add a document — a large multi-section markdown artifact (plan, audit, design spec, retrospective). Use over knowledge(action=\"create\") when content is long/narrative rather than a single short structured fact. Documents are surfaced by recall by title only; the body is never auto-injected, so an agent must call fetch DOC-N to read it. component_id is optional (omit to land at the project root; re-parent later via node(action=\"update\")). For the body pass exactly one of `body` (inline markdown) or `file_path` (repo-relative path the server reads, UTF-8 only, max 1 MB) — or neither for a title-only stub. file_path works only on a local connection; over a hosted connection pass the body inline.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "body": { - "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the doc at the project root." - }, - "file_path": { - "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"create_diagram\": Add a diagram — a visual design artifact (architecture, workflow, sequence, or flow diagram) authored in d2 source and rendered to an SVG on its page. Use when a picture explains a design better than prose: the diagram lives in the graph as a durable, referenceable node the next agent and the human can open. The `body` is d2 source — it is rendered server-side (you never write the image). Diagram source is excluded from search and recall (it's structural), but the title + topics stay findable and any shareable ids you label in the diagram (e.g. TASK-123) still create mention links. To show the rendered diagram inline in another node's body, embed it with `{{DIAG-N:short description}}` — it renders as a live image there, not just a link. component_id is optional (omit to land at the project root).", - "properties": { - "action": { - "const": "create_diagram", - "type": "string" - }, - "body": { - "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the diagram at the project root." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "document action \"create\".", + "properties": { + "action": { + "const": "create", + "description": "Run the create action.", + "type": "string" + }, + "body": { + "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the doc at the project root." + }, + "file_path": { + "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "document action \"create_diagram\".", + "properties": { + "action": { + "const": "create_diagram", + "description": "Run the create_diagram action.", + "type": "string" + }, + "body": { + "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the diagram at the project root." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + } +]
- Changed
inspect1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"context\": Walk the project graph from FTS (or topic-tag) seeds and return a scored neighborhood — not just flat search hits. Use BEFORE starting non-trivial work on a topic: it surfaces decisions, blockers, and prior lessons connected via edges that don't themselves contain the search keywords. Unlike search (flat FTS, no traversal), this seeds via FTS then follows edges, scoring by category + relation + recency. Hub nodes contribute only their most-recent edges, so a root component doesn't drag in the whole project. Bodies are truncated to ~400 chars and the total payload is capped at ~30k chars (lowest-scored non-seed nodes dropped, `_truncated: true` set); call fetch with the short id for a full body.", - "properties": { - "action": { - "const": "context", - "type": "string" - }, - "depth": { - "description": "(context only) BFS depth from seeds (default 1, max 3). 0 returns just seeds; 1 follows one edge.", - "maximum": 3, - "minimum": 0, - "type": "integer" - }, - "hub_edge_limit": { - "description": "(context only) Soft limit: nodes with more edges than this contribute only their N most-recent edges (default 12).", - "exclusiveMinimum": 0, - "maximum": 50, - "type": "integer" - }, - "max_entries": { - "description": "(context only) Cap on returned nodes (default 30, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "(context/provenance only) Shareable id (TASK-N/KNOW-N/COMP-N/DOC-N) to seed by: the node's title becomes the FTS query, then the walk proceeds from there. Use to expand the neighborhood around a specific node without already knowing good search terms. Overrides `query`.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "(context/provenance only) FTS query, in the same syntax as keyword search. Either query or topic must be set.", - "minLength": 1, - "type": "string" - }, - "seed_limit": { - "description": "(context only) Cap on seed nodes from FTS / topic match (default 10).", - "exclusiveMinimum": 0, - "maximum": 50, - "type": "integer" - }, - "topic": { - "description": "(context only) Topic tag to seed by (case-insensitive exact match on the topics array). Skips FTS — use when you want a clean topic-scoped neighborhood.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"provenance\": Walk the graph linearly from a seed and return a single ordered lineage answering 'why is X the way it is?'. At each hop it follows the strongest un-visited edge (fulfills > caused-by/supersedes > contradicts/blocks > mentions/relates-to), stopping at a risk-category node, when edges run out, or at max_hops. Use this when you want a cited chain you can narrate; use inspect(action=\"context\") for a broad unordered neighborhood to reason against. Supply exactly ONE of `node_id` (a shareable id) or `query` (free text resolved to the top FTS hit) — both or neither is a hard error.", - "properties": { - "action": { - "const": "provenance", - "type": "string" - }, - "max_hops": { - "description": "(provenance only) Walker depth cap (default 5, max 8). 0 returns just the seed.", - "maximum": 8, - "minimum": 0, - "type": "integer" - }, - "node_id": { - "description": "Starting node — shareable id (TASK-N | KNOW-N | COMP-N | DOC-N | NODE-N). Exactly one of `node_id` or `query` must be set.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Free-text 'why' prompt — resolved to a starting node via FTS top-1 hit. Exactly one of `node_id` or `query` must be set.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"briefing\": Return a compact, stateless briefing that teaches a visiting agent how to use Stele safely for the current project — for hosted/remote clients (ChatGPT, Claude web, Cursor, Codex, Gemini) that connect via MCP but lack hooks, skills, or automatic recall. The response is a PURE READ: it does NOT set an active project or store any session state, so every subsequent tool still requires project_slug explicitly. Call once at the start of a session or task when nothing is being surfaced to you automatically.", - "properties": { - "action": { - "const": "briefing", - "type": "string" - }, - "environment": { - "additionalProperties": false, - "description": "(briefing only) Describes which Stele harness features the calling client has. All fields default to false/unknown when absent — the safest assumption for hosted clients.", - "properties": { - "auto_rag_available": { - "description": "True if this client can point automatic recall at a topic and receive related context on later turns. False/absent → the briefing tells the agent to look things up with recall itself, every time.", - "type": "boolean" - }, - "hooks_available": { - "description": "True if the host client runs Stele lifecycle hooks (recall, session_start, etc.). False/absent → the briefing includes manual fallback instructions.", - "type": "boolean" - }, - "skills_available": { - "description": "True if the host client can invoke Stele skills (e.g. /stele:start). False/absent → the briefing instructs the agent to follow protocol manually.", - "type": "boolean" - } - }, - "type": "object" - }, - "intent": { - "description": "(briefing only) Short description of the current request or goal (e.g. 'review launch plan', 'fix the auth bug'). When provided, the briefing includes intent-specific next-call suggestions with project_slug already filled in.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"claim_history\": Full append-only claim history for a task — every claim/release/complete/cancel with agent, model, session, worktree, and operator lineage. Capped at 200 events.", - "properties": { - "action": { - "const": "claim_history", - "type": "string" - }, - "node_id": { - "description": "(claim_history only) Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"workspace\": Fetch one workspace by NAME or uuid, including its branch associations and whether it's active for this session.", - "properties": { - "action": { - "const": "workspace", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "(workspace only) The workspace to fetch — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"stats\": One-call overview of the active project — counts, top topics, overdue deadlines (any active knowledge with a past due_at), likely-abandoned tasks (claimed > 24h ago), and recent activity. Cheaper than running list(what=\"components\") + list_tasks + search separately.", - "properties": { - "action": { - "const": "stats", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"loop\": Fail closed unless trusted local Git proves the branch left every declared Check path untouched. Exit 0 certifies oracle integrity, 1 names protected paths the branch modified, and 2 means unknown. Changed files are computed locally; callers cannot supply them.", - "properties": { - "action": { - "const": "loop", - "type": "string" - }, - "base": { - "description": "Base git ref (default main).", - "minLength": 1, - "type": "string" - }, - "branch": { - "description": "Worker branch git ref (default HEAD).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "Task whose declared Check and Check paths form the loop contract.", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"rag_status\": Report this session's automatic topic recall: whether a focus is set, what it is, and whether recall is paused. Use it when you are unsure whether the context you are being shown still matches the work — if the focus names last hour's task, replace it.", - "properties": { - "action": { - "const": "rag_status", - "type": "string" - }, - "recall_session": { - "description": "The recall session handle Stele printed in this conversation. Without it this reports only that automatic topic recall is unavailable here.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "inspect action \"context\".", + "properties": { + "action": { + "const": "context", + "description": "Run the context action.", + "type": "string" + }, + "depth": { + "description": "(context only) BFS depth from seeds (default 1, max 3). 0 returns just seeds; 1 follows one edge.", + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "hub_edge_limit": { + "description": "(context only) Soft limit: nodes with more edges than this contribute only their N most-recent edges (default 12).", + "exclusiveMinimum": 0, + "maximum": 50, + "type": "integer" + }, + "max_entries": { + "description": "(context only) Cap on returned nodes (default 30, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "(context/provenance only) Shareable id (TASK-N/KNOW-N/COMP-N/DOC-N) to seed by: the node's title becomes the FTS query, then the walk proceeds from there. Use to expand the neighborhood around a specific node without already knowing good search terms. Overrides `query`.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "(context/provenance only) FTS query, in the same syntax as keyword search. Either query or topic must be set.", + "minLength": 1, + "type": "string" + }, + "seed_limit": { + "description": "(context only) Cap on seed nodes from FTS / topic match (default 10).", + "exclusiveMinimum": 0, + "maximum": 50, + "type": "integer" + }, + "topic": { + "description": "(context only) Topic tag to seed by (case-insensitive exact match on the topics array). Skips FTS — use when you want a clean topic-scoped neighborhood.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"provenance\".", + "properties": { + "action": { + "const": "provenance", + "description": "Run the provenance action.", + "type": "string" + }, + "max_hops": { + "description": "(provenance only) Walker depth cap (default 5, max 8). 0 returns just the seed.", + "maximum": 8, + "minimum": 0, + "type": "integer" + }, + "node_id": { + "description": "Starting node — shareable id (TASK-N | KNOW-N | COMP-N | DOC-N | NODE-N). Exactly one of `node_id` or `query` must be set.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Free-text 'why' prompt — resolved to a starting node via FTS top-1 hit. Exactly one of `node_id` or `query` must be set.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"briefing\".", + "properties": { + "action": { + "const": "briefing", + "description": "Run the briefing action.", + "type": "string" + }, + "environment": { + "additionalProperties": false, + "description": "(briefing only) Describes which Stele harness features the calling client has. All fields default to false/unknown when absent — the safest assumption for hosted clients.", + "properties": { + "auto_rag_available": { + "description": "True if this client can point automatic recall at a topic and receive related context on later turns. False/absent → the briefing tells the agent to look things up with recall itself, every time.", + "type": "boolean" + }, + "hooks_available": { + "description": "True if the host client runs Stele lifecycle hooks (recall, session_start, etc.). False/absent → the briefing includes manual fallback instructions.", + "type": "boolean" + }, + "skills_available": { + "description": "True if the host client can invoke Stele skills (e.g. /stele:start). False/absent → the briefing instructs the agent to follow protocol manually.", + "type": "boolean" + } + }, + "type": "object" + }, + "intent": { + "description": "(briefing only) Short description of the current request or goal (e.g. 'review launch plan', 'fix the auth bug'). When provided, the briefing includes intent-specific next-call suggestions with project_slug already filled in.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"claim_history\".", + "properties": { + "action": { + "const": "claim_history", + "description": "Run the claim_history action.", + "type": "string" + }, + "node_id": { + "description": "(claim_history only) Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"workspace\".", + "properties": { + "action": { + "const": "workspace", + "description": "Run the workspace action.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "(workspace only) The workspace to fetch — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"stats\".", + "properties": { + "action": { + "const": "stats", + "description": "Run the stats action.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"loop\".", + "properties": { + "action": { + "const": "loop", + "description": "Run the loop action.", + "type": "string" + }, + "base": { + "description": "Base git ref (default main).", + "minLength": 1, + "type": "string" + }, + "branch": { + "description": "Worker branch git ref (default HEAD).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "Task whose declared Check and Check paths form the loop contract.", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "inspect action \"rag_status\".", + "properties": { + "action": { + "const": "rag_status", + "description": "Run the rag_status action.", + "type": "string" + }, + "recall_session": { + "description": "The recall session handle Stele printed in this conversation. Without it this reports only that automatic topic recall is unavailable here.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + } +]
- Changed
knowledge1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Add a knowledge node under an existing active component (component_id is\nREQUIRED) — prefer an existing component over creating one. Search before\nadding: if a near-match exists, update it with node(action=\"update\") instead of duplicating. Always\ninclude topics (without them the node won't surface in topic queries) and link\nrelated nodes — isolated nodes are nearly invisible.\n\n## The knowledge categories\n\nPick the one that matches what the fact *is*, and give the body the shape it wants. Thin one-liners\ndilute the graph — a node that restates its own title helps nobody.\n\n| Category | It is | Body shape |\n|---|---|---|\n| `decision` | A choice that was made, and could have gone another way. | ## Context / ## Alternatives considered / ## Why this won / ## Consequences |\n| `architecture` | A structural fact about how the system is built. | ## What / ## How it works / ## Why this shape |\n| `goal` | What success looks like. | ## Outcome / ## Why it matters / ## How we'll know we're done |\n| `risk` | Something that might go wrong, and hasn't yet. | ## Risk / ## Likelihood & impact / ## Mitigation / ## Trigger |\n| `gap` | A known missing piece — real, but not yet work anyone has committed to. | ## Gap / ## Why it matters / ## What needs to happen to close it |\n| `lesson` | Something learned the hard way, usually from a failure. | ## Symptom / ## Root cause / ## Fix / ## Why this matters |\n| `opportunity` | Possible future work worth considering. | ## Opportunity / ## Why now / ## What it would take |\n| `priority` | What the focus is right now, and what it is not. | ## Focus / ## Why this and not X / ## Time horizon |\n| `fyi` | Context worth remembering that calls for no action. | A paragraph or two; short is fine. |\n\nTwo that get confused: a **`gap`** is a hole someone still has to decide what to do about; once the work\nis agreed it is a **task**, not a gap. A **`risk`** has not happened; a **`lesson`** already did.\n\n**A lesson from a bug is worth little without its trigger.** Put the literal error message, the failing\nsymbol, the command that broke, into the body. The graph resurfaces knowledge by matching it against\nwhat an agent is doing, so a lesson carrying the real error string reappears the moment someone hits that\nerror again. Worded abstractly, it stays invisible exactly when it would have helped.\n\n\nLIFECYCLE IS REQUIRED. Decide all three — expires_on_task, expires_at,\ndue_at — every time; pass null to skip any (durable decision/architecture\nusually skip all three). Bound nodes retire themselves, which is what keeps\nthe graph self-maintaining — that's why these aren't optional.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "body": { - "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "category": { - "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "due_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." - }, - "expires_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." - }, - "expires_on_task": { - "anyOf": [ - { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ], - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_type": { - "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — one-line summary.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "category", - "title", - "expires_on_task", - "due_at", - "expires_at" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"review\": Verify a knowledge node: record that you deliberately confirmed its claims are STILL TRUE against current reality, stamping `last_verified_at` to now. This verification axis is orthogonal to freshness — the only signal that catches a recently-written yet factually-wrong node. Actually check it first; if it's wrong, do NOT verify — use node(action=\"update\") or a supersedes edge instead. Does NOT change content, freshness, or source_type. Knowledge only; tasks/components are rejected.", - "properties": { - "action": { - "const": "review", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "knowledge action \"create\".", + "properties": { + "action": { + "const": "create", + "description": "Run the create action.", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "body": { + "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "category": { + "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "due_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." + }, + "expires_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." + }, + "expires_on_task": { + "anyOf": [ + { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ], + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_type": { + "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — one-line summary.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "category", + "title", + "expires_on_task", + "due_at", + "expires_at" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "knowledge action \"review\".", + "properties": { + "action": { + "const": "review", + "description": "Run the review action.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + } +]
- Changed
list1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "what=\"nodes\": Generic, paginated node listing — the lean single tool the typed CLI `list` commands ride on (knowledge/document/component plain lists). Filter by type, status, and parent component; ordered by most-recently-updated. Bodies are omitted — fetch one with fetch for the full body. For the faceted task view use list_tasks; for the objective tracker use list(what=\"objectives\").", - "properties": { - "component_id": { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - "limit": { - "description": "(nodes/documents/diagrams/node_versions only) Page size / max results (default 50, max 200; defaults vary by collection).", - "exclusiveMinimum": 0, - "maximum": 200, - "type": "integer" - }, - "offset": { - "description": "(nodes/documents/diagrams only) Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "(nodes/documents/diagrams only) Filter to one exact status.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "type": { - "description": "(nodes only) Limit to one node type (component | knowledge | task | document | diagram).", - "enum": [ - "component", - "knowledge", - "task", - "document", - "diagram" - ], - "type": "string" - }, - "what": { - "const": "nodes", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"components\": List components in the active project. Omit `parent_id` to get the ENTIRE tree (every component, bodyless) — the default for finding the right component to file a task/knowledge under. Pass `parent_id` (COMP-N) for a component's direct children, or null for root-level only. Always check this before creating a component: prefer reusing an existing one, they shouldn't proliferate. Cached ~30s; invalidated on add/archive_component.", - "properties": { - "parent_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(components only) Omit to list the WHOLE component tree (every component, bodyless) — do this to find where a task/knowledge should be filed. Pass a component id (COMP-N) for just its direct children, or null for root-level only." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "components", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"topics\": List every topic in the active project with its node count, counted across active knowledge + open/in_progress tasks only. Use before rename_topic / merge_topics to spot near-duplicates (releng / release-eng / release-engineering).", - "properties": { - "min_count": { - "description": "(topics only) Minimum count to include a topic in the result. Default 1.", - "minimum": 1, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "topics", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"documents\": List documents in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` (first 10 lines, long lines truncated) so agents can triage in one call; use fetch DOC-N for the full body.", - "properties": { - "component_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Parent component shareable id. Omit for all docs." - }, - "limit": { - "description": "Page size (default 50, max 100).", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "description": "Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Default 'active'.", - "enum": [ - "active", - "archived" - ], - "type": "string" - }, - "what": { - "const": "documents", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"diagrams\": List diagrams in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` of the d2 source so agents can triage in one call; use fetch DIAG-N for the full source.", - "properties": { - "component_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Parent component shareable id. Omit for all diagrams." - }, - "limit": { - "description": "Page size (default 50, max 100).", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "description": "Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Default 'active'.", - "enum": [ - "active", - "archived" - ], - "type": "string" - }, - "what": { - "const": "diagrams", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"objectives\": Overview of the project's objectives — for each: derived status (on-track / drifting / done / superseded), milestone count, task rollup (done/total), and how many loose (un-milestoned) tasks it has. Plus the project vision and the count of drift tasks serving no objective. Use this to answer 'what are we steering toward, and is anything drifting?'", - "properties": { - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "objectives", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"workspaces\": List a project's workspaces (named scoped overlays), marking which are ACTIVE for this session. Trunk is always implicitly active and is not listed.", - "properties": { - "include_archived": { - "description": "(workspaces only) Include archived/merged workspaces (default false).", - "type": "boolean" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "workspaces", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"comments\": List a node's comments as threads (top-level comment + replies), each flagged seen/unseen for you with its anchor status. Resolved threads and soft-deleted tombstones are included but don't count toward active/unread. Reading does NOT mark them seen (pass mark_seen=true to acknowledge).", - "properties": { - "include_replies": { - "description": "(comments only) Include replies under each thread (default true).", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for comments — the node whose comments to list.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "comments", - "type": "string" - } - }, - "required": [ - "what", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"node_versions\": List a node's content version history — every superseded title/body snapshot, newest first, with the editor and timestamp. Captured automatically by a DB trigger on every content edit, so it's a complete backup. PULL-ONLY: never auto-injected; call deliberately to inspect churn or recover what a node said before a bad edit.", - "properties": { - "limit": { - "description": "Max versions to return (default 100, server cap 500).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "node_id": { - "description": "REQUIRED for node_versions — node shareable id (e.g. KNOW-42, DOC-7).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "node_versions", - "type": "string" - } - }, - "required": [ - "what", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"projects\": List Stele projects the current user is a member of. Use the `slug` from here as the `project_slug` argument on every other tool. Each project carries its `visibility`: anyone can read a public project. Cached for ~30s per MCP session — cheap to call repeatedly.", - "properties": { - "what": { - "const": "projects", - "type": "string" - } - }, - "required": [ - "what" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "list action \"nodes\".", + "properties": { + "component_id": { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + "limit": { + "description": "(nodes/documents/diagrams/node_versions only) Page size / max results (default 50, max 200; defaults vary by collection).", + "exclusiveMinimum": 0, + "maximum": 200, + "type": "integer" + }, + "offset": { + "description": "(nodes/documents/diagrams only) Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "(nodes/documents/diagrams only) Filter to one exact status.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "type": { + "description": "(nodes only) Limit to one node type (component | knowledge | task | document | diagram).", + "enum": [ + "component", + "knowledge", + "task", + "document", + "diagram" + ], + "type": "string" + }, + "what": { + "const": "nodes", + "description": "Run the nodes action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"components\".", + "properties": { + "parent_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(components only) Omit to list the WHOLE component tree (every component, bodyless) — do this to find where a task/knowledge should be filed. Pass a component id (COMP-N) for just its direct children, or null for root-level only." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "components", + "description": "Run the components action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"topics\".", + "properties": { + "min_count": { + "description": "(topics only) Minimum count to include a topic in the result. Default 1.", + "minimum": 1, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "topics", + "description": "Run the topics action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"documents\".", + "properties": { + "component_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Parent component shareable id. Omit for all docs." + }, + "limit": { + "description": "Page size (default 50, max 100).", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Default 'active'.", + "enum": [ + "active", + "archived" + ], + "type": "string" + }, + "what": { + "const": "documents", + "description": "Run the documents action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"diagrams\".", + "properties": { + "component_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Parent component shareable id. Omit for all diagrams." + }, + "limit": { + "description": "Page size (default 50, max 100).", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Default 'active'.", + "enum": [ + "active", + "archived" + ], + "type": "string" + }, + "what": { + "const": "diagrams", + "description": "Run the diagrams action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"objectives\".", + "properties": { + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "objectives", + "description": "Run the objectives action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"workspaces\".", + "properties": { + "include_archived": { + "description": "(workspaces only) Include archived/merged workspaces (default false).", + "type": "boolean" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "workspaces", + "description": "Run the workspaces action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"comments\".", + "properties": { + "include_replies": { + "description": "(comments only) Include replies under each thread (default true).", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for comments — the node whose comments to list.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "comments", + "description": "Run the comments action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"node_versions\".", + "properties": { + "limit": { + "description": "Max versions to return (default 100, server cap 500).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "node_id": { + "description": "REQUIRED for node_versions — node shareable id (e.g. KNOW-42, DOC-7).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "node_versions", + "description": "Run the node_versions action.", + "type": "string" + } + }, + "required": [ + "what", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "list action \"projects\".", + "properties": { + "what": { + "const": "projects", + "description": "Run the projects action.", + "type": "string" + } + }, + "required": [ + "what" + ], + "type": "object" + } +]
- Changed
node1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"update\": Update editable fields on any node; each field is validated against what's valid for the node's type, so type-mismatched fields (e.g. category on a task) are rejected. A task's status may be set to open or backlog here, but claim, completion, and cancellation are REJECTED — use task(action=\"claim\") / task(action=\"complete\") so the claim and completion (with session + agent identity) are recorded atomically, or destructive(action=\"cancel_task\") so the destructive transition records a reason. Archiving a component is also rejected; use destructive(action=\"archive_component\") so active children cannot be orphaned. `parent_component_id` reparents across components in the same project. Surgical edits: pass `edits` (literal old_string→new_string replacements applied server-side) instead of `body` to fix a substring without resending the whole body — they're mutually exclusive, and a non-matching old_string errors rather than no-ops. Optional `expected_version` gives optimistic concurrency.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "anchor_files": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." - }, - "anchor_symbols": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — user uuid (not a shareable id)" - }, - "body": { - "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": [ - "string", - "null" - ] - }, - "category": { - "anyOf": [ - { - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge-type nodes only" - }, - "due_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." - }, - "edits": { - "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", - "items": { - "additionalProperties": false, - "properties": { - "new_string": { - "description": "Replacement text.", - "type": "string" - }, - "old_string": { - "description": "Exact substring to find in the current body.", - "minLength": 1, - "type": "string" - }, - "replace_all": { - "description": "Replace every occurrence (default: require a unique match).", - "type": "boolean" - } - }, - "required": [ - "old_string", - "new_string" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "expected_version": { - "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "expires_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." - }, - "hidden": { - "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", - "type": "string" - }, - "parent_component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "source": { - "anyOf": [ - { - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." - }, - "source_type": { - "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "anyOf": [ - { - "format": "uri", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." - }, - "status": { - "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "title": { - "description": "(update only) New title.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(update only) knowledge or task nodes only", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"move\": Move an EXISTING node between scopes — into a workspace (hiding it from trunk readers) or back to trunk (shared truth). Re-scopes by id; reversible, never deletes. Distinct from destructive(action=\"cherry_pick_workspace\") (which promotes to trunk WITH provenance) — This is a plain re-assignment in any direction.", - "properties": { - "action": { - "const": "move", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"link\": Create a typed semantic link between two nodes (src → dst). Idempotent: re-linking the same (src, dst, relation) returns the existing edge, no error.\n\nSome relations change the graph: `supersedes` retires dst (it stays findable, but comes back marked SUPERSEDED and ranked below live knowledge), and `expires-on` retires src later. Every relation except `relates-to` is a claim and needs a `reason`; `supersedes` also needs `evidence`. If you cannot state the reason, use `relates-to`.\n\n## What each link means — and what it does\n\nAn edge is not decoration. Some of these change the graph.\n\n| Relation | Direction | Use it when | What it actually does |\n|---|---|---|---|\n| `relates-to` | A → B | Someone who pulls up B should know A exists. The weakest signal — use it when nothing typed fits. | Nothing but a pointer. Both nodes stay active. |\n| `blocks` | child → parent | A must land before B can be completed or become true. Also the **subtask** relation. | Surfaces A as a subtask of B and drives B's progress. |\n| `fulfills` | task → knowledge | This task delivers or acts on that decision, goal, or risk. | Records that the work discharged the commitment. |\n| `caused-by` | A → B | B is the root cause of A. | Builds the causal chain a provenance walk follows. |\n| `contradicts` | A → B | Two facts conflict and you cannot tell which is true. | Flags it for a human. **Both stay active** — this is the honest \"I don't know\" edge. |\n| `supersedes` | new → old | The old node has become **wrong to act on** — an agent following it would now do the wrong thing. **Never** for something that is merely older: history that is still true must stay. | **Retires the old node.** It stays in the graph and stays findable, but it comes back marked `SUPERSEDED` and ranked below live knowledge — so nothing acts on it as current. |\n| `expires-on` | knowledge → task | This knowledge is only true until that task closes. | Retires the knowledge when the task completes — same marked-not-deleted treatment. |\n\n`mentions` is not linkable by hand — write the id into a body and it is created for you.", - "properties": { - "action": { - "const": "link", - "type": "string" - }, - "dst_id": { - "description": "REQUIRED for link — destination node shareable id.", - "type": "string" - }, - "evidence": { - "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", - "maxLength": 4000, - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reason": { - "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", - "maxLength": 2000, - "minLength": 1, - "type": "string" - }, - "relation": { - "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", - "enum": [ - "relates-to", - "blocks", - "fulfills", - "contradicts", - "supersedes", - "caused-by", - "expires-on" - ], - "type": "string" - }, - "src_id": { - "description": "REQUIRED for link — source node shareable id.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "src_id", - "dst_id", - "relation" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "node action \"update\".", + "properties": { + "action": { + "const": "update", + "description": "Run the update action.", + "type": "string" + }, + "anchor_files": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." + }, + "anchor_symbols": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — user uuid (not a shareable id)" + }, + "body": { + "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": [ + "string", + "null" + ] + }, + "category": { + "anyOf": [ + { + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge-type nodes only" + }, + "due_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." + }, + "edits": { + "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", + "items": { + "additionalProperties": false, + "properties": { + "new_string": { + "description": "Replacement text.", + "type": "string" + }, + "old_string": { + "description": "Exact substring to find in the current body.", + "minLength": 1, + "type": "string" + }, + "replace_all": { + "description": "Replace every occurrence (default: require a unique match).", + "type": "boolean" + } + }, + "required": [ + "old_string", + "new_string" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "expected_version": { + "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." + }, + "hidden": { + "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", + "type": "string" + }, + "parent_component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "source": { + "anyOf": [ + { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." + }, + "source_type": { + "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "anyOf": [ + { + "format": "uri", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." + }, + "status": { + "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "title": { + "description": "(update only) New title.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(update only) knowledge or task nodes only", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "node action \"move\".", + "properties": { + "action": { + "const": "move", + "description": "Run the move action.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "node action \"link\".", + "properties": { + "action": { + "const": "link", + "description": "Run the link action.", + "type": "string" + }, + "dst_id": { + "description": "REQUIRED for link — destination node shareable id.", + "type": "string" + }, + "evidence": { + "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", + "maxLength": 4000, + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reason": { + "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "relation": { + "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", + "enum": [ + "relates-to", + "blocks", + "fulfills", + "contradicts", + "supersedes", + "caused-by", + "expires-on" + ], + "type": "string" + }, + "src_id": { + "description": "REQUIRED for link — source node shareable id.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "src_id", + "dst_id", + "relation" + ], + "type": "object" + } +]
- Changed
objective1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create an objective — a standing goal whose end-state lives in its body, with ordered milestone phases under it. Non-claimable; it rolls up from its milestones and the tasks attached to them. Add milestones with objective(action=\"add_milestone\"), then attach tasks with objective(action=\"attach\").", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "body": { - "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create/add_milestone — parent component shareable id.", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", - "type": "string" - }, - "target_date": { - "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create/add_milestone only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"add_milestone\": Create a milestone — an ordered phase under an objective. Appended after the objective's existing milestones unless you pass an explicit position. Attach tasks to it with objective(action=\"attach\").", - "properties": { - "action": { - "const": "add_milestone", - "type": "string" - }, - "body": { - "description": "Optional milestone description.", - "type": "string" - }, - "component_id": { - "description": "Parent component shareable id. REQUIRED.", - "type": "string" - }, - "objective_id": { - "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", - "type": "string" - }, - "position": { - "description": "1-based order within the objective. Appended last when omitted.", - "type": "integer" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "target_date": { - "description": "Optional soft target date, ISO e.g. 2026-09-30.", - "type": "string" - }, - "title": { - "description": "Milestone one-liner.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"attach\": Attach a plain task to a milestone, re-parenting it off any prior milestone/objective. Real task→task `blocks` dependencies are preserved — only the milestone/objective containment moves.", - "properties": { - "action": { - "const": "attach", - "type": "string" - }, - "milestone_id": { - "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "milestone_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"move\": Attach a plain task directly to an objective — a 'loose' task serving the objective without a milestone. Re-parents off any prior container. Once an objective has milestones, loose tasks are its drift signal.", - "properties": { - "action": { - "const": "move", - "type": "string" - }, - "objective_id": { - "description": "The objective to attach it to directly (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The plain task to attach (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "objective_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"detach\": Remove a task's milestone/objective containment. Genuine task→task `blocks` dependencies are left intact.", - "properties": { - "action": { - "const": "detach", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The task to detach from its milestone/objective (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"reorder\": Reorder an objective's milestones. `milestone_ids` must list exactly the objective's own milestones, in the new order (a total permutation); a partial or foreign list is rejected so positions never go inconsistent.", - "properties": { - "action": { - "const": "reorder", - "type": "string" - }, - "milestone_ids": { - "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", - "items": { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "objective_id": { - "description": "The objective whose milestones to reorder (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "milestone_ids" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"set_target\": Set (or clear, with null) a milestone's soft target date.", - "properties": { - "action": { - "const": "set_target", - "type": "string" - }, - "milestone_id": { - "description": "The milestone to retarget (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "ISO target date e.g. 2026-09-30, or null to clear.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "action", - "project_slug", - "milestone_id", - "target_date" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"achieve\": Mark an objective as achieved (status → done).", - "properties": { - "action": { - "const": "achieve", - "type": "string" - }, - "objective_id": { - "description": "The objective to mark achieved (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "objective action \"create\".", + "properties": { + "action": { + "const": "create", + "description": "Run the create action.", + "type": "string" + }, + "body": { + "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create/add_milestone — parent component shareable id.", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", + "type": "string" + }, + "target_date": { + "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create/add_milestone only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"add_milestone\".", + "properties": { + "action": { + "const": "add_milestone", + "description": "Run the add_milestone action.", + "type": "string" + }, + "body": { + "description": "Optional milestone description.", + "type": "string" + }, + "component_id": { + "description": "Parent component shareable id. REQUIRED.", + "type": "string" + }, + "objective_id": { + "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", + "type": "string" + }, + "position": { + "description": "1-based order within the objective. Appended last when omitted.", + "type": "integer" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "target_date": { + "description": "Optional soft target date, ISO e.g. 2026-09-30.", + "type": "string" + }, + "title": { + "description": "Milestone one-liner.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"attach\".", + "properties": { + "action": { + "const": "attach", + "description": "Run the attach action.", + "type": "string" + }, + "milestone_id": { + "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "milestone_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"move\".", + "properties": { + "action": { + "const": "move", + "description": "Run the move action.", + "type": "string" + }, + "objective_id": { + "description": "The objective to attach it to directly (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The plain task to attach (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "objective_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"detach\".", + "properties": { + "action": { + "const": "detach", + "description": "Run the detach action.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The task to detach from its milestone/objective (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"reorder\".", + "properties": { + "action": { + "const": "reorder", + "description": "Run the reorder action.", + "type": "string" + }, + "milestone_ids": { + "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", + "items": { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "objective_id": { + "description": "The objective whose milestones to reorder (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "milestone_ids" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"set_target\".", + "properties": { + "action": { + "const": "set_target", + "description": "Run the set_target action.", + "type": "string" + }, + "milestone_id": { + "description": "The milestone to retarget (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "ISO target date e.g. 2026-09-30, or null to clear.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "action", + "project_slug", + "milestone_id", + "target_date" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "objective action \"achieve\".", + "properties": { + "action": { + "const": "achieve", + "description": "Run the achieve action.", + "type": "string" + }, + "objective_id": { + "description": "The objective to mark achieved (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id" + ], + "type": "object" + } +]
- Changed
organize1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", - "properties": { - "action": { - "const": "add_component", - "type": "string" - }, - "body": { - "description": "(add_component only) 1-3 paragraph charter (markdown).", - "type": "string" - }, - "name": { - "description": "REQUIRED for add_component — short noun-phrase title.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "parent_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "parent_id", - "name" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", - "properties": { - "action": { - "const": "rename_topic", - "type": "string" - }, - "from": { - "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", - "properties": { - "action": { - "const": "feedback", - "type": "string" - }, - "hindrance": { - "description": "(feedback only) One place Stele was more friction than help.", - "type": "string" - }, - "improvement": { - "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "score": { - "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", - "maximum": 10, - "minimum": 0, - "type": "integer" - }, - "session_id": { - "description": "(feedback only) The session being reflected on. Defaults to the current session.", - "type": "string" - }, - "source": { - "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", - "enum": [ - "agent", - "human" - ], - "type": "string" - }, - "strength": { - "description": "(feedback only) One concrete way Stele helped this session.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", - "properties": { - "action": { - "const": "pause_rag", - "type": "string" - }, - "reason": { - "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", - "type": "string" - }, - "recall_session": { - "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle Stele printed in this conversation, in its recall note or notice. It identifies which local session to update and grants no access on its own.", - "minLength": 1, - "type": "string" - }, - "turns": { - "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "recall_session", - "reason", - "turns" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "organize action \"add_component\".", + "properties": { + "action": { + "const": "add_component", + "description": "Run the add_component action.", + "type": "string" + }, + "body": { + "description": "(add_component only) 1-3 paragraph charter (markdown).", + "type": "string" + }, + "name": { + "description": "REQUIRED for add_component — short noun-phrase title.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "parent_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "parent_id", + "name" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "organize action \"rename_topic\".", + "properties": { + "action": { + "const": "rename_topic", + "description": "Run the rename_topic action.", + "type": "string" + }, + "from": { + "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "organize action \"feedback\".", + "properties": { + "action": { + "const": "feedback", + "description": "Run the feedback action.", + "type": "string" + }, + "hindrance": { + "description": "(feedback only) One place Stele was more friction than help.", + "type": "string" + }, + "improvement": { + "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "score": { + "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", + "maximum": 10, + "minimum": 0, + "type": "integer" + }, + "session_id": { + "description": "(feedback only) The session being reflected on. Defaults to the current session.", + "type": "string" + }, + "source": { + "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", + "enum": [ + "agent", + "human" + ], + "type": "string" + }, + "strength": { + "description": "(feedback only) One concrete way Stele helped this session.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "organize action \"pause_rag\".", + "properties": { + "action": { + "const": "pause_rag", + "description": "Run the pause_rag action.", + "type": "string" + }, + "reason": { + "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", + "type": "string" + }, + "recall_session": { + "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle Stele printed in this conversation, in its recall note or notice. It identifies which local session to update and grants no access on its own.", + "minLength": 1, + "type": "string" + }, + "turns": { + "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "recall_session", + "reason", + "turns" + ], + "type": "object" + } +]
- Changed
project2 fields changed- changed
Input schema / descriptionPrevious value: -"action=\"create\": Create a new Stele project — a fresh knowledge-and-task graph owned by you — and return its `slug`. Use this when you have no project to work in yet (for example, the first time you connect); then pass the returned `slug` as `project_slug` on every other tool. `name` is required; `description`, `repo_url`, and `visibility` are optional. A root component is created for you automatically. Projects are private unless you pass `visibility: \"public\"`; plans allow a limited number of private projects but unlimited public ones, so if this fails on a plan limit, the error explains how to retry."New value: +"project action \"create\"." - added
Input schema / properties / action / descriptionAdded value: +"Run the create action."
- Changed
task1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create a task under a component. One task ≈ one PR. `success_criteria` is required — it is the contract the implementer and the reviewer both work against. Pass `check` only when a command can genuinely decide done without a human; for unattended certification also pass `check_paths`. Most tasks have neither, and that is fine. Give a markdown body with ## Why / ## How / ## Test plan — the canonical contract headings are written for you from the arguments.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this task is about (function / class / constant / env var).", - "items": { - "type": "string" - }, - "type": "array" - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." - }, - "body": { - "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "check": { - "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", - "type": "string" - }, - "check_paths": { - "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "kind": { - "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal with ordered 'milestone' phases under it. Every kind still takes `success_criteria`: for an objective it is the end state that means the goal is reached.", - "enum": [ - "task", - "epic", - "objective", - "milestone" - ], - "type": "string" - }, - "position": { - "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", - "type": "integer" - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "success_criteria": { - "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title", - "success_criteria" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"claim\": Atomically claim a task (open → in_progress), recording you as owner. Claim before you start so others know you're on it. Returns the task, or { result: 'conflict' } if someone else holds it — no race window.", - "properties": { - "action": { - "const": "claim", - "type": "string" - }, - "dispatched_with": { - "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", - "type": "string" - }, - "parent_task_id": { - "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"complete\": Mark a task done. REQUIRED `summary` is appended under ## Completion summary (what shipped, where it landed, what you learned). Also closes a container (epic/objective/milestone): no claim needed, but it is REFUSED while any child is unfinished, and the error names them. A container normally closes itself when its last child closes, so completing one is mainly how you record what it delivered — and it marks the close as yours, so a child reopening later won't undo it. The result may list nearby decision/architecture nodes to re-verify (still-true / edit / supersede); that part is advisory.", - "properties": { - "action": { - "const": "complete", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "summary": { - "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "summary" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"release\": Release a task you claimed (in_progress → open) so someone else can pick it up — use when you can't finish (out of context, blocked, wrong scope).", - "properties": { - "action": { - "const": "release", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"reopen\": Recovery: send a closed task (done or cancelled) back to open and clear its completion metadata so it reads as unheld and claimable again. For a stuck in_progress task, use task(action=\"force_release\") instead (this is for closed tasks).", - "properties": { - "action": { - "const": "reopen", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"force_release\": Admin recovery: release an in_progress task WITHOUT holding the claim, so it returns to open for others. The release is attributed to you in the claim history (not the prior claimer). For a closed task use task(action=\"reopen\"); to take a task over rather than free it, use destructive(action=\"force_claim_task\").", - "properties": { - "action": { - "const": "force_release", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"loop_finalize\": Create the durable final loop envelope. Deterministic correctness comes only from the latest mechanically evidenced attempt; independent reviewer and operator intent/scope judgments remain separate fields. Approval requires all three, so model judgment can reject work but can never manufacture correctness.", - "properties": { - "action": { - "const": "loop_finalize", - "type": "string" - }, - "node_id": { - "description": "The loop task to finalize (TASK-N).", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "operator_summary": { - "description": "Why the operator judged the change in or out of intent and scope.", - "minLength": 1, - "type": "string" - }, - "operator_verdict": { - "description": "The operator's judgment of whether the change stays within the task's intent and scope.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reviewer_summary": { - "description": "Why the reviewer approved or rejected the change.", - "minLength": 1, - "type": "string" - }, - "reviewer_verdict": { - "description": "The independent reviewer's judgment of the change.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "reviewer_verdict", - "reviewer_summary", - "operator_verdict", - "operator_summary" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "task action \"create\".", + "properties": { + "action": { + "const": "create", + "description": "Run the create action.", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this task is about (function / class / constant / env var).", + "items": { + "type": "string" + }, + "type": "array" + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." + }, + "body": { + "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "check": { + "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", + "type": "string" + }, + "check_paths": { + "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "kind": { + "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal with ordered 'milestone' phases under it. Every kind still takes `success_criteria`: for an objective it is the end state that means the goal is reached.", + "enum": [ + "task", + "epic", + "objective", + "milestone" + ], + "type": "string" + }, + "position": { + "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", + "type": "integer" + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "success_criteria": { + "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title", + "success_criteria" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "task action \"claim\".", + "properties": { + "action": { + "const": "claim", + "description": "Run the claim action.", + "type": "string" + }, + "dispatched_with": { + "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", + "type": "string" + }, + "parent_task_id": { + "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "task action \"complete\".", + "properties": { + "action": { + "const": "complete", + "description": "Run the complete action.", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "summary": { + "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "summary" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "task action \"release\".", + "properties": { + "action": { + "const": "release", + "description": "Run the release action.", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "task action \"reopen\".", + "properties": { + "action": { + "const": "reopen", + "description": "Run the reopen action.", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "task action \"force_release\".", + "properties": { + "action": { + "const": "force_release", + "description": "Run the force_release action.", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "task action \"loop_finalize\".", + "properties": { + "action": { + "const": "loop_finalize", + "description": "Run the loop_finalize action.", + "type": "string" + }, + "node_id": { + "description": "The loop task to finalize (TASK-N).", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "operator_summary": { + "description": "Why the operator judged the change in or out of intent and scope.", + "minLength": 1, + "type": "string" + }, + "operator_verdict": { + "description": "The operator's judgment of whether the change stays within the task's intent and scope.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reviewer_summary": { + "description": "Why the reviewer approved or rejected the change.", + "minLength": 1, + "type": "string" + }, + "reviewer_verdict": { + "description": "The independent reviewer's judgment of the change.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "reviewer_verdict", + "reviewer_summary", + "operator_verdict", + "operator_summary" + ], + "type": "object" + } +]
- Changed
timeline1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"list\": List the project's Timeline: automatic events captured on graph writes and task lifecycle actions, interleaved with one-line custom entries logged by agents and automations, newest first. Filters (AND-combined): verbs, action type, actor user/agent, capture source, status, component, node (matches the entry's subject or a linked node), and a time range. A custom entry's linked nodes resolve to compact references (id, type, title, status) — never raw uuids. Keyset-paginated, not offset-based: pass a previous page's `next_before` back as `before` to fetch the next (older) page.", - "properties": { - "action": { - "const": "list", - "type": "string" - }, - "agent": { - "description": "Filter to entries attributed to this agent name.", - "type": "string" - }, - "before": { - "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", - "type": "string" - }, - "component_id": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "from": { - "description": "ISO timestamp — only entries at or after this time.", - "type": "string" - }, - "limit": { - "description": "Page size (default 50, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "Limit to entries whose subject is this node, OR that link to it.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", - "type": "string" - }, - "source": { - "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", - "type": "string" - }, - "status": { - "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "to": { - "description": "ISO timestamp — only entries before this time.", - "type": "string" - }, - "type": { - "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", - "type": "string" - }, - "user": { - "description": "Filter to entries attributed to this user (uuid, not a shareable id).", - "format": "uuid", - "type": "string" - }, - "verbs": { - "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", - "items": { - "enum": [ - "created", - "updated", - "status_changed", - "superseded", - "commented", - "claimed", - "released", - "custom", - "linked", - "unlinked" - ], - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"log\": Append a one-line entry to the project's Timeline — for an agent or automation to journal a milestone, with links to the graph items holding the substance. `message` is a short, human-readable line: one thing that happened, never the reasoning behind it or the decision it produced — put those in a knowledge or task node and reference them with `link` instead. `type` is a free-form, caller-namespaced label so entries from different automations don't collide. The Timeline is append-only: a correction is a new entry, not an edit. This capability can be turned off for a project; when it is, the call succeeds but writes nothing, and the response says so.", - "properties": { - "action": { - "const": "log", - "type": "string" - }, - "link": { - "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", - "items": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "message": { - "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Outcome of the journaled action. Omit unless you have one to report.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "type": { - "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", - "maxLength": 100, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "type", - "message" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "timeline action \"list\".", + "properties": { + "action": { + "const": "list", + "description": "Run the list action.", + "type": "string" + }, + "agent": { + "description": "Filter to entries attributed to this agent name.", + "type": "string" + }, + "before": { + "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", + "type": "string" + }, + "component_id": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "from": { + "description": "ISO timestamp — only entries at or after this time.", + "type": "string" + }, + "limit": { + "description": "Page size (default 50, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "Limit to entries whose subject is this node, OR that link to it.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", + "type": "string" + }, + "source": { + "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", + "type": "string" + }, + "status": { + "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "to": { + "description": "ISO timestamp — only entries before this time.", + "type": "string" + }, + "type": { + "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", + "type": "string" + }, + "user": { + "description": "Filter to entries attributed to this user (uuid, not a shareable id).", + "format": "uuid", + "type": "string" + }, + "verbs": { + "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", + "items": { + "enum": [ + "created", + "updated", + "status_changed", + "superseded", + "commented", + "claimed", + "released", + "custom", + "linked", + "unlinked" + ], + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "timeline action \"log\".", + "properties": { + "action": { + "const": "log", + "description": "Run the log action.", + "type": "string" + }, + "link": { + "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", + "items": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "message": { + "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Outcome of the journaled action. Omit unless you have one to report.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "type": { + "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", + "maxLength": 100, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "type", + "message" + ], + "type": "object" + } +]
- Changed
workspace1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create a named workspace — a scoped overlay on the project's trunk (shared truth). Knowledge written while it's active scopes to it; collaborators off that workspace never see it. permanent = a domain/team overlay that never graduates (promote per-item); experimental = ephemeral, expected to merge or be archived. Prefer OFFERING before creating unless the user asked. Does NOT activate it — call workspace(action=\"activate\") to scope this session to it.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "auto_activate_hint": { - "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", - "type": "string" - }, - "branches": { - "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "description": { - "description": "(create/update only) One-line description of what this overlay is for.", - "type": "string" - }, - "kind": { - "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "name", - "kind" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"update\": Update a workspace's editable fields (name, kind, description, auto-activation hint) and/or ADD branch associations. Only the fields you pass change. Does NOT promote knowledge or change scope — merge/archive have dedicated tools.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "add_branches": { - "description": "(update only) Branch associations to ADD (existing ones are kept).", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "auto_activate_hint": { - "description": "New plain-language auto-activation hint.", - "type": "string" - }, - "description": { - "description": "New one-line description.", - "type": "string" - }, - "kind": { - "description": "Change the workspace kind.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "New name.", - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "workspace action \"create\".", + "properties": { + "action": { + "const": "create", + "description": "Run the create action.", + "type": "string" + }, + "auto_activate_hint": { + "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", + "type": "string" + }, + "branches": { + "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "description": { + "description": "(create/update only) One-line description of what this overlay is for.", + "type": "string" + }, + "kind": { + "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "name", + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "workspace action \"update\".", + "properties": { + "action": { + "const": "update", + "description": "Run the update action.", + "type": "string" + }, + "add_branches": { + "description": "(update only) Branch associations to ADD (existing ones are kept).", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "auto_activate_hint": { + "description": "New plain-language auto-activation hint.", + "type": "string" + }, + "description": { + "description": "New one-line description.", + "type": "string" + }, + "kind": { + "description": "Change the workspace kind.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "New name.", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + } +]
2 tool updates
- Changed
inspect1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"context\": Walk the project graph from FTS (or topic-tag) seeds and return a scored neighborhood — not just flat search hits. Use BEFORE starting non-trivial work on a topic: it surfaces decisions, blockers, and prior lessons connected via edges that don't themselves contain the search keywords. Unlike search (flat FTS, no traversal), this seeds via FTS then follows edges, scoring by category + relation + recency. Hub nodes contribute only their most-recent edges, so a root component doesn't drag in the whole project. Bodies are truncated to ~400 chars and the total payload is capped at ~30k chars (lowest-scored non-seed nodes dropped, `_truncated: true` set); call fetch with the short id for a full body.", - "properties": { - "action": { - "const": "context", - "type": "string" - }, - "depth": { - "description": "(context only) BFS depth from seeds (default 1, max 3). 0 returns just seeds; 1 follows one edge.", - "maximum": 3, - "minimum": 0, - "type": "integer" - }, - "hub_edge_limit": { - "description": "(context only) Soft limit: nodes with more edges than this contribute only their N most-recent edges (default 12).", - "exclusiveMinimum": 0, - "maximum": 50, - "type": "integer" - }, - "max_entries": { - "description": "(context only) Cap on returned nodes (default 30, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "(context/provenance only) Shareable id (TASK-N/KNOW-N/COMP-N/DOC-N) to seed by: the node's title becomes the FTS query, then the walk proceeds from there. Use to expand the neighborhood around a specific node without already knowing good search terms. Overrides `query`.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "(context/provenance only) FTS query, in the same syntax as keyword search. Either query or topic must be set.", - "minLength": 1, - "type": "string" - }, - "seed_limit": { - "description": "(context only) Cap on seed nodes from FTS / topic match (default 10).", - "exclusiveMinimum": 0, - "maximum": 50, - "type": "integer" - }, - "topic": { - "description": "(context only) Topic tag to seed by (case-insensitive exact match on the topics array). Skips FTS — use when you want a clean topic-scoped neighborhood.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"provenance\": Walk the graph linearly from a seed and return a single ordered lineage answering 'why is X the way it is?'. At each hop it follows the strongest un-visited edge (fulfills > caused-by/supersedes > contradicts/blocks > mentions/relates-to), stopping at a risk-category node, when edges run out, or at max_hops. Use this when you want a cited chain you can narrate; use inspect(action=\"context\") for a broad unordered neighborhood to reason against. Supply exactly ONE of `node_id` (a shareable id) or `query` (free text resolved to the top FTS hit) — both or neither is a hard error.", - "properties": { - "action": { - "const": "provenance", - "type": "string" - }, - "max_hops": { - "description": "(provenance only) Walker depth cap (default 5, max 8). 0 returns just the seed.", - "maximum": 8, - "minimum": 0, - "type": "integer" - }, - "node_id": { - "description": "Starting node — shareable id (TASK-N | KNOW-N | COMP-N | DOC-N | NODE-N). Exactly one of `node_id` or `query` must be set.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Free-text 'why' prompt — resolved to a starting node via FTS top-1 hit. Exactly one of `node_id` or `query` must be set.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"briefing\": Return a compact, stateless briefing that teaches a visiting agent how to use Stele safely for the current project — for hosted/remote clients (ChatGPT, Claude web, Cursor, Codex, Gemini) that connect via MCP but lack hooks, skills, or automatic recall. The response is a PURE READ: it does NOT set an active project or store any session state, so every subsequent tool still requires project_slug explicitly. Call once at the start of a session or task when nothing is being surfaced to you automatically.", - "properties": { - "action": { - "const": "briefing", - "type": "string" - }, - "environment": { - "additionalProperties": false, - "description": "(briefing only) Describes which Stele harness features the calling client has. All fields default to false/unknown when absent — the safest assumption for hosted clients.", - "properties": { - "auto_rag_available": { - "description": "True if this client can point automatic recall at a topic and receive related context on later turns. False/absent → the briefing tells the agent to look things up with recall itself, every time.", - "type": "boolean" - }, - "hooks_available": { - "description": "True if the host client runs Stele lifecycle hooks (recall, session_start, etc.). False/absent → the briefing includes manual fallback instructions.", - "type": "boolean" - }, - "skills_available": { - "description": "True if the host client can invoke Stele skills (e.g. /stele:start). False/absent → the briefing instructs the agent to follow protocol manually.", - "type": "boolean" - } - }, - "type": "object" - }, - "intent": { - "description": "(briefing only) Short description of the current request or goal (e.g. 'review launch plan', 'fix the auth bug'). When provided, the briefing includes intent-specific next-call suggestions with project_slug already filled in.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"claim_history\": Full append-only claim history for a task — every claim/release/complete/cancel with agent, model, session, worktree, and operator lineage. Capped at 200 events.", - "properties": { - "action": { - "const": "claim_history", - "type": "string" - }, - "node_id": { - "description": "(claim_history only) Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"workspace\": Fetch one workspace by NAME or uuid, including its branch associations and whether it's active for this session.", - "properties": { - "action": { - "const": "workspace", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "(workspace only) The workspace to fetch — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"stats\": One-call overview of the active project — counts, top topics, overdue deadlines (any active knowledge with a past due_at), likely-abandoned tasks (claimed > 24h ago), and recent activity. Cheaper than running list(what=\"components\") + list_tasks + search separately.", - "properties": { - "action": { - "const": "stats", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"loop\": Fail closed unless trusted local Git proves the branch left every declared Check path untouched. Exit 0 certifies oracle integrity, 1 names protected paths the branch modified, and 2 means unknown. Changed files are computed locally; callers cannot supply them.", - "properties": { - "action": { - "const": "loop", - "type": "string" - }, - "base": { - "description": "Base git ref (default main).", - "minLength": 1, - "type": "string" - }, - "branch": { - "description": "Worker branch git ref (default HEAD).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "Task whose declared Check and Check paths form the loop contract.", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"rag_status\": Report this session's automatic topic recall: whether a focus is set, what it is, and whether recall is paused. Use it when you are unsure whether the context you are being shown still matches the work — if the focus names last hour's task, replace it.", - "properties": { - "action": { - "const": "rag_status", - "type": "string" - }, - "recall_session": { - "description": "The recall session handle from the awaiting-focus notice. Without it this reports only that automatic topic recall is unavailable here.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"context\": Walk the project graph from FTS (or topic-tag) seeds and return a scored neighborhood — not just flat search hits. Use BEFORE starting non-trivial work on a topic: it surfaces decisions, blockers, and prior lessons connected via edges that don't themselves contain the search keywords. Unlike search (flat FTS, no traversal), this seeds via FTS then follows edges, scoring by category + relation + recency. Hub nodes contribute only their most-recent edges, so a root component doesn't drag in the whole project. Bodies are truncated to ~400 chars and the total payload is capped at ~30k chars (lowest-scored non-seed nodes dropped, `_truncated: true` set); call fetch with the short id for a full body.", + "properties": { + "action": { + "const": "context", + "type": "string" + }, + "depth": { + "description": "(context only) BFS depth from seeds (default 1, max 3). 0 returns just seeds; 1 follows one edge.", + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "hub_edge_limit": { + "description": "(context only) Soft limit: nodes with more edges than this contribute only their N most-recent edges (default 12).", + "exclusiveMinimum": 0, + "maximum": 50, + "type": "integer" + }, + "max_entries": { + "description": "(context only) Cap on returned nodes (default 30, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "(context/provenance only) Shareable id (TASK-N/KNOW-N/COMP-N/DOC-N) to seed by: the node's title becomes the FTS query, then the walk proceeds from there. Use to expand the neighborhood around a specific node without already knowing good search terms. Overrides `query`.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "(context/provenance only) FTS query, in the same syntax as keyword search. Either query or topic must be set.", + "minLength": 1, + "type": "string" + }, + "seed_limit": { + "description": "(context only) Cap on seed nodes from FTS / topic match (default 10).", + "exclusiveMinimum": 0, + "maximum": 50, + "type": "integer" + }, + "topic": { + "description": "(context only) Topic tag to seed by (case-insensitive exact match on the topics array). Skips FTS — use when you want a clean topic-scoped neighborhood.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"provenance\": Walk the graph linearly from a seed and return a single ordered lineage answering 'why is X the way it is?'. At each hop it follows the strongest un-visited edge (fulfills > caused-by/supersedes > contradicts/blocks > mentions/relates-to), stopping at a risk-category node, when edges run out, or at max_hops. Use this when you want a cited chain you can narrate; use inspect(action=\"context\") for a broad unordered neighborhood to reason against. Supply exactly ONE of `node_id` (a shareable id) or `query` (free text resolved to the top FTS hit) — both or neither is a hard error.", + "properties": { + "action": { + "const": "provenance", + "type": "string" + }, + "max_hops": { + "description": "(provenance only) Walker depth cap (default 5, max 8). 0 returns just the seed.", + "maximum": 8, + "minimum": 0, + "type": "integer" + }, + "node_id": { + "description": "Starting node — shareable id (TASK-N | KNOW-N | COMP-N | DOC-N | NODE-N). Exactly one of `node_id` or `query` must be set.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Free-text 'why' prompt — resolved to a starting node via FTS top-1 hit. Exactly one of `node_id` or `query` must be set.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"briefing\": Return a compact, stateless briefing that teaches a visiting agent how to use Stele safely for the current project — for hosted/remote clients (ChatGPT, Claude web, Cursor, Codex, Gemini) that connect via MCP but lack hooks, skills, or automatic recall. The response is a PURE READ: it does NOT set an active project or store any session state, so every subsequent tool still requires project_slug explicitly. Call once at the start of a session or task when nothing is being surfaced to you automatically.", + "properties": { + "action": { + "const": "briefing", + "type": "string" + }, + "environment": { + "additionalProperties": false, + "description": "(briefing only) Describes which Stele harness features the calling client has. All fields default to false/unknown when absent — the safest assumption for hosted clients.", + "properties": { + "auto_rag_available": { + "description": "True if this client can point automatic recall at a topic and receive related context on later turns. False/absent → the briefing tells the agent to look things up with recall itself, every time.", + "type": "boolean" + }, + "hooks_available": { + "description": "True if the host client runs Stele lifecycle hooks (recall, session_start, etc.). False/absent → the briefing includes manual fallback instructions.", + "type": "boolean" + }, + "skills_available": { + "description": "True if the host client can invoke Stele skills (e.g. /stele:start). False/absent → the briefing instructs the agent to follow protocol manually.", + "type": "boolean" + } + }, + "type": "object" + }, + "intent": { + "description": "(briefing only) Short description of the current request or goal (e.g. 'review launch plan', 'fix the auth bug'). When provided, the briefing includes intent-specific next-call suggestions with project_slug already filled in.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"claim_history\": Full append-only claim history for a task — every claim/release/complete/cancel with agent, model, session, worktree, and operator lineage. Capped at 200 events.", + "properties": { + "action": { + "const": "claim_history", + "type": "string" + }, + "node_id": { + "description": "(claim_history only) Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"workspace\": Fetch one workspace by NAME or uuid, including its branch associations and whether it's active for this session.", + "properties": { + "action": { + "const": "workspace", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "(workspace only) The workspace to fetch — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"stats\": One-call overview of the active project — counts, top topics, overdue deadlines (any active knowledge with a past due_at), likely-abandoned tasks (claimed > 24h ago), and recent activity. Cheaper than running list(what=\"components\") + list_tasks + search separately.", + "properties": { + "action": { + "const": "stats", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"loop\": Fail closed unless trusted local Git proves the branch left every declared Check path untouched. Exit 0 certifies oracle integrity, 1 names protected paths the branch modified, and 2 means unknown. Changed files are computed locally; callers cannot supply them.", + "properties": { + "action": { + "const": "loop", + "type": "string" + }, + "base": { + "description": "Base git ref (default main).", + "minLength": 1, + "type": "string" + }, + "branch": { + "description": "Worker branch git ref (default HEAD).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "Task whose declared Check and Check paths form the loop contract.", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"rag_status\": Report this session's automatic topic recall: whether a focus is set, what it is, and whether recall is paused. Use it when you are unsure whether the context you are being shown still matches the work — if the focus names last hour's task, replace it.", + "properties": { + "action": { + "const": "rag_status", + "type": "string" + }, + "recall_session": { + "description": "The recall session handle Stele printed in this conversation. Without it this reports only that automatic topic recall is unavailable here.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + } +]
- Changed
organize1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", - "properties": { - "action": { - "const": "add_component", - "type": "string" - }, - "body": { - "description": "(add_component only) 1-3 paragraph charter (markdown).", - "type": "string" - }, - "name": { - "description": "REQUIRED for add_component — short noun-phrase title.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "parent_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "parent_id", - "name" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", - "properties": { - "action": { - "const": "rename_topic", - "type": "string" - }, - "from": { - "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", - "properties": { - "action": { - "const": "feedback", - "type": "string" - }, - "hindrance": { - "description": "(feedback only) One place Stele was more friction than help.", - "type": "string" - }, - "improvement": { - "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "score": { - "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", - "maximum": 10, - "minimum": 0, - "type": "integer" - }, - "session_id": { - "description": "(feedback only) The session being reflected on. Defaults to the current session.", - "type": "string" - }, - "source": { - "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", - "enum": [ - "agent", - "human" - ], - "type": "string" - }, - "strength": { - "description": "(feedback only) One concrete way Stele helped this session.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", - "properties": { - "action": { - "const": "pause_rag", - "type": "string" - }, - "reason": { - "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", - "type": "string" - }, - "recall_session": { - "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", - "minLength": 1, - "type": "string" - }, - "turns": { - "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "recall_session", - "reason", - "turns" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", + "properties": { + "action": { + "const": "add_component", + "type": "string" + }, + "body": { + "description": "(add_component only) 1-3 paragraph charter (markdown).", + "type": "string" + }, + "name": { + "description": "REQUIRED for add_component — short noun-phrase title.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "parent_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "parent_id", + "name" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", + "properties": { + "action": { + "const": "rename_topic", + "type": "string" + }, + "from": { + "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", + "properties": { + "action": { + "const": "feedback", + "type": "string" + }, + "hindrance": { + "description": "(feedback only) One place Stele was more friction than help.", + "type": "string" + }, + "improvement": { + "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "score": { + "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", + "maximum": 10, + "minimum": 0, + "type": "integer" + }, + "session_id": { + "description": "(feedback only) The session being reflected on. Defaults to the current session.", + "type": "string" + }, + "source": { + "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", + "enum": [ + "agent", + "human" + ], + "type": "string" + }, + "strength": { + "description": "(feedback only) One concrete way Stele helped this session.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", + "properties": { + "action": { + "const": "pause_rag", + "type": "string" + }, + "reason": { + "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", + "type": "string" + }, + "recall_session": { + "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle Stele printed in this conversation, in its recall note or notice. It identifies which local session to update and grants no access on its own.", + "minLength": 1, + "type": "string" + }, + "turns": { + "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "recall_session", + "reason", + "turns" + ], + "type": "object" + } +]
10 tool updates
- Changed
comment1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add\": Leave a comment on any node — the human↔agent steering channel; the next agent that reads the node sees it flagged unseen. Reply by passing parent_comment_id (replies are one level deep). Pass `anchor` to attach to a quoted span (inline comment); omit for node-level. Commenting watches the node, so future comments on it show as unread to you.", - "properties": { - "action": { - "const": "add", - "type": "string" - }, - "anchor": { - "additionalProperties": false, - "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", - "properties": { - "node_version": { - "description": "The node `version` the quote was taken from, as returned when you fetched the node.", - "exclusiveMinimum": 0, - "type": "integer" - }, - "offset": { - "description": "Rough char offset to disambiguate a repeated quote.", - "minimum": 0, - "type": "integer" - }, - "quote": { - "description": "The exact quoted substring to attach the comment to.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "node_version", - "quote" - ], - "type": "object" - }, - "body": { - "description": "REQUIRED for add/update — comment text (markdown).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "REQUIRED for add — the node to comment on (any node type).", - "type": "string" - }, - "parent_comment_id": { - "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", - "format": "uuid", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"update\": Edit the body of a comment you authored. AUTHOR-ONLY. Sets the 'edited' marker and does not re-flag the comment unread for others.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "body": { - "description": "New comment text.", - "minLength": 1, - "type": "string" - }, - "comment_id": { - "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", - "format": "uuid", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"resolve\": Resolve (or reopen) a comment thread. ANY project member may do this, not just the author. Resolve a steering comment once addressed so it stops nagging the next agent — resolved threads drop out of the [N comments, M unread] marker. Applies to the whole thread (resolving a reply resolves its parent).", - "properties": { - "action": { - "const": "resolve", - "type": "string" - }, - "comment_id": { - "description": "A comment_id in the thread (top-level or a reply).", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "resolved": { - "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", - "type": "boolean" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "resolved" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"delete\": Soft-delete a comment you authored — the row stays as a tombstone and replies remain intact (never orphaned). AUTHOR-ONLY; there is no hard delete.", - "properties": { - "action": { - "const": "delete", - "type": "string" - }, - "comment_id": { - "description": "The comment_id to delete.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add\": Leave a comment on any node — the human↔agent steering channel; the next agent that reads the node sees it flagged unseen. Reply by passing parent_comment_id (replies are one level deep). Pass `anchor` to attach to a quoted span (inline comment); omit for node-level. Commenting watches the node, so future comments on it show as unread to you.", + "properties": { + "action": { + "const": "add", + "type": "string" + }, + "anchor": { + "additionalProperties": false, + "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", + "properties": { + "node_version": { + "description": "The node `version` the quote was taken from, as returned when you fetched the node.", + "exclusiveMinimum": 0, + "type": "integer" + }, + "offset": { + "description": "Rough char offset to disambiguate a repeated quote.", + "minimum": 0, + "type": "integer" + }, + "quote": { + "description": "The exact quoted substring to attach the comment to.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "node_version", + "quote" + ], + "type": "object" + }, + "body": { + "description": "REQUIRED for add/update — comment text (markdown).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "REQUIRED for add — the node to comment on (any node type).", + "type": "string" + }, + "parent_comment_id": { + "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", + "format": "uuid", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"update\": Edit the body of a comment you authored. AUTHOR-ONLY. Sets the 'edited' marker and does not re-flag the comment unread for others.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "body": { + "description": "New comment text.", + "minLength": 1, + "type": "string" + }, + "comment_id": { + "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", + "format": "uuid", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"resolve\": Resolve (or reopen) a comment thread. ANY project member may do this, not just the author. Resolve a steering comment once addressed so it stops nagging the next agent — resolved threads drop out of the [N comments, M unread] marker. Applies to the whole thread (resolving a reply resolves its parent).", + "properties": { + "action": { + "const": "resolve", + "type": "string" + }, + "comment_id": { + "description": "A comment_id in the thread (top-level or a reply).", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "resolved": { + "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", + "type": "boolean" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "resolved" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"delete\": Soft-delete a comment you authored — the row stays as a tombstone and replies remain intact (never orphaned). AUTHOR-ONLY; there is no hard delete.", + "properties": { + "action": { + "const": "delete", + "type": "string" + }, + "comment_id": { + "description": "The comment_id to delete.", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id" + ], + "type": "object" + } +]
- Changed
document1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Add a document — a large multi-section markdown artifact (plan, audit, design spec, retrospective). Use over knowledge(action=\"create\") when content is long/narrative rather than a single short structured fact. Documents are surfaced by recall by title only; the body is never auto-injected, so an agent must call fetch DOC-N to read it. component_id is optional (omit to land at the project root; re-parent later via node(action=\"update\")). For the body pass exactly one of `body` (inline markdown) or `file_path` (repo-relative path the server reads, UTF-8 only, max 1 MB) — or neither for a title-only stub. file_path works only on a local connection; over a hosted connection pass the body inline.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "body": { - "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the doc at the project root." - }, - "file_path": { - "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"create_diagram\": Add a diagram — a visual design artifact (architecture, workflow, sequence, or flow diagram) authored in d2 source and rendered to an SVG on its page. Use when a picture explains a design better than prose: the diagram lives in the graph as a durable, referenceable node the next agent and the human can open. The `body` is d2 source — it is rendered server-side (you never write the image). Diagram source is excluded from search and recall (it's structural), but the title + topics stay findable and any shareable ids you label in the diagram (e.g. TASK-123) still create mention links. To show the rendered diagram inline in another node's body, embed it with `{{DIAG-N:short description}}` — it renders as a live image there, not just a link. component_id is optional (omit to land at the project root).", - "properties": { - "action": { - "const": "create_diagram", - "type": "string" - }, - "body": { - "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the diagram at the project root." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Add a document — a large multi-section markdown artifact (plan, audit, design spec, retrospective). Use over knowledge(action=\"create\") when content is long/narrative rather than a single short structured fact. Documents are surfaced by recall by title only; the body is never auto-injected, so an agent must call fetch DOC-N to read it. component_id is optional (omit to land at the project root; re-parent later via node(action=\"update\")). For the body pass exactly one of `body` (inline markdown) or `file_path` (repo-relative path the server reads, UTF-8 only, max 1 MB) — or neither for a title-only stub. file_path works only on a local connection; over a hosted connection pass the body inline.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "body": { + "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the doc at the project root." + }, + "file_path": { + "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"create_diagram\": Add a diagram — a visual design artifact (architecture, workflow, sequence, or flow diagram) authored in d2 source and rendered to an SVG on its page. Use when a picture explains a design better than prose: the diagram lives in the graph as a durable, referenceable node the next agent and the human can open. The `body` is d2 source — it is rendered server-side (you never write the image). Diagram source is excluded from search and recall (it's structural), but the title + topics stay findable and any shareable ids you label in the diagram (e.g. TASK-123) still create mention links. To show the rendered diagram inline in another node's body, embed it with `{{DIAG-N:short description}}` — it renders as a live image there, not just a link. component_id is optional (omit to land at the project root).", + "properties": { + "action": { + "const": "create_diagram", + "type": "string" + }, + "body": { + "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the diagram at the project root." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + } +]
- Changed
knowledge1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Add a knowledge node under an existing active component (component_id is\nREQUIRED) — prefer an existing component over creating one. Search before\nadding: if a near-match exists, update it with node(action=\"update\") instead of duplicating. Always\ninclude topics (without them the node won't surface in topic queries) and link\nrelated nodes — isolated nodes are nearly invisible.\n\n## The knowledge categories\n\nPick the one that matches what the fact *is*, and give the body the shape it wants. Thin one-liners\ndilute the graph — a node that restates its own title helps nobody.\n\n| Category | It is | Body shape |\n|---|---|---|\n| `decision` | A choice that was made, and could have gone another way. | ## Context / ## Alternatives considered / ## Why this won / ## Consequences |\n| `architecture` | A structural fact about how the system is built. | ## What / ## How it works / ## Why this shape |\n| `goal` | What success looks like. | ## Outcome / ## Why it matters / ## How we'll know we're done |\n| `risk` | Something that might go wrong, and hasn't yet. | ## Risk / ## Likelihood & impact / ## Mitigation / ## Trigger |\n| `gap` | A known missing piece — real, but not yet work anyone has committed to. | ## Gap / ## Why it matters / ## What needs to happen to close it |\n| `lesson` | Something learned the hard way, usually from a failure. | ## Symptom / ## Root cause / ## Fix / ## Why this matters |\n| `opportunity` | Possible future work worth considering. | ## Opportunity / ## Why now / ## What it would take |\n| `priority` | What the focus is right now, and what it is not. | ## Focus / ## Why this and not X / ## Time horizon |\n| `fyi` | Context worth remembering that calls for no action. | A paragraph or two; short is fine. |\n\nTwo that get confused: a **`gap`** is a hole someone still has to decide what to do about; once the work\nis agreed it is a **task**, not a gap. A **`risk`** has not happened; a **`lesson`** already did.\n\n**A lesson from a bug is worth little without its trigger.** Put the literal error message, the failing\nsymbol, the command that broke, into the body. The graph resurfaces knowledge by matching it against\nwhat an agent is doing, so a lesson carrying the real error string reappears the moment someone hits that\nerror again. Worded abstractly, it stays invisible exactly when it would have helped.\n\n\nLIFECYCLE IS REQUIRED. Decide all three — expires_on_task, expires_at,\ndue_at — every time; pass null to skip any (durable decision/architecture\nusually skip all three). Bound nodes retire themselves, which is what keeps\nthe graph self-maintaining — that's why these aren't optional.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "body": { - "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "category": { - "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "due_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." - }, - "expires_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." - }, - "expires_on_task": { - "anyOf": [ - { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ], - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_type": { - "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — one-line summary.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "category", - "title", - "expires_on_task", - "due_at", - "expires_at" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"review\": Verify a knowledge node: record that you deliberately confirmed its claims are STILL TRUE against current reality, stamping `last_verified_at` to now. This verification axis is orthogonal to freshness — the only signal that catches a recently-written yet factually-wrong node. Actually check it first; if it's wrong, do NOT verify — use node(action=\"update\") or a supersedes edge instead. Does NOT change content, freshness, or source_type. Knowledge only; tasks/components are rejected.", - "properties": { - "action": { - "const": "review", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Add a knowledge node under an existing active component (component_id is\nREQUIRED) — prefer an existing component over creating one. Search before\nadding: if a near-match exists, update it with node(action=\"update\") instead of duplicating. Always\ninclude topics (without them the node won't surface in topic queries) and link\nrelated nodes — isolated nodes are nearly invisible.\n\n## The knowledge categories\n\nPick the one that matches what the fact *is*, and give the body the shape it wants. Thin one-liners\ndilute the graph — a node that restates its own title helps nobody.\n\n| Category | It is | Body shape |\n|---|---|---|\n| `decision` | A choice that was made, and could have gone another way. | ## Context / ## Alternatives considered / ## Why this won / ## Consequences |\n| `architecture` | A structural fact about how the system is built. | ## What / ## How it works / ## Why this shape |\n| `goal` | What success looks like. | ## Outcome / ## Why it matters / ## How we'll know we're done |\n| `risk` | Something that might go wrong, and hasn't yet. | ## Risk / ## Likelihood & impact / ## Mitigation / ## Trigger |\n| `gap` | A known missing piece — real, but not yet work anyone has committed to. | ## Gap / ## Why it matters / ## What needs to happen to close it |\n| `lesson` | Something learned the hard way, usually from a failure. | ## Symptom / ## Root cause / ## Fix / ## Why this matters |\n| `opportunity` | Possible future work worth considering. | ## Opportunity / ## Why now / ## What it would take |\n| `priority` | What the focus is right now, and what it is not. | ## Focus / ## Why this and not X / ## Time horizon |\n| `fyi` | Context worth remembering that calls for no action. | A paragraph or two; short is fine. |\n\nTwo that get confused: a **`gap`** is a hole someone still has to decide what to do about; once the work\nis agreed it is a **task**, not a gap. A **`risk`** has not happened; a **`lesson`** already did.\n\n**A lesson from a bug is worth little without its trigger.** Put the literal error message, the failing\nsymbol, the command that broke, into the body. The graph resurfaces knowledge by matching it against\nwhat an agent is doing, so a lesson carrying the real error string reappears the moment someone hits that\nerror again. Worded abstractly, it stays invisible exactly when it would have helped.\n\n\nLIFECYCLE IS REQUIRED. Decide all three — expires_on_task, expires_at,\ndue_at — every time; pass null to skip any (durable decision/architecture\nusually skip all three). Bound nodes retire themselves, which is what keeps\nthe graph self-maintaining — that's why these aren't optional.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "body": { + "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "category": { + "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "due_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." + }, + "expires_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." + }, + "expires_on_task": { + "anyOf": [ + { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ], + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_type": { + "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — one-line summary.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "category", + "title", + "expires_on_task", + "due_at", + "expires_at" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"review\": Verify a knowledge node: record that you deliberately confirmed its claims are STILL TRUE against current reality, stamping `last_verified_at` to now. This verification axis is orthogonal to freshness — the only signal that catches a recently-written yet factually-wrong node. Actually check it first; if it's wrong, do NOT verify — use node(action=\"update\") or a supersedes edge instead. Does NOT change content, freshness, or source_type. Knowledge only; tasks/components are rejected.", + "properties": { + "action": { + "const": "review", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + } +]
- Changed
list1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "what=\"nodes\": Generic, paginated node listing — the lean single tool the typed CLI `list` commands ride on (knowledge/document/component plain lists). Filter by type, status, and parent component; ordered by most-recently-updated. Bodies are omitted — fetch one with fetch for the full body. For the faceted task view use list_tasks; for the objective tracker use list(what=\"objectives\").", - "properties": { - "component_id": { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - "limit": { - "description": "(nodes/documents/diagrams/node_versions only) Page size / max results (default 50, max 200; defaults vary by collection).", - "exclusiveMinimum": 0, - "maximum": 200, - "type": "integer" - }, - "offset": { - "description": "(nodes/documents/diagrams only) Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "(nodes/documents/diagrams only) Filter to one exact status.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "type": { - "description": "(nodes only) Limit to one node type (component | knowledge | task | document | diagram).", - "enum": [ - "component", - "knowledge", - "task", - "document", - "diagram" - ], - "type": "string" - }, - "what": { - "const": "nodes", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"components\": List components in the active project. Omit `parent_id` to get the ENTIRE tree (every component, bodyless) — the default for finding the right component to file a task/knowledge under. Pass `parent_id` (COMP-N) for a component's direct children, or null for root-level only. Always check this before creating a component: prefer reusing an existing one, they shouldn't proliferate. Cached ~30s; invalidated on add/archive_component.", - "properties": { - "parent_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(components only) Omit to list the WHOLE component tree (every component, bodyless) — do this to find where a task/knowledge should be filed. Pass a component id (COMP-N) for just its direct children, or null for root-level only." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "components", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"topics\": List every topic in the active project with its node count, counted across active knowledge + open/in_progress tasks only. Use before rename_topic / merge_topics to spot near-duplicates (releng / release-eng / release-engineering).", - "properties": { - "min_count": { - "description": "(topics only) Minimum count to include a topic in the result. Default 1.", - "minimum": 1, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "topics", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"documents\": List documents in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` (first 10 lines, long lines truncated) so agents can triage in one call; use fetch DOC-N for the full body.", - "properties": { - "component_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Parent component shareable id. Omit for all docs." - }, - "limit": { - "description": "Page size (default 50, max 100).", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "description": "Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Default 'active'.", - "enum": [ - "active", - "archived" - ], - "type": "string" - }, - "what": { - "const": "documents", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"diagrams\": List diagrams in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` of the d2 source so agents can triage in one call; use fetch DIAG-N for the full source.", - "properties": { - "component_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Parent component shareable id. Omit for all diagrams." - }, - "limit": { - "description": "Page size (default 50, max 100).", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "description": "Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Default 'active'.", - "enum": [ - "active", - "archived" - ], - "type": "string" - }, - "what": { - "const": "diagrams", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"objectives\": Overview of the project's objectives — for each: derived status (on-track / drifting / done / superseded), milestone count, task rollup (done/total), and how many loose (un-milestoned) tasks it has. Plus the project vision and the count of drift tasks serving no objective. Use this to answer 'what are we steering toward, and is anything drifting?'", - "properties": { - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "objectives", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"workspaces\": List a project's workspaces (named scoped overlays), marking which are ACTIVE for this session. Trunk is always implicitly active and is not listed.", - "properties": { - "include_archived": { - "description": "(workspaces only) Include archived/merged workspaces (default false).", - "type": "boolean" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "workspaces", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"comments\": List a node's comments as threads (top-level comment + replies), each flagged seen/unseen for you with its anchor status. Resolved threads and soft-deleted tombstones are included but don't count toward active/unread. Reading does NOT mark them seen (pass mark_seen=true to acknowledge).", - "properties": { - "include_replies": { - "description": "(comments only) Include replies under each thread (default true).", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for comments — the node whose comments to list.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "comments", - "type": "string" - } - }, - "required": [ - "what", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"node_versions\": List a node's content version history — every superseded title/body snapshot, newest first, with the editor and timestamp. Captured automatically by a DB trigger on every content edit, so it's a complete backup. PULL-ONLY: never auto-injected; call deliberately to inspect churn or recover what a node said before a bad edit.", - "properties": { - "limit": { - "description": "Max versions to return (default 100, server cap 500).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "node_id": { - "description": "REQUIRED for node_versions — node shareable id (e.g. KNOW-42, DOC-7).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "node_versions", - "type": "string" - } - }, - "required": [ - "what", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "what=\"projects\": List Stele projects the current user is a member of. Use the `slug` from here as the `project_slug` argument on every other tool. Cached for ~30s per MCP session — cheap to call repeatedly.", - "properties": { - "what": { - "const": "projects", - "type": "string" - } - }, - "required": [ - "what" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "what=\"nodes\": Generic, paginated node listing — the lean single tool the typed CLI `list` commands ride on (knowledge/document/component plain lists). Filter by type, status, and parent component; ordered by most-recently-updated. Bodies are omitted — fetch one with fetch for the full body. For the faceted task view use list_tasks; for the objective tracker use list(what=\"objectives\").", + "properties": { + "component_id": { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + "limit": { + "description": "(nodes/documents/diagrams/node_versions only) Page size / max results (default 50, max 200; defaults vary by collection).", + "exclusiveMinimum": 0, + "maximum": 200, + "type": "integer" + }, + "offset": { + "description": "(nodes/documents/diagrams only) Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "(nodes/documents/diagrams only) Filter to one exact status.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "type": { + "description": "(nodes only) Limit to one node type (component | knowledge | task | document | diagram).", + "enum": [ + "component", + "knowledge", + "task", + "document", + "diagram" + ], + "type": "string" + }, + "what": { + "const": "nodes", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"components\": List components in the active project. Omit `parent_id` to get the ENTIRE tree (every component, bodyless) — the default for finding the right component to file a task/knowledge under. Pass `parent_id` (COMP-N) for a component's direct children, or null for root-level only. Always check this before creating a component: prefer reusing an existing one, they shouldn't proliferate. Cached ~30s; invalidated on add/archive_component.", + "properties": { + "parent_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(components only) Omit to list the WHOLE component tree (every component, bodyless) — do this to find where a task/knowledge should be filed. Pass a component id (COMP-N) for just its direct children, or null for root-level only." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "components", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"topics\": List every topic in the active project with its node count, counted across active knowledge + open/in_progress tasks only. Use before rename_topic / merge_topics to spot near-duplicates (releng / release-eng / release-engineering).", + "properties": { + "min_count": { + "description": "(topics only) Minimum count to include a topic in the result. Default 1.", + "minimum": 1, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "topics", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"documents\": List documents in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` (first 10 lines, long lines truncated) so agents can triage in one call; use fetch DOC-N for the full body.", + "properties": { + "component_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Parent component shareable id. Omit for all docs." + }, + "limit": { + "description": "Page size (default 50, max 100).", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Default 'active'.", + "enum": [ + "active", + "archived" + ], + "type": "string" + }, + "what": { + "const": "documents", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"diagrams\": List diagrams in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` of the d2 source so agents can triage in one call; use fetch DIAG-N for the full source.", + "properties": { + "component_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Parent component shareable id. Omit for all diagrams." + }, + "limit": { + "description": "Page size (default 50, max 100).", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Default 'active'.", + "enum": [ + "active", + "archived" + ], + "type": "string" + }, + "what": { + "const": "diagrams", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"objectives\": Overview of the project's objectives — for each: derived status (on-track / drifting / done / superseded), milestone count, task rollup (done/total), and how many loose (un-milestoned) tasks it has. Plus the project vision and the count of drift tasks serving no objective. Use this to answer 'what are we steering toward, and is anything drifting?'", + "properties": { + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "objectives", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"workspaces\": List a project's workspaces (named scoped overlays), marking which are ACTIVE for this session. Trunk is always implicitly active and is not listed.", + "properties": { + "include_archived": { + "description": "(workspaces only) Include archived/merged workspaces (default false).", + "type": "boolean" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "workspaces", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"comments\": List a node's comments as threads (top-level comment + replies), each flagged seen/unseen for you with its anchor status. Resolved threads and soft-deleted tombstones are included but don't count toward active/unread. Reading does NOT mark them seen (pass mark_seen=true to acknowledge).", + "properties": { + "include_replies": { + "description": "(comments only) Include replies under each thread (default true).", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for comments — the node whose comments to list.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "comments", + "type": "string" + } + }, + "required": [ + "what", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"node_versions\": List a node's content version history — every superseded title/body snapshot, newest first, with the editor and timestamp. Captured automatically by a DB trigger on every content edit, so it's a complete backup. PULL-ONLY: never auto-injected; call deliberately to inspect churn or recover what a node said before a bad edit.", + "properties": { + "limit": { + "description": "Max versions to return (default 100, server cap 500).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "node_id": { + "description": "REQUIRED for node_versions — node shareable id (e.g. KNOW-42, DOC-7).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "node_versions", + "type": "string" + } + }, + "required": [ + "what", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"projects\": List Stele projects the current user is a member of. Use the `slug` from here as the `project_slug` argument on every other tool. Each project carries its `visibility`: anyone can read a public project. Cached for ~30s per MCP session — cheap to call repeatedly.", + "properties": { + "what": { + "const": "projects", + "type": "string" + } + }, + "required": [ + "what" + ], + "type": "object" + } +]
- Changed
node1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"update\": Update editable fields on any node; each field is validated against what's valid for the node's type, so type-mismatched fields (e.g. category on a task) are rejected. A task's status may be set to open or backlog here, but claim, completion, and cancellation are REJECTED — use task(action=\"claim\") / task(action=\"complete\") so the claim and completion (with session + agent identity) are recorded atomically, or destructive(action=\"cancel_task\") so the destructive transition records a reason. Archiving a component is also rejected; use destructive(action=\"archive_component\") so active children cannot be orphaned. `parent_component_id` reparents across components in the same project. Surgical edits: pass `edits` (literal old_string→new_string replacements applied server-side) instead of `body` to fix a substring without resending the whole body — they're mutually exclusive, and a non-matching old_string errors rather than no-ops. Optional `expected_version` gives optimistic concurrency.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "anchor_files": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." - }, - "anchor_symbols": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — user uuid (not a shareable id)" - }, - "body": { - "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": [ - "string", - "null" - ] - }, - "category": { - "anyOf": [ - { - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge-type nodes only" - }, - "due_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." - }, - "edits": { - "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", - "items": { - "additionalProperties": false, - "properties": { - "new_string": { - "description": "Replacement text.", - "type": "string" - }, - "old_string": { - "description": "Exact substring to find in the current body.", - "minLength": 1, - "type": "string" - }, - "replace_all": { - "description": "Replace every occurrence (default: require a unique match).", - "type": "boolean" - } - }, - "required": [ - "old_string", - "new_string" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "expected_version": { - "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "expires_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." - }, - "hidden": { - "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", - "type": "string" - }, - "parent_component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "source": { - "anyOf": [ - { - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." - }, - "source_type": { - "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "anyOf": [ - { - "format": "uri", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." - }, - "status": { - "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "title": { - "description": "(update only) New title.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(update only) knowledge or task nodes only", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"move\": Move an EXISTING node between scopes — into a workspace (hiding it from trunk readers) or back to trunk (shared truth). Re-scopes by id; reversible, never deletes. Distinct from destructive(action=\"cherry_pick_workspace\") (which promotes to trunk WITH provenance) — This is a plain re-assignment in any direction.", - "properties": { - "action": { - "const": "move", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"link\": Create a typed semantic link between two nodes (src → dst). Idempotent: re-linking the same (src, dst, relation) returns the existing edge, no error.\n\nSome relations change the graph: `supersedes` retires dst (it stays findable, but comes back marked SUPERSEDED and ranked below live knowledge), and `expires-on` retires src later. Every relation except `relates-to` is a claim and needs a `reason`; `supersedes` also needs `evidence`. If you cannot state the reason, use `relates-to`.\n\n## What each link means — and what it does\n\nAn edge is not decoration. Some of these change the graph.\n\n| Relation | Direction | Use it when | What it actually does |\n|---|---|---|---|\n| `relates-to` | A → B | Someone who pulls up B should know A exists. The weakest signal — use it when nothing typed fits. | Nothing but a pointer. Both nodes stay active. |\n| `blocks` | child → parent | A must land before B can be completed or become true. Also the **subtask** relation. | Surfaces A as a subtask of B and drives B's progress. |\n| `fulfills` | task → knowledge | This task delivers or acts on that decision, goal, or risk. | Records that the work discharged the commitment. |\n| `caused-by` | A → B | B is the root cause of A. | Builds the causal chain a provenance walk follows. |\n| `contradicts` | A → B | Two facts conflict and you cannot tell which is true. | Flags it for a human. **Both stay active** — this is the honest \"I don't know\" edge. |\n| `supersedes` | new → old | The old node has become **wrong to act on** — an agent following it would now do the wrong thing. **Never** for something that is merely older: history that is still true must stay. | **Retires the old node.** It stays in the graph and stays findable, but it comes back marked `SUPERSEDED` and ranked below live knowledge — so nothing acts on it as current. |\n| `expires-on` | knowledge → task | This knowledge is only true until that task closes. | Retires the knowledge when the task completes — same marked-not-deleted treatment. |\n\n`mentions` is not linkable by hand — write the id into a body and it is created for you.", - "properties": { - "action": { - "const": "link", - "type": "string" - }, - "dst_id": { - "description": "REQUIRED for link — destination node shareable id.", - "type": "string" - }, - "evidence": { - "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", - "maxLength": 4000, - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reason": { - "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", - "maxLength": 2000, - "minLength": 1, - "type": "string" - }, - "relation": { - "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", - "enum": [ - "relates-to", - "blocks", - "fulfills", - "contradicts", - "supersedes", - "caused-by", - "expires-on" - ], - "type": "string" - }, - "src_id": { - "description": "REQUIRED for link — source node shareable id.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "src_id", - "dst_id", - "relation" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"update\": Update editable fields on any node; each field is validated against what's valid for the node's type, so type-mismatched fields (e.g. category on a task) are rejected. A task's status may be set to open or backlog here, but claim, completion, and cancellation are REJECTED — use task(action=\"claim\") / task(action=\"complete\") so the claim and completion (with session + agent identity) are recorded atomically, or destructive(action=\"cancel_task\") so the destructive transition records a reason. Archiving a component is also rejected; use destructive(action=\"archive_component\") so active children cannot be orphaned. `parent_component_id` reparents across components in the same project. Surgical edits: pass `edits` (literal old_string→new_string replacements applied server-side) instead of `body` to fix a substring without resending the whole body — they're mutually exclusive, and a non-matching old_string errors rather than no-ops. Optional `expected_version` gives optimistic concurrency.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "anchor_files": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." + }, + "anchor_symbols": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — user uuid (not a shareable id)" + }, + "body": { + "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": [ + "string", + "null" + ] + }, + "category": { + "anyOf": [ + { + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge-type nodes only" + }, + "due_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." + }, + "edits": { + "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", + "items": { + "additionalProperties": false, + "properties": { + "new_string": { + "description": "Replacement text.", + "type": "string" + }, + "old_string": { + "description": "Exact substring to find in the current body.", + "minLength": 1, + "type": "string" + }, + "replace_all": { + "description": "Replace every occurrence (default: require a unique match).", + "type": "boolean" + } + }, + "required": [ + "old_string", + "new_string" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "expected_version": { + "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." + }, + "hidden": { + "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", + "type": "string" + }, + "parent_component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "source": { + "anyOf": [ + { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." + }, + "source_type": { + "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "anyOf": [ + { + "format": "uri", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." + }, + "status": { + "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "title": { + "description": "(update only) New title.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(update only) knowledge or task nodes only", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"move\": Move an EXISTING node between scopes — into a workspace (hiding it from trunk readers) or back to trunk (shared truth). Re-scopes by id; reversible, never deletes. Distinct from destructive(action=\"cherry_pick_workspace\") (which promotes to trunk WITH provenance) — This is a plain re-assignment in any direction.", + "properties": { + "action": { + "const": "move", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"link\": Create a typed semantic link between two nodes (src → dst). Idempotent: re-linking the same (src, dst, relation) returns the existing edge, no error.\n\nSome relations change the graph: `supersedes` retires dst (it stays findable, but comes back marked SUPERSEDED and ranked below live knowledge), and `expires-on` retires src later. Every relation except `relates-to` is a claim and needs a `reason`; `supersedes` also needs `evidence`. If you cannot state the reason, use `relates-to`.\n\n## What each link means — and what it does\n\nAn edge is not decoration. Some of these change the graph.\n\n| Relation | Direction | Use it when | What it actually does |\n|---|---|---|---|\n| `relates-to` | A → B | Someone who pulls up B should know A exists. The weakest signal — use it when nothing typed fits. | Nothing but a pointer. Both nodes stay active. |\n| `blocks` | child → parent | A must land before B can be completed or become true. Also the **subtask** relation. | Surfaces A as a subtask of B and drives B's progress. |\n| `fulfills` | task → knowledge | This task delivers or acts on that decision, goal, or risk. | Records that the work discharged the commitment. |\n| `caused-by` | A → B | B is the root cause of A. | Builds the causal chain a provenance walk follows. |\n| `contradicts` | A → B | Two facts conflict and you cannot tell which is true. | Flags it for a human. **Both stay active** — this is the honest \"I don't know\" edge. |\n| `supersedes` | new → old | The old node has become **wrong to act on** — an agent following it would now do the wrong thing. **Never** for something that is merely older: history that is still true must stay. | **Retires the old node.** It stays in the graph and stays findable, but it comes back marked `SUPERSEDED` and ranked below live knowledge — so nothing acts on it as current. |\n| `expires-on` | knowledge → task | This knowledge is only true until that task closes. | Retires the knowledge when the task completes — same marked-not-deleted treatment. |\n\n`mentions` is not linkable by hand — write the id into a body and it is created for you.", + "properties": { + "action": { + "const": "link", + "type": "string" + }, + "dst_id": { + "description": "REQUIRED for link — destination node shareable id.", + "type": "string" + }, + "evidence": { + "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", + "maxLength": 4000, + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reason": { + "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "relation": { + "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", + "enum": [ + "relates-to", + "blocks", + "fulfills", + "contradicts", + "supersedes", + "caused-by", + "expires-on" + ], + "type": "string" + }, + "src_id": { + "description": "REQUIRED for link — source node shareable id.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "src_id", + "dst_id", + "relation" + ], + "type": "object" + } +]
- Changed
objective1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create an objective — a standing goal whose end-state lives in its body, with ordered milestone phases under it. Non-claimable; it rolls up from its milestones and the tasks attached to them. Add milestones with objective(action=\"add_milestone\"), then attach tasks with objective(action=\"attach\").", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "body": { - "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create/add_milestone — parent component shareable id.", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", - "type": "string" - }, - "target_date": { - "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create/add_milestone only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"add_milestone\": Create a milestone — an ordered phase under an objective. Appended after the objective's existing milestones unless you pass an explicit position. Attach tasks to it with objective(action=\"attach\").", - "properties": { - "action": { - "const": "add_milestone", - "type": "string" - }, - "body": { - "description": "Optional milestone description.", - "type": "string" - }, - "component_id": { - "description": "Parent component shareable id. REQUIRED.", - "type": "string" - }, - "objective_id": { - "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", - "type": "string" - }, - "position": { - "description": "1-based order within the objective. Appended last when omitted.", - "type": "integer" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "target_date": { - "description": "Optional soft target date, ISO e.g. 2026-09-30.", - "type": "string" - }, - "title": { - "description": "Milestone one-liner.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"attach\": Attach a plain task to a milestone, re-parenting it off any prior milestone/objective. Real task→task `blocks` dependencies are preserved — only the milestone/objective containment moves.", - "properties": { - "action": { - "const": "attach", - "type": "string" - }, - "milestone_id": { - "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "milestone_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"move\": Attach a plain task directly to an objective — a 'loose' task serving the objective without a milestone. Re-parents off any prior container. Once an objective has milestones, loose tasks are its drift signal.", - "properties": { - "action": { - "const": "move", - "type": "string" - }, - "objective_id": { - "description": "The objective to attach it to directly (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The plain task to attach (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "objective_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"detach\": Remove a task's milestone/objective containment. Genuine task→task `blocks` dependencies are left intact.", - "properties": { - "action": { - "const": "detach", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The task to detach from its milestone/objective (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"reorder\": Reorder an objective's milestones. `milestone_ids` must list exactly the objective's own milestones, in the new order (a total permutation); a partial or foreign list is rejected so positions never go inconsistent.", - "properties": { - "action": { - "const": "reorder", - "type": "string" - }, - "milestone_ids": { - "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", - "items": { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "objective_id": { - "description": "The objective whose milestones to reorder (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "milestone_ids" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"set_target\": Set (or clear, with null) a milestone's soft target date.", - "properties": { - "action": { - "const": "set_target", - "type": "string" - }, - "milestone_id": { - "description": "The milestone to retarget (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "ISO target date e.g. 2026-09-30, or null to clear.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "action", - "project_slug", - "milestone_id", - "target_date" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"achieve\": Mark an objective as achieved (status → done).", - "properties": { - "action": { - "const": "achieve", - "type": "string" - }, - "objective_id": { - "description": "The objective to mark achieved (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create an objective — a standing goal whose end-state lives in its body, with ordered milestone phases under it. Non-claimable; it rolls up from its milestones and the tasks attached to them. Add milestones with objective(action=\"add_milestone\"), then attach tasks with objective(action=\"attach\").", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "body": { + "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create/add_milestone — parent component shareable id.", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", + "type": "string" + }, + "target_date": { + "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create/add_milestone only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"add_milestone\": Create a milestone — an ordered phase under an objective. Appended after the objective's existing milestones unless you pass an explicit position. Attach tasks to it with objective(action=\"attach\").", + "properties": { + "action": { + "const": "add_milestone", + "type": "string" + }, + "body": { + "description": "Optional milestone description.", + "type": "string" + }, + "component_id": { + "description": "Parent component shareable id. REQUIRED.", + "type": "string" + }, + "objective_id": { + "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", + "type": "string" + }, + "position": { + "description": "1-based order within the objective. Appended last when omitted.", + "type": "integer" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "target_date": { + "description": "Optional soft target date, ISO e.g. 2026-09-30.", + "type": "string" + }, + "title": { + "description": "Milestone one-liner.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"attach\": Attach a plain task to a milestone, re-parenting it off any prior milestone/objective. Real task→task `blocks` dependencies are preserved — only the milestone/objective containment moves.", + "properties": { + "action": { + "const": "attach", + "type": "string" + }, + "milestone_id": { + "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "milestone_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"move\": Attach a plain task directly to an objective — a 'loose' task serving the objective without a milestone. Re-parents off any prior container. Once an objective has milestones, loose tasks are its drift signal.", + "properties": { + "action": { + "const": "move", + "type": "string" + }, + "objective_id": { + "description": "The objective to attach it to directly (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The plain task to attach (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "objective_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"detach\": Remove a task's milestone/objective containment. Genuine task→task `blocks` dependencies are left intact.", + "properties": { + "action": { + "const": "detach", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The task to detach from its milestone/objective (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"reorder\": Reorder an objective's milestones. `milestone_ids` must list exactly the objective's own milestones, in the new order (a total permutation); a partial or foreign list is rejected so positions never go inconsistent.", + "properties": { + "action": { + "const": "reorder", + "type": "string" + }, + "milestone_ids": { + "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", + "items": { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "objective_id": { + "description": "The objective whose milestones to reorder (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "milestone_ids" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"set_target\": Set (or clear, with null) a milestone's soft target date.", + "properties": { + "action": { + "const": "set_target", + "type": "string" + }, + "milestone_id": { + "description": "The milestone to retarget (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "ISO target date e.g. 2026-09-30, or null to clear.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "action", + "project_slug", + "milestone_id", + "target_date" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"achieve\": Mark an objective as achieved (status → done).", + "properties": { + "action": { + "const": "achieve", + "type": "string" + }, + "objective_id": { + "description": "The objective to mark achieved (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id" + ], + "type": "object" + } +]
- Changed
organize1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", - "properties": { - "action": { - "const": "add_component", - "type": "string" - }, - "body": { - "description": "(add_component only) 1-3 paragraph charter (markdown).", - "type": "string" - }, - "name": { - "description": "REQUIRED for add_component — short noun-phrase title.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "parent_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "parent_id", - "name" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", - "properties": { - "action": { - "const": "rename_topic", - "type": "string" - }, - "from": { - "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", - "properties": { - "action": { - "const": "feedback", - "type": "string" - }, - "hindrance": { - "description": "(feedback only) One place Stele was more friction than help.", - "type": "string" - }, - "improvement": { - "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "score": { - "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", - "maximum": 10, - "minimum": 0, - "type": "integer" - }, - "session_id": { - "description": "(feedback only) The session being reflected on. Defaults to the current session.", - "type": "string" - }, - "source": { - "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", - "enum": [ - "agent", - "human" - ], - "type": "string" - }, - "strength": { - "description": "(feedback only) One concrete way Stele helped this session.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", - "properties": { - "action": { - "const": "pause_rag", - "type": "string" - }, - "reason": { - "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", - "type": "string" - }, - "recall_session": { - "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", - "minLength": 1, - "type": "string" - }, - "turns": { - "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "recall_session", - "reason", - "turns" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", + "properties": { + "action": { + "const": "add_component", + "type": "string" + }, + "body": { + "description": "(add_component only) 1-3 paragraph charter (markdown).", + "type": "string" + }, + "name": { + "description": "REQUIRED for add_component — short noun-phrase title.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "parent_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "parent_id", + "name" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", + "properties": { + "action": { + "const": "rename_topic", + "type": "string" + }, + "from": { + "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", + "properties": { + "action": { + "const": "feedback", + "type": "string" + }, + "hindrance": { + "description": "(feedback only) One place Stele was more friction than help.", + "type": "string" + }, + "improvement": { + "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "score": { + "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", + "maximum": 10, + "minimum": 0, + "type": "integer" + }, + "session_id": { + "description": "(feedback only) The session being reflected on. Defaults to the current session.", + "type": "string" + }, + "source": { + "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", + "enum": [ + "agent", + "human" + ], + "type": "string" + }, + "strength": { + "description": "(feedback only) One concrete way Stele helped this session.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", + "properties": { + "action": { + "const": "pause_rag", + "type": "string" + }, + "reason": { + "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", + "type": "string" + }, + "recall_session": { + "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", + "minLength": 1, + "type": "string" + }, + "turns": { + "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "recall_session", + "reason", + "turns" + ], + "type": "object" + } +]
- Changed
task1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create a task under a component. One task ≈ one PR. `success_criteria` is required — it is the contract the implementer and the reviewer both work against. Pass `check` only when a command can genuinely decide done without a human; for unattended certification also pass `check_paths`. Most tasks have neither, and that is fine. Give a markdown body with ## Why / ## How / ## Test plan — the canonical contract headings are written for you from the arguments.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this task is about (function / class / constant / env var).", - "items": { - "type": "string" - }, - "type": "array" - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." - }, - "body": { - "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "check": { - "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", - "type": "string" - }, - "check_paths": { - "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "kind": { - "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal (end-state in its body) with ordered 'milestone' phases under it.", - "enum": [ - "task", - "epic", - "objective", - "milestone" - ], - "type": "string" - }, - "position": { - "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", - "type": "integer" - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "success_criteria": { - "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title", - "success_criteria" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"claim\": Atomically claim a task (open → in_progress), recording you as owner. Claim before you start so others know you're on it. Returns the task, or { result: 'conflict' } if someone else holds it — no race window.", - "properties": { - "action": { - "const": "claim", - "type": "string" - }, - "dispatched_with": { - "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", - "type": "string" - }, - "parent_task_id": { - "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"complete\": Mark a task done. REQUIRED `summary` is appended under ## Completion summary (what shipped, where it landed, what you learned). Also closes a container (epic/objective/milestone): no claim needed, but it is REFUSED while any child is unfinished, and the error names them. A container normally closes itself when its last child closes, so completing one is mainly how you record what it delivered — and it marks the close as yours, so a child reopening later won't undo it. The result may list nearby decision/architecture nodes to re-verify (still-true / edit / supersede); that part is advisory.", - "properties": { - "action": { - "const": "complete", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "summary": { - "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "summary" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"release\": Release a task you claimed (in_progress → open) so someone else can pick it up — use when you can't finish (out of context, blocked, wrong scope).", - "properties": { - "action": { - "const": "release", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"reopen\": Recovery: send a closed task (done or cancelled) back to open and clear its completion metadata so it reads as unheld and claimable again. For a stuck in_progress task, use task(action=\"force_release\") instead (this is for closed tasks).", - "properties": { - "action": { - "const": "reopen", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"force_release\": Admin recovery: release an in_progress task WITHOUT holding the claim, so it returns to open for others. The release is attributed to you in the claim history (not the prior claimer). For a closed task use task(action=\"reopen\"); to take a task over rather than free it, use destructive(action=\"force_claim_task\").", - "properties": { - "action": { - "const": "force_release", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"loop_finalize\": Create the durable final loop envelope. Deterministic correctness comes only from the latest mechanically evidenced attempt; independent reviewer and operator intent/scope judgments remain separate fields. Approval requires all three, so model judgment can reject work but can never manufacture correctness.", - "properties": { - "action": { - "const": "loop_finalize", - "type": "string" - }, - "node_id": { - "description": "The loop task to finalize (TASK-N).", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "operator_summary": { - "description": "Why the operator judged the change in or out of intent and scope.", - "minLength": 1, - "type": "string" - }, - "operator_verdict": { - "description": "The operator's judgment of whether the change stays within the task's intent and scope.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reviewer_summary": { - "description": "Why the reviewer approved or rejected the change.", - "minLength": 1, - "type": "string" - }, - "reviewer_verdict": { - "description": "The independent reviewer's judgment of the change.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "reviewer_verdict", - "reviewer_summary", - "operator_verdict", - "operator_summary" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create a task under a component. One task ≈ one PR. `success_criteria` is required — it is the contract the implementer and the reviewer both work against. Pass `check` only when a command can genuinely decide done without a human; for unattended certification also pass `check_paths`. Most tasks have neither, and that is fine. Give a markdown body with ## Why / ## How / ## Test plan — the canonical contract headings are written for you from the arguments.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this task is about (function / class / constant / env var).", + "items": { + "type": "string" + }, + "type": "array" + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." + }, + "body": { + "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "check": { + "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", + "type": "string" + }, + "check_paths": { + "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "kind": { + "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal with ordered 'milestone' phases under it. Every kind still takes `success_criteria`: for an objective it is the end state that means the goal is reached.", + "enum": [ + "task", + "epic", + "objective", + "milestone" + ], + "type": "string" + }, + "position": { + "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", + "type": "integer" + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "success_criteria": { + "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title", + "success_criteria" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"claim\": Atomically claim a task (open → in_progress), recording you as owner. Claim before you start so others know you're on it. Returns the task, or { result: 'conflict' } if someone else holds it — no race window.", + "properties": { + "action": { + "const": "claim", + "type": "string" + }, + "dispatched_with": { + "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", + "type": "string" + }, + "parent_task_id": { + "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"complete\": Mark a task done. REQUIRED `summary` is appended under ## Completion summary (what shipped, where it landed, what you learned). Also closes a container (epic/objective/milestone): no claim needed, but it is REFUSED while any child is unfinished, and the error names them. A container normally closes itself when its last child closes, so completing one is mainly how you record what it delivered — and it marks the close as yours, so a child reopening later won't undo it. The result may list nearby decision/architecture nodes to re-verify (still-true / edit / supersede); that part is advisory.", + "properties": { + "action": { + "const": "complete", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "summary": { + "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "summary" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"release\": Release a task you claimed (in_progress → open) so someone else can pick it up — use when you can't finish (out of context, blocked, wrong scope).", + "properties": { + "action": { + "const": "release", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"reopen\": Recovery: send a closed task (done or cancelled) back to open and clear its completion metadata so it reads as unheld and claimable again. For a stuck in_progress task, use task(action=\"force_release\") instead (this is for closed tasks).", + "properties": { + "action": { + "const": "reopen", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"force_release\": Admin recovery: release an in_progress task WITHOUT holding the claim, so it returns to open for others. The release is attributed to you in the claim history (not the prior claimer). For a closed task use task(action=\"reopen\"); to take a task over rather than free it, use destructive(action=\"force_claim_task\").", + "properties": { + "action": { + "const": "force_release", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"loop_finalize\": Create the durable final loop envelope. Deterministic correctness comes only from the latest mechanically evidenced attempt; independent reviewer and operator intent/scope judgments remain separate fields. Approval requires all three, so model judgment can reject work but can never manufacture correctness.", + "properties": { + "action": { + "const": "loop_finalize", + "type": "string" + }, + "node_id": { + "description": "The loop task to finalize (TASK-N).", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "operator_summary": { + "description": "Why the operator judged the change in or out of intent and scope.", + "minLength": 1, + "type": "string" + }, + "operator_verdict": { + "description": "The operator's judgment of whether the change stays within the task's intent and scope.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reviewer_summary": { + "description": "Why the reviewer approved or rejected the change.", + "minLength": 1, + "type": "string" + }, + "reviewer_verdict": { + "description": "The independent reviewer's judgment of the change.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "reviewer_verdict", + "reviewer_summary", + "operator_verdict", + "operator_summary" + ], + "type": "object" + } +]
- Changed
timeline1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"list\": List the project's Timeline: automatic events captured on graph writes and task lifecycle actions, interleaved with one-line custom entries logged by agents and automations, newest first. Filters (AND-combined): verbs, action type, actor user/agent, capture source, status, component, node (matches the entry's subject or a linked node), and a time range. A custom entry's linked nodes resolve to compact references (id, type, title, status) — never raw uuids. Keyset-paginated, not offset-based: pass a previous page's `next_before` back as `before` to fetch the next (older) page.", - "properties": { - "action": { - "const": "list", - "type": "string" - }, - "agent": { - "description": "Filter to entries attributed to this agent name.", - "type": "string" - }, - "before": { - "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", - "type": "string" - }, - "component_id": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "from": { - "description": "ISO timestamp — only entries at or after this time.", - "type": "string" - }, - "limit": { - "description": "Page size (default 50, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "Limit to entries whose subject is this node, OR that link to it.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", - "type": "string" - }, - "source": { - "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", - "type": "string" - }, - "status": { - "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "to": { - "description": "ISO timestamp — only entries before this time.", - "type": "string" - }, - "type": { - "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", - "type": "string" - }, - "user": { - "description": "Filter to entries attributed to this user (uuid, not a shareable id).", - "format": "uuid", - "type": "string" - }, - "verbs": { - "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", - "items": { - "enum": [ - "created", - "updated", - "status_changed", - "superseded", - "commented", - "claimed", - "released", - "custom", - "linked", - "unlinked" - ], - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"log\": Append a one-line entry to the project's Timeline — for an agent or automation to journal a milestone, with links to the graph items holding the substance. `message` is a short, human-readable line: one thing that happened, never the reasoning behind it or the decision it produced — put those in a knowledge or task node and reference them with `link` instead. `type` is a free-form, caller-namespaced label so entries from different automations don't collide. The Timeline is append-only: a correction is a new entry, not an edit. This capability can be turned off for a project; when it is, the call succeeds but writes nothing, and the response says so.", - "properties": { - "action": { - "const": "log", - "type": "string" - }, - "link": { - "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", - "items": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "message": { - "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Outcome of the journaled action. Omit unless you have one to report.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "type": { - "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", - "maxLength": 100, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "type", - "message" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"list\": List the project's Timeline: automatic events captured on graph writes and task lifecycle actions, interleaved with one-line custom entries logged by agents and automations, newest first. Filters (AND-combined): verbs, action type, actor user/agent, capture source, status, component, node (matches the entry's subject or a linked node), and a time range. A custom entry's linked nodes resolve to compact references (id, type, title, status) — never raw uuids. Keyset-paginated, not offset-based: pass a previous page's `next_before` back as `before` to fetch the next (older) page.", + "properties": { + "action": { + "const": "list", + "type": "string" + }, + "agent": { + "description": "Filter to entries attributed to this agent name.", + "type": "string" + }, + "before": { + "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", + "type": "string" + }, + "component_id": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "from": { + "description": "ISO timestamp — only entries at or after this time.", + "type": "string" + }, + "limit": { + "description": "Page size (default 50, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "Limit to entries whose subject is this node, OR that link to it.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", + "type": "string" + }, + "source": { + "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", + "type": "string" + }, + "status": { + "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "to": { + "description": "ISO timestamp — only entries before this time.", + "type": "string" + }, + "type": { + "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", + "type": "string" + }, + "user": { + "description": "Filter to entries attributed to this user (uuid, not a shareable id).", + "format": "uuid", + "type": "string" + }, + "verbs": { + "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", + "items": { + "enum": [ + "created", + "updated", + "status_changed", + "superseded", + "commented", + "claimed", + "released", + "custom", + "linked", + "unlinked" + ], + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"log\": Append a one-line entry to the project's Timeline — for an agent or automation to journal a milestone, with links to the graph items holding the substance. `message` is a short, human-readable line: one thing that happened, never the reasoning behind it or the decision it produced — put those in a knowledge or task node and reference them with `link` instead. `type` is a free-form, caller-namespaced label so entries from different automations don't collide. The Timeline is append-only: a correction is a new entry, not an edit. This capability can be turned off for a project; when it is, the call succeeds but writes nothing, and the response says so.", + "properties": { + "action": { + "const": "log", + "type": "string" + }, + "link": { + "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", + "items": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "message": { + "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Outcome of the journaled action. Omit unless you have one to report.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "type": { + "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", + "maxLength": 100, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "type", + "message" + ], + "type": "object" + } +]
- Changed
workspace1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create a named workspace — a scoped overlay on the project's trunk (shared truth). Knowledge written while it's active scopes to it; collaborators off that workspace never see it. permanent = a domain/team overlay that never graduates (promote per-item); experimental = ephemeral, expected to merge or be archived. Prefer OFFERING before creating unless the user asked. Does NOT activate it — call workspace(action=\"activate\") to scope this session to it.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "auto_activate_hint": { - "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", - "type": "string" - }, - "branches": { - "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "description": { - "description": "(create/update only) One-line description of what this overlay is for.", - "type": "string" - }, - "kind": { - "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "name", - "kind" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"update\": Update a workspace's editable fields (name, kind, description, auto-activation hint) and/or ADD branch associations. Only the fields you pass change. Does NOT promote knowledge or change scope — merge/archive have dedicated tools.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "add_branches": { - "description": "(update only) Branch associations to ADD (existing ones are kept).", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "auto_activate_hint": { - "description": "New plain-language auto-activation hint.", - "type": "string" - }, - "description": { - "description": "New one-line description.", - "type": "string" - }, - "kind": { - "description": "Change the workspace kind.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "New name.", - "minLength": 1, - "type": "string" - }, - "privacy_bypass": { - "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", - "type": "boolean" - }, - "privacy_false_positive": { - "description": "With privacy_bypass: true reports the privacy flag as wrong.", - "type": "boolean" - }, - "privacy_judgement_id": { - "description": "With privacy_bypass: the judgement_id the privacy flag returned.", - "type": "string" - }, - "privacy_note": { - "description": "With privacy_bypass: an optional short reason, kept with the bypass.", - "maxLength": 500, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create a named workspace — a scoped overlay on the project's trunk (shared truth). Knowledge written while it's active scopes to it; collaborators off that workspace never see it. permanent = a domain/team overlay that never graduates (promote per-item); experimental = ephemeral, expected to merge or be archived. Prefer OFFERING before creating unless the user asked. Does NOT activate it — call workspace(action=\"activate\") to scope this session to it.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "auto_activate_hint": { + "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", + "type": "string" + }, + "branches": { + "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "description": { + "description": "(create/update only) One-line description of what this overlay is for.", + "type": "string" + }, + "kind": { + "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "name", + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"update\": Update a workspace's editable fields (name, kind, description, auto-activation hint) and/or ADD branch associations. Only the fields you pass change. Does NOT promote knowledge or change scope — merge/archive have dedicated tools.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "add_branches": { + "description": "(update only) Branch associations to ADD (existing ones are kept).", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "auto_activate_hint": { + "description": "New plain-language auto-activation hint.", + "type": "string" + }, + "description": { + "description": "New one-line description.", + "type": "string" + }, + "kind": { + "description": "Change the workspace kind.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "New name.", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag. Only meaningful after a flag; the user decides whether the content can be written.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + } +]
9 tool updates
- Changed
comment1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add\": Leave a comment on any node — the human↔agent steering channel; the next agent that reads the node sees it flagged unseen. Reply by passing parent_comment_id (replies are one level deep). Pass `anchor` to attach to a quoted span (inline comment); omit for node-level. Commenting watches the node, so future comments on it show as unread to you.", - "properties": { - "action": { - "const": "add", - "type": "string" - }, - "anchor": { - "additionalProperties": false, - "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", - "properties": { - "node_version": { - "description": "The node `version` the quote was taken from, as returned when you fetched the node.", - "exclusiveMinimum": 0, - "type": "integer" - }, - "offset": { - "description": "Rough char offset to disambiguate a repeated quote.", - "minimum": 0, - "type": "integer" - }, - "quote": { - "description": "The exact quoted substring to attach the comment to.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "node_version", - "quote" - ], - "type": "object" - }, - "body": { - "description": "REQUIRED for add/update — comment text (markdown).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "REQUIRED for add — the node to comment on (any node type).", - "type": "string" - }, - "parent_comment_id": { - "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"update\": Edit the body of a comment you authored. AUTHOR-ONLY. Sets the 'edited' marker and does not re-flag the comment unread for others.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "body": { - "description": "New comment text.", - "minLength": 1, - "type": "string" - }, - "comment_id": { - "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"resolve\": Resolve (or reopen) a comment thread. ANY project member may do this, not just the author. Resolve a steering comment once addressed so it stops nagging the next agent — resolved threads drop out of the [N comments, M unread] marker. Applies to the whole thread (resolving a reply resolves its parent).", - "properties": { - "action": { - "const": "resolve", - "type": "string" - }, - "comment_id": { - "description": "A comment_id in the thread (top-level or a reply).", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "resolved": { - "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", - "type": "boolean" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "resolved" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"delete\": Soft-delete a comment you authored — the row stays as a tombstone and replies remain intact (never orphaned). AUTHOR-ONLY; there is no hard delete.", - "properties": { - "action": { - "const": "delete", - "type": "string" - }, - "comment_id": { - "description": "The comment_id to delete.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add\": Leave a comment on any node — the human↔agent steering channel; the next agent that reads the node sees it flagged unseen. Reply by passing parent_comment_id (replies are one level deep). Pass `anchor` to attach to a quoted span (inline comment); omit for node-level. Commenting watches the node, so future comments on it show as unread to you.", + "properties": { + "action": { + "const": "add", + "type": "string" + }, + "anchor": { + "additionalProperties": false, + "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", + "properties": { + "node_version": { + "description": "The node `version` the quote was taken from, as returned when you fetched the node.", + "exclusiveMinimum": 0, + "type": "integer" + }, + "offset": { + "description": "Rough char offset to disambiguate a repeated quote.", + "minimum": 0, + "type": "integer" + }, + "quote": { + "description": "The exact quoted substring to attach the comment to.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "node_version", + "quote" + ], + "type": "object" + }, + "body": { + "description": "REQUIRED for add/update — comment text (markdown).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "REQUIRED for add — the node to comment on (any node type).", + "type": "string" + }, + "parent_comment_id": { + "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", + "format": "uuid", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"update\": Edit the body of a comment you authored. AUTHOR-ONLY. Sets the 'edited' marker and does not re-flag the comment unread for others.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "body": { + "description": "New comment text.", + "minLength": 1, + "type": "string" + }, + "comment_id": { + "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", + "format": "uuid", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"resolve\": Resolve (or reopen) a comment thread. ANY project member may do this, not just the author. Resolve a steering comment once addressed so it stops nagging the next agent — resolved threads drop out of the [N comments, M unread] marker. Applies to the whole thread (resolving a reply resolves its parent).", + "properties": { + "action": { + "const": "resolve", + "type": "string" + }, + "comment_id": { + "description": "A comment_id in the thread (top-level or a reply).", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "resolved": { + "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", + "type": "boolean" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "resolved" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"delete\": Soft-delete a comment you authored — the row stays as a tombstone and replies remain intact (never orphaned). AUTHOR-ONLY; there is no hard delete.", + "properties": { + "action": { + "const": "delete", + "type": "string" + }, + "comment_id": { + "description": "The comment_id to delete.", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id" + ], + "type": "object" + } +]
- Changed
document1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Add a document — a large multi-section markdown artifact (plan, audit, design spec, retrospective). Use over knowledge(action=\"create\") when content is long/narrative rather than a single short structured fact. Documents are surfaced by recall by title only; the body is never auto-injected, so an agent must call fetch DOC-N to read it. component_id is optional (omit to land at the project root; re-parent later via node(action=\"update\")). For the body pass exactly one of `body` (inline markdown) or `file_path` (repo-relative path the server reads, UTF-8 only, max 1 MB) — or neither for a title-only stub. file_path works only on a local connection; over a hosted connection pass the body inline.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "body": { - "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the doc at the project root." - }, - "file_path": { - "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"create_diagram\": Add a diagram — a visual design artifact (architecture, workflow, sequence, or flow diagram) authored in d2 source and rendered to an SVG on its page. Use when a picture explains a design better than prose: the diagram lives in the graph as a durable, referenceable node the next agent and the human can open. The `body` is d2 source — it is rendered server-side (you never write the image). Diagram source is excluded from search and recall (it's structural), but the title + topics stay findable and any shareable ids you label in the diagram (e.g. TASK-123) still create mention links. To show the rendered diagram inline in another node's body, embed it with `{{DIAG-N:short description}}` — it renders as a live image there, not just a link. component_id is optional (omit to land at the project root).", - "properties": { - "action": { - "const": "create_diagram", - "type": "string" - }, - "body": { - "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the diagram at the project root." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Add a document — a large multi-section markdown artifact (plan, audit, design spec, retrospective). Use over knowledge(action=\"create\") when content is long/narrative rather than a single short structured fact. Documents are surfaced by recall by title only; the body is never auto-injected, so an agent must call fetch DOC-N to read it. component_id is optional (omit to land at the project root; re-parent later via node(action=\"update\")). For the body pass exactly one of `body` (inline markdown) or `file_path` (repo-relative path the server reads, UTF-8 only, max 1 MB) — or neither for a title-only stub. file_path works only on a local connection; over a hosted connection pass the body inline.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "body": { + "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the doc at the project root." + }, + "file_path": { + "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"create_diagram\": Add a diagram — a visual design artifact (architecture, workflow, sequence, or flow diagram) authored in d2 source and rendered to an SVG on its page. Use when a picture explains a design better than prose: the diagram lives in the graph as a durable, referenceable node the next agent and the human can open. The `body` is d2 source — it is rendered server-side (you never write the image). Diagram source is excluded from search and recall (it's structural), but the title + topics stay findable and any shareable ids you label in the diagram (e.g. TASK-123) still create mention links. To show the rendered diagram inline in another node's body, embed it with `{{DIAG-N:short description}}` — it renders as a live image there, not just a link. component_id is optional (omit to land at the project root).", + "properties": { + "action": { + "const": "create_diagram", + "type": "string" + }, + "body": { + "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the diagram at the project root." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + } +]
- Changed
knowledge1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Add a knowledge node under an existing active component (component_id is\nREQUIRED) — prefer an existing component over creating one. Search before\nadding: if a near-match exists, update it with node(action=\"update\") instead of duplicating. Always\ninclude topics (without them the node won't surface in topic queries) and link\nrelated nodes — isolated nodes are nearly invisible.\n\n## The knowledge categories\n\nPick the one that matches what the fact *is*, and give the body the shape it wants. Thin one-liners\ndilute the graph — a node that restates its own title helps nobody.\n\n| Category | It is | Body shape |\n|---|---|---|\n| `decision` | A choice that was made, and could have gone another way. | ## Context / ## Alternatives considered / ## Why this won / ## Consequences |\n| `architecture` | A structural fact about how the system is built. | ## What / ## How it works / ## Why this shape |\n| `goal` | What success looks like. | ## Outcome / ## Why it matters / ## How we'll know we're done |\n| `risk` | Something that might go wrong, and hasn't yet. | ## Risk / ## Likelihood & impact / ## Mitigation / ## Trigger |\n| `gap` | A known missing piece — real, but not yet work anyone has committed to. | ## Gap / ## Why it matters / ## What needs to happen to close it |\n| `lesson` | Something learned the hard way, usually from a failure. | ## Symptom / ## Root cause / ## Fix / ## Why this matters |\n| `opportunity` | Possible future work worth considering. | ## Opportunity / ## Why now / ## What it would take |\n| `priority` | What the focus is right now, and what it is not. | ## Focus / ## Why this and not X / ## Time horizon |\n| `fyi` | Context worth remembering that calls for no action. | A paragraph or two; short is fine. |\n\nTwo that get confused: a **`gap`** is a hole someone still has to decide what to do about; once the work\nis agreed it is a **task**, not a gap. A **`risk`** has not happened; a **`lesson`** already did.\n\n**A lesson from a bug is worth little without its trigger.** Put the literal error message, the failing\nsymbol, the command that broke, into the body. The graph resurfaces knowledge by matching it against\nwhat an agent is doing, so a lesson carrying the real error string reappears the moment someone hits that\nerror again. Worded abstractly, it stays invisible exactly when it would have helped.\n\n\nLIFECYCLE IS REQUIRED. Decide all three — expires_on_task, expires_at,\ndue_at — every time; pass null to skip any (durable decision/architecture\nusually skip all three). Bound nodes retire themselves, which is what keeps\nthe graph self-maintaining — that's why these aren't optional.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "body": { - "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "category": { - "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "due_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." - }, - "expires_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." - }, - "expires_on_task": { - "anyOf": [ - { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ], - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_type": { - "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — one-line summary.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "category", - "title", - "expires_on_task", - "due_at", - "expires_at" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"review\": Verify a knowledge node: record that you deliberately confirmed its claims are STILL TRUE against current reality, stamping `last_verified_at` to now. This verification axis is orthogonal to freshness — the only signal that catches a recently-written yet factually-wrong node. Actually check it first; if it's wrong, do NOT verify — use node(action=\"update\") or a supersedes edge instead. Does NOT change content, freshness, or source_type. Knowledge only; tasks/components are rejected.", - "properties": { - "action": { - "const": "review", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Add a knowledge node under an existing active component (component_id is\nREQUIRED) — prefer an existing component over creating one. Search before\nadding: if a near-match exists, update it with node(action=\"update\") instead of duplicating. Always\ninclude topics (without them the node won't surface in topic queries) and link\nrelated nodes — isolated nodes are nearly invisible.\n\n## The knowledge categories\n\nPick the one that matches what the fact *is*, and give the body the shape it wants. Thin one-liners\ndilute the graph — a node that restates its own title helps nobody.\n\n| Category | It is | Body shape |\n|---|---|---|\n| `decision` | A choice that was made, and could have gone another way. | ## Context / ## Alternatives considered / ## Why this won / ## Consequences |\n| `architecture` | A structural fact about how the system is built. | ## What / ## How it works / ## Why this shape |\n| `goal` | What success looks like. | ## Outcome / ## Why it matters / ## How we'll know we're done |\n| `risk` | Something that might go wrong, and hasn't yet. | ## Risk / ## Likelihood & impact / ## Mitigation / ## Trigger |\n| `gap` | A known missing piece — real, but not yet work anyone has committed to. | ## Gap / ## Why it matters / ## What needs to happen to close it |\n| `lesson` | Something learned the hard way, usually from a failure. | ## Symptom / ## Root cause / ## Fix / ## Why this matters |\n| `opportunity` | Possible future work worth considering. | ## Opportunity / ## Why now / ## What it would take |\n| `priority` | What the focus is right now, and what it is not. | ## Focus / ## Why this and not X / ## Time horizon |\n| `fyi` | Context worth remembering that calls for no action. | A paragraph or two; short is fine. |\n\nTwo that get confused: a **`gap`** is a hole someone still has to decide what to do about; once the work\nis agreed it is a **task**, not a gap. A **`risk`** has not happened; a **`lesson`** already did.\n\n**A lesson from a bug is worth little without its trigger.** Put the literal error message, the failing\nsymbol, the command that broke, into the body. The graph resurfaces knowledge by matching it against\nwhat an agent is doing, so a lesson carrying the real error string reappears the moment someone hits that\nerror again. Worded abstractly, it stays invisible exactly when it would have helped.\n\n\nLIFECYCLE IS REQUIRED. Decide all three — expires_on_task, expires_at,\ndue_at — every time; pass null to skip any (durable decision/architecture\nusually skip all three). Bound nodes retire themselves, which is what keeps\nthe graph self-maintaining — that's why these aren't optional.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "body": { + "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "category": { + "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "due_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." + }, + "expires_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." + }, + "expires_on_task": { + "anyOf": [ + { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ], + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_type": { + "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — one-line summary.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "category", + "title", + "expires_on_task", + "due_at", + "expires_at" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"review\": Verify a knowledge node: record that you deliberately confirmed its claims are STILL TRUE against current reality, stamping `last_verified_at` to now. This verification axis is orthogonal to freshness — the only signal that catches a recently-written yet factually-wrong node. Actually check it first; if it's wrong, do NOT verify — use node(action=\"update\") or a supersedes edge instead. Does NOT change content, freshness, or source_type. Knowledge only; tasks/components are rejected.", + "properties": { + "action": { + "const": "review", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + } +]
- Changed
node1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"update\": Update editable fields on any node; each field is validated against what's valid for the node's type, so type-mismatched fields (e.g. category on a task) are rejected. A task's status may be set to open or backlog here, but claim, completion, and cancellation are REJECTED — use task(action=\"claim\") / task(action=\"complete\") so the claim and completion (with session + agent identity) are recorded atomically, or destructive(action=\"cancel_task\") so the destructive transition records a reason. Archiving a component is also rejected; use destructive(action=\"archive_component\") so active children cannot be orphaned. `parent_component_id` reparents across components in the same project. Surgical edits: pass `edits` (literal old_string→new_string replacements applied server-side) instead of `body` to fix a substring without resending the whole body — they're mutually exclusive, and a non-matching old_string errors rather than no-ops. Optional `expected_version` gives optimistic concurrency.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "anchor_files": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." - }, - "anchor_symbols": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — user uuid (not a shareable id)" - }, - "body": { - "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": [ - "string", - "null" - ] - }, - "category": { - "anyOf": [ - { - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge-type nodes only" - }, - "due_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." - }, - "edits": { - "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", - "items": { - "additionalProperties": false, - "properties": { - "new_string": { - "description": "Replacement text.", - "type": "string" - }, - "old_string": { - "description": "Exact substring to find in the current body.", - "minLength": 1, - "type": "string" - }, - "replace_all": { - "description": "Replace every occurrence (default: require a unique match).", - "type": "boolean" - } - }, - "required": [ - "old_string", - "new_string" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "expected_version": { - "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "expires_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." - }, - "hidden": { - "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", - "type": "string" - }, - "parent_component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "source": { - "anyOf": [ - { - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." - }, - "source_type": { - "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "anyOf": [ - { - "format": "uri", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." - }, - "status": { - "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "title": { - "description": "(update only) New title.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(update only) knowledge or task nodes only", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"move\": Move an EXISTING node between scopes — into a workspace (hiding it from trunk readers) or back to trunk (shared truth). Re-scopes by id; reversible, never deletes. Distinct from destructive(action=\"cherry_pick_workspace\") (which promotes to trunk WITH provenance) — This is a plain re-assignment in any direction.", - "properties": { - "action": { - "const": "move", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"link\": Create a typed semantic link between two nodes (src → dst). Idempotent: re-linking the same (src, dst, relation) returns the existing edge, no error.\n\nSome relations change the graph: `supersedes` retires dst (it stays findable, but comes back marked SUPERSEDED and ranked below live knowledge), and `expires-on` retires src later. Every relation except `relates-to` is a claim and needs a `reason`; `supersedes` also needs `evidence`. If you cannot state the reason, use `relates-to`.\n\n## What each link means — and what it does\n\nAn edge is not decoration. Some of these change the graph.\n\n| Relation | Direction | Use it when | What it actually does |\n|---|---|---|---|\n| `relates-to` | A → B | Someone who pulls up B should know A exists. The weakest signal — use it when nothing typed fits. | Nothing but a pointer. Both nodes stay active. |\n| `blocks` | child → parent | A must land before B can be completed or become true. Also the **subtask** relation. | Surfaces A as a subtask of B and drives B's progress. |\n| `fulfills` | task → knowledge | This task delivers or acts on that decision, goal, or risk. | Records that the work discharged the commitment. |\n| `caused-by` | A → B | B is the root cause of A. | Builds the causal chain a provenance walk follows. |\n| `contradicts` | A → B | Two facts conflict and you cannot tell which is true. | Flags it for a human. **Both stay active** — this is the honest \"I don't know\" edge. |\n| `supersedes` | new → old | The old node has become **wrong to act on** — an agent following it would now do the wrong thing. **Never** for something that is merely older: history that is still true must stay. | **Retires the old node.** It stays in the graph and stays findable, but it comes back marked `SUPERSEDED` and ranked below live knowledge — so nothing acts on it as current. |\n| `expires-on` | knowledge → task | This knowledge is only true until that task closes. | Retires the knowledge when the task completes — same marked-not-deleted treatment. |\n\n`mentions` is not linkable by hand — write the id into a body and it is created for you.", - "properties": { - "action": { - "const": "link", - "type": "string" - }, - "dst_id": { - "description": "REQUIRED for link — destination node shareable id.", - "type": "string" - }, - "evidence": { - "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", - "maxLength": 4000, - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reason": { - "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", - "maxLength": 2000, - "minLength": 1, - "type": "string" - }, - "relation": { - "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", - "enum": [ - "relates-to", - "blocks", - "fulfills", - "contradicts", - "supersedes", - "caused-by", - "expires-on" - ], - "type": "string" - }, - "src_id": { - "description": "REQUIRED for link — source node shareable id.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "src_id", - "dst_id", - "relation" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"update\": Update editable fields on any node; each field is validated against what's valid for the node's type, so type-mismatched fields (e.g. category on a task) are rejected. A task's status may be set to open or backlog here, but claim, completion, and cancellation are REJECTED — use task(action=\"claim\") / task(action=\"complete\") so the claim and completion (with session + agent identity) are recorded atomically, or destructive(action=\"cancel_task\") so the destructive transition records a reason. Archiving a component is also rejected; use destructive(action=\"archive_component\") so active children cannot be orphaned. `parent_component_id` reparents across components in the same project. Surgical edits: pass `edits` (literal old_string→new_string replacements applied server-side) instead of `body` to fix a substring without resending the whole body — they're mutually exclusive, and a non-matching old_string errors rather than no-ops. Optional `expected_version` gives optimistic concurrency.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "anchor_files": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." + }, + "anchor_symbols": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — user uuid (not a shareable id)" + }, + "body": { + "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": [ + "string", + "null" + ] + }, + "category": { + "anyOf": [ + { + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge-type nodes only" + }, + "due_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." + }, + "edits": { + "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", + "items": { + "additionalProperties": false, + "properties": { + "new_string": { + "description": "Replacement text.", + "type": "string" + }, + "old_string": { + "description": "Exact substring to find in the current body.", + "minLength": 1, + "type": "string" + }, + "replace_all": { + "description": "Replace every occurrence (default: require a unique match).", + "type": "boolean" + } + }, + "required": [ + "old_string", + "new_string" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "expected_version": { + "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." + }, + "hidden": { + "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", + "type": "string" + }, + "parent_component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "source": { + "anyOf": [ + { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." + }, + "source_type": { + "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "anyOf": [ + { + "format": "uri", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." + }, + "status": { + "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "title": { + "description": "(update only) New title.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(update only) knowledge or task nodes only", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"move\": Move an EXISTING node between scopes — into a workspace (hiding it from trunk readers) or back to trunk (shared truth). Re-scopes by id; reversible, never deletes. Distinct from destructive(action=\"cherry_pick_workspace\") (which promotes to trunk WITH provenance) — This is a plain re-assignment in any direction.", + "properties": { + "action": { + "const": "move", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"link\": Create a typed semantic link between two nodes (src → dst). Idempotent: re-linking the same (src, dst, relation) returns the existing edge, no error.\n\nSome relations change the graph: `supersedes` retires dst (it stays findable, but comes back marked SUPERSEDED and ranked below live knowledge), and `expires-on` retires src later. Every relation except `relates-to` is a claim and needs a `reason`; `supersedes` also needs `evidence`. If you cannot state the reason, use `relates-to`.\n\n## What each link means — and what it does\n\nAn edge is not decoration. Some of these change the graph.\n\n| Relation | Direction | Use it when | What it actually does |\n|---|---|---|---|\n| `relates-to` | A → B | Someone who pulls up B should know A exists. The weakest signal — use it when nothing typed fits. | Nothing but a pointer. Both nodes stay active. |\n| `blocks` | child → parent | A must land before B can be completed or become true. Also the **subtask** relation. | Surfaces A as a subtask of B and drives B's progress. |\n| `fulfills` | task → knowledge | This task delivers or acts on that decision, goal, or risk. | Records that the work discharged the commitment. |\n| `caused-by` | A → B | B is the root cause of A. | Builds the causal chain a provenance walk follows. |\n| `contradicts` | A → B | Two facts conflict and you cannot tell which is true. | Flags it for a human. **Both stay active** — this is the honest \"I don't know\" edge. |\n| `supersedes` | new → old | The old node has become **wrong to act on** — an agent following it would now do the wrong thing. **Never** for something that is merely older: history that is still true must stay. | **Retires the old node.** It stays in the graph and stays findable, but it comes back marked `SUPERSEDED` and ranked below live knowledge — so nothing acts on it as current. |\n| `expires-on` | knowledge → task | This knowledge is only true until that task closes. | Retires the knowledge when the task completes — same marked-not-deleted treatment. |\n\n`mentions` is not linkable by hand — write the id into a body and it is created for you.", + "properties": { + "action": { + "const": "link", + "type": "string" + }, + "dst_id": { + "description": "REQUIRED for link — destination node shareable id.", + "type": "string" + }, + "evidence": { + "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", + "maxLength": 4000, + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reason": { + "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "relation": { + "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", + "enum": [ + "relates-to", + "blocks", + "fulfills", + "contradicts", + "supersedes", + "caused-by", + "expires-on" + ], + "type": "string" + }, + "src_id": { + "description": "REQUIRED for link — source node shareable id.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "src_id", + "dst_id", + "relation" + ], + "type": "object" + } +]
- Changed
objective1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create an objective — a standing goal whose end-state lives in its body, with ordered milestone phases under it. Non-claimable; it rolls up from its milestones and the tasks attached to them. Add milestones with objective(action=\"add_milestone\"), then attach tasks with objective(action=\"attach\").", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "body": { - "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create/add_milestone — parent component shareable id.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", - "type": "string" - }, - "target_date": { - "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create/add_milestone only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"add_milestone\": Create a milestone — an ordered phase under an objective. Appended after the objective's existing milestones unless you pass an explicit position. Attach tasks to it with objective(action=\"attach\").", - "properties": { - "action": { - "const": "add_milestone", - "type": "string" - }, - "body": { - "description": "Optional milestone description.", - "type": "string" - }, - "component_id": { - "description": "Parent component shareable id. REQUIRED.", - "type": "string" - }, - "objective_id": { - "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", - "type": "string" - }, - "position": { - "description": "1-based order within the objective. Appended last when omitted.", - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "target_date": { - "description": "Optional soft target date, ISO e.g. 2026-09-30.", - "type": "string" - }, - "title": { - "description": "Milestone one-liner.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"attach\": Attach a plain task to a milestone, re-parenting it off any prior milestone/objective. Real task→task `blocks` dependencies are preserved — only the milestone/objective containment moves.", - "properties": { - "action": { - "const": "attach", - "type": "string" - }, - "milestone_id": { - "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "milestone_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"move\": Attach a plain task directly to an objective — a 'loose' task serving the objective without a milestone. Re-parents off any prior container. Once an objective has milestones, loose tasks are its drift signal.", - "properties": { - "action": { - "const": "move", - "type": "string" - }, - "objective_id": { - "description": "The objective to attach it to directly (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The plain task to attach (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "objective_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"detach\": Remove a task's milestone/objective containment. Genuine task→task `blocks` dependencies are left intact.", - "properties": { - "action": { - "const": "detach", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The task to detach from its milestone/objective (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"reorder\": Reorder an objective's milestones. `milestone_ids` must list exactly the objective's own milestones, in the new order (a total permutation); a partial or foreign list is rejected so positions never go inconsistent.", - "properties": { - "action": { - "const": "reorder", - "type": "string" - }, - "milestone_ids": { - "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", - "items": { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "objective_id": { - "description": "The objective whose milestones to reorder (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "milestone_ids" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"set_target\": Set (or clear, with null) a milestone's soft target date.", - "properties": { - "action": { - "const": "set_target", - "type": "string" - }, - "milestone_id": { - "description": "The milestone to retarget (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "ISO target date e.g. 2026-09-30, or null to clear.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "action", - "project_slug", - "milestone_id", - "target_date" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"achieve\": Mark an objective as achieved (status → done).", - "properties": { - "action": { - "const": "achieve", - "type": "string" - }, - "objective_id": { - "description": "The objective to mark achieved (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create an objective — a standing goal whose end-state lives in its body, with ordered milestone phases under it. Non-claimable; it rolls up from its milestones and the tasks attached to them. Add milestones with objective(action=\"add_milestone\"), then attach tasks with objective(action=\"attach\").", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "body": { + "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create/add_milestone — parent component shareable id.", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", + "type": "string" + }, + "target_date": { + "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create/add_milestone only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"add_milestone\": Create a milestone — an ordered phase under an objective. Appended after the objective's existing milestones unless you pass an explicit position. Attach tasks to it with objective(action=\"attach\").", + "properties": { + "action": { + "const": "add_milestone", + "type": "string" + }, + "body": { + "description": "Optional milestone description.", + "type": "string" + }, + "component_id": { + "description": "Parent component shareable id. REQUIRED.", + "type": "string" + }, + "objective_id": { + "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", + "type": "string" + }, + "position": { + "description": "1-based order within the objective. Appended last when omitted.", + "type": "integer" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "target_date": { + "description": "Optional soft target date, ISO e.g. 2026-09-30.", + "type": "string" + }, + "title": { + "description": "Milestone one-liner.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"attach\": Attach a plain task to a milestone, re-parenting it off any prior milestone/objective. Real task→task `blocks` dependencies are preserved — only the milestone/objective containment moves.", + "properties": { + "action": { + "const": "attach", + "type": "string" + }, + "milestone_id": { + "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "milestone_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"move\": Attach a plain task directly to an objective — a 'loose' task serving the objective without a milestone. Re-parents off any prior container. Once an objective has milestones, loose tasks are its drift signal.", + "properties": { + "action": { + "const": "move", + "type": "string" + }, + "objective_id": { + "description": "The objective to attach it to directly (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The plain task to attach (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "objective_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"detach\": Remove a task's milestone/objective containment. Genuine task→task `blocks` dependencies are left intact.", + "properties": { + "action": { + "const": "detach", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The task to detach from its milestone/objective (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"reorder\": Reorder an objective's milestones. `milestone_ids` must list exactly the objective's own milestones, in the new order (a total permutation); a partial or foreign list is rejected so positions never go inconsistent.", + "properties": { + "action": { + "const": "reorder", + "type": "string" + }, + "milestone_ids": { + "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", + "items": { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "objective_id": { + "description": "The objective whose milestones to reorder (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "milestone_ids" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"set_target\": Set (or clear, with null) a milestone's soft target date.", + "properties": { + "action": { + "const": "set_target", + "type": "string" + }, + "milestone_id": { + "description": "The milestone to retarget (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "ISO target date e.g. 2026-09-30, or null to clear.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "action", + "project_slug", + "milestone_id", + "target_date" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"achieve\": Mark an objective as achieved (status → done).", + "properties": { + "action": { + "const": "achieve", + "type": "string" + }, + "objective_id": { + "description": "The objective to mark achieved (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id" + ], + "type": "object" + } +]
- Changed
organize1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", - "properties": { - "action": { - "const": "add_component", - "type": "string" - }, - "body": { - "description": "(add_component only) 1-3 paragraph charter (markdown).", - "type": "string" - }, - "name": { - "description": "REQUIRED for add_component — short noun-phrase title.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "parent_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "parent_id", - "name" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", - "properties": { - "action": { - "const": "rename_topic", - "type": "string" - }, - "from": { - "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", - "properties": { - "action": { - "const": "feedback", - "type": "string" - }, - "hindrance": { - "description": "(feedback only) One place Stele was more friction than help.", - "type": "string" - }, - "improvement": { - "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "score": { - "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", - "maximum": 10, - "minimum": 0, - "type": "integer" - }, - "session_id": { - "description": "(feedback only) The session being reflected on. Defaults to the current session.", - "type": "string" - }, - "source": { - "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", - "enum": [ - "agent", - "human" - ], - "type": "string" - }, - "strength": { - "description": "(feedback only) One concrete way Stele helped this session.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", - "properties": { - "action": { - "const": "pause_rag", - "type": "string" - }, - "reason": { - "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", - "type": "string" - }, - "recall_session": { - "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", - "minLength": 1, - "type": "string" - }, - "turns": { - "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "recall_session", - "reason", - "turns" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", + "properties": { + "action": { + "const": "add_component", + "type": "string" + }, + "body": { + "description": "(add_component only) 1-3 paragraph charter (markdown).", + "type": "string" + }, + "name": { + "description": "REQUIRED for add_component — short noun-phrase title.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "parent_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "parent_id", + "name" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", + "properties": { + "action": { + "const": "rename_topic", + "type": "string" + }, + "from": { + "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", + "properties": { + "action": { + "const": "feedback", + "type": "string" + }, + "hindrance": { + "description": "(feedback only) One place Stele was more friction than help.", + "type": "string" + }, + "improvement": { + "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "score": { + "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", + "maximum": 10, + "minimum": 0, + "type": "integer" + }, + "session_id": { + "description": "(feedback only) The session being reflected on. Defaults to the current session.", + "type": "string" + }, + "source": { + "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", + "enum": [ + "agent", + "human" + ], + "type": "string" + }, + "strength": { + "description": "(feedback only) One concrete way Stele helped this session.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", + "properties": { + "action": { + "const": "pause_rag", + "type": "string" + }, + "reason": { + "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", + "type": "string" + }, + "recall_session": { + "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", + "minLength": 1, + "type": "string" + }, + "turns": { + "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "recall_session", + "reason", + "turns" + ], + "type": "object" + } +]
- Changed
task1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create a task under a component. One task ≈ one PR. `success_criteria` is required — it is the contract the implementer and the reviewer both work against. Pass `check` only when a command can genuinely decide done without a human; for unattended certification also pass `check_paths`. Most tasks have neither, and that is fine. Give a markdown body with ## Why / ## How / ## Test plan — the canonical contract headings are written for you from the arguments.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this task is about (function / class / constant / env var).", - "items": { - "type": "string" - }, - "type": "array" - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." - }, - "body": { - "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "check": { - "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", - "type": "string" - }, - "check_paths": { - "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "kind": { - "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal (end-state in its body) with ordered 'milestone' phases under it.", - "enum": [ - "task", - "epic", - "objective", - "milestone" - ], - "type": "string" - }, - "position": { - "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", - "type": "integer" - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "success_criteria": { - "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title", - "success_criteria" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"claim\": Atomically claim a task (open → in_progress), recording you as owner. Claim before you start so others know you're on it. Returns the task, or { result: 'conflict' } if someone else holds it — no race window.", - "properties": { - "action": { - "const": "claim", - "type": "string" - }, - "dispatched_with": { - "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", - "type": "string" - }, - "parent_task_id": { - "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"complete\": Mark a task done. REQUIRED `summary` is appended under ## Completion summary (what shipped, where it landed, what you learned). Also closes a container (epic/objective/milestone): no claim needed, but it is REFUSED while any child is unfinished, and the error names them. A container normally closes itself when its last child closes, so completing one is mainly how you record what it delivered — and it marks the close as yours, so a child reopening later won't undo it. The result may list nearby decision/architecture nodes to re-verify (still-true / edit / supersede); that part is advisory.", - "properties": { - "action": { - "const": "complete", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "summary": { - "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "summary" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"release\": Release a task you claimed (in_progress → open) so someone else can pick it up — use when you can't finish (out of context, blocked, wrong scope).", - "properties": { - "action": { - "const": "release", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"reopen\": Recovery: send a closed task (done or cancelled) back to open and clear its completion metadata so it reads as unheld and claimable again. For a stuck in_progress task, use task(action=\"force_release\") instead (this is for closed tasks).", - "properties": { - "action": { - "const": "reopen", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"force_release\": Admin recovery: release an in_progress task WITHOUT holding the claim, so it returns to open for others. The release is attributed to you in the claim history (not the prior claimer). For a closed task use task(action=\"reopen\"); to take a task over rather than free it, use destructive(action=\"force_claim_task\").", - "properties": { - "action": { - "const": "force_release", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"loop_finalize\": Create the durable final loop envelope. Deterministic correctness comes only from the latest mechanically evidenced attempt; independent reviewer and operator intent/scope judgments remain separate fields. Approval requires all three, so model judgment can reject work but can never manufacture correctness.", - "properties": { - "action": { - "const": "loop_finalize", - "type": "string" - }, - "node_id": { - "description": "The loop task to finalize (TASK-N).", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "operator_summary": { - "description": "Why the operator judged the change in or out of intent and scope.", - "minLength": 1, - "type": "string" - }, - "operator_verdict": { - "description": "The operator's judgment of whether the change stays within the task's intent and scope.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reviewer_summary": { - "description": "Why the reviewer approved or rejected the change.", - "minLength": 1, - "type": "string" - }, - "reviewer_verdict": { - "description": "The independent reviewer's judgment of the change.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "reviewer_verdict", - "reviewer_summary", - "operator_verdict", - "operator_summary" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create a task under a component. One task ≈ one PR. `success_criteria` is required — it is the contract the implementer and the reviewer both work against. Pass `check` only when a command can genuinely decide done without a human; for unattended certification also pass `check_paths`. Most tasks have neither, and that is fine. Give a markdown body with ## Why / ## How / ## Test plan — the canonical contract headings are written for you from the arguments.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this task is about (function / class / constant / env var).", + "items": { + "type": "string" + }, + "type": "array" + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." + }, + "body": { + "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "check": { + "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", + "type": "string" + }, + "check_paths": { + "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "kind": { + "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal (end-state in its body) with ordered 'milestone' phases under it.", + "enum": [ + "task", + "epic", + "objective", + "milestone" + ], + "type": "string" + }, + "position": { + "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", + "type": "integer" + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "success_criteria": { + "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title", + "success_criteria" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"claim\": Atomically claim a task (open → in_progress), recording you as owner. Claim before you start so others know you're on it. Returns the task, or { result: 'conflict' } if someone else holds it — no race window.", + "properties": { + "action": { + "const": "claim", + "type": "string" + }, + "dispatched_with": { + "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", + "type": "string" + }, + "parent_task_id": { + "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"complete\": Mark a task done. REQUIRED `summary` is appended under ## Completion summary (what shipped, where it landed, what you learned). Also closes a container (epic/objective/milestone): no claim needed, but it is REFUSED while any child is unfinished, and the error names them. A container normally closes itself when its last child closes, so completing one is mainly how you record what it delivered — and it marks the close as yours, so a child reopening later won't undo it. The result may list nearby decision/architecture nodes to re-verify (still-true / edit / supersede); that part is advisory.", + "properties": { + "action": { + "const": "complete", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "summary": { + "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "summary" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"release\": Release a task you claimed (in_progress → open) so someone else can pick it up — use when you can't finish (out of context, blocked, wrong scope).", + "properties": { + "action": { + "const": "release", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"reopen\": Recovery: send a closed task (done or cancelled) back to open and clear its completion metadata so it reads as unheld and claimable again. For a stuck in_progress task, use task(action=\"force_release\") instead (this is for closed tasks).", + "properties": { + "action": { + "const": "reopen", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"force_release\": Admin recovery: release an in_progress task WITHOUT holding the claim, so it returns to open for others. The release is attributed to you in the claim history (not the prior claimer). For a closed task use task(action=\"reopen\"); to take a task over rather than free it, use destructive(action=\"force_claim_task\").", + "properties": { + "action": { + "const": "force_release", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"loop_finalize\": Create the durable final loop envelope. Deterministic correctness comes only from the latest mechanically evidenced attempt; independent reviewer and operator intent/scope judgments remain separate fields. Approval requires all three, so model judgment can reject work but can never manufacture correctness.", + "properties": { + "action": { + "const": "loop_finalize", + "type": "string" + }, + "node_id": { + "description": "The loop task to finalize (TASK-N).", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "operator_summary": { + "description": "Why the operator judged the change in or out of intent and scope.", + "minLength": 1, + "type": "string" + }, + "operator_verdict": { + "description": "The operator's judgment of whether the change stays within the task's intent and scope.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reviewer_summary": { + "description": "Why the reviewer approved or rejected the change.", + "minLength": 1, + "type": "string" + }, + "reviewer_verdict": { + "description": "The independent reviewer's judgment of the change.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "reviewer_verdict", + "reviewer_summary", + "operator_verdict", + "operator_summary" + ], + "type": "object" + } +]
- Changed
timeline1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"list\": List the project's Timeline: automatic events captured on graph writes and task lifecycle actions, interleaved with one-line custom entries logged by agents and automations, newest first. Filters (AND-combined): verbs, action type, actor user/agent, capture source, status, component, node (matches the entry's subject or a linked node), and a time range. A custom entry's linked nodes resolve to compact references (id, type, title, status) — never raw uuids. Keyset-paginated, not offset-based: pass a previous page's `next_before` back as `before` to fetch the next (older) page.", - "properties": { - "action": { - "const": "list", - "type": "string" - }, - "agent": { - "description": "Filter to entries attributed to this agent name.", - "type": "string" - }, - "before": { - "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", - "type": "string" - }, - "component_id": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "from": { - "description": "ISO timestamp — only entries at or after this time.", - "type": "string" - }, - "limit": { - "description": "Page size (default 50, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "Limit to entries whose subject is this node, OR that link to it.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", - "type": "string" - }, - "source": { - "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", - "type": "string" - }, - "status": { - "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "to": { - "description": "ISO timestamp — only entries before this time.", - "type": "string" - }, - "type": { - "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", - "type": "string" - }, - "user": { - "description": "Filter to entries attributed to this user (uuid, not a shareable id).", - "format": "uuid", - "type": "string" - }, - "verbs": { - "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", - "items": { - "enum": [ - "created", - "updated", - "status_changed", - "superseded", - "commented", - "claimed", - "released", - "custom", - "linked", - "unlinked" - ], - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"log\": Append a one-line entry to the project's Timeline — for an agent or automation to journal a milestone, with links to the graph items holding the substance. `message` is a short, human-readable line: one thing that happened, never the reasoning behind it or the decision it produced — put those in a knowledge or task node and reference them with `link` instead. `type` is a free-form, caller-namespaced label so entries from different automations don't collide. The Timeline is append-only: a correction is a new entry, not an edit. This capability can be turned off for a project; when it is, the call succeeds but writes nothing, and the response says so.", - "properties": { - "action": { - "const": "log", - "type": "string" - }, - "link": { - "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", - "items": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "message": { - "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Outcome of the journaled action. Omit unless you have one to report.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "type": { - "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", - "maxLength": 100, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "type", - "message" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"list\": List the project's Timeline: automatic events captured on graph writes and task lifecycle actions, interleaved with one-line custom entries logged by agents and automations, newest first. Filters (AND-combined): verbs, action type, actor user/agent, capture source, status, component, node (matches the entry's subject or a linked node), and a time range. A custom entry's linked nodes resolve to compact references (id, type, title, status) — never raw uuids. Keyset-paginated, not offset-based: pass a previous page's `next_before` back as `before` to fetch the next (older) page.", + "properties": { + "action": { + "const": "list", + "type": "string" + }, + "agent": { + "description": "Filter to entries attributed to this agent name.", + "type": "string" + }, + "before": { + "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", + "type": "string" + }, + "component_id": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "from": { + "description": "ISO timestamp — only entries at or after this time.", + "type": "string" + }, + "limit": { + "description": "Page size (default 50, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "Limit to entries whose subject is this node, OR that link to it.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", + "type": "string" + }, + "source": { + "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", + "type": "string" + }, + "status": { + "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "to": { + "description": "ISO timestamp — only entries before this time.", + "type": "string" + }, + "type": { + "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", + "type": "string" + }, + "user": { + "description": "Filter to entries attributed to this user (uuid, not a shareable id).", + "format": "uuid", + "type": "string" + }, + "verbs": { + "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", + "items": { + "enum": [ + "created", + "updated", + "status_changed", + "superseded", + "commented", + "claimed", + "released", + "custom", + "linked", + "unlinked" + ], + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"log\": Append a one-line entry to the project's Timeline — for an agent or automation to journal a milestone, with links to the graph items holding the substance. `message` is a short, human-readable line: one thing that happened, never the reasoning behind it or the decision it produced — put those in a knowledge or task node and reference them with `link` instead. `type` is a free-form, caller-namespaced label so entries from different automations don't collide. The Timeline is append-only: a correction is a new entry, not an edit. This capability can be turned off for a project; when it is, the call succeeds but writes nothing, and the response says so.", + "properties": { + "action": { + "const": "log", + "type": "string" + }, + "link": { + "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", + "items": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "message": { + "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Outcome of the journaled action. Omit unless you have one to report.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "type": { + "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", + "maxLength": 100, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "type", + "message" + ], + "type": "object" + } +]
- Changed
workspace1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "action=\"create\": Create a named workspace — a scoped overlay on the project's trunk (shared truth). Knowledge written while it's active scopes to it; collaborators off that workspace never see it. permanent = a domain/team overlay that never graduates (promote per-item); experimental = ephemeral, expected to merge or be archived. Prefer OFFERING before creating unless the user asked. Does NOT activate it — call workspace(action=\"activate\") to scope this session to it.", - "properties": { - "action": { - "const": "create", - "type": "string" - }, - "auto_activate_hint": { - "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", - "type": "string" - }, - "branches": { - "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "description": { - "description": "(create/update only) One-line description of what this overlay is for.", - "type": "string" - }, - "kind": { - "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "name", - "kind" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "action=\"update\": Update a workspace's editable fields (name, kind, description, auto-activation hint) and/or ADD branch associations. Only the fields you pass change. Does NOT promote knowledge or change scope — merge/archive have dedicated tools.", - "properties": { - "action": { - "const": "update", - "type": "string" - }, - "add_branches": { - "description": "(update only) Branch associations to ADD (existing ones are kept).", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "auto_activate_hint": { - "description": "New plain-language auto-activation hint.", - "type": "string" - }, - "description": { - "description": "New one-line description.", - "type": "string" - }, - "kind": { - "description": "Change the workspace kind.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "New name.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create a named workspace — a scoped overlay on the project's trunk (shared truth). Knowledge written while it's active scopes to it; collaborators off that workspace never see it. permanent = a domain/team overlay that never graduates (promote per-item); experimental = ephemeral, expected to merge or be archived. Prefer OFFERING before creating unless the user asked. Does NOT activate it — call workspace(action=\"activate\") to scope this session to it.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "auto_activate_hint": { + "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", + "type": "string" + }, + "branches": { + "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "description": { + "description": "(create/update only) One-line description of what this overlay is for.", + "type": "string" + }, + "kind": { + "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "name", + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"update\": Update a workspace's editable fields (name, kind, description, auto-activation hint) and/or ADD branch associations. Only the fields you pass change. Does NOT promote knowledge or change scope — merge/archive have dedicated tools.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "add_branches": { + "description": "(update only) Branch associations to ADD (existing ones are kept).", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "auto_activate_hint": { + "description": "New plain-language auto-activation hint.", + "type": "string" + }, + "description": { + "description": "New one-line description.", + "type": "string" + }, + "kind": { + "description": "Change the workspace kind.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "New name.", + "minLength": 1, + "type": "string" + }, + "privacy_bypass": { + "description": "true writes despite a privacy flag on a public project. Only meaningful after a flag; the user decides whether the content can be public.", + "type": "boolean" + }, + "privacy_false_positive": { + "description": "With privacy_bypass: true reports the privacy flag as wrong.", + "type": "boolean" + }, + "privacy_judgement_id": { + "description": "With privacy_bypass: the judgement_id the privacy flag returned.", + "type": "string" + }, + "privacy_note": { + "description": "With privacy_bypass: an optional short reason, kept with the bypass.", + "maxLength": 500, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + } +]
14 tool updates
- Changed
comment1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "comment action \"add\".", - "properties": { - "action": { - "const": "add", - "description": "Run the add action.", - "type": "string" - }, - "anchor": { - "additionalProperties": false, - "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", - "properties": { - "node_version": { - "description": "The node `version` the quote was taken from (from get_node).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "offset": { - "description": "Rough char offset to disambiguate a repeated quote.", - "minimum": 0, - "type": "integer" - }, - "quote": { - "description": "The exact quoted substring to attach the comment to.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "node_version", - "quote" - ], - "type": "object" - }, - "body": { - "description": "REQUIRED for add/update — comment text (markdown).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "REQUIRED for add — the node to comment on (any node type).", - "type": "string" - }, - "parent_comment_id": { - "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "comment action \"update\".", - "properties": { - "action": { - "const": "update", - "description": "Run the update action.", - "type": "string" - }, - "body": { - "description": "New comment text.", - "minLength": 1, - "type": "string" - }, - "comment_id": { - "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "body" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "comment action \"resolve\".", - "properties": { - "action": { - "const": "resolve", - "description": "Run the resolve action.", - "type": "string" - }, - "comment_id": { - "description": "A comment_id in the thread (top-level or a reply).", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "resolved": { - "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", - "type": "boolean" - } - }, - "required": [ - "action", - "project_slug", - "comment_id", - "resolved" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "comment action \"delete\".", - "properties": { - "action": { - "const": "delete", - "description": "Run the delete action.", - "type": "string" - }, - "comment_id": { - "description": "The comment_id to delete.", - "format": "uuid", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "comment_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add\": Leave a comment on any node — the human↔agent steering channel; the next agent that reads the node sees it flagged unseen. Reply by passing parent_comment_id (replies are one level deep). Pass `anchor` to attach to a quoted span (inline comment); omit for node-level. Commenting watches the node, so future comments on it show as unread to you.", + "properties": { + "action": { + "const": "add", + "type": "string" + }, + "anchor": { + "additionalProperties": false, + "description": "(add only) Attach the comment to a quoted span of the node's body (per-line / inline comment). Omit for a node-level comment. Anchored to TEXT, never line numbers: on a later edit the quote is relocated; if it vanishes the comment goes 'outdated' (never deleted).", + "properties": { + "node_version": { + "description": "The node `version` the quote was taken from, as returned when you fetched the node.", + "exclusiveMinimum": 0, + "type": "integer" + }, + "offset": { + "description": "Rough char offset to disambiguate a repeated quote.", + "minimum": 0, + "type": "integer" + }, + "quote": { + "description": "The exact quoted substring to attach the comment to.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "node_version", + "quote" + ], + "type": "object" + }, + "body": { + "description": "REQUIRED for add/update — comment text (markdown).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "REQUIRED for add — the node to comment on (any node type).", + "type": "string" + }, + "parent_comment_id": { + "description": "(add only) Reply target — the comment_id of a TOP-LEVEL comment. Replies are one level deep; replying to a reply is rejected.", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"update\": Edit the body of a comment you authored. AUTHOR-ONLY. Sets the 'edited' marker and does not re-flag the comment unread for others.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "body": { + "description": "New comment text.", + "minLength": 1, + "type": "string" + }, + "comment_id": { + "description": "REQUIRED for update/resolve/delete — the comment_id to act on.", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "body" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"resolve\": Resolve (or reopen) a comment thread. ANY project member may do this, not just the author. Resolve a steering comment once addressed so it stops nagging the next agent — resolved threads drop out of the [N comments, M unread] marker. Applies to the whole thread (resolving a reply resolves its parent).", + "properties": { + "action": { + "const": "resolve", + "type": "string" + }, + "comment_id": { + "description": "A comment_id in the thread (top-level or a reply).", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "resolved": { + "description": "REQUIRED for resolve — true to resolve the thread, false to reopen it.", + "type": "boolean" + } + }, + "required": [ + "action", + "project_slug", + "comment_id", + "resolved" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"delete\": Soft-delete a comment you authored — the row stays as a tombstone and replies remain intact (never orphaned). AUTHOR-ONLY; there is no hard delete.", + "properties": { + "action": { + "const": "delete", + "type": "string" + }, + "comment_id": { + "description": "The comment_id to delete.", + "format": "uuid", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "comment_id" + ], + "type": "object" + } +]
- Changed
destructive1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "destructive action \"cancel_task\".", - "properties": { - "action": { - "const": "cancel_task", - "description": "Run the cancel_task action.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for cancel_task/force_claim_task/restore_node_version/archive_component — shareable id of the target (TASK-N / KNOW-N / COMP-N / …).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reason": { - "description": "REQUIRED for cancel_task — why this task is being cancelled.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "reason" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"force_claim_task\".", - "properties": { - "action": { - "const": "force_claim_task", - "description": "Run the force_claim_task action.", - "type": "string" - }, - "dispatched_with": { - "description": "(force_claim_task only) Operator's full briefing prompt when this force-claim is from a dispatched sub-agent. Up to 16 KB.", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "parent_task_id": { - "description": "(force_claim_task only) Operator's claimed task id when this force-claim is from a dispatched sub-agent. Leave null for direct claims.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"dedupe_knowledge\".", - "properties": { - "action": { - "const": "dedupe_knowledge", - "description": "Run the dedupe_knowledge action.", - "type": "string" - }, - "category": { - "description": "(dedupe_knowledge only) Restrict the scan to one knowledge category. Default: all categories.", - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - "limit": { - "description": "(dedupe_knowledge only) Max pairs to return. Default 20.", - "maximum": 200, - "minimum": 1, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "threshold": { - "description": "(dedupe_knowledge only) Similarity threshold (0–1). Default 0.7. Raise toward 1 for stricter matches.", - "maximum": 1, - "minimum": 0, - "type": "number" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"dedupe_pair\".", - "properties": { - "action": { - "const": "dedupe_pair", - "description": "Run the dedupe_pair action.", - "type": "string" - }, - "drop_id": { - "description": "REQUIRED for dedupe_pair — the duplicate to archive; its edges are rewired onto keep_id.", - "type": "string" - }, - "keep_id": { - "description": "REQUIRED for dedupe_pair — the node to keep.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "keep_id", - "drop_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"unlink\".", - "properties": { - "action": { - "const": "unlink", - "description": "Run the unlink action.", - "type": "string" - }, - "dst_id": { - "description": "REQUIRED for unlink — destination node shareable id.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "relation": { - "description": "REQUIRED for unlink — edge type to remove.", - "enum": [ - "relates-to", - "blocks", - "fulfills", - "contradicts", - "supersedes", - "caused-by", - "expires-on" - ], - "type": "string" - }, - "src_id": { - "description": "REQUIRED for unlink — source node shareable id.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "src_id", - "dst_id", - "relation" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"restore_node_version\".", - "properties": { - "action": { - "const": "restore_node_version", - "description": "Run the restore_node_version action.", - "type": "string" - }, - "node_id": { - "description": "Node shareable id (e.g. KNOW-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "version": { - "description": "REQUIRED for restore_node_version — the version number to restore as the new live content.", - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "version" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"retire_objective\".", - "properties": { - "action": { - "const": "retire_objective", - "description": "Run the retire_objective action.", - "type": "string" - }, - "objective_id": { - "description": "REQUIRED for retire_objective — the objective being retired (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "replaced_by": { - "description": "(retire_objective only) Optional replacement objective (TASK-N) — records a `supersedes` edge.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"merge_workspace\".", - "properties": { - "action": { - "const": "merge_workspace", - "description": "Run the merge_workspace action.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"cherry_pick_workspace\".", - "properties": { - "action": { - "const": "cherry_pick_workspace", - "description": "Run the cherry_pick_workspace action.", - "type": "string" - }, - "node_ids": { - "description": "REQUIRED for cherry_pick_workspace — short-ids of the nodes to promote (KNOW-N / TASK-N / DOC-N / …). Only items currently scoped to this workspace are promoted.", - "items": { - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "The workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace", - "node_ids" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"archive_workspace\".", - "properties": { - "action": { - "const": "archive_workspace", - "description": "Run the archive_workspace action.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"archive_component\".", - "properties": { - "action": { - "const": "archive_component", - "description": "Run the archive_component action.", - "type": "string" - }, - "node_id": { - "description": "Component shareable id to archive.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reparent_children_to": { - "description": "(archive_component only) Component to reparent active children under. Required if any active children exist.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"merge_topics\".", - "properties": { - "action": { - "const": "merge_topics", - "description": "Run the merge_topics action.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "sources": { - "description": "REQUIRED for merge_topics — list of source topics to merge into target. Must be non-empty.", - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "target": { - "description": "REQUIRED for merge_topics — target topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "sources", - "target" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"run_loop_attempt\".", - "properties": { - "action": { - "const": "run_loop_attempt", - "description": "Run the run_loop_attempt action.", - "type": "string" - }, - "base": { - "description": "Base git ref (default main).", - "minLength": 1, - "type": "string" - }, - "branch": { - "description": "Worker branch git ref (default HEAD).", - "minLength": 1, - "type": "string" - }, - "command": { - "description": "Exact declared check command. It is shown at the destructive approval boundary and must match the task contract.", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "Task whose declared Check and Check paths form the loop contract.", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "command" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "destructive action \"doctor\".", - "properties": { - "action": { - "const": "doctor", - "description": "Run the doctor action.", - "type": "string" - }, - "deep": { - "description": "(doctor only) Alias for `expensive`. Default false.", - "type": "boolean" - }, - "expensive": { - "description": "(doctor only) Run costly checks too (fuzzy near-duplicate detection, folded into the duplicates bucket). Default false.", - "type": "boolean" - }, - "fix": { - "description": "(doctor only) Apply safe auto-fixes (archive >90d-stale knowledge). Default false.", - "type": "boolean" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"cancel_task\": Cancel a task (→ cancelled) with a required `reason` (appended to the body). For wrong/duplicate/obsolete tasks — not ones you merely couldn't finish (use release). Like complete, may surface nearby nodes to re-verify.", + "properties": { + "action": { + "const": "cancel_task", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for cancel_task/force_claim_task/restore_node_version/archive_component — shareable id of the target (TASK-N / KNOW-N / COMP-N / …).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reason": { + "description": "REQUIRED for cancel_task — why this task is being cancelled.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "reason" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"force_claim_task\": Emergency override: take over a task in any state. Use only when the prior claimer is abandoned (check destructive(action=\"doctor\") first) or the user asks. Returns the prior claimer's identity so you can notify them; concurrent force-claims are last-write-wins.", + "properties": { + "action": { + "const": "force_claim_task", + "type": "string" + }, + "dispatched_with": { + "description": "(force_claim_task only) Operator's full briefing prompt when this force-claim is from a dispatched sub-agent. Up to 16 KB.", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "parent_task_id": { + "description": "(force_claim_task only) Operator's claimed task id when this force-claim is from a dispatched sub-agent. Leave null for direct claims.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"dedupe_knowledge\": Find near-duplicate knowledge nodes by fuzzy title similarity (pg_trgm). Read-only — never deletes or merges; returns candidate pairs sorted by similarity for the caller to act on (use node(action=\"update\") to archive the dup side).", + "properties": { + "action": { + "const": "dedupe_knowledge", + "type": "string" + }, + "category": { + "description": "(dedupe_knowledge only) Restrict the scan to one knowledge category. Default: all categories.", + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + "limit": { + "description": "(dedupe_knowledge only) Max pairs to return. Default 20.", + "maximum": 200, + "minimum": 1, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "threshold": { + "description": "(dedupe_knowledge only) Similarity threshold (0–1). Default 0.7. Raise toward 1 for stricter matches.", + "maximum": 1, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"dedupe_pair\": Mark `drop_id` as a duplicate of `keep_id`. Atomically rewires every edge that touches the drop onto the keep (skipping conflicts on the unique (src,dst,relation) constraint), deletes any stragglers, archives the drop, and appends a `## Deduped` pointer to its body — all in one transaction. Returns the kept node and the pre-conflict count of edges that touched the drop. DESTRUCTIVE (the drop is archived). Find the candidate pair first (a duplicate scan or a search); this action merges one.", + "properties": { + "action": { + "const": "dedupe_pair", + "type": "string" + }, + "drop_id": { + "description": "REQUIRED for dedupe_pair — the duplicate to archive; its edges are rewired onto keep_id.", + "type": "string" + }, + "keep_id": { + "description": "REQUIRED for dedupe_pair — the node to keep.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "keep_id", + "drop_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"unlink\": Remove the (src, dst, relation) edge if it exists; no-op if it doesn't. To preserve what the edge USED to mean, prefer link with `supersedes` over removing it.", + "properties": { + "action": { + "const": "unlink", + "type": "string" + }, + "dst_id": { + "description": "REQUIRED for unlink — destination node shareable id.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "relation": { + "description": "REQUIRED for unlink — edge type to remove.", + "enum": [ + "relates-to", + "blocks", + "fulfills", + "contradicts", + "supersedes", + "caused-by", + "expires-on" + ], + "type": "string" + }, + "src_id": { + "description": "REQUIRED for unlink — source node shareable id.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "src_id", + "dst_id", + "relation" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"restore_node_version\": Restore a prior version's content as the new live content. APPEND, NEVER DESTRUCTIVE: restoring v2 over a live v3 writes v2 as a new live v4, snapshotting v3 into history first — both stay recoverable, so a bad restore is just another restore. Recorded with restored_from_version so history shows it was a restore, not a fresh edit.", + "properties": { + "action": { + "const": "restore_node_version", + "type": "string" + }, + "node_id": { + "description": "Node shareable id (e.g. KNOW-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "version": { + "description": "REQUIRED for restore_node_version — the version number to restore as the new live content.", + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "version" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"retire_objective\": Retire an objective: cancels it (its status reads as 'superseded') and, when a replacement objective is named, records a `supersedes` edge so the chain stays navigable.", + "properties": { + "action": { + "const": "retire_objective", + "type": "string" + }, + "objective_id": { + "description": "REQUIRED for retire_objective — the objective being retired (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "replaced_by": { + "description": "(retire_objective only) Optional replacement objective (TASK-N) — records a `supersedes` edge.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"merge_workspace\": Promote ALL of a workspace's scoped knowledge to trunk (shared truth), then mark the workspace 'merged'. Each promoted node flips to trunk and is stamped with provenance (which workspace + when). To graduate only SOME items, use destructive(action=\"cherry_pick_workspace\") instead. Permanent (domain) workspaces cannot be merged — they never graduate.", + "properties": { + "action": { + "const": "merge_workspace", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"cherry_pick_workspace\": Promote ONLY the named knowledge/tasks from a workspace to trunk, leaving everything else scoped and the workspace active — the partial-merge / salvage case. Each promoted node flips to trunk and is stamped with provenance (which workspace + when). The workspace itself is NOT changed (archive or merge it separately). Ids not scoped to this workspace are ignored.", + "properties": { + "action": { + "const": "cherry_pick_workspace", + "type": "string" + }, + "node_ids": { + "description": "REQUIRED for cherry_pick_workspace — short-ids of the nodes to promote (KNOW-N / TASK-N / DOC-N / …). Only items currently scoped to this workspace are promoted.", + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "The workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace", + "node_ids" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"archive_workspace\": Archive a workspace (abandon it). RECOVERABLE — the workspace and ALL its scoped knowledge are kept as-is and can be reactivated; never deletes. Salvage anything worth keeping with destructive(action=\"cherry_pick_workspace\") FIRST — those items graduate to trunk and survive the archive.", + "properties": { + "action": { + "const": "archive_workspace", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for merge_workspace/cherry_pick_workspace/archive_workspace — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"archive_component\": Archive a component (soft-delete). If it has any ACTIVE children (sub-components, knowledge, or tasks), `reparent_children_to` is REQUIRED — they move to that target. We never silently orphan or cascade-delete.", + "properties": { + "action": { + "const": "archive_component", + "type": "string" + }, + "node_id": { + "description": "Component shareable id to archive.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reparent_children_to": { + "description": "(archive_component only) Component to reparent active children under. Required if any active children exist.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"merge_topics\": Merge multiple source topics into a single target across every knowledge AND task node in the active project — like rename_topic once per source, in one transaction with the same dedup semantics. Sources equal to the target (or empty) are silently skipped. Members-only.", + "properties": { + "action": { + "const": "merge_topics", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "sources": { + "description": "REQUIRED for merge_topics — list of source topics to merge into target. Must be non-empty.", + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "target": { + "description": "REQUIRED for merge_topics — target topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "sources", + "target" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"run_loop_attempt\": Locally execute the task's declared Check in the worker branch's registered worktree, challenge its protected wrapper with a fresh nonce, accept only a versioned STELE_LOOP_EVIDENCE JSON line, persist exact counts/output on the task, and halt on the same failure twice or five failed attempts. Hosted MCP fails closed because it cannot execute the repository.", + "properties": { + "action": { + "const": "run_loop_attempt", + "type": "string" + }, + "base": { + "description": "Base git ref (default main).", + "minLength": 1, + "type": "string" + }, + "branch": { + "description": "Worker branch git ref (default HEAD).", + "minLength": 1, + "type": "string" + }, + "command": { + "description": "Exact declared check command. It is shown at the destructive approval boundary and must match the task contract.", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "Task whose declared Check and Check paths form the loop contract.", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "command" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"doctor\": Run health checks on the active project, returning grouped issue buckets (stale/archivable/expired/superseded knowledge, stale claims, oversized/dangling/orphaned components, overdue or unbound risks, unverified load-bearing decisions, duplicates, empty bodies, over-fulfilled tasks). Run periodically — especially before destructive(action=\"force_claim_task\") on a task you suspect is abandoned. fix=true applies safe auto-fixes (archives archivable/expired/superseded knowledge and expire-on-close risks); all other buckets are reported only. expensive=true (alias deep=true) also runs costly fuzzy/trigram near-duplicate detection, folded into the duplicates bucket.", + "properties": { + "action": { + "const": "doctor", + "type": "string" + }, + "deep": { + "description": "(doctor only) Alias for `expensive`. Default false.", + "type": "boolean" + }, + "expensive": { + "description": "(doctor only) Run costly checks too (fuzzy near-duplicate detection, folded into the duplicates bucket). Default false.", + "type": "boolean" + }, + "fix": { + "description": "Apply safe auto-fixes: archive stale, expired and superseded knowledge, and risks bound to closed tasks. Default false.", + "type": "boolean" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + } +]
- Changed
document1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "document action \"create\".", - "properties": { - "action": { - "const": "create", - "description": "Run the create action.", - "type": "string" - }, - "body": { - "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the doc at the project root." - }, - "file_path": { - "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "document action \"create_diagram\".", - "properties": { - "action": { - "const": "create_diagram", - "description": "Run the create_diagram action.", - "type": "string" - }, - "body": { - "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", - "type": "string" - }, - "component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional parent component shareable id. Omit to land the diagram at the project root." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_url": { - "description": "Optional canonical URL for the source.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Add a document — a large multi-section markdown artifact (plan, audit, design spec, retrospective). Use over knowledge(action=\"create\") when content is long/narrative rather than a single short structured fact. Documents are surfaced by recall by title only; the body is never auto-injected, so an agent must call fetch DOC-N to read it. component_id is optional (omit to land at the project root; re-parent later via node(action=\"update\")). For the body pass exactly one of `body` (inline markdown) or `file_path` (repo-relative path the server reads, UTF-8 only, max 1 MB) — or neither for a title-only stub. file_path works only on a local connection; over a hosted connection pass the body inline.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "body": { + "description": "The artifact body: for create, markdown (multi-section is fine — the whole point of a document is a long narrative; no upper bound; mutually exclusive with `file_path`). For create_diagram, d2 source. Write prose in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the doc at the project root." + }, + "file_path": { + "description": "(create only) Optional alternative to `body`. Path relative to the MCP server's cwd (the session working directory). The server reads the file and uses its content as the body — use this to import existing repo docs without burning agent context. UTF-8 text only, max 1 MB. Mutually exclusive with `body`. Stdio-only.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04', 'pr:stele#117', 'meeting:planning-q2'. When `file_path` is used and `source` is omitted, defaults to 'file:<relative-path>'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source — Notion page, Google doc, PR description, etc.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/create_diagram — one-line summary, e.g. \"v1.5 implementation plan\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"plan\", \"audit\", \"v1.5\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"create_diagram\": Add a diagram — a visual design artifact (architecture, workflow, sequence, or flow diagram) authored in d2 source and rendered to an SVG on its page. Use when a picture explains a design better than prose: the diagram lives in the graph as a durable, referenceable node the next agent and the human can open. The `body` is d2 source — it is rendered server-side (you never write the image). Diagram source is excluded from search and recall (it's structural), but the title + topics stay findable and any shareable ids you label in the diagram (e.g. TASK-123) still create mention links. To show the rendered diagram inline in another node's body, embed it with `{{DIAG-N:short description}}` — it renders as a live image there, not just a link. component_id is optional (omit to land at the project root).", + "properties": { + "action": { + "const": "create_diagram", + "type": "string" + }, + "body": { + "description": "The diagram source, written in d2 (the terrastruct.com/d2 language). It is rendered to an SVG (and ASCII) on the diagram's page — you author the source, not the image. Keep it valid d2; load the d2 authoring guidance if you're unsure of the syntax.", + "type": "string" + }, + "component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional parent component shareable id. Omit to land the diagram at the project root." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "Optional free-form provenance label, e.g. 'session:2026-05-04' or 'design:sign-in'.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_url": { + "description": "Optional canonical URL for the source.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "One-line summary, e.g. \"Sign-in sequence\" or \"Service topology\".", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list (\"architecture\", \"sequence\", \"auth\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "title" + ], + "type": "object" + } +]
- Changed
inspect1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "inspect action \"context\".", - "properties": { - "action": { - "const": "context", - "description": "Run the context action.", - "type": "string" - }, - "depth": { - "description": "(context only) BFS depth from seeds (default 1, max 3). 0 returns just seeds; 1 follows one edge.", - "maximum": 3, - "minimum": 0, - "type": "integer" - }, - "hub_edge_limit": { - "description": "(context only) Soft limit: nodes with more edges than this contribute only their N most-recent edges (default 12).", - "exclusiveMinimum": 0, - "maximum": 50, - "type": "integer" - }, - "max_entries": { - "description": "(context only) Cap on returned nodes (default 30, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "(context/provenance only) Shareable id (TASK-N/KNOW-N/COMP-N/DOC-N) to seed by: the node's title becomes the FTS query, then the walk proceeds from there. Use to expand the neighborhood around a specific node without already knowing good search terms. Overrides `query`.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "(context/provenance only) FTS query. Same syntax as search_nodes. Either query or topic must be set.", - "minLength": 1, - "type": "string" - }, - "seed_limit": { - "description": "(context only) Cap on seed nodes from FTS / topic match (default 10).", - "exclusiveMinimum": 0, - "maximum": 50, - "type": "integer" - }, - "topic": { - "description": "(context only) Topic tag to seed by (case-insensitive exact match on the topics array). Skips FTS — use when you want a clean topic-scoped neighborhood.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"provenance\".", - "properties": { - "action": { - "const": "provenance", - "description": "Run the provenance action.", - "type": "string" - }, - "max_hops": { - "description": "(provenance only) Walker depth cap (default 5, max 8). 0 returns just the seed.", - "maximum": 8, - "minimum": 0, - "type": "integer" - }, - "node_id": { - "description": "Starting node — shareable id (TASK-N | KNOW-N | COMP-N | DOC-N | NODE-N). Exactly one of `node_id` or `query` must be set.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Free-text 'why' prompt — resolved to a starting node via FTS top-1 hit. Exactly one of `node_id` or `query` must be set.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"briefing\".", - "properties": { - "action": { - "const": "briefing", - "description": "Run the briefing action.", - "type": "string" - }, - "environment": { - "additionalProperties": false, - "description": "(briefing only) Describes which Stele harness features the calling client has. All fields default to false/unknown when absent — the safest assumption for hosted clients.", - "properties": { - "auto_rag_available": { - "description": "True if this client can point automatic recall at a topic and receive related context on later turns. False/absent → the briefing tells the agent to look things up with recall itself, every time.", - "type": "boolean" - }, - "hooks_available": { - "description": "True if the host client runs Stele lifecycle hooks (recall, session_start, etc.). False/absent → the briefing includes manual fallback instructions.", - "type": "boolean" - }, - "skills_available": { - "description": "True if the host client can invoke Stele skills (e.g. /stele:start). False/absent → the briefing instructs the agent to follow protocol manually.", - "type": "boolean" - } - }, - "type": "object" - }, - "intent": { - "description": "(briefing only) Short description of the current request or goal (e.g. 'review launch plan', 'fix the auth bug'). When provided, the briefing includes intent-specific next-call suggestions with project_slug already filled in.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"claim_history\".", - "properties": { - "action": { - "const": "claim_history", - "description": "Run the claim_history action.", - "type": "string" - }, - "node_id": { - "description": "(claim_history only) Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"workspace\".", - "properties": { - "action": { - "const": "workspace", - "description": "Run the workspace action.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "(workspace only) The workspace to fetch — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"stats\".", - "properties": { - "action": { - "const": "stats", - "description": "Run the stats action.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"loop\".", - "properties": { - "action": { - "const": "loop", - "description": "Run the loop action.", - "type": "string" - }, - "base": { - "description": "Base git ref (default main).", - "minLength": 1, - "type": "string" - }, - "branch": { - "description": "Worker branch git ref (default HEAD).", - "minLength": 1, - "type": "string" - }, - "node_id": { - "description": "Task whose declared Check and Check paths form the loop contract.", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "inspect action \"rag_status\".", - "properties": { - "action": { - "const": "rag_status", - "description": "Run the rag_status action.", - "type": "string" - }, - "recall_session": { - "description": "The recall session handle from the awaiting-focus notice. Without it this reports only that automatic topic recall is unavailable here.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"context\": Walk the project graph from FTS (or topic-tag) seeds and return a scored neighborhood — not just flat search hits. Use BEFORE starting non-trivial work on a topic: it surfaces decisions, blockers, and prior lessons connected via edges that don't themselves contain the search keywords. Unlike search (flat FTS, no traversal), this seeds via FTS then follows edges, scoring by category + relation + recency. Hub nodes contribute only their most-recent edges, so a root component doesn't drag in the whole project. Bodies are truncated to ~400 chars and the total payload is capped at ~30k chars (lowest-scored non-seed nodes dropped, `_truncated: true` set); call fetch with the short id for a full body.", + "properties": { + "action": { + "const": "context", + "type": "string" + }, + "depth": { + "description": "(context only) BFS depth from seeds (default 1, max 3). 0 returns just seeds; 1 follows one edge.", + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "hub_edge_limit": { + "description": "(context only) Soft limit: nodes with more edges than this contribute only their N most-recent edges (default 12).", + "exclusiveMinimum": 0, + "maximum": 50, + "type": "integer" + }, + "max_entries": { + "description": "(context only) Cap on returned nodes (default 30, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "(context/provenance only) Shareable id (TASK-N/KNOW-N/COMP-N/DOC-N) to seed by: the node's title becomes the FTS query, then the walk proceeds from there. Use to expand the neighborhood around a specific node without already knowing good search terms. Overrides `query`.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "(context/provenance only) FTS query, in the same syntax as keyword search. Either query or topic must be set.", + "minLength": 1, + "type": "string" + }, + "seed_limit": { + "description": "(context only) Cap on seed nodes from FTS / topic match (default 10).", + "exclusiveMinimum": 0, + "maximum": 50, + "type": "integer" + }, + "topic": { + "description": "(context only) Topic tag to seed by (case-insensitive exact match on the topics array). Skips FTS — use when you want a clean topic-scoped neighborhood.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"provenance\": Walk the graph linearly from a seed and return a single ordered lineage answering 'why is X the way it is?'. At each hop it follows the strongest un-visited edge (fulfills > caused-by/supersedes > contradicts/blocks > mentions/relates-to), stopping at a risk-category node, when edges run out, or at max_hops. Use this when you want a cited chain you can narrate; use inspect(action=\"context\") for a broad unordered neighborhood to reason against. Supply exactly ONE of `node_id` (a shareable id) or `query` (free text resolved to the top FTS hit) — both or neither is a hard error.", + "properties": { + "action": { + "const": "provenance", + "type": "string" + }, + "max_hops": { + "description": "(provenance only) Walker depth cap (default 5, max 8). 0 returns just the seed.", + "maximum": 8, + "minimum": 0, + "type": "integer" + }, + "node_id": { + "description": "Starting node — shareable id (TASK-N | KNOW-N | COMP-N | DOC-N | NODE-N). Exactly one of `node_id` or `query` must be set.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Free-text 'why' prompt — resolved to a starting node via FTS top-1 hit. Exactly one of `node_id` or `query` must be set.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"briefing\": Return a compact, stateless briefing that teaches a visiting agent how to use Stele safely for the current project — for hosted/remote clients (ChatGPT, Claude web, Cursor, Codex, Gemini) that connect via MCP but lack hooks, skills, or automatic recall. The response is a PURE READ: it does NOT set an active project or store any session state, so every subsequent tool still requires project_slug explicitly. Call once at the start of a session or task when nothing is being surfaced to you automatically.", + "properties": { + "action": { + "const": "briefing", + "type": "string" + }, + "environment": { + "additionalProperties": false, + "description": "(briefing only) Describes which Stele harness features the calling client has. All fields default to false/unknown when absent — the safest assumption for hosted clients.", + "properties": { + "auto_rag_available": { + "description": "True if this client can point automatic recall at a topic and receive related context on later turns. False/absent → the briefing tells the agent to look things up with recall itself, every time.", + "type": "boolean" + }, + "hooks_available": { + "description": "True if the host client runs Stele lifecycle hooks (recall, session_start, etc.). False/absent → the briefing includes manual fallback instructions.", + "type": "boolean" + }, + "skills_available": { + "description": "True if the host client can invoke Stele skills (e.g. /stele:start). False/absent → the briefing instructs the agent to follow protocol manually.", + "type": "boolean" + } + }, + "type": "object" + }, + "intent": { + "description": "(briefing only) Short description of the current request or goal (e.g. 'review launch plan', 'fix the auth bug'). When provided, the briefing includes intent-specific next-call suggestions with project_slug already filled in.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"claim_history\": Full append-only claim history for a task — every claim/release/complete/cancel with agent, model, session, worktree, and operator lineage. Capped at 200 events.", + "properties": { + "action": { + "const": "claim_history", + "type": "string" + }, + "node_id": { + "description": "(claim_history only) Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"workspace\": Fetch one workspace by NAME or uuid, including its branch associations and whether it's active for this session.", + "properties": { + "action": { + "const": "workspace", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "(workspace only) The workspace to fetch — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"stats\": One-call overview of the active project — counts, top topics, overdue deadlines (any active knowledge with a past due_at), likely-abandoned tasks (claimed > 24h ago), and recent activity. Cheaper than running list(what=\"components\") + list_tasks + search separately.", + "properties": { + "action": { + "const": "stats", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"loop\": Fail closed unless trusted local Git proves the branch left every declared Check path untouched. Exit 0 certifies oracle integrity, 1 names protected paths the branch modified, and 2 means unknown. Changed files are computed locally; callers cannot supply them.", + "properties": { + "action": { + "const": "loop", + "type": "string" + }, + "base": { + "description": "Base git ref (default main).", + "minLength": 1, + "type": "string" + }, + "branch": { + "description": "Worker branch git ref (default HEAD).", + "minLength": 1, + "type": "string" + }, + "node_id": { + "description": "Task whose declared Check and Check paths form the loop contract.", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"rag_status\": Report this session's automatic topic recall: whether a focus is set, what it is, and whether recall is paused. Use it when you are unsure whether the context you are being shown still matches the work — if the focus names last hour's task, replace it.", + "properties": { + "action": { + "const": "rag_status", + "type": "string" + }, + "recall_session": { + "description": "The recall session handle from the awaiting-focus notice. Without it this reports only that automatic topic recall is unavailable here.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + } +]
- Changed
knowledge1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "knowledge action \"create\".", - "properties": { - "action": { - "const": "create", - "description": "Run the create action.", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", - "items": { - "type": "string" - }, - "type": "array" - }, - "body": { - "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "category": { - "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "due_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." - }, - "expires_at": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ] - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): the node auto-archives once the date passes — for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." - }, - "expires_on_task": { - "anyOf": [ - { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "const": "none", - "type": "string" - } - ], - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." - }, - { - "type": "null" - } - ], - "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "source": { - "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "source_type": { - "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", - "format": "uri", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — one-line summary.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "category", - "title", - "expires_on_task", - "due_at", - "expires_at" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "knowledge action \"review\".", - "properties": { - "action": { - "const": "review", - "description": "Run the review action.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Add a knowledge node under an existing active component (component_id is\nREQUIRED) — prefer an existing component over creating one. Search before\nadding: if a near-match exists, update it with node(action=\"update\") instead of duplicating. Always\ninclude topics (without them the node won't surface in topic queries) and link\nrelated nodes — isolated nodes are nearly invisible.\n\n## The knowledge categories\n\nPick the one that matches what the fact *is*, and give the body the shape it wants. Thin one-liners\ndilute the graph — a node that restates its own title helps nobody.\n\n| Category | It is | Body shape |\n|---|---|---|\n| `decision` | A choice that was made, and could have gone another way. | ## Context / ## Alternatives considered / ## Why this won / ## Consequences |\n| `architecture` | A structural fact about how the system is built. | ## What / ## How it works / ## Why this shape |\n| `goal` | What success looks like. | ## Outcome / ## Why it matters / ## How we'll know we're done |\n| `risk` | Something that might go wrong, and hasn't yet. | ## Risk / ## Likelihood & impact / ## Mitigation / ## Trigger |\n| `gap` | A known missing piece — real, but not yet work anyone has committed to. | ## Gap / ## Why it matters / ## What needs to happen to close it |\n| `lesson` | Something learned the hard way, usually from a failure. | ## Symptom / ## Root cause / ## Fix / ## Why this matters |\n| `opportunity` | Possible future work worth considering. | ## Opportunity / ## Why now / ## What it would take |\n| `priority` | What the focus is right now, and what it is not. | ## Focus / ## Why this and not X / ## Time horizon |\n| `fyi` | Context worth remembering that calls for no action. | A paragraph or two; short is fine. |\n\nTwo that get confused: a **`gap`** is a hole someone still has to decide what to do about; once the work\nis agreed it is a **task**, not a gap. A **`risk`** has not happened; a **`lesson`** already did.\n\n**A lesson from a bug is worth little without its trigger.** Put the literal error message, the failing\nsymbol, the command that broke, into the body. The graph resurfaces knowledge by matching it against\nwhat an agent is doing, so a lesson carrying the real error string reappears the moment someone hits that\nerror again. Worded abstractly, it stays invisible exactly when it would have helped.\n\n\nLIFECYCLE IS REQUIRED. Decide all three — expires_on_task, expires_at,\ndue_at — every time; pass null to skip any (durable decision/architecture\nusually skip all three). Bound nodes retire themselves, which is what keeps\nthe graph self-maintaining — that's why these aren't optional.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched.", + "items": { + "type": "string" + }, + "type": "array" + }, + "body": { + "description": "(create only) Markdown body with sections that fit the category: a decision's context, alternatives, and why it won; a lesson's symptom, root cause, and fix; a risk's likelihood, impact, and mitigation. Thin one-sentence bodies are discouraged. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "category": { + "description": "REQUIRED for create — what the fact is: decision (a choice that could have gone otherwise), architecture (how the system is built), goal, risk (may go wrong, hasn't yet), lesson (already went wrong; include the literal error or symbol), gap (missing, not yet committed work), opportunity, priority, fyi.", + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "due_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp. A deadline; active knowledge past its due_at surfaces as an overdue commitment. Set it on anything carrying a real deadline. Distinct from expires_at (a reminder, never auto-archived)." + }, + "expires_at": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ] + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — a date (2026-12-31) or an ISO-8601 timestamp (date TTL): once the date passes the node is reported as expired, and the next health-check run with fix archives it. Use it for EPHEMERAL knowledge (fyi notes, temporary workarounds). Do NOT set on decision/architecture (those retire by supersession, not a date)." + }, + "expires_on_task": { + "anyOf": [ + { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "const": "none", + "type": "string" + } + ], + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any)." + }, + { + "type": "null" + } + ], + "description": "REQUIRED for create (pass null to skip) — bind a TEMPORARY risk/gap to the OPEN task (TASK-N) that will resolve it: the node auto-archives when that task completes or cancels — no manual cleanup. Durable decisions/lessons/architecture must NOT bind (they retire by supersession, not a task closing) — pass null. Binding to an already-closed task is a dead trigger and is dropped with a warning." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "source": { + "description": "(create only) Optional free-form provenance label — where this fact came from. Conventional formats: 'workplace:cdi-fyi', 'meeting:1on1-aaron', 'pr:stele#117', 'doc:rfc-042'. Include this whenever the knowledge originates outside the agent's own reasoning (chat thread, meeting, doc, PR). Omit when the fact is derived from the agent reading code or running tools in this session.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "source_type": { + "description": "(create only) Provenance / trust signal. Defaults to 'claude' from MCP (agent-authored). Pass 'human' only when transcribing a user-verified fact, or 'import' for bulk-imported entries.", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "description": "(create only) Optional canonical URL for the source — Slack permalink, PR url, doc link. Rendered as a link icon on the knowledge detail page.", + "format": "uri", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — one-line summary.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list (\"oauth\", \"perf\", \"v2\").", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "category", + "title", + "expires_on_task", + "due_at", + "expires_at" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"review\": Verify a knowledge node: record that you deliberately confirmed its claims are STILL TRUE against current reality, stamping `last_verified_at` to now. This verification axis is orthogonal to freshness — the only signal that catches a recently-written yet factually-wrong node. Actually check it first; if it's wrong, do NOT verify — use node(action=\"update\") or a supersedes edge instead. Does NOT change content, freshness, or source_type. Knowledge only; tasks/components are rejected.", + "properties": { + "action": { + "const": "review", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for review — knowledge shareable id (e.g. KNOW-7). Tasks/components are rejected.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + } +]
- Changed
list1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "list action \"nodes\".", - "properties": { - "component_id": { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - "limit": { - "description": "(nodes/documents/diagrams/node_versions only) Page size / max results (default 50, max 200; defaults vary by collection).", - "exclusiveMinimum": 0, - "maximum": 200, - "type": "integer" - }, - "offset": { - "description": "(nodes/documents/diagrams only) Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "(nodes/documents/diagrams only) Filter to one exact status.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "type": { - "description": "(nodes only) Limit to one node type (component | knowledge | task | document | diagram).", - "enum": [ - "component", - "knowledge", - "task", - "document", - "diagram" - ], - "type": "string" - }, - "what": { - "const": "nodes", - "description": "Run the nodes action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"components\".", - "properties": { - "parent_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(components only) Omit to list the WHOLE component tree (every component, bodyless) — do this to find where a task/knowledge should be filed. Pass a component id (COMP-N) for just its direct children, or null for root-level only." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "components", - "description": "Run the components action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"topics\".", - "properties": { - "min_count": { - "description": "(topics only) Minimum count to include a topic in the result. Default 1.", - "minimum": 1, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "topics", - "description": "Run the topics action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"documents\".", - "properties": { - "component_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Parent component shareable id. Omit for all docs." - }, - "limit": { - "description": "Page size (default 50, max 100).", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "description": "Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Default 'active'.", - "enum": [ - "active", - "archived" - ], - "type": "string" - }, - "what": { - "const": "documents", - "description": "Run the documents action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"diagrams\".", - "properties": { - "component_id": { - "anyOf": [ - { - "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Parent component shareable id. Omit for all diagrams." - }, - "limit": { - "description": "Page size (default 50, max 100).", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "description": "Skip N results (default 0).", - "minimum": 0, - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Default 'active'.", - "enum": [ - "active", - "archived" - ], - "type": "string" - }, - "what": { - "const": "diagrams", - "description": "Run the diagrams action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"objectives\".", - "properties": { - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "objectives", - "description": "Run the objectives action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"workspaces\".", - "properties": { - "include_archived": { - "description": "(workspaces only) Include archived/merged workspaces (default false).", - "type": "boolean" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "workspaces", - "description": "Run the workspaces action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"comments\".", - "properties": { - "include_replies": { - "description": "(comments only) Include replies under each thread (default true).", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for comments — the node whose comments to list.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "comments", - "description": "Run the comments action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"node_versions\".", - "properties": { - "limit": { - "description": "Max versions to return (default 100, server cap 500).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "node_id": { - "description": "REQUIRED for node_versions — node shareable id (e.g. KNOW-42, DOC-7).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "what": { - "const": "node_versions", - "description": "Run the node_versions action.", - "type": "string" - } - }, - "required": [ - "what", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "list action \"projects\".", - "properties": { - "what": { - "const": "projects", - "description": "Run the projects action.", - "type": "string" - } - }, - "required": [ - "what" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "what=\"nodes\": Generic, paginated node listing — the lean single tool the typed CLI `list` commands ride on (knowledge/document/component plain lists). Filter by type, status, and parent component; ordered by most-recently-updated. Bodies are omitted — fetch one with fetch for the full body. For the faceted task view use list_tasks; for the objective tracker use list(what=\"objectives\").", + "properties": { + "component_id": { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + "limit": { + "description": "(nodes/documents/diagrams/node_versions only) Page size / max results (default 50, max 200; defaults vary by collection).", + "exclusiveMinimum": 0, + "maximum": 200, + "type": "integer" + }, + "offset": { + "description": "(nodes/documents/diagrams only) Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "(nodes/documents/diagrams only) Filter to one exact status.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "type": { + "description": "(nodes only) Limit to one node type (component | knowledge | task | document | diagram).", + "enum": [ + "component", + "knowledge", + "task", + "document", + "diagram" + ], + "type": "string" + }, + "what": { + "const": "nodes", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"components\": List components in the active project. Omit `parent_id` to get the ENTIRE tree (every component, bodyless) — the default for finding the right component to file a task/knowledge under. Pass `parent_id` (COMP-N) for a component's direct children, or null for root-level only. Always check this before creating a component: prefer reusing an existing one, they shouldn't proliferate. Cached ~30s; invalidated on add/archive_component.", + "properties": { + "parent_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(components only) Omit to list the WHOLE component tree (every component, bodyless) — do this to find where a task/knowledge should be filed. Pass a component id (COMP-N) for just its direct children, or null for root-level only." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "components", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"topics\": List every topic in the active project with its node count, counted across active knowledge + open/in_progress tasks only. Use before rename_topic / merge_topics to spot near-duplicates (releng / release-eng / release-engineering).", + "properties": { + "min_count": { + "description": "(topics only) Minimum count to include a topic in the result. Default 1.", + "minimum": 1, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "topics", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"documents\": List documents in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` (first 10 lines, long lines truncated) so agents can triage in one call; use fetch DOC-N for the full body.", + "properties": { + "component_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Parent component shareable id. Omit for all docs." + }, + "limit": { + "description": "Page size (default 50, max 100).", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Default 'active'.", + "enum": [ + "active", + "archived" + ], + "type": "string" + }, + "what": { + "const": "documents", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"diagrams\": List diagrams in the active project, optionally filtered (filters AND-combine). Each row carries a short `preview` of the d2 source so agents can triage in one call; use fetch DIAG-N for the full source.", + "properties": { + "component_id": { + "anyOf": [ + { + "description": "(nodes/documents/diagrams only) Limit results to nodes/docs/diagrams under this parent component (COMP-N).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Parent component shareable id. Omit for all diagrams." + }, + "limit": { + "description": "Page size (default 50, max 100).", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "Skip N results (default 0).", + "minimum": 0, + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Default 'active'.", + "enum": [ + "active", + "archived" + ], + "type": "string" + }, + "what": { + "const": "diagrams", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"objectives\": Overview of the project's objectives — for each: derived status (on-track / drifting / done / superseded), milestone count, task rollup (done/total), and how many loose (un-milestoned) tasks it has. Plus the project vision and the count of drift tasks serving no objective. Use this to answer 'what are we steering toward, and is anything drifting?'", + "properties": { + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "objectives", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"workspaces\": List a project's workspaces (named scoped overlays), marking which are ACTIVE for this session. Trunk is always implicitly active and is not listed.", + "properties": { + "include_archived": { + "description": "(workspaces only) Include archived/merged workspaces (default false).", + "type": "boolean" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "workspaces", + "type": "string" + } + }, + "required": [ + "what", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"comments\": List a node's comments as threads (top-level comment + replies), each flagged seen/unseen for you with its anchor status. Resolved threads and soft-deleted tombstones are included but don't count toward active/unread. Reading does NOT mark them seen (pass mark_seen=true to acknowledge).", + "properties": { + "include_replies": { + "description": "(comments only) Include replies under each thread (default true).", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for comments — the node whose comments to list.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "comments", + "type": "string" + } + }, + "required": [ + "what", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"node_versions\": List a node's content version history — every superseded title/body snapshot, newest first, with the editor and timestamp. Captured automatically by a DB trigger on every content edit, so it's a complete backup. PULL-ONLY: never auto-injected; call deliberately to inspect churn or recover what a node said before a bad edit.", + "properties": { + "limit": { + "description": "Max versions to return (default 100, server cap 500).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "node_id": { + "description": "REQUIRED for node_versions — node shareable id (e.g. KNOW-42, DOC-7).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "what": { + "const": "node_versions", + "type": "string" + } + }, + "required": [ + "what", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "what=\"projects\": List Stele projects the current user is a member of. Use the `slug` from here as the `project_slug` argument on every other tool. Cached for ~30s per MCP session — cheap to call repeatedly.", + "properties": { + "what": { + "const": "projects", + "type": "string" + } + }, + "required": [ + "what" + ], + "type": "object" + } +]
- Changed
node1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "node action \"update\".", - "properties": { - "action": { - "const": "update", - "description": "Run the update action.", - "type": "string" - }, - "anchor_files": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." - }, - "anchor_symbols": { - "anyOf": [ - { - "anyOf": [ - { - "not": {} - }, - { - "items": { - "type": "string" - }, - "type": "array" - } - ], - "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — user uuid (not a shareable id)" - }, - "body": { - "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": [ - "string", - "null" - ] - }, - "category": { - "anyOf": [ - { - "enum": [ - "decision", - "architecture", - "goal", - "risk", - "gap", - "fyi", - "opportunity", - "priority", - "lesson" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge-type nodes only" - }, - "due_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." - }, - "edits": { - "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with grep_nodes: use a matching line as old_string.", - "items": { - "additionalProperties": false, - "properties": { - "new_string": { - "description": "Replacement text.", - "type": "string" - }, - "old_string": { - "description": "Exact substring to find in the current body.", - "minLength": 1, - "type": "string" - }, - "replace_all": { - "description": "Replace every occurrence (default: require a unique match).", - "type": "boolean" - } - }, - "required": [ - "old_string", - "new_string" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "expected_version": { - "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", - "exclusiveMinimum": 0, - "type": "integer" - }, - "expires_at": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, doctor(fix=true) auto-archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." - }, - "hidden": { - "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", - "type": "boolean" - }, - "node_id": { - "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", - "type": "string" - }, - "parent_component_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "source": { - "anyOf": [ - { - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." - }, - "source_type": { - "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", - "enum": [ - "human", - "claude", - "import" - ], - "type": "string" - }, - "source_url": { - "anyOf": [ - { - "format": "uri", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." - }, - "status": { - "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use claim_task, complete_task, or cancel_task so lifecycle records and agent identity remain atomic.", - "enum": [ - "active", - "archived", - "backlog", - "open", - "in_progress", - "done", - "cancelled" - ], - "type": "string" - }, - "title": { - "description": "(update only) New title.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(update only) knowledge or task nodes only", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "node action \"move\".", - "properties": { - "action": { - "const": "move", - "description": "Run the move action.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "node action \"link\".", - "properties": { - "action": { - "const": "link", - "description": "Run the link action.", - "type": "string" - }, - "dst_id": { - "description": "REQUIRED for link — destination node shareable id.", - "type": "string" - }, - "evidence": { - "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", - "maxLength": 4000, - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reason": { - "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", - "maxLength": 2000, - "minLength": 1, - "type": "string" - }, - "relation": { - "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", - "enum": [ - "relates-to", - "blocks", - "fulfills", - "contradicts", - "supersedes", - "caused-by", - "expires-on" - ], - "type": "string" - }, - "src_id": { - "description": "REQUIRED for link — source node shareable id.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "src_id", - "dst_id", - "relation" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"update\": Update editable fields on any node; each field is validated against what's valid for the node's type, so type-mismatched fields (e.g. category on a task) are rejected. A task's status may be set to open or backlog here, but claim, completion, and cancellation are REJECTED — use task(action=\"claim\") / task(action=\"complete\") so the claim and completion (with session + agent identity) are recorded atomically, or destructive(action=\"cancel_task\") so the destructive transition records a reason. Archiving a component is also rejected; use destructive(action=\"archive_component\") so active children cannot be orphaned. `parent_component_id` reparents across components in the same project. Surgical edits: pass `edits` (literal old_string→new_string replacements applied server-side) instead of `body` to fix a substring without resending the whole body — they're mutually exclusive, and a non-matching old_string errors rather than no-ops. Optional `expected_version` gives optimistic concurrency.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "anchor_files": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Repo-relative file paths this note is about (e.g. 'src/auth/session.ts'). When someone later edits a file named here, this node surfaces to them. Set it on a lesson/risk/gap scoped to specific code so it resurfaces exactly when that code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — repo-relative file paths this node is about; surfaces it when one is later edited (for a task: don't step on toes). Pass [] or null to clear." + }, + "anchor_symbols": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Symbol names this note is about — function / class / constant / env var (e.g. 'refreshToken', 'SESSION_TTL'). Same purpose as anchor_files: a precise hook so the note resurfaces when that symbol's code is touched." + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge or task nodes — symbol names this node is about (function / class / constant / env var). Pass [] or null to clear." + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — user uuid (not a shareable id)" + }, + "body": { + "description": "(update only) New markdown body (full replace) — mutually exclusive with edits. Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": [ + "string", + "null" + ] + }, + "category": { + "anyOf": [ + { + "enum": [ + "decision", + "architecture", + "goal", + "risk", + "gap", + "fyi", + "opportunity", + "priority", + "lesson" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge-type nodes only" + }, + "due_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 deadline. Pass null to clear. Any active knowledge with a past due_at surfaces in stats.overdue_commitments." + }, + "edits": { + "description": "(update only) Literal string-replacements applied to the body server-side, in order. Mutually exclusive with `body`. Pair with an exact search: use a matching line as old_string.", + "items": { + "additionalProperties": false, + "properties": { + "new_string": { + "description": "Replacement text.", + "type": "string" + }, + "old_string": { + "description": "Exact substring to find in the current body.", + "minLength": 1, + "type": "string" + }, + "replace_all": { + "description": "Replace every occurrence (default: require a unique match).", + "type": "boolean" + } + }, + "required": [ + "old_string", + "new_string" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "expected_version": { + "description": "(update only) Optimistic concurrency: reject the write if the node's current version differs (it changed since you read it).", + "exclusiveMinimum": 0, + "type": "integer" + }, + "expires_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — ISO-8601 expiry (date TTL). Pass null to clear. When it passes, the next health-check run with fix archives the node. For ephemeral `fyi`/temporary-note knowledge; decision/architecture retire by supersession instead." + }, + "hidden": { + "description": "(update only) Set true to HIDE this node from every retrieval surface (search, recall, lists) — the one explicit invisibility. It stays fetchable by its shareable id (shown with a HIDDEN banner), so hiding is never a delete. Use for a genuine mistake, expired ephemera, or a secret that was pasted in by accident. Set false to un-hide. On the hosted assistant this pauses for human approval.", + "type": "boolean" + }, + "node_id": { + "description": "REQUIRED for update — shareable id of the node to edit (TASK-N/KNOW-N/COMP-N/DOC-N/NODE-N).", + "type": "string" + }, + "parent_component_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) shareable id of the new parent component. Accepted for component, knowledge, task, and document nodes. null = move to project root (documents and components only). Same-project enforcement is implicit — short ids resolve only within the active project." + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) task nodes only — priority P0 (most urgent) … P3. Pass null to clear." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "source": { + "anyOf": [ + { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — free-form provenance label. Pass null to clear." + }, + "source_type": { + "description": "(update only) knowledge nodes only — provenance / trust signal (human, claude, import).", + "enum": [ + "human", + "claude", + "import" + ], + "type": "string" + }, + "source_url": { + "anyOf": [ + { + "format": "uri", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(update only) knowledge nodes only — canonical URL for the source. Pass null to clear." + }, + "status": { + "description": "(update only) A task may be set to open or backlog here. Claim, completion, and cancellation must use the task lifecycle actions (claim, complete, cancel) so lifecycle records and agent identity remain atomic.", + "enum": [ + "active", + "archived", + "backlog", + "open", + "in_progress", + "done", + "cancelled" + ], + "type": "string" + }, + "title": { + "description": "(update only) New title.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(update only) knowledge or task nodes only", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"move\": Move an EXISTING node between scopes — into a workspace (hiding it from trunk readers) or back to trunk (shared truth). Re-scopes by id; reversible, never deletes. Distinct from destructive(action=\"cherry_pick_workspace\") (which promotes to trunk WITH provenance) — This is a plain re-assignment in any direction.", + "properties": { + "action": { + "const": "move", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for move — the node to move, a short-id (KNOW-N / TASK-N / DOC-N / COMP-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for move — destination scope: 'trunk' for shared truth, or a workspace NAME (e.g. \"Billing\") or uuid to scope it there.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"link\": Create a typed semantic link between two nodes (src → dst). Idempotent: re-linking the same (src, dst, relation) returns the existing edge, no error.\n\nSome relations change the graph: `supersedes` retires dst (it stays findable, but comes back marked SUPERSEDED and ranked below live knowledge), and `expires-on` retires src later. Every relation except `relates-to` is a claim and needs a `reason`; `supersedes` also needs `evidence`. If you cannot state the reason, use `relates-to`.\n\n## What each link means — and what it does\n\nAn edge is not decoration. Some of these change the graph.\n\n| Relation | Direction | Use it when | What it actually does |\n|---|---|---|---|\n| `relates-to` | A → B | Someone who pulls up B should know A exists. The weakest signal — use it when nothing typed fits. | Nothing but a pointer. Both nodes stay active. |\n| `blocks` | child → parent | A must land before B can be completed or become true. Also the **subtask** relation. | Surfaces A as a subtask of B and drives B's progress. |\n| `fulfills` | task → knowledge | This task delivers or acts on that decision, goal, or risk. | Records that the work discharged the commitment. |\n| `caused-by` | A → B | B is the root cause of A. | Builds the causal chain a provenance walk follows. |\n| `contradicts` | A → B | Two facts conflict and you cannot tell which is true. | Flags it for a human. **Both stay active** — this is the honest \"I don't know\" edge. |\n| `supersedes` | new → old | The old node has become **wrong to act on** — an agent following it would now do the wrong thing. **Never** for something that is merely older: history that is still true must stay. | **Retires the old node.** It stays in the graph and stays findable, but it comes back marked `SUPERSEDED` and ranked below live knowledge — so nothing acts on it as current. |\n| `expires-on` | knowledge → task | This knowledge is only true until that task closes. | Retires the knowledge when the task completes — same marked-not-deleted treatment. |\n\n`mentions` is not linkable by hand — write the id into a body and it is created for you.", + "properties": { + "action": { + "const": "link", + "type": "string" + }, + "dst_id": { + "description": "REQUIRED for link — destination node shareable id.", + "type": "string" + }, + "evidence": { + "description": "The VERBATIM line from the source that proves the claim — REQUIRED for `supersedes`. It must show the old state actually ENDED, not merely that something newer exists. \"We also added a read replica\" is additive and supersedes nothing; \"we've moved off DynamoDB entirely\" is a termination. If you cannot quote a termination, the correct relation is relates-to.", + "maxLength": 4000, + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reason": { + "description": "WHY this edge exists — REQUIRED for supersedes / contradicts / caused-by / blocks / fulfills. State the claim in your own words: for `supersedes`, name the standing state that ENDED (\"we moved the event store off DynamoDB to Postgres on Jul 13\"); for `caused-by`, name the causal mechanism. Not required for relates-to (the 'nothing typed fits' default) — if you cannot state a reason, that is the signal to use relates-to instead of forcing a typed edge.", + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "relation": { + "description": "REQUIRED for link — edge type, read src → dst. relates-to: a pointer, no claim. blocks: src (child) must land before dst (parent) can finish; the subtask edge. fulfills: src task delivers dst. caused-by: dst is src's root cause. contradicts: both stay active for a human to resolve. supersedes: retires dst as wrong to act on, not merely older. expires-on: src knowledge retires when dst task closes.", + "enum": [ + "relates-to", + "blocks", + "fulfills", + "contradicts", + "supersedes", + "caused-by", + "expires-on" + ], + "type": "string" + }, + "src_id": { + "description": "REQUIRED for link — source node shareable id.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "src_id", + "dst_id", + "relation" + ], + "type": "object" + } +]
- Changed
objective1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "objective action \"create\".", - "properties": { - "action": { - "const": "create", - "description": "Run the create action.", - "type": "string" - }, - "body": { - "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", - "type": "string" - }, - "component_id": { - "description": "REQUIRED for create/add_milestone — parent component shareable id.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", - "type": "string" - }, - "target_date": { - "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", - "type": "string" - }, - "title": { - "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create/add_milestone only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"add_milestone\".", - "properties": { - "action": { - "const": "add_milestone", - "description": "Run the add_milestone action.", - "type": "string" - }, - "body": { - "description": "Optional milestone description.", - "type": "string" - }, - "component_id": { - "description": "Parent component shareable id. REQUIRED.", - "type": "string" - }, - "objective_id": { - "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", - "type": "string" - }, - "position": { - "description": "1-based order within the objective. Appended last when omitted.", - "type": "integer" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "target_date": { - "description": "Optional soft target date, ISO e.g. 2026-09-30.", - "type": "string" - }, - "title": { - "description": "Milestone one-liner.", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "component_id", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"attach\".", - "properties": { - "action": { - "const": "attach", - "description": "Run the attach action.", - "type": "string" - }, - "milestone_id": { - "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "milestone_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"move\".", - "properties": { - "action": { - "const": "move", - "description": "Run the move action.", - "type": "string" - }, - "objective_id": { - "description": "The objective to attach it to directly (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The plain task to attach (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id", - "objective_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"detach\".", - "properties": { - "action": { - "const": "detach", - "description": "Run the detach action.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "task_id": { - "description": "The task to detach from its milestone/objective (TASK-N).", - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "task_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"reorder\".", - "properties": { - "action": { - "const": "reorder", - "description": "Run the reorder action.", - "type": "string" - }, - "milestone_ids": { - "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", - "items": { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "objective_id": { - "description": "The objective whose milestones to reorder (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id", - "milestone_ids" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"set_target\".", - "properties": { - "action": { - "const": "set_target", - "description": "Run the set_target action.", - "type": "string" - }, - "milestone_id": { - "description": "The milestone to retarget (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "ISO target date e.g. 2026-09-30, or null to clear.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "action", - "project_slug", - "milestone_id", - "target_date" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "objective action \"achieve\".", - "properties": { - "action": { - "const": "achieve", - "description": "Run the achieve action.", - "type": "string" - }, - "objective_id": { - "description": "The objective to mark achieved (TASK-N).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "objective_id" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create an objective — a standing goal whose end-state lives in its body, with ordered milestone phases under it. Non-claimable; it rolls up from its milestones and the tasks attached to them. Add milestones with objective(action=\"add_milestone\"), then attach tasks with objective(action=\"attach\").", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "body": { + "description": "(create/add_milestone only) Optional markdown body — the objective's end-state prose (what 'done' looks like), or a milestone description.", + "type": "string" + }, + "component_id": { + "description": "REQUIRED for create/add_milestone — parent component shareable id.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create/add_milestone only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it.", + "type": "string" + }, + "target_date": { + "description": "(create/add_milestone/set_target only) Soft target date, ISO e.g. 2026-09-30. Optional on create/add_milestone; REQUIRED for set_target (pass null there to clear).", + "type": "string" + }, + "title": { + "description": "REQUIRED for create/add_milestone — one-line title (an objective's end-state, or a milestone name).", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create/add_milestone only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"add_milestone\": Create a milestone — an ordered phase under an objective. Appended after the objective's existing milestones unless you pass an explicit position. Attach tasks to it with objective(action=\"attach\").", + "properties": { + "action": { + "const": "add_milestone", + "type": "string" + }, + "body": { + "description": "Optional milestone description.", + "type": "string" + }, + "component_id": { + "description": "Parent component shareable id. REQUIRED.", + "type": "string" + }, + "objective_id": { + "description": "REQUIRED for add_milestone/move/reorder/achieve — the objective (TASK-N).", + "type": "string" + }, + "position": { + "description": "1-based order within the objective. Appended last when omitted.", + "type": "integer" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "target_date": { + "description": "Optional soft target date, ISO e.g. 2026-09-30.", + "type": "string" + }, + "title": { + "description": "Milestone one-liner.", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "component_id", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"attach\": Attach a plain task to a milestone, re-parenting it off any prior milestone/objective. Real task→task `blocks` dependencies are preserved — only the milestone/objective containment moves.", + "properties": { + "action": { + "const": "attach", + "type": "string" + }, + "milestone_id": { + "description": "REQUIRED for attach/set_target — the milestone (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "REQUIRED for attach/move/detach — the plain task (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "milestone_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"move\": Attach a plain task directly to an objective — a 'loose' task serving the objective without a milestone. Re-parents off any prior container. Once an objective has milestones, loose tasks are its drift signal.", + "properties": { + "action": { + "const": "move", + "type": "string" + }, + "objective_id": { + "description": "The objective to attach it to directly (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The plain task to attach (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id", + "objective_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"detach\": Remove a task's milestone/objective containment. Genuine task→task `blocks` dependencies are left intact.", + "properties": { + "action": { + "const": "detach", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "task_id": { + "description": "The task to detach from its milestone/objective (TASK-N).", + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "task_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"reorder\": Reorder an objective's milestones. `milestone_ids` must list exactly the objective's own milestones, in the new order (a total permutation); a partial or foreign list is rejected so positions never go inconsistent.", + "properties": { + "action": { + "const": "reorder", + "type": "string" + }, + "milestone_ids": { + "description": "REQUIRED for reorder — the objective's milestones (TASK-N) in the desired order. Must be exactly the objective's own milestones — a full permutation.", + "items": { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "objective_id": { + "description": "The objective whose milestones to reorder (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id", + "milestone_ids" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"set_target\": Set (or clear, with null) a milestone's soft target date.", + "properties": { + "action": { + "const": "set_target", + "type": "string" + }, + "milestone_id": { + "description": "The milestone to retarget (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "ISO target date e.g. 2026-09-30, or null to clear.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "action", + "project_slug", + "milestone_id", + "target_date" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"achieve\": Mark an objective as achieved (status → done).", + "properties": { + "action": { + "const": "achieve", + "type": "string" + }, + "objective_id": { + "description": "The objective to mark achieved (TASK-N).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "objective_id" + ], + "type": "object" + } +]
- Changed
organize1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "organize action \"add_component\".", - "properties": { - "action": { - "const": "add_component", - "description": "Run the add_component action.", - "type": "string" - }, - "body": { - "description": "(add_component only) 1-3 paragraph charter (markdown).", - "type": "string" - }, - "name": { - "description": "REQUIRED for add_component — short noun-phrase title.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "parent_id": { - "anyOf": [ - { - "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "parent_id", - "name" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "organize action \"rename_topic\".", - "properties": { - "action": { - "const": "rename_topic", - "description": "Run the rename_topic action.", - "type": "string" - }, - "from": { - "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "to": { - "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "from", - "to" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "organize action \"feedback\".", - "properties": { - "action": { - "const": "feedback", - "description": "Run the feedback action.", - "type": "string" - }, - "hindrance": { - "description": "(feedback only) One place Stele was more friction than help.", - "type": "string" - }, - "improvement": { - "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "score": { - "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", - "maximum": 10, - "minimum": 0, - "type": "integer" - }, - "session_id": { - "description": "(feedback only) The session being reflected on. Defaults to the current session.", - "type": "string" - }, - "source": { - "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", - "enum": [ - "agent", - "human" - ], - "type": "string" - }, - "strength": { - "description": "(feedback only) One concrete way Stele helped this session.", - "type": "string" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "organize action \"pause_rag\".", - "properties": { - "action": { - "const": "pause_rag", - "description": "Run the pause_rag action.", - "type": "string" - }, - "reason": { - "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", - "type": "string" - }, - "recall_session": { - "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", - "minLength": 1, - "type": "string" - }, - "turns": { - "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "action", - "recall_session", - "reason", - "turns" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"add_component\": Create a new component under `parent_id` (or at root if null). Components are STRUCTURAL hubs — knowledge and tasks attach to them. Before calling, run list(what=\"components\") and confirm a similar one doesn't already exist.", + "properties": { + "action": { + "const": "add_component", + "type": "string" + }, + "body": { + "description": "(add_component only) 1-3 paragraph charter (markdown).", + "type": "string" + }, + "name": { + "description": "REQUIRED for add_component — short noun-phrase title.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "parent_id": { + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "REQUIRED for add_component — parent component shareable id, or null for a root-level component." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "parent_id", + "name" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"rename_topic\": Rename a topic across every knowledge AND task node in the active project, in one transactional update (deduped if a node already had `to`). For typos and casing fixes; for multi-source consolidations use destructive(action=\"merge_topics\"). Members-only.", + "properties": { + "action": { + "const": "rename_topic", + "type": "string" + }, + "from": { + "description": "REQUIRED for rename_topic — topic to replace. Must be non-empty.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "to": { + "description": "REQUIRED for rename_topic — replacement topic. Must be non-empty.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "from", + "to" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"feedback\": Submit a short, structured reflection on how well Stele helped during a session: one strength, one hindrance, a 0–10 anchored score, and one sentence on what to improve. Keep every field about Stele itself — never include project, code, or customer detail. Both agents (reflecting on their own session) and people (their own feedback) submit through this; set `source` to distinguish them. All fields are optional, but the score is the most useful — it's the trend signal.", + "properties": { + "action": { + "const": "feedback", + "type": "string" + }, + "hindrance": { + "description": "(feedback only) One place Stele was more friction than help.", + "type": "string" + }, + "improvement": { + "description": "(feedback only) One sentence on what would make Stele better — about Stele only, no project or code detail.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "score": { + "description": "(feedback only) 0–10: how much Stele helped this session. Anchored — 0 = fought you all session · 5 = neutral, no effect · 10 = changed the outcome.", + "maximum": 10, + "minimum": 0, + "type": "integer" + }, + "session_id": { + "description": "(feedback only) The session being reflected on. Defaults to the current session.", + "type": "string" + }, + "source": { + "description": "(feedback only) Who is giving this feedback: the agent reflecting on its own session ('agent', the default), or a person giving their own ('human').", + "enum": [ + "agent", + "human" + ], + "type": "string" + }, + "strength": { + "description": "(feedback only) One concrete way Stele helped this session.", + "type": "string" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"pause_rag\": Suspend automatic topic recall for a bounded number of upcoming prompts, then let it resume on its own. Use it inside a planning or review loop where you already have what you need. Your focus survives the pause — this is a mute, not a reset. There is no separate resume control: call it again with turns 0 to clear a pause early. Pausing is per-session, so a parallel session in the same repo is unaffected.", + "properties": { + "action": { + "const": "pause_rag", + "type": "string" + }, + "reason": { + "description": "REQUIRED for pause_rag — short reason for the pause, shown to the user in the countdown line.", + "type": "string" + }, + "recall_session": { + "description": "REQUIRED for every recall control (activate_recall, deactivate_recall, pause_rag, recall_status) — the recall session handle from the awaiting-focus notice in this conversation. It identifies which local session to update and grants no access on its own.", + "minLength": 1, + "type": "string" + }, + "turns": { + "description": "REQUIRED for pause_rag — number of upcoming prompts to skip automatic topic recall for. Must be >= 0. 0 clears an active pause immediately. No default.", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "action", + "recall_session", + "reason", + "turns" + ], + "type": "object" + } +]
- Changed
project2 fields changed- changed
Input schema / descriptionPrevious value: -"project action \"create\"."New value: +"action=\"create\": Create a new Stele project — a fresh knowledge-and-task graph owned by you — and return its `slug`. Use this when you have no project to work in yet (for example, the first time you connect); then pass the returned `slug` as `project_slug` on every other tool. `name` is required; `description`, `repo_url`, and `visibility` are optional. A root component is created for you automatically. Projects are private unless you pass `visibility: \"public\"`; plans allow a limited number of private projects but unlimited public ones, so if this fails on a plan limit, the error explains how to retry." - removed
Input schema / properties / action / descriptionRemoved value: -"Run the create action."
- Changed
search6 fields changed- added
Input schema / properties / case_sensitiveAdded value: +{ + "description": "For scope \"documents\" only. Default false (case-insensitive).", + "type": "boolean" +} - added
Input schema / properties / component_idAdded value: +{ + "anyOf": [ + { + "description": "Shareable id: TASK-N (task), KNOW-N (knowledge), COMP-N (component), DOC-N (document), or NODE-N (any).", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "For scope \"documents\" only. Optional parent-component filter. Pass a COMP-N to scope the search to docs under that component." +} - added
Input schema / properties / context_linesAdded value: +{ + "description": "For scope \"documents\" only. Lines of surrounding context returned before and after each match. Default 1.", + "maximum": 10, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / max_docsAdded value: +{ + "description": "For scope \"documents\" only. Max docs in the response. Default 10.", + "maximum": 50, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / max_matches_per_docAdded value: +{ + "description": "For scope \"documents\" only. Max matches surfaced per doc. Default 5.", + "maximum": 50, + "minimum": 1, + "type": "integer" +} - changed
Input schema / properties / regex / descriptionPrevious value: -"For method:exact — treat query as a POSIX regex."New value: +"For method \"exact\": treat query as a POSIX regex. For scope \"documents\": treat query as a JavaScript regex."
- Changed
task1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "task action \"create\".", - "properties": { - "action": { - "const": "create", - "description": "Run the create action.", - "type": "string" - }, - "anchor_files": { - "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", - "items": { - "type": "string" - }, - "type": "array" - }, - "anchor_symbols": { - "description": "(create only) Symbol names this task is about (function / class / constant / env var).", - "items": { - "type": "string" - }, - "type": "array" - }, - "assignee_user_id": { - "anyOf": [ - { - "format": "uuid", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." - }, - "body": { - "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", - "type": "string" - }, - "check": { - "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", - "type": "string" - }, - "check_paths": { - "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "component_id": { - "description": "REQUIRED for create — parent component shareable id.", - "type": "string" - }, - "kind": { - "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal (end-state in its body) with ordered 'milestone' phases under it.", - "enum": [ - "task", - "epic", - "objective", - "milestone" - ], - "type": "string" - }, - "position": { - "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", - "type": "integer" - }, - "priority": { - "anyOf": [ - { - "enum": [ - "P0", - "P1", - "P2", - "P3" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "scope": { - "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", - "type": "string" - }, - "success_criteria": { - "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", - "minLength": 1, - "type": "string" - }, - "target_date": { - "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", - "type": "string" - }, - "title": { - "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "topics": { - "description": "(create only) Short tag list.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug", - "component_id", - "title", - "success_criteria" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "task action \"claim\".", - "properties": { - "action": { - "const": "claim", - "description": "Run the claim action.", - "type": "string" - }, - "dispatched_with": { - "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", - "type": "string" - }, - "node_id": { - "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", - "type": "string" - }, - "parent_task_id": { - "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "task action \"complete\".", - "properties": { - "action": { - "const": "complete", - "description": "Run the complete action.", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "summary": { - "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "summary" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "task action \"release\".", - "properties": { - "action": { - "const": "release", - "description": "Run the release action.", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "task action \"reopen\".", - "properties": { - "action": { - "const": "reopen", - "description": "Run the reopen action.", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "task action \"force_release\".", - "properties": { - "action": { - "const": "force_release", - "description": "Run the force_release action.", - "type": "string" - }, - "node_id": { - "description": "Task shareable id (e.g. TASK-42).", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "task action \"loop_finalize\".", - "properties": { - "action": { - "const": "loop_finalize", - "description": "Run the loop_finalize action.", - "type": "string" - }, - "node_id": { - "description": "The loop task to finalize (TASK-N).", - "pattern": "^TASK-\\d+$", - "type": "string" - }, - "operator_summary": { - "description": "Why the operator judged the change in or out of intent and scope.", - "minLength": 1, - "type": "string" - }, - "operator_verdict": { - "description": "The operator's judgment of whether the change stays within the task's intent and scope.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "reviewer_summary": { - "description": "Why the reviewer approved or rejected the change.", - "minLength": 1, - "type": "string" - }, - "reviewer_verdict": { - "description": "The independent reviewer's judgment of the change.", - "enum": [ - "approved", - "rejected" - ], - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "node_id", - "reviewer_verdict", - "reviewer_summary", - "operator_verdict", - "operator_summary" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create a task under a component. One task ≈ one PR. `success_criteria` is required — it is the contract the implementer and the reviewer both work against. Pass `check` only when a command can genuinely decide done without a human; for unattended certification also pass `check_paths`. Most tasks have neither, and that is fine. Give a markdown body with ## Why / ## How / ## Test plan — the canonical contract headings are written for you from the arguments.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "anchor_files": { + "description": "(create only) Repo-relative file paths this task is about. Anchoring an open task to the code it touches surfaces it when an agent later edits that file — so they don't step on toes or duplicate planned work.", + "items": { + "type": "string" + }, + "type": "array" + }, + "anchor_symbols": { + "description": "(create only) Symbol names this task is about (function / class / constant / env var).", + "items": { + "type": "string" + }, + "type": "array" + }, + "assignee_user_id": { + "anyOf": [ + { + "format": "uuid", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Pre-assign to a user uuid (NOT a shareable id); usually leave null." + }, + "body": { + "description": "(create only) Markdown body (optional). Write it in plain style: one idea per sentence, active voice, lead with the condition, one term per concept, exact technical names.", + "type": "string" + }, + "check": { + "description": "OPTIONAL — a success criterion you can RUN: a command that decides done WITHOUT a human (e.g. a test command that must exit 0, or a script that prints a number to compare against a target). Appended under `## Check`. A task that has one is runnable by a loop; unattended certification additionally requires `check_paths` so the maker cannot silently modify its own verifier. The verdict then comes from the command, not from anyone's opinion. Most tasks have no runnable check, and that is fine: omit it and a human decides when it's done, which is the normal answer for anything whose 'done' is a judgment (design, architecture, docs, ambiguous work). Do not invent a check to fill this field in. A made-up check is worse than no check: it launders a guess into something that looks like a fact, and an agent will then iterate against it and merge on it.", + "type": "string" + }, + "check_paths": { + "description": "OPTIONAL — repo-relative paths the declared check depends on: its test/script and every fixture or directory it reads. Required to CERTIFY an unattended loop: `stele loop verify` fails closed when a task has a check but no declared check paths. These paths are explicit because guessing them from an arbitrary shell command silently under-protects the verifier. Repeat paths as needed.", + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "component_id": { + "description": "REQUIRED for create — parent component shareable id.", + "type": "string" + }, + "kind": { + "description": "(create only) Task kind; defaults to 'task'. Non-'task' kinds are non-claimable containers that roll up from their `blocks`-children: 'epic' groups related tasks; 'objective' is a standing goal (end-state in its body) with ordered 'milestone' phases under it.", + "enum": [ + "task", + "epic", + "objective", + "milestone" + ], + "type": "string" + }, + "position": { + "description": "(create only) Explicit ordering of a milestone within its objective (1-based).", + "type": "integer" + }, + "priority": { + "anyOf": [ + { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "(create only) Priority P0 (most urgent) … P3. Omit/null = unprioritized." + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "scope": { + "description": "(create only) Workspace scope. Omit or 'auto' = scope to the workspace mapped to the current git branch, else trunk (shared truth). 'trunk' = force shared truth even on a branch. Or pass a workspace name/id to target it. Branch-scoped writes are invisible to collaborators off that workspace.", + "type": "string" + }, + "success_criteria": { + "description": "REQUIRED — how anyone (a person, or an agent picking this up cold) knows the task is DONE. State what must be TRUE when it's finished, not what you intend to do. Appended to the body under `## Success criteria`, which is the contract the implementer and the reviewer both work against — so a task without one is a task nobody can finish or check.", + "minLength": 1, + "type": "string" + }, + "target_date": { + "description": "(create only) Optional soft target date — ISO date e.g. 2026-09-30. Valid on any kind: the date an objective or milestone is aimed at, and the date a plain task is expected by. Set it whenever someone committed to a deadline (\"I'll have it by Friday\"); a commitment with no date on it is a wish.", + "type": "string" + }, + "title": { + "description": "REQUIRED for create — imperative one-liner (\"Add OAuth refresh\").", + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "topics": { + "description": "(create only) Short tag list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug", + "component_id", + "title", + "success_criteria" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"claim\": Atomically claim a task (open → in_progress), recording you as owner. Claim before you start so others know you're on it. Returns the task, or { result: 'conflict' } if someone else holds it — no race window.", + "properties": { + "action": { + "const": "claim", + "type": "string" + }, + "dispatched_with": { + "description": "(claim only) Operator's full briefing prompt when this claim is from a dispatched sub-agent. It is stored with the claim, up to 16 KB; longer text is truncated with a warning.", + "type": "string" + }, + "node_id": { + "description": "REQUIRED for claim/complete/release/reopen/force_release — task shareable id (e.g. TASK-42).", + "type": "string" + }, + "parent_task_id": { + "description": "(claim only) Operator's claimed task id (e.g. TASK-487) when this claim is from a sub-agent dispatched by the operator. Leave null for direct claims.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"complete\": Mark a task done. REQUIRED `summary` is appended under ## Completion summary (what shipped, where it landed, what you learned). Also closes a container (epic/objective/milestone): no claim needed, but it is REFUSED while any child is unfinished, and the error names them. A container normally closes itself when its last child closes, so completing one is mainly how you record what it delivered — and it marks the close as yours, so a child reopening later won't undo it. The result may list nearby decision/architecture nodes to re-verify (still-true / edit / supersede); that part is advisory.", + "properties": { + "action": { + "const": "complete", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "summary": { + "description": "REQUIRED — what actually happened. The graph only learns from what closing writes down: what shipped, where it landed, what you found out, what you'd warn the next person about. A task closed with nothing said is a task the next agent has to re-derive from the diff.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "summary" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"release\": Release a task you claimed (in_progress → open) so someone else can pick it up — use when you can't finish (out of context, blocked, wrong scope).", + "properties": { + "action": { + "const": "release", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"reopen\": Recovery: send a closed task (done or cancelled) back to open and clear its completion metadata so it reads as unheld and claimable again. For a stuck in_progress task, use task(action=\"force_release\") instead (this is for closed tasks).", + "properties": { + "action": { + "const": "reopen", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"force_release\": Admin recovery: release an in_progress task WITHOUT holding the claim, so it returns to open for others. The release is attributed to you in the claim history (not the prior claimer). For a closed task use task(action=\"reopen\"); to take a task over rather than free it, use destructive(action=\"force_claim_task\").", + "properties": { + "action": { + "const": "force_release", + "type": "string" + }, + "node_id": { + "description": "Task shareable id (e.g. TASK-42).", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"loop_finalize\": Create the durable final loop envelope. Deterministic correctness comes only from the latest mechanically evidenced attempt; independent reviewer and operator intent/scope judgments remain separate fields. Approval requires all three, so model judgment can reject work but can never manufacture correctness.", + "properties": { + "action": { + "const": "loop_finalize", + "type": "string" + }, + "node_id": { + "description": "The loop task to finalize (TASK-N).", + "pattern": "^TASK-\\d+$", + "type": "string" + }, + "operator_summary": { + "description": "Why the operator judged the change in or out of intent and scope.", + "minLength": 1, + "type": "string" + }, + "operator_verdict": { + "description": "The operator's judgment of whether the change stays within the task's intent and scope.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "reviewer_summary": { + "description": "Why the reviewer approved or rejected the change.", + "minLength": 1, + "type": "string" + }, + "reviewer_verdict": { + "description": "The independent reviewer's judgment of the change.", + "enum": [ + "approved", + "rejected" + ], + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "node_id", + "reviewer_verdict", + "reviewer_summary", + "operator_verdict", + "operator_summary" + ], + "type": "object" + } +]
- Changed
timeline1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "timeline action \"list\".", - "properties": { - "action": { - "const": "list", - "description": "Run the list action.", - "type": "string" - }, - "agent": { - "description": "Filter to entries attributed to this agent name.", - "type": "string" - }, - "before": { - "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", - "type": "string" - }, - "component_id": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "from": { - "description": "ISO timestamp — only entries at or after this time.", - "type": "string" - }, - "limit": { - "description": "Page size (default 50, max 100).", - "exclusiveMinimum": 0, - "maximum": 100, - "type": "integer" - }, - "node_id": { - "description": "Limit to entries whose subject is this node, OR that link to it.", - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "query": { - "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", - "type": "string" - }, - "source": { - "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", - "type": "string" - }, - "status": { - "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "to": { - "description": "ISO timestamp — only entries before this time.", - "type": "string" - }, - "type": { - "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", - "type": "string" - }, - "user": { - "description": "Filter to entries attributed to this user (uuid, not a shareable id).", - "format": "uuid", - "type": "string" - }, - "verbs": { - "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", - "items": { - "enum": [ - "created", - "updated", - "status_changed", - "superseded", - "commented", - "claimed", - "released", - "custom", - "linked", - "unlinked" - ], - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "action", - "project_slug" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "timeline action \"log\".", - "properties": { - "action": { - "const": "log", - "description": "Run the log action.", - "type": "string" - }, - "link": { - "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", - "items": { - "description": "Limit to entries about a node under this component.", - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "message": { - "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", - "maxLength": 500, - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "status": { - "description": "Outcome of the journaled action. Omit unless you have one to report.", - "enum": [ - "completed", - "partial", - "failed", - "blocked" - ], - "type": "string" - }, - "type": { - "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", - "maxLength": 100, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "type", - "message" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"list\": List the project's Timeline: automatic events captured on graph writes and task lifecycle actions, interleaved with one-line custom entries logged by agents and automations, newest first. Filters (AND-combined): verbs, action type, actor user/agent, capture source, status, component, node (matches the entry's subject or a linked node), and a time range. A custom entry's linked nodes resolve to compact references (id, type, title, status) — never raw uuids. Keyset-paginated, not offset-based: pass a previous page's `next_before` back as `before` to fetch the next (older) page.", + "properties": { + "action": { + "const": "list", + "type": "string" + }, + "agent": { + "description": "Filter to entries attributed to this agent name.", + "type": "string" + }, + "before": { + "description": "Cursor from a previous page's `next_before` — fetch the next (older) page. Omit for the first (newest) page. Not offset-based: pass the value back unmodified.", + "type": "string" + }, + "component_id": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "from": { + "description": "ISO timestamp — only entries at or after this time.", + "type": "string" + }, + "limit": { + "description": "Page size (default 50, max 100).", + "exclusiveMinimum": 0, + "maximum": 100, + "type": "integer" + }, + "node_id": { + "description": "Limit to entries whose subject is this node, OR that link to it.", + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "query": { + "description": "Substring scan over the entry text and the subject node's title — not ranked search. For a broad relevance-ranked pull use `search` or `recall` instead.", + "type": "string" + }, + "source": { + "description": "Filter to one capture surface, e.g. \"mcp\", \"cli\", \"web\".", + "type": "string" + }, + "status": { + "description": "Filter to custom entries with this outcome. Has no effect on automatic events.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "to": { + "description": "ISO timestamp — only entries before this time.", + "type": "string" + }, + "type": { + "description": "Filter to custom entries with this exact action type (e.g. \"operator.run\"). Has no effect on automatic events, which carry no action type.", + "type": "string" + }, + "user": { + "description": "Filter to entries attributed to this user (uuid, not a shareable id).", + "format": "uuid", + "type": "string" + }, + "verbs": { + "description": "Filter to these event verbs. Omit for every verb, automatic and custom interleaved.", + "items": { + "enum": [ + "created", + "updated", + "status_changed", + "superseded", + "commented", + "claimed", + "released", + "custom", + "linked", + "unlinked" + ], + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action", + "project_slug" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"log\": Append a one-line entry to the project's Timeline — for an agent or automation to journal a milestone, with links to the graph items holding the substance. `message` is a short, human-readable line: one thing that happened, never the reasoning behind it or the decision it produced — put those in a knowledge or task node and reference them with `link` instead. `type` is a free-form, caller-namespaced label so entries from different automations don't collide. The Timeline is append-only: a correction is a new entry, not an edit. This capability can be turned off for a project; when it is, the call succeeds but writes nothing, and the response says so.", + "properties": { + "action": { + "const": "log", + "type": "string" + }, + "link": { + "description": "Up to 20 nodes this entry is about — the graph items holding the substance.", + "items": { + "description": "Limit to entries about a node under this component.", + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "message": { + "description": "REQUIRED — one short line describing what happened. Timeline entries are a journal, not a place for reasoning, decisions, or knowledge — write those as graph nodes instead and reference them with `link`.", + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "status": { + "description": "Outcome of the journaled action. Omit unless you have one to report.", + "enum": [ + "completed", + "partial", + "failed", + "blocked" + ], + "type": "string" + }, + "type": { + "description": "REQUIRED — caller-namespaced action type, e.g. \"operator.run\". Free-form; namespace it so entries from different automations don't collide.", + "maxLength": 100, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "type", + "message" + ], + "type": "object" + } +]
- Changed
workspace1 field changed- changed
Input schema / anyOfPrevious value: -[ - { - "additionalProperties": false, - "description": "workspace action \"create\".", - "properties": { - "action": { - "const": "create", - "description": "Run the create action.", - "type": "string" - }, - "auto_activate_hint": { - "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", - "type": "string" - }, - "branches": { - "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "description": { - "description": "(create/update only) One-line description of what this overlay is for.", - "type": "string" - }, - "kind": { - "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "name", - "kind" - ], - "type": "object" - }, - { - "additionalProperties": false, - "description": "workspace action \"update\".", - "properties": { - "action": { - "const": "update", - "description": "Run the update action.", - "type": "string" - }, - "add_branches": { - "description": "(update only) Branch associations to ADD (existing ones are kept).", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "auto_activate_hint": { - "description": "New plain-language auto-activation hint.", - "type": "string" - }, - "description": { - "description": "New one-line description.", - "type": "string" - }, - "kind": { - "description": "Change the workspace kind.", - "enum": [ - "permanent", - "experimental" - ], - "type": "string" - }, - "name": { - "description": "New name.", - "minLength": 1, - "type": "string" - }, - "project_slug": { - "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", - "minLength": 1, - "type": "string" - }, - "workspace": { - "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "action", - "project_slug", - "workspace" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "action=\"create\": Create a named workspace — a scoped overlay on the project's trunk (shared truth). Knowledge written while it's active scopes to it; collaborators off that workspace never see it. permanent = a domain/team overlay that never graduates (promote per-item); experimental = ephemeral, expected to merge or be archived. Prefer OFFERING before creating unless the user asked. Does NOT activate it — call workspace(action=\"activate\") to scope this session to it.", + "properties": { + "action": { + "const": "create", + "type": "string" + }, + "auto_activate_hint": { + "description": "(create/update only) Plain-language 'when should this auto-activate' hint, read by the agent at session start. Globs/paths welcome; no rules DSL.", + "type": "string" + }, + "branches": { + "description": "(create only) Branch associations (0..N). Exact match auto-activates; a glob is offered. Omit for a pure domain overlay with no branch.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "description": { + "description": "(create/update only) One-line description of what this overlay is for.", + "type": "string" + }, + "kind": { + "description": "REQUIRED for create — permanent = standing domain/team overlay (never graduates); experimental = ephemeral, expected to merge or be archived.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "REQUIRED for create — workspace name, shown in scope tags (e.g. \"Billing\").", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "name", + "kind" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "action=\"update\": Update a workspace's editable fields (name, kind, description, auto-activation hint) and/or ADD branch associations. Only the fields you pass change. Does NOT promote knowledge or change scope — merge/archive have dedicated tools.", + "properties": { + "action": { + "const": "update", + "type": "string" + }, + "add_branches": { + "description": "(update only) Branch associations to ADD (existing ones are kept).", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "auto_activate_hint": { + "description": "New plain-language auto-activation hint.", + "type": "string" + }, + "description": { + "description": "New one-line description.", + "type": "string" + }, + "kind": { + "description": "Change the workspace kind.", + "enum": [ + "permanent", + "experimental" + ], + "type": "string" + }, + "name": { + "description": "New name.", + "minLength": 1, + "type": "string" + }, + "project_slug": { + "description": "Project slug, e.g. \"acme-app\" — list(what=\"projects\") returns yours.", + "minLength": 1, + "type": "string" + }, + "workspace": { + "description": "REQUIRED for update/activate/deactivate — the workspace to act on — its NAME (e.g. \"Billing\") or its uuid. NOT a short-id (workspaces aren't nodes).", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action", + "project_slug", + "workspace" + ], + "type": "object" + } +]
20 tool updates
- First observed
comment - First observed
destructive - First observed
document - First observed
fetch - First observed
inspect - First observed
knowledge - First observed
list - First observed
list_tasks - First observed
node - First observed
objective - First observed
open_project_board - First observed
open_stele - First observed
organize - First observed
project - First observed
recall - First observed
search - First observed
show_node - First observed
task - First observed
timeline - First observed
workspace
Publisher details
- Operator
- Stele AI LLC · Publisher source
- Operator website
- https://stele-ai.dev
- Vendor relationship
- First-party
- Documentation
- https://stele-ai.dev/docs
- Trust center
- https://stele-ai.dev/security
- Restrictions
- free for OSS and personal use. Pro plan available. · Publisher source
Related MCP Connectors
Project memory for coding agents: requirements, decisions, code graph and delivery telemetry.
Shared debugging memory for AI coding agents
Shared long-term memory for AI agents: save and recall context as a searchable knowledge graph.
Graph memory for AI agents: entities, cause-effect links, cross-session recall, time travel.
Related MCP Servers
- FlicenseNot gradedqualityAmaintenancePersistent memory for AI coding agents: local-first semantic search, dependency graphs, git history.1-
- AlicenseNot gradedqualityDmaintenanceShared team memory for AI coding agents with Bayesian confidence scoring and temporal decay, enabling persistent storage and retrieval of engineering patterns across sessions.7 npm17MIT
- AlicenseNot gradedqualityAmaintenancePersistent memory for AI coding agents2,191 PyPI225Apache 2.0
- AlicenseAqualityCmaintenancePersistent shared memory for AI coding agents. Stores facts as entity/key/value triples with hybrid semantic search, task checkpoints, and conflict resolution — shared across Claude Code, Codex CLI, and GitHub Copilot.16235 npm5AGPL 3.0
Glama MCP Gateway
Add one secure layer between your agents and this server.