CortexDB · docs

CortexDB 0.10.4 — questions across the customers under a parent scope (experimental, off by default), an honest note on parent-scope packs, and your request id in every error

0.10.4 is a feature release on top of 0.10.3, the last published release. Everything in RELEASE_NOTES_0.10.3.md still applies; this page lists only what changed since. It adds no column family, directory, WAL row kind, index field or on-disk format (one key prefix, erasure_job/, for the erasure receipts in an existing column family), and nothing is migrated: a 0.10.3 server opens and serves a data dir 0.10.4 has used (see "Rollback").

Summary

Upgrade from 0.10.3

Before you upgrade: what clients and operators will notice

With every new flag at its default (the cohort lanes off), these are the only changes:

Upgrade steps

Nothing is migrated and there is nothing to run afterwards. Replace the binary or image and restart as for any release, giving the server 120 s to stop. Then check:

  1. GET /v1/admin/version: crate_version 0.10.4 and the git_sha you deployed. The capability list is the same 11 names as 0.10.3 (the two cohort names are known but not advertised; see "Detecting support").
  2. GET /v1/admin/ready: ready: true.
  3. If a client of yours treats any warnings[] entry as an error and calls parent scopes, either let it ignore parent_pack_unranked_sample or set CORTEX_PARENT_SAMPLE_WARNING=0.
  4. To try the cohort lanes, see "Turning the lanes on".

Upgrading from 0.10.2 or earlier

0.10.4 contains everything in 0.10.3. Read RELEASE_NOTES_0.10.3.md, "Upgrade from 0.10.2" (and from there the earlier notes) first; this page applies on top.

Questions across customers (experimental)

API reference: §8.3.1 (subtree on /v1/recall), §8.4.2 (cohort on /v1/answer), §8.4.3 (POST /v1/cohort/query, GET /v1/cohort/vocabulary, the plan grammar and the cursor), §8.15 (POST /v1/admin/cohort/warm, GET /v1/admin/cohort/status), §9 (pack warnings and subtree). Operator settings: docs/generated/env-vars.md §9.2.

Why

A recall at a parent scope reads the child scopes' events in storage order, takes the first 10,000, and the token budget then keeps a few dozen of them. The query plays no part, so every question at the parent got the same pack, usually from one customer, and /v1/answer either abstained or described that one customer as "the customers". No existing setting (filters, views, recency order, the answer's supplemental passes) answers "which customers did X", "how many" or "what do customers say about Y" correctly at a parent. The new lanes route each kind of question to a mechanism that can answer it, and refuse what they cannot answer.

Turning the lanes on

StepWhat
Exact laneCORTEX_COHORT=on in the server's environment. Needs the unified WAL (the default); a legacy-WAL server answers 503 COHORT_REQUIRES_UNIFIED_WAL.
Ranked laneCORTEX_SUBTREE_RECALL=request as well. The ranked and synthesis questions on /v1/answer need both flags.
Read allOn with the ranked lane: a synthesis or a "which customers said X" question reads every conversation of its cohort (CORTEX_COHORT_SYNTH_MAX_CALLS, default 32 map calls; 0 reads the ranked sample instead). It spends map-model tokens per question, about the cohort's conversation text; CORTEX_COHORT_SYNTH_MAP_MODEL picks a faster, cheaper model for it.
CallerThe scope.read.descend capability on the parent scope (the static operator key has it). Every count and list covers only the customer scopes the caller may read.
Request/v1/answer with "cohort": {} (or with options), /v1/recall with "subtree": {}, or the /v1/cohort/* routes. view must be holistic, descend or absent.
Warm-up (optional)POST /v1/admin/cohort/warm {"scope": "org:acme"} builds the scope's projection in the background ahead of the first question, and CORTEX_COHORT_WARM_ROOTS=org:acme does the same at boot; until it is ready a question answers 503 COHORT_WARMING with Retry-After.
Glossary (optional)<data_dir>/cohort_glossary.json maps your business phrases to facets ("paid for an order" -> intent shop.order.payment.done). Configuration, never customer data; read on each cohort request.
CheckGET /v1/admin/cohort/status shows the switches, the knobs, the resident projections and their counters, whatever the flags say.

The flags are read from the process environment only: the admin console's settings and feature overrides do not reach them. Both switches are read on every request; the numeric knobs once, at start.

Detecting support. cohort_query_v1 and subtree_recall_v1 are known names but are not listed in GET /v1/admin/version capabilities[] in 0.10.4, so do not gate on them yet. Probe with POST /v1/cohort/query instead: 404 means a server without the lanes (0.10.3 or earlier), 409 COHORT_DISABLED means the flag is off, anything else means the lane is served. An older server ignores an unknown cohort field on /v1/answer and answers from the storage-order sample, so treat a 2xx answer without a cohort block as "no cohort lanes here" and never show its text as a cohort answer.

In the admin console. Console › Ask has an Across customers switch next to the scope. When it is on, the question is asked at a parent scope (by default the root of the working scope, so org:acme for org:acme/user:u1; the field is editable) with cohort, and the page shows the narration above the structured block: the lane, the plan in words with the planner's notes, the coverage ("read N of N"), the customers table (alias, real scope, counts, cited), the top-N, funnel and segment tables, and citations that open each event at its own scope. It detects support as described above, words every refusal (409, 422, 503), waits out 503 COHORT_WARMING, and has no client timeout, so a question that reads conversations can take its time (8-48 s measured on 52-626 conversations, inside the 85 s answer budget). When the switch is off, Ask sends exactly the request it sent in 0.10.3. What the console shows of the time, the cost and a read-all answer is under "In the admin console".

What each lane answers

QuestionLaneExact?Example (synthetic shop)
Which customers did XExact listYes, complete over the authorized customers"Which customers cancelled an order?"
None did XExact listYes: an empty set with its coverage ("0 of 240")"Which customers viewed a Zephyr?"
How manyExact countYes"How many customers confirmed an order last week?"
Top NExact top (by customers or events; by intent, label family, day or week)Yes; ties at the cut-off are listed"Top 5 titles by viewers"
FunnelExact funnel (2-8 steps, optionally ordered)Yes"Order funnel: placed, paid, delivered"
SegmentsExact all_of / none_ofYes"Viewed both X and Y"; "confirmed but never had a conversation"
SequenceExact sequenceYes"Cancelled, then later confirmed"
ShareExact shareYes"What share of views were hardcover?"
LatestExact latestYes"The 10 newest orders, one per customer"
Attribute joined through an identifierExact, with a via join the planner infers from the vocabularyYes, when the attribute is a label"Viewed paperbacks but ordered a hardcover"
What customers saidRead all: every conversation of the exact cohort the question names is read, and the customers whose conversations show it are listed (read: "sample" or read-all off: ranked, one or two excerpts per customer)Read all: complete over every conversation read, as the model reads each one; sample: no, coverage stated"Which customers discussed refunds in a chat?"
Themes across conversationsRead all: every conversation read, the themes grouped and counted by customer by the server (read: "sample" or read-all off: synthesis over one excerpt per customer)Read all: counts complete over what was read, themes as the model reads each conversation; sample: no, coverage stated"What do customers ask about financing?"
One customerFocus: the alias from an earlier answer, answered by the single-customer pathAs today"What did C-4F2AQ7M do?"
Anything the planner cannot mapNoneRefused with a reason, no model callunknown_term, needs_text_attribute, needs_conversation_reading

The exact lane only knows typed facets: an event's context.intent, its labels and its modality. An attribute that lives only in event text (a colour, a size, a city written in a sentence) cannot be counted exactly; label it at ingest. On the measured store, six attribute questions that 0.10.3 could not answer became exact once those attributes were written as labels.

The planner. /v1/answer maps the question to a plan with deterministic rules (CORTEX_COHORT_PLANNER=rules, the default): it matches the words against the caller's authorized vocabulary (intents, label values, label families, modalities, the glossary), reads time windows (in June 2026, between ... and ..., last week, past 30 days, resolved against the anchor), clauses, negation and sequence words, and refuses with unknown_term rather than drop a word that could narrow the cohort. CORTEX_COHORT_PLANNER=llm adds one JSON-plan call (the model sees the question and the vocabulary, never scopes, event ids or event text). CORTEX_COHORT_PLANNER=auto, recommended when users ask in more than one language, runs the rules first and makes that call only when they cannot plan the question (unsupported, unknown_term above all) or the question is not English (Latin-script Hinglish, Devanagari, any non-Latin script): an English question the rules can plan gets exactly the rules plan and answer, with no model call, and cohort.notes says why the model planned when it did. An explicit cohort.plan always wins. Every answer echoes the plan and how each phrase was mapped (cohort.notes), so a reader can see that "cancelled" meant intent shop.order.cancel. GET /v1/cohort/vocabulary lists what can be matched.

The time anchor. Relative windows resolve against cohort.anchor: now, data_end (the newest authorized event under the scope) or an RFC 3339 instant. A store whose data ended weeks ago answers "last week" with data_end correctly and with now as an empty week, and both say which anchor and window they used.

Aliases and attribution

Coverage and the number check

Refusals

The lanes refuse rather than answer from a sample presented as complete:

ConditionResponse
A flag is off409 COHORT_DISABLED / SUBTREE_RECALL_DISABLED
The caller lacks scope.read.descend403 POLICY_DENIED (a /v1/recall subtree instead falls back to today's pack with the warning below)
view other than holistic / descend, cohort with stream or filters, an option out of range422 INVALID_BODY (a /v1/recall subtree with another view falls back with the warning below)
No authorized customer scope under the scope holds data (a leaf, for example)422 COHORT_NO_DESCENDANTS
More than CORTEX_COHORT_MAX_EVENTS events under the scope, or a projection larger than CORTEX_COHORT_MAX_BYTES413 COHORT_SUBTREE_TOO_LARGE (nothing cached)
The projection is being built or repaired after a forget, or the build queue is full503 COHORT_WARMING with Retry-After; stale data is never served
A question the planner cannot map200 with cohort.status: "unsupported", the reason and a pointer to the vocabulary; no model call
The ranked lane cannot run (the scope-partition map still building after a restart, more than CORTEX_SUBTREE_EXACT_VECTOR_MAX vectors in the subtree, an unsupported store)/v1/recall: today's pack plus warnings: ["subtree_recall_unavailable: <reason>"]; /v1/answer: unsupported with degraded_sources: ["subtree_recall_unavailable"], never narrated from the storage-order sample
The ranked lane is at its in-flight cap or deadline503 SUBTREE_BUSY with Retry-After

Safety

Performance

Measured on the 300-customer test store (about 129,000 events and 139,000 vectors) in a 3-vCPU, 3 GB container:

Timing and cost

API reference §8.4.2 ("Timing and cost") and §8.4.3.

Enrichment on chosen scopes, and where each fact came from

Erasure: receipts and proof

POST /v1/erasures hard-deletes a scope's events (or the named ones) and everything derived from them; docs/ERASURE_COVERAGE.md lists every store it clears. 0.10.4 makes the result provable:

What stays by design, and is listed on every receipt: the receipt itself, the audit rows, a whole-scope tombstone (the scope path and cutoff), the no-resurrection markers (event ids), the redacted copies' identities (ids, scope, timestamps), the scope registry entry until DELETE /v1/scopes, the deleted idempotency records' bytes until RocksDB compacts v1_idempotency/ (each write's idempotency key and scope path), copies outside the server (backups, the source system: a re-sync from it re-creates the customer, model provider logs of earlier answers), and, on a receipt whose events had been enriched while CORTEX_ENTITY_GRAPH was off, the entity names and aliases extracted from them.

In the admin console

The embedded console uses the new fields. Every page and switch that existed in 0.10.3 sends the same requests and renders the same way while the new switches are off (pinned by the web_ui tests: the leaf Ask, Compose and Recall code is hashed against the previous candidate, and a Recall record renders without the new provenance links).

Fixes

Measurements

The figures come from this release's code on a test copy of a 300-customer event-stream store, with the cohort flags on and the static operator key, graded against ground truth computed from the store's source data and frozen before the runs (version 2). The exact-lane and attribute figures are from the last full pass of the logic as released (2026-10-02); the read-all figures were measured again after the last change to its prompts (below). No question, customer or theme of that store is named here.

A cross-customer question set

30 questions (31 items with an anchored variant of one), graded on the structured result (cohort.result), never on the prose:

Questions/v1/cohort/query with a plan/v1/cohort/query with the question/v1/answer with cohort
19 exact-lane questions: lookups, counts, top-N, a funnel, segments, a sequence, an empty set, a relative window19/1919/1919/19
6 attribute questions answered from labels added at ingest6/66/66/6
6 questions about what customers said: 1 content lookup, 5 conversation syntheses, each reading every conversation of its cohortn/an/a (this route is the exact lane; it answers them unsupported)1/6

0.10.3 answered none of the 31 (its parent answers came from the storage-order sample).

Reading every conversation (lane: read_all)

Map model claude-haiku-4-5, check and narration calls on claude-opus-4-8, CORTEX_COHORT_SYNTH_PARALLELISM=8, _BATCH_TOKENS=40000, _BATCH_MAX=20, _MAX_CALLS=64, the same strict grader:

Content lookupSyntheses (5)
Conversations read52 of 5252 to 626 of 52 to 626, every cohort equal to the ground truth's
Customers listed12: precision 1.00, recall 0.67, F1 0.80 (bar 0.8: met)n/a
Ground-truth top-5 themes foundn/a3, 2, 3, 1, 4 (bar: 4 on 4 of 5 questions: not met)
Every narrated theme backed by 2 or more customersn/ayes, all five
Cited example conversations that carry the theme they are cited forn/a27 of 42 (64 %; per question 12/12, 8/10, 0/6, 0/2, 7/12; bar: 100 %: not met)
End to end12.7 s16-48 s
Model calls24 map, 1 narration8-33 map, 1 theme, 6 check, 1 narration
Cost per question$0.18$0.89-2.11 (0.30-1.49 M input tokens)

1 of the 6 items passes the strict grade (the lookup).

Measured again after a blind-rule fix. The worked examples in the read-all map, theme and check prompts had been written while iterating against this question set, and some of them paraphrased its questions and its list of themes. They were replaced with examples from unrelated domains (a bookshop, a phone app, a book club) or removed, and the six items were measured again: the figures above. With the earlier prompts the same items scored F1 0.81, top-5 themes 4, 2, 3, 1 and 4, and 33 of 42 valid examples (79 %); those were development-set figures for prompts tuned on the same questions, and are not claimed. What is left is agreement between two readers: the ground truth was labelled by a different model against a fixed list of themes, and on some questions the two name different reasons or aspects for the same conversation; which themes reach the top five also varies from run to run. Map models compared on the lookup and two syntheses (earlier prompts): claude-haiku-4-5 found the most of the lookup's customers (F1 0.77, against 0.62 for claude-sonnet-5 and the answer model, which read "discussed X" more narrowly) at the lowest cost ($2.14 for the three questions, against $2.92 and $5.55), with the same pass rate on the syntheses; it is the setting measured above. The development passes cost $44.83 in model calls on the test store, and the measurement after the fix $7.93.

Single-customer scopes do not change

Questions in Hindi and Hinglish

Measured on a synthetic store shaped like the customer-journey data (12 customers under one parent, typed event streams of about 35 events each, four of them bulked to about 435, one conversation each in English, Hinglish or Hindi script), answer model claude-opus-4-8, read-all map model claude-haiku-4-5, the same store and questions before and after. "After" is the multilingual recency words on (CORTEX_RECENCY_CUES_MULTILINGUAL=1, measured before the switch existed, when they were always read; off by default in this release) and CORTEX_COHORT_PLANNER=auto:

Question setBeforeAfter
"Latest" / "recent" questions on one customer in Hindi and Hinglish without an English cue word (5)0 of 5 named the newest item5 of 5
"Latest call / chat" questions in Hindi and Hinglish (4)3 of 44 of 4
The same shapes asked with an English cue word (4)4 of 44 of 4, the same inference
Across customers, CORTEX_COHORT_PLANNER=auto: a count, a list, a read-all lookup and themes in Hinglish and in Hindi (8)7 refused unknown_term, 1 planned as a lookup of "karte hain"8 of 8, the same customers as the English question
The 4 English cross-customer questions under autothe same plan, count and customers as rules, no planner call

A newest-slice answer now reads the newest-events tail, about 10,900 input tokens instead of about 7,300 (about $0.018 more per answer at the opus rate); the median answer time stayed at about 2.5 s. The planner call adds about 1-5 s and about $0.01 to a non-English cross-customer question ($0.018-0.024 for an exact one, $0.033-0.086 with read-all).

Other checks

On the release commit's code: the workspace test suite (8,374 tests passed, 0 failed, 209 test binaries), the English regression gate with its multilingual-cue pin, the erasure proof suite, the script gates, and the multilingual mock evaluation (no top-5 change over 1,214 baseline queries; it measures /v1/recall, which CORTEX_RECENCY_CUES_MULTILINGUAL does not touch). On the previous candidate, whose code differs only in the erasure proof's checks, the cohort answer's deadlines, the read-all prompts and that default-off switch: the live Docker suite (43 of 43 and every scenario), the upgrade and rollback probe (85 of 85, "Rollback"), the server, tenant-isolation, backup and restore, retention, WAL-migration and commitment smokes, and the Python SDK, CLI, MCP and TypeScript client suites.

Environment variables

All new, all read from the process environment only (docs/generated/env-vars.md §9.2 has the full rows):

VariableDefaultWhat it does
CORTEX_COHORToffon: the exact lane, /v1/cohort/* and /v1/answer cohort.
CORTEX_SUBTREE_RECALLoffrequest: /v1/recall subtree and the ranked and synthesis lanes of /v1/answer cohort.
CORTEX_COHORT_PLANNERrulesllm adds one JSON-plan call per cohort answer; auto (recommended for multilingual users) makes it only for a question the rules cannot plan or that is not English.
CORTEX_COHORT_MAX_EVENTS2,000,000Events under one scope; past it 413.
CORTEX_COHORT_MAX_BYTES1 GiBMemory of every resident projection together; least recently used evicted, 413 for one alone past it.
CORTEX_COHORT_CACHE_ROOTS4Resident projections, and the depth of the build queue.
CORTEX_COHORT_WAIT_MS3,000Wait for a build or repair before 503 COHORT_WARMING.
CORTEX_COHORT_WARM_ROOTSunset (nothing warmed)Comma-separated scopes whose projections are queued for a background build at boot.
CORTEX_COHORT_DEADLINE_MS5,000Reserved for a large-scope tier that is not built.
CORTEX_COHORT_SYNTH_MAX_CALLS32Read-all synthesis: the most map calls one cohort answer makes to read every conversation of its cohort (at most 256); past it the newest are read and the answer is partial. 0: the ranked sample instead.
CORTEX_COHORT_SYNTH_BATCH_TOKENS60,000Read-all: estimated conversation tokens per map call.
CORTEX_COHORT_SYNTH_BATCH_MAX40Read-all: conversations per map call.
CORTEX_COHORT_SYNTH_PARALLELISM6Read-all: map calls in flight per answer (at most 8); each also takes a permit of the shared provider lane.
CORTEX_COHORT_SYNTH_DEADLINE_MS45,000Read-all: budget of the map and theme calls (at most 60,000), ending at least 35 s before the answer budget; unfinished calls leave their conversations unread (partial).
CORTEX_COHORT_ANSWER_BUDGET_MS85,000The whole cohort /v1/answer from the request's start (40,000-95,000): planner call (at most 15 s), reads, map, theme and check calls and narration all finish inside it; a narration that misses it answers with the server summary.
CORTEX_COHORT_SYNTH_CONVERSATION_MAX_BYTES48,000Read-all: bytes of one conversation a map call reads; a longer one keeps its head and tail and is counted.
CORTEX_COHORT_SYNTH_MAP_MODELthe answer modelRead-all: the model of the map and theme calls, on the answer provider and key.
CORTEX_SUBTREE_MAX_INFLIGHT2Concurrent ranked passes; the excess waits to the deadline, then 503 SUBTREE_BUSY.
CORTEX_SUBTREE_DEADLINE_MS2,000Deadline of one ranked pass, checked inside its scan.
CORTEX_SUBTREE_EXACT_VECTOR_MAX1,000,000Vectors in the authorized subtree, counted before any scan; past it the ranked lane is refused (subtree_too_large).
CORTEX_PARENT_SAMPLE_WARNINGonThe parent-scope note; 0 / off / false / no turns it off.
CORTEX_ENRICHMENT_SCOPE_PREFIXESunset (every scope)Enrichment only for captures under these comma-separated scopes; set but naming no valid scope = nothing is enriched (§4.2).
CORTEX_ENRICHMENT_MAX_LLM_CALLS_PER_EVENT8; with long-text windowing on, 4 per window, at least 16Not new: only its default with CORTEX_ENRICHMENT_LONG_TEXT_WINDOWING on changed (Fixes).
CORTEX_RECENCY_CUES_MULTILINGUALoff1: /v1/answer's inferred newest-first order also reads Hindi and Hinglish "latest" / "last" words (Fixes). Off: non-English answers as in 0.10.3.

Error codes

HTTPCodeWhereRetriable
409COHORT_DISABLED/v1/cohort/*, /v1/admin/cohort/warm, /v1/answer with cohort, while CORTEX_COHORT is offno
409SUBTREE_RECALL_DISABLED/v1/recall with subtree, a ranked plan, while CORTEX_SUBTREE_RECALL is offno
404COHORT_ALIAS_NOT_FOUNDA focus alias that names no customer the caller may readno
413COHORT_SUBTREE_TOO_LARGEPast CORTEX_COHORT_MAX_EVENTS or CORTEX_COHORT_MAX_BYTESno
422COHORT_NO_DESCENDANTSNo authorized customer scope under the scope holds datano
503COHORT_WARMINGThe projection is building or repairing, or the build queue is full (Retry-After)yes
503COHORT_STATE_CHANGEDPOST /v1/cohort/query with a cursor whose last customer is no longer listed with the same count: restart the listingyes
503COHORT_UNAVAILABLEA read the cohort lane needs failedyes
503SUBTREE_BUSYThe ranked lane's in-flight cap or deadline (Retry-After)yes
503COHORT_REQUIRES_UNIFIED_WALA cohort request on a legacy-WAL serverno
422ENRICHMENT_SCOPE_NOT_ALLOWEDPOST /v1/admin/derived/rederive re-enriching an event outside CORTEX_ENRICHMENT_SCOPE_PREFIXES (dry run too; nothing changed)no
409ERASURE_NOT_COMPLETEDGET /v1/erasures/{id}/verify on a running or failed jobyes, once it completes
409ERASURE_RECEIPT_UNAVAILABLE/verify or /receipt on a job with no receipt (still running, or run by an older binary)no

New strings, not error codes: the pack warnings parent_pack_unranked_sample: ... and subtree_recall_unavailable: <reason>, and the /v1/answer diagnostics.degraded_sources codes subtree_recall_unavailable, cohort_narration_unverified, cohort_narration_failed and cohort_synthesis_partial (a read-all answer that did not read every conversation of its cohort).

Files in the data dir

Created only when a cohort lane is used; 0.10.3 ignores both:

Written by every POST /v1/erasures job that ends (not a file: keys in an existing store):

Startup warnings

The startup configuration lint lists each new variable with its effective value. With CORTEX_ENRICHMENT_SCOPE_PREFIXES set it also names what the allowlist does not confine: ENRICHMENT_SCOPE_ALLOWLIST_LAYERS_AUTO (CORTEX_V1_LAYERS_AUTO on), ENRICHMENT_SCOPE_ALLOWLIST_COMPACTION (CORTEX_CONSOLIDATION_ENABLED opted in), ENRICHMENT_SCOPE_ALLOWLIST_INERT (enrichment off) and ENRICHMENT_SCOPE_PREFIX_INVALID.

Rollback

A 0.10.3 image opens a data dir 0.10.4 has used, with the cohort flags on or off: no column family, directory, WAL row kind or index field is added (the one new key prefix, erasure_job/, holds erasure receipts that 0.10.3 never reads: after a rollback GET /v1/erasures/{id} answers 404 for jobs 0.10.4 ran, as for any pre-restart job on 0.10.3), and the cohort projection is in memory only. To roll back, stop 0.10.4 with the usual 120 s grace and start 0.10.3 on the same data dir; to turn the lanes off without a rollback, unset the flags and restart. A fact extracted by 0.10.4 carries one more metadata key (extractor_model), which 0.10.3 keeps and ignores; 0.10.3 does not read CORTEX_ENRICHMENT_SCOPE_PREFIXES, so turn enrichment off in its environment (unset CORTEX_ENRICHMENT_DELAY_SECONDS) before rolling back a store that relied on the allowlist: 0.10.3 would otherwise enrich every scope.

What was checked, on one data dir, with a graceful stop between each step:

  1. 0.10.3 wrote captures under several customer scopes.
  2. 0.10.4 (flags on) read them, wrote more, built a projection and answered exact, joined (via) and ranked cohort questions, checked the vocabulary, and confirmed that a leaf recall has no subtree.
  3. 0.10.3 again opened the store and served everything; its /v1/cohort/query answered 404, and a customer forget succeeded.
  4. 0.10.4 again rebuilt the projection without the forgotten customer.

85 of 85 checks passed, with no error line and no panic; the only new top-level entry was cohort_alias.key.

Known issues

New in 0.10.4:

Carried over from 0.10.3: see "Known issues" in RELEASE_NOTES_0.10.3.md.

Clients

No client changes. Every client released with 0.10.3 (Python SDK cortexdbai 0.12.0, TypeScript SDK cortexdbai 0.10.1, CLI cortexdb-cli 0.6.0, MCP cortexdb-mcp 0.8.0, connectors 0.2.22) works against 0.10.4 unchanged. None of them sends cohort or subtree yet; call the routes and fields directly (they are plain JSON). What existing clients can see:

Notes

Single-node remains the only supported topology.

Recommended for stores whose users ask in more than one language: CORTEX_COHORT_PLANNER=auto. The default stays rules, so an existing deployment plans every question as before until it opts in.