cortexdb-mcp 0.8.0__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. cortexdb_mcp-0.9.0/COVERAGE.md +149 -0
  2. cortexdb_mcp-0.9.0/PKG-INFO +539 -0
  3. cortexdb_mcp-0.9.0/README.md +522 -0
  4. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/cortexdb_mcp/api.py +10 -2
  5. cortexdb_mcp-0.9.0/cortexdb_mcp/check_call.py +471 -0
  6. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/cortexdb_mcp/config.py +226 -3
  7. cortexdb_mcp-0.9.0/cortexdb_mcp/errors.py +531 -0
  8. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/cortexdb_mcp/insights.py +21 -2
  9. cortexdb_mcp-0.9.0/cortexdb_mcp/render.py +481 -0
  10. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/cortexdb_mcp/server.py +1287 -125
  11. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/pyproject.toml +1 -1
  12. cortexdb_mcp-0.9.0/tests/live/live_stdio.py +767 -0
  13. cortexdb_mcp-0.9.0/tests/test_alignment.py +605 -0
  14. cortexdb_mcp-0.9.0/tests/test_audit.py +348 -0
  15. cortexdb_mcp-0.9.0/tests/test_auth.py +469 -0
  16. cortexdb_mcp-0.9.0/tests/test_new_tools.py +606 -0
  17. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/tests/test_server.py +53 -3
  18. cortexdb_mcp-0.9.0/tests/test_stdio.py +160 -0
  19. cortexdb_mcp-0.8.0/PKG-INFO +0 -301
  20. cortexdb_mcp-0.8.0/README.md +0 -284
  21. cortexdb_mcp-0.8.0/cortexdb_mcp/check_call.py +0 -266
  22. cortexdb_mcp-0.8.0/cortexdb_mcp/render.py +0 -204
  23. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/Dockerfile +0 -0
  24. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/cortexdb_mcp/__init__.py +0 -0
  25. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/cortexdb_mcp/__main__.py +0 -0
  26. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/tests/__init__.py +0 -0
  27. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/tests/test_check_call.py +0 -0
  28. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/tests/test_insights.py +0 -0
  29. {cortexdb_mcp-0.8.0 → cortexdb_mcp-0.9.0}/tests/test_integration.py +0 -0
@@ -0,0 +1,149 @@
1
+ # cortexdb-mcp coverage of CortexDB 0.14.0
2
+
3
+ This file records how `cortexdb-mcp` (the stdio MCP server in this directory) covers the CortexDB 0.14.0 API: the
4
+ audit of every tool that existed before 0.14.0, and what each finding changed. The spec, in order of authority:
5
+ the server code (the `sharedcontext` branch with lanes p5g, p5h and p5j merged), `docs/schemas/openapi.json`,
6
+ `docs/shared_memory/IDENTITY_AND_API_CONTRACT.md` with `docs/API_REFERENCE_V1.md`, and the release-notes drafts
7
+ `docs/shared_memory/PHASE{0..5}_RELEASE_NOTES_DRAFT.md`.
8
+
9
+ The server's own HTTP MCP endpoint (`POST /mcp`, `crates/cortex-api/src/v1/mcp.rs`, `CORTEX_MCP=on`) is a separate
10
+ surface with its own five tools. The stdio server keeps today's tools, output shapes and default profile (design
11
+ §13.8, §14.3); the tools it adds follow `/mcp`'s rule, read by default and writes opt-in.
12
+
13
+ ## What 0.14.0 breaks, and what it did to the tools
14
+
15
+ | 0.14.0 change | Tools it reached | What happened before 0.9.0 | Now |
16
+ |---|---|---|---|
17
+ | **Strict forget and erasure selectors**: a selector field a route does not apply is 422 `unknown_selector_field`, a misspelt layer `unknown_layer`, and the erasure routes refuse the key selectors | `memory_forget`, `memory_delete`, `memory_bulk_delete`, `forget_preview` | Their bodies were valid (only `about_subject`, `memory_ids`, `cascade`, `reason`, `confirm_all`, `from_preview_id`). The same class of mistake was open one level up: FastMCP drops an argument a tool does not take, so `memory_forget(reason, memory_ids=[…])` dropped the ids and forgot the whole scope's derived records | Every body is checked against the strict rules in the tests (`test_audit.py::test_the_bodies_pass_the_strict_selector`). Every tool refuses an argument it does not take, naming it and listing the ones it does (`additionalProperties: false` in each schema), as the server refuses a field it does not apply and `/mcp` refuses one on its destructive tools |
18
+ | **`/v1/forget` cascades**: a forget that names no cascade runs `derived_only`, which deletes derived records only; a whole-scope forget under it deletes no memory and says so in `warnings` | `memory_forget`, `memory_bulk_delete` | `memory_forget` said a call without `query` "deletes everything under the scope"; `memory_bulk_delete` said it deletes "multiple memories" and reported `0 event(s) removed`. The server's `warnings` were dropped | The descriptions say what is deleted (derived records) and what is KEPT (the events), and point to `memory_delete` and `forget_by_key` for memories. Every forget answer lists its derived counts, `requested` and `matched`, its audit id and the server's warnings |
19
+ | **Total confinement**: a token that carries `scopes` holds only the families its entries name (403 `POLICY_DENIED`, `details.link: "confinement"`, `details.capability`) | every tool | A refusal surfaced as `CortexDB returned HTTP 403: {raw json}` | Every refusal is a typed `CortexDBError`: code, retriable, request id, and for a confinement refusal the capability and the `scopes` entry to mint (`<capability>:<scope>`, `<scope>/*`, `<capability>:*`), with the view hint for `scope.read.holistic` and `scope.read.descend`. A signup token names its 18 families, so every tool still works inside the signup user's own subtrees |
20
+ | **Break-glass**: on `cloud_shared_saas` the operator key's data reads (and its in-band writes) are 403 `BREAK_GLASS_REQUIRED` without `X-Cortex-Break-Glass` | every read tool, `memory_forget`/`memory_bulk_delete`/`forget_preview` by query, the insights engine | Nothing could send the header; the insights engine answered "no insights" to the refusal | `CORTEXDB_BREAK_GLASS_REASON`, `_TICKET` and `_NOTE` send it, validated as the server validates it; `BREAK_GLASS_REQUIRED` says to use a person's token or set the two settings. A forget or preview by ids or keys needs none (it reads nothing first) |
21
+ | **`tree.display_name` refused** in version-1 registry writes (422 `INVALID_SCOPE_TREE`) | none: no tool registers scopes | `cortexdb_check_call` did not know `POST /v1/scopes` or `PUT /v1/scopes/tree` | `cortexdb_check_call` flags `tree.display_name` there and points to `PUT /v1/directory/{id}` |
22
+ | **Org trees' views**: at a shared org-tree scope (org, team, group, compartment) a walking view is 422 `VIEW_NOT_ALLOWED_AT_SCOPE`, and a recall naming no view reads granular | `memory_search`, `get_context`, `advanced_search` | They always sent `view` (the default holistic by name), so every search at a team scope was refused | `view` is sent only when `CORTEXDB_VIEW` is set; otherwise the server picks (holistic elsewhere, as before, granular at a shared scope) |
23
+ | **Agent writes redirected** (DB-28): a non-human chain's write whose read record is unknown, tainted or carries a private atom lands in its writer's private space, `redirected_to` | `memory_store`, `entity_link`, `belief_declare` | The receipt's `redirected_to` was dropped: the agent was told it stored at the scope it asked for | The answer names where it landed |
24
+ | **Per-person and chain tokens**: a minted token needs `X-Cortex-Actor` equal to its `sub`; an issuers-file token in `prefix`, `hash` or `directory` mode goes alone | every tool | A minted token without `CORTEXDB_ACTOR` was 401 `ACTOR_MISMATCH` on every call | `X-Cortex-Actor` is read from a PASETO or JWT bearer whose `sub` is an actor id, left out for an IdP subject, and `CORTEXDB_ACTOR=-` sends none |
25
+
26
+ ## The audit, tool by tool
27
+
28
+ "Request" is the body or query the tool sends, checked against the route's handler; "output" the fields it reads;
29
+ "errors" what the agent sees on a refusal. Every tool's errors are the typed errors above, so the column names only
30
+ what is particular to the tool.
31
+
32
+ | Tool | Route | Request | Output | Errors | Changed in 0.9.0 |
33
+ |---|---|---|---|---|---|
34
+ | `cortexdb_check_call` | none (a static contract) | — | — | — | Knows the strict forget body and selector (`unknown_selector_field`, `unknown_layer`, unknown top-level fields), the key selectors and their bounds, the erasure routes' refusals (`key_selector_not_supported`, `UNSUPPORTED_SELECTOR`, `confirm` without `subject`), `tree.display_name`, and the new routes (access check and explain, rollups, publish, notices, the directory) |
35
+ | `memory_store` | `POST /v1/experience` | ok | dropped `redirected_to`, `replayed_from_idempotency`, `warnings` | `RUN_REQUIRED`, `ATOMS_REDIRECT_REFUSED`, confinement | names a redirect, a replay and the server's warnings |
36
+ | `memory_status` | `GET /v1/experience/status` | ok | ignored the server's `resend` and `note`: claimed "re-storing is safe" for every not-found answer, where by event id the server answers `check_first` (erased, forgotten or not visible: do not re-send); said "indexing in progress" for a cancelled write | — | renders `resend` and `note`; takes `tenant_id` (`?scope=`, the alignment pass below) |
37
+ | `memory_search`, `get_context`, `advanced_search` | `POST /v1/recall` | `view` always sent (see above) | ok | `VIEW_NOT_ALLOWED_AT_SCOPE`, confinement of `scope.read.holistic` | `view` only when set |
38
+ | `memory_forget` | `POST /v1/forget` | ok | dropped `warnings`; the description was wrong (see above) | `BREAK_GLASS_REQUIRED` for a query under `deny` | accurate description, warnings, counts |
39
+ | `forget_preview` | `POST /v1/forget/preview` | ok | told the agent to execute a `memory_ids` preview with `memory_forget`, which takes no ids | — | names the tool that sends exactly the previewed selector (`memory_delete` for one id, which now takes `from_preview_id`) and shows `requested`/`matched` |
40
+ | `memory_list` | `GET /v1/events` | sent `offset` as a numeric `cursor`, which the final server refuses (422 `INVALID_CURSOR`: its cursor is `le1.`, newest first, bound to the query) | dropped `next_cursor` | `INVALID_CURSOR` | pages by `cursor` (`offset` is gone) and ends a page with `More: cursor=`; a type is filtered by the server (`labels`), the default type here |
41
+ | `memory_get` | `GET /v1/events/{id}` | ignored `tenant_id`, the exact scope `?scope=` confines the read to, which a cluster routes a by-id read by (a break-glass by-id read without it was 503 `NOTICE_UNAVAILABLE` `scope_unnamed` until the final 0.14.0 server, which decides it by the row's own scope) | ok | `NOTICE_UNAVAILABLE` | sends `tenant_id` as `?scope=` (the event's exact scope) when given |
42
+ | `memory_delete` | `POST /v1/forget` (`memory_ids`) | ok | dropped `matched` and `warnings` (an id stored elsewhere answers `matched: 0`) | — | counts, `matched`, warnings; takes `from_preview_id` |
43
+ | `memory_bulk_delete` | `POST /v1/forget` (`about_subject`), `/v1/forget/preview` | ok | reported events only, always 0 | — | derived counts and warnings; accurate description |
44
+ | `entity_list`, `entity_get`, `entity_edges` | `GET /v1/facts` | ok | ok | — | none |
45
+ | `entity_link` | `POST /v1/experience` | ok | dropped `redirected_to` | as `memory_store` | names a redirect |
46
+ | `belief_declare` | `POST /v1/beliefs` | ok (strictness unchanged; `provenance` is optional) | dropped `redirected_to` | `RUN_REQUIRED`, `FORMAT_UPGRADE_PENDING` (atoms below format 10) | names a redirect |
47
+ | `list_conflicts`, `resolve_conflict`, `claim_history` | `GET /v1/conflicts`, `POST /v1/conflicts/{id}/resolve`, `GET /v1/claims/history` | ok | ok | — | `resolve_conflict` takes `tenant_id` (`?scope=`, the alignment pass below) |
48
+ | `health_check` | `GET /v1/auth/whoami` | ok | dropped `chain` (0.14.0: an agent's chain token names its links) | — | shows `chain` |
49
+ | `get_usage` | `GET /v1/auth/whoami` | ok | dropped `chain` | raised a raw `httpx` error | typed errors; shows the chain |
50
+ | `get_insights`, `cortexdb://insights` | `GET /v1/events`, `/v1/facts`, `/v1/beliefs` | sent only the bearer and the actor | ok | every failure was swallowed: a refused read (`BREAK_GLASS_REQUIRED`, confinement) answered "no notable insights" | reads with the tools' headers; names a layer it could not read, and fails when it read none |
51
+ | `code_explore`, `code_impact`, `code_inventory`, `code_graph` | `/v1/code/*` | ok | ok | `CODE_PLANE_DISABLED`; code reads are D-rows routes, so the operator key needs break-glass under `deny` | none |
52
+ | resources `cortexdb://health`, `cortexdb://episodes` | `GET /v1/auth/whoami`, `GET /v1/events` | ok | ok | — | none |
53
+ | the insights API (`cortexdb_mcp.api`) | as `get_insights` | sent no `X-Cortex-Actor`, so a minted token was 401 | ok | — | the tools' headers |
54
+
55
+ ## The tools 0.9.0 adds
56
+
57
+ Profiles (`--tool-profile`, or `CORTEXDB_TOOL_PROFILE`): `all`, the default, is every tool but the opt-in writes,
58
+ so an existing install keeps its tools and gains the read tools below; `write` adds `publish` and `forget_by_key`;
59
+ `read` lists only the tools that write no memory (`/mcp`'s default); `code` the four code tools. Every tool carries
60
+ MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`), as `/mcp`'s do.
61
+
62
+ | Tool | Profile | Route | What it answers |
63
+ |---|---|---|---|
64
+ | `access_check` | all, read, write | `POST /v1/access/check` | Can a principal (the token's own chain by default, or another: `principal`, `agent`, `app`, `chain_scopes`) read, write or own a scope, or read a memory: ALLOWED or DENIED with the deciding link and each link's path, the delivered role, the label cap, conditions and directory names |
65
+ | `access_explain` | all, read, write | `POST /v1/access/explain` | Why: the home's decision link by link, one decision per atom (a redacted one says so), a derived memory's visible supports, the label against the cap |
66
+ | `rollups` | all, read, write | `GET /v1/rollups`, `/v1/rollups/{id}`, `/v1/rollups/totals` | A unit's rollups at its `aud:roll` home (a unit's path is completed), by kind or topic, one by id with its citations, or the merged typed totals over a month range |
67
+ | `notices` | all, read, write | `GET /v1/access/notices?mine=true` | The caller's own unacknowledged notices at its root: who read what, when and why (a grant, a break-glass read, a sign-in account change), with directory names |
68
+ | `acknowledge_notices` | all, read, write | `POST /v1/access/notices/ack` | Acknowledges notices by `seqs` or a `through` position (changes no memory; a stale position acknowledges nothing and says so) |
69
+ | `directory_lookup` | all, read, write | `GET /v1/directory`, `GET /v1/directory/{id}` | A name or email prefix search, or one entry, with only the fields the caller may read |
70
+ | `publish` | write | `POST /v1/publish` | A person (directly, or through an admitted application whose token says `client: person`) copies an event or a rollup into a home they write; the answer names the copy and its original, and with `consistency` a token to read it back |
71
+ | `forget_by_key` | write | `POST /v1/forget`, `/v1/forget/preview` (`selector.idempotency_keys`, `selector.capture_keys`) | The read-free forget, with its preview and `from_preview_id` |
72
+
73
+ New fields on existing tools, each sent only when used, so a call without them sends the 0.8.0 request (but for `belief_declare`, which sends a fresh `idempotency_key` when none is given, as `memory_store` does):
74
+
75
+ | Tool | Fields |
76
+ |---|---|
77
+ | `memory_search` | `extra_scopes` (at most 40), `coverage`, `at_least` (a consistency token) |
78
+ | `get_context` | the same, and `merged_beliefs` (`beliefs: "merged"`) and `app_mounts` (`mounts: "default"`) |
79
+ | `advanced_search` | `coverage`, `at_least` |
80
+ | `memory_list`, `memory_get` | `at_least` (`?at_least=`) |
81
+ | `memory_store` | `idempotency_key`, `provenance`, `consistency` (`?consistency=1`, the answer's token) |
82
+ | `belief_declare` | `provenance`, `idempotency_key` |
83
+
84
+ ## CortexDB 0.14.0's route and field families
85
+
86
+ What this server covers of each family the 0.14.0 clients cover, and why the rest is not here. The stdio server is an
87
+ agent's tool surface, not an SDK: an operator's or an identity provider's route has no tool.
88
+
89
+ | Family | Covered | By, or why not |
90
+ |---|---|---|
91
+ | Access API: `check`, `explain` | yes | `access_check`, `access_explain` |
92
+ | Access API: `limits`, `lookup/scopes`, `lookup/principals`, `enrich` | no | sizing and planning inputs for an application or an Owner; `access_check` answers an agent's question about a given scope |
93
+ | AuthZEN (`/access/v1/*`, `/.well-known/authzen-configuration`) | no | a policy decision point's interface for policy engines |
94
+ | Change sets (`/v1/access/changes*`) | no | registry administration (CortexOS's compiler, an Owner) |
95
+ | Watch | no | the root's Owner's and the operator's feed |
96
+ | Notices and their acknowledgement | yes | `notices`, `acknowledge_notices` |
97
+ | Restores (`/v1/access/{records,restore/ack}`, `/v1/admin/access/restore`) | no | an access source's or the operator's acknowledgement; a pending root's refusal (`ACCESS_RESTORE_PENDING`) is mapped |
98
+ | Source containers (`rehome`, `watermarks`, `accounts/resolve`) | no | a connector's work; a source's watermark reaches `coverage` |
99
+ | Applications (`/v1/apps/{id}`) | no | administration; `app_mounts` reads an application's mounts |
100
+ | Directory (`/v1/directory[/{id}]`) | reads | `directory_lookup`; writes are an Owner's or the operator's |
101
+ | SCIM (`/scim/v2/*`) | no | an identity provider's interface; a SCIM error body is parsed if one reaches a tool |
102
+ | Rollups (`/v1/rollups`, `/totals`, `/{id}`) | yes | `rollups`; `/v1/admin/rollups/status` is the operator's |
103
+ | Publish (`POST /v1/publish`) | yes | `publish` (write profile) |
104
+ | Erasures (`/v1/erasures/{person,tenant}`, `subject: {email}`) | no | the operator's GDPR jobs; `cortexdb_check_call` knows their selector rules |
105
+ | The read-free forget (`/v1/forget` key selectors) | yes | `forget_by_key` (write profile) |
106
+ | Admin (`/v1/admin/access/*`, `/v1/admin/auth/reload`) | no | operator routes |
107
+ | Read fields: `extra_scopes` (40), `coverage`, `beliefs: "merged"`, `mounts: "default"`, `consistency`, `purpose` | yes | the recall tools (above); `purpose` on the access tools too |
108
+ | Write fields: `provenance`, `?consistency=1` | yes | `memory_store`, `belief_declare` |
109
+ | Consistency tokens: `consistency_token` in answers, `?at_least=` on GETs | yes | shown when returned; `memory_list`, `memory_get` |
110
+ | Tokens: confinement, lifetime ceilings, `act`, issuers-file tokens | yes | any bearer in `CORTEXDB_API_KEY`, `X-Cortex-Actor` from the token, typed refusals |
111
+ | The operator key's break-glass | yes | `CORTEXDB_BREAK_GLASS_*`; `BREAK_GLASS_REQUIRED` and a malformed header's `INVALID_HEADER` mapped |
112
+ | The subject-token shadow | yes | `CORTEXDB_SUBJECT_TOKEN` |
113
+ | New error codes, retriable ones marked | yes | `CortexDBError`: the envelope's code, `retriable` and `Retry-After`, with what to do for each code a caller can act on |
114
+ | Format 10 arming (`FORMAT_UPGRADE_PENDING` on an unarmed node) | yes | mapped by `details.reason` |
115
+
116
+ ## The alignment pass: the final 0.14.0 server
117
+
118
+ What changed on the server after 0.9.0 was first built (`sharedcontext` 8b8bfb6d), and what the tools do about it.
119
+
120
+ | Server change | Tools | Now |
121
+ |---|---|---|
122
+ | `GET /v1/events` pages with a newest-first cursor (`le1.`) bound to its query; any other cursor is 422 `INVALID_CURSOR` | `memory_list` | `cursor` replaces `offset`; each page ends with `More: cursor=<next>`; `episode_type` is the server's `labels` filter, so the cursor pages the filtered listing |
123
+ | Codes: 400 `INVALID_HEADER` for any header that does not parse, the break-glass header's included (`details {part, field, reason}`); 410 `ROLLUP_SUPERSEDED` (`details.current`); 422 `act_not_delegable` (and the token's 401 `INVALID_TOKEN_CLAIM`); 422 `field_not_applied`; 504 `DB_TIMEOUT`; 500 `INTERNAL_ERROR` (was `INTERNAL`); 500 `SCOPE_STORAGE_MISCONFIGURED`; 503 `WAL_OUTCOME_UNKNOWN` (was `SCOPE_MUTATION_UNKNOWN`); 405 `METHOD_NOT_ALLOWED`; every envelope's `request_id` | every tool | each says what to do: the setting behind the header, the current rollup to read or publish, the employee's own chain, the fields to drop, to narrow a timed-out call; `memory_store`'s retriable refusal names the idempotency key to retry with, so a write whose outcome is unknown is replayed, not stored twice. `IDEMPOTENCY_CONFLICT` and `CLUSTER_ROUTE_UNSUPPORTED` are mapped too. `cortexdb_check_call` flags a `field_not_applied` forget. The client keeps no table of statuses or retriable flags: it reads each envelope's |
124
+ | `read_purpose_v1`: a read declares `purpose` (`[a-z0-9_-]{1,64}`) on `/v1/recall`, `/recall/stream`, `/answer`, `/compose` and `POST /v1/access/check`/`explain`; a roster path whose `when` names it holds only then, and every decision line records it | `memory_search`, `get_context`, `advanced_search`, `access_check`, `access_explain` | `purpose` on each, default `CORTEXDB_PURPOSE`; checked against the grammar before anything is sent (the setting's error names it); a call naming none sends the request it sent before. The layer GETs (`memory_list`, `memory_get`, the entity tools) take no purpose on the server, so none is sent there. `cortexdb_check_call` checks the grammar |
125
+ | Forget and its preview gain `events_under_legal_hold` and `derived_under_legal_hold` (facts, beliefs, episodes, understanding): what a legal hold keeps, counted in no deleted field | `memory_forget`, `memory_delete`, `memory_bulk_delete` (and its dry run), `forget_preview`, `forget_by_key` (and its preview) | each answer says what the hold kept ("Kept under a legal hold", a preview's "Would keep"): the events, erased when the hold is released, and the derived records by layer; nothing when it kept nothing |
126
+ | The by-id reads take `?scope=` (a row elsewhere answers 404), and a cluster routes every by-id route by it, refusing one without (501 `CLUSTER_ROUTE_UNSUPPORTED`); `include_vectors=true` returns the embeddings | `memory_get`, `memory_status`, `resolve_conflict` | `memory_get` sent it already; `memory_status` (the exact scope the write went to) and `resolve_conflict` (the scope the conflict was listed at) take `tenant_id` and send it as `?scope=`, only when given, so a single node's call is unchanged. `rollups` by id and `directory_lookup` by id send their home and root already. No tool has a by-id fact, belief, concept or lifecycle read. `include_vectors` is not sent: an embedding is no text an agent reads, and it would fill the tool's answer |
127
+ | `POST /v1/beliefs` takes `idempotency_key` (at most 256 bytes): the same key at the same scope answers the first declaration's belief, another declaration under it is 409 `IDEMPOTENCY_CONFLICT` | `belief_declare` | takes `idempotency_key`, and sends a fresh one when none is given (as `memory_store` does), so a retriable refusal names the key to retry with and a declaration whose outcome is unknown is never made twice; a server before 0.14.0 ignores the field |
128
+ | Publish: a person may publish through an application the tenant admitted (`client: person`); refused are an agent's `act`, a run, a service subject and an `app` token with no `client`. `?consistency=1` returns `consistency_token`; a superseded rollup is 410 `ROLLUP_SUPERSEDED` | `publish` | the description and the `PUBLISH_PERSON_REQUIRED` hint no longer list "an application" among the refused, and say which chains are; `consistency` sends `?consistency=1` and shows the token; the 410 names the build to publish instead |
129
+ | `GET /v1/rollups/totals` takes a bound of exactly four digits, a hyphen and a month 01 to 12 (p5o item 9: `2026-+1` used to pass), else 422 `INVALID_QUERY` | `rollups` (`totals`) | the client's check is the server's: ASCII digits only, the whole value (a trailing newline or another script's digits no longer pass it) |
130
+ | The final catalog (API reference §16): `NON_HUMAN_WRITE_UNSUPPORTED` retired, `RATE_LIMITED` answered by no route (`SUBTREE_RATE_LIMITED`, `RECALL_OVERLOADED`), `NOTICE_UNAVAILABLE`'s reasons `moving`, `limit`, `arming_disabled`, `members_behind`, `refused` (`scope_unnamed` gone: a by-id read's notice is its row's region's), and a failed write's `resend: no` | every tool; `memory_get`, `memory_status` | the retired codes' hints are gone and `RECALL_OVERLOADED` has one; `NOTICE_UNAVAILABLE` reads the new reasons; `memory_get` no longer says a break-glass read needs `tenant_id`; `memory_status` says a failed write is never re-sent (it said `memory_store` re-stores it under a new key) |
131
+ | p5w: under `deny` the operator key's change-set activation is in-band: 403 `BREAK_GLASS_REQUIRED` (`details.class: "in_band"`) without the header, 503 `NOTICE_UNAVAILABLE`, 503 `REGISTRY_STALE`; the preview gains `in_band` and `in_band_parts` | none previews or activates a change set; the operator key's tools meet `in_band` on a write into a private region or a shared scope | the `BREAK_GLASS_REQUIRED` hint says what the class refused (in-band work, or data reads); `REGISTRY_STALE` is retriable and read as the envelope says |
132
+ | Codes no tool can meet | none | `ERASURE_NOT_CANCELLABLE`, `COHORT_UNSUPPORTED`, `SCIM_UNSUPPORTED`, `TENANT_SETTINGS_UNAVAILABLE`, the mint's `scopes_exceed_minter`, `caps_exceed_minter` and `deployment_mint_requires_operator`, `BLOB_STORE_ERROR`, and p5o's admin codes (`PERSIST_FAILED`, `ICE_FORK_UNSUPPORTED`, the repairs' `UNSUPPORTED_MEDIA_TYPE`, `CROSS_SHARD_REQUEST`): no tool cancels an erasure, asks a cohort, speaks SCIM, mints or calls an admin route; any of them still renders as a typed error |
133
+
134
+ ## Server behaviour noted, not changed here
135
+
136
+ - Fixed on the final server: `GET /v1/experience/status` answered `resend: "new_key"` for a `failed` write, which
137
+ the contract (`INDEXING_FAILED`, §8 O6) says never to re-send; it answers `no` now, and since p5v 17 its note says
138
+ never to re-send it, at any age, under any key (the key replays the failure; the operator re-includes the stored
139
+ event in place, and a copy under a new key would store the memory twice). `memory_status` and the
140
+ `INDEXING_FAILED` hint say the same. The reference's §11.2 no longer says the forget selector ignores a field it
141
+ does not know.
142
+ - Fixed in p5v, after the live run of this pass found them: a forget whose every named event a legal hold kept
143
+ answered `matched: 0` and the "matched no records ... check the ids" warning (p5v 1: `matched` counts what a hold
144
+ kept, at most `requested`, and the warning fires only when nothing matched); a read refused for want of its
145
+ purpose answered as if the caller held no role (p5v 2: 403 `POLICY_DENIED`, `details.reason: "purpose_required"`
146
+ and `details.purpose`, which the hint names); the reference said a redirected write is found by key only without
147
+ `scope` (p5v 3: by either, as the code does). A format hold's `NOTICE_UNAVAILABLE` keeps its own reason (p5v 12):
148
+ `reserved_path_in_use` is not retriable, and the hint says the operator clears it.
149
+ - Tenant settings carry `deployment_mode` (p5v 13); no tool reads them.