Skip to content

feat(cortex): org:<uid> root — tenant-relative hosted paths and a retired user: root for the transition - #244

Merged
senamakel merged 17 commits into
mainfrom
memory-org-root
Oct 8, 2026
Merged

senamakel merged 17 commits into
mainfrom
memory-org-root

Conversation

@senamakel

Copy link
Copy Markdown
Member

What

The canonical memory root is now org:<uid>, with no user:<uid> segment anywhere in a scope path (memory audit 01, F10).

  • Hosted wire: tenant-relative paths. New CortexEngine::with_tenant_root() / EngineSettings::tenant_root. A v3 engine on the TinyHumans wire names no root and sends ws:main/app:conversations. memory-api pins org:<uid>, so the stored path is org:<uid>/ws:main/app:conversations instead of org:<uid>/user:<uid>/ws:main/…. An unprefixed scope listing covers the whole tenant: the backend bounds it to the caller, and an empty prefix= is never sent because memory-api refuses it. This root is refused on the direct wire.
  • Transition reads: a retired root. New CortexEngine::with_retired_root(root) / EngineSettings::retired_scope_root.
    • Every read goes through ScopeLayout::paths / KindScope::read. That covers list, fetch, recall, get, explore, assembled conversations and beliefs, and it reads each node below both the root and the retired user:<uid> root. Results merge by item id.
    • Forget by id, forget by filter and direct erasure remove from both roots.
    • Writes go only to the new root.
    • A path below the retired root is parsed before the empty root, so user:<uid>/ws:main/… reads back as ws:main, not as a node that starts with user:.
  • Direct engine. Hosts root it at org:<uid> with the actor user:<uid> as owner. The layout code is generic, so that is a host setting and needs no tinymemory change; the docs now show it.
  • Docs. README ("Where a person's memory lives"), docs/architecture/cortex-layout.md, docs/integration.md, the cortex README and memory-v2.md now show org:<uid>/… paths.

Tests

  • envelope/layout_tests.rs: tenant-layout paths with no root, a retired root doubling reads but not the write path, retired-root parse precedence and validation.
  • engine/mod_retired_root_tests.rs:
    • the hosted tenant engine sends no root segment and no empty prefix=;
    • hosted dual read;
    • direct dual read merged by id, with writes only to org:42;
    • forget by id and by filter hits both roots;
    • erasure hits both roots;
    • constructor errors.
  • registry/mod_tests.rs: tenant_root works only on the hosted wire, and retired_scope_root needs v3.
  • cargo test --workspace, cargo clippy --workspace --all-targets -D warnings and cargo fmt --check are green.

Rollout order

  1. Ship the client with dual read: this release, plus OpenHuman with [memory] legacy_user_segment_read = true (default).
  2. Run cortexdb-saas reroot-user-segment: --dry-run first, then for real. This copies org:<uid>/user:<uid>/<rest> to org:<uid>/<rest>, verifies per scope, then erases the old subtree.
  3. Verify: every tenant reports kept_old: null, and nothing remains under any org:<uid>/user:<uid>.
  4. Turn off the legacy read (legacy_user_segment_read = false); the host then stops passing retired_scope_root.

Follow-ups: cortexdb-saas reroot PR, then an OpenHuman gitlink bump with the host wiring.

senamakel and others added 12 commits October 8, 2026 22:39
Moved the envelope layout definitions out of the parent module into a
dedicated layout module so the serialization structure is easier to
locate and evolve independently. No behaviour changes.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The V3 scope layout's prefix and parse logic is split into free functions
that take the root as a parameter, so the same code can serve layouts with
and without a root prefix. Behaviour is unchanged for existing layouts.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce engine modules for cortex items and scopes, providing the
underlying storage and lookup logic these entities need.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Moved item handling and log reading helpers into their own modules to keep the engine file focused. No behaviour change.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce configuration and registry modules for the integrations crate so
providers can be declared and looked up through a shared entry point.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The retired root tests were extracted from the engine module into their own
file so the engine module stays focused on runtime behaviour. No test logic
changed.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The retired-root read test now builds GetRequest with explicit ids and reach fields instead of the removed constructor, keeping the test compiling against the current request type.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The hosted tenant test now filters the engine without the retired root to an
exact reach at the person's own nodes and asserts the single remaining item's
text, so the check pins down which root the surviving entry came from instead
of only counting results.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add layout tests for the tenant layout and for a layout with a retired
root, checking that reads double across both roots while writes stay on
the active one and that retired roots are validated. Also cover the
registry rules that make a tenant root hosted-only and require a layout
before a retired root can be set.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Rustfmt-compliant wrapping was applied to the retired-root engine and
envelope layout tests, splitting long store chains and assert! calls
across lines. No test behaviour or coverage changed.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The layout docs now describe the hosted wire's tenant root, where the
backend pins every scope below the caller's `org:<id>`, and the retired
`user:<id>` root that stays readable and forgettable while its memory is
moved. Examples and integration guidance were updated to use `org:<id>`
as the scope root, with `user:` reserved for the actor.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a README section describing the per-person scope layout, showing how
org, app, workspace, and service segments compose under a single root. It
also notes how each engine is configured and that memory written under the
older user-scoped layout stays readable until migrated.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper

tinysweeper Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper completed its review; deterministic results follow.

State: Changes requested
Priority: high
Reviewed head: 76ed6312ce1d
Updated: 1791484445 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 9 Active findings 13
Tests 4 Noted findings 0
Documentation 5 Resolved findings 105
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

Introduces a tenant-rooted hosted layout with an `org:<id>` root and no `user:` scope segment, plus a retired-root transition. `EngineSettings` gains `tenant_root` and `retired_scope_root` (`crates/tinymemory-integrations/src/config/mod.rs#pub struct EngineSettings {`); `CortexEngine` gains `with_tenant_root` and `with_retired_root`, with registration skipped when the root is empty (`crates/tinymemory-integrations/src/cortex/engine/mod.rs#impl CortexEngine {`). `ScopeLayout` gains `tenant()`, `with_retired_root`, `retired`, `roots`, `paths`, `is_retired`, and free functions `checked_root`, `render`, `node_prefixes_below`, `parse_below` (`crates/tinymemory-integrations/src/cortex/envelope/layout.rs#impl ScopeLayout {`). `KindScope` gains `read` and `listed`, and reads are doubled over `roots()` in `held`, `every_scope` and the discovery paths (`crates/tinymemory-integrations/src/cortex/engine/scopes.rs#impl KindScope {`, `crates/tinymemory-integrations/src/cortex/engine/scopes.rs#impl CortexEngine {`). List/get results are deduplicated by item id (`crates/tinymemory-integrations/src/cortex/engine/items.rs#impl CortexEngine {`), and recall interleaving dedupes by id so an item held in both roots is one hit at its best rank (`crates/tinymemory-integrations/src/cortex/engine/fetch.rs#impl CortexEngine {`). Listing shadows retired-root twins against the active root (`crates/tinymemory-integrations/src/cortex/engine/list.rs#impl CortexEngine {`). An empty prefix omits the `prefix=` query parameter (`crates/tinymemory-integrations/src/cortex/log/read.rs#impl Log {`). `build_engine`/`list_engines` wire the new settings and their error documentation (`crates/tinymemory-integrations/src/registry/mod.rs#pub fn build_engine(`, `crates/tinymemory-integrations/src/registry/mod.rs#pub fn list_engines() -> Vec<EngineDescriptor> {`). Documentation is rewritten from `user:` to `org:` roots with new hosted-wire and retired-root sections (`README.md#route has no keyword/vector switch, so both wires declare hybrid fetch only. See`, `crates/tinymemory-integrations/src/cortex/README.md#app:tinymemory/team:acme/agent:writer/app:learnings a team m`, `docs/architecture/cortex-layout.md#A root is `type:id` segments of the types CortexDB's hosted API admits`, `docs/integration.md#actor header and the headers the HTTP stack sets, so the credential stays`, `docs/specs/memory-v2.md#credentialed cleartext non-loopback endpoint, all as `Error::Config`.`).

Features

  • Added — Hosted tenant-relative root (no root segment sent): A tinyhumans engine in layout v3 sends paths relative to the backend-pinned tenant root (`org:<id>`), e.g. `ws:main/app:conversations`; the setting is refused on the direct wire with `Error::Config`, where nothing pins a root and every install would share one tree, and an empty root skips direct root registration. Reviewers flagged the hosted tenant-prefix parsing heuristic (`incorrect-prefix-parsing`) as an open critique finding. (crates/tinymemory-integrations/src/cortex/engine/mod.rs#impl CortexEngine {, crates/tinymemory-integrations/src/cortex/envelope/layout.rs#pub(crate) enum ScopeLayout {, crates/tinymemory-integrations/src/cortex/envelope/layout.rs#impl ScopeLayout {, crates/tinymemory-integrations/src/config/mod.rs#pub struct EngineSettings {)
  • Added — Retired `user:<id>` root during the transition: While a host names `retired_scope_root`, every read and forget/erasure covers both the root and the retired root, merged by item id so an item held in both is one item, and writes go only below the new root; the option requires a v3 layout and refuses a retired root that is the root itself or is invalid. Reviewers flagged scope enumeration for erasure and write-target validation as remaining critique findings. (crates/tinymemory-integrations/src/cortex/engine/mod.rs#impl CortexEngine {, crates/tinymemory-integrations/src/cortex/engine/scopes.rs#impl KindScope {, crates/tinymemory-integrations/src/cortex/engine/scopes.rs#impl CortexEngine {, crates/tinymemory-integrations/src/config/mod.rs#pub struct EngineSettings {, crates/tinymemory-integrations/src/cortex/envelope/layout.rs#impl ScopeLayout {)
  • Modified — Deduplication of items held under two roots: Get results are keyed by item id and inserted with `or_insert_with`, later duplicates are skipped with the root's lookup first in read order, and recall interleaving keeps each id once at its best rank; listing shadows retired-root twins via `shadowed`, though the security lane flagged shadowing an item before confirming the active copy is complete. (crates/tinymemory-integrations/src/cortex/engine/items.rs#impl CortexEngine {, crates/tinymemory-integrations/src/cortex/engine/fetch.rs#impl CortexEngine {, crates/tinymemory-integrations/src/cortex/engine/list.rs#impl CortexEngine {)
  • Modified — Empty scope prefix omits `prefix=` on the hosted wire: With an empty prefix the scope listing request is built without `prefix=`, because the backend bounds an unprefixed listing to the caller's tenant and refuses an empty prefix. (crates/tinymemory-integrations/src/cortex/log/read.rs#impl Log {)
  • Modified — Registry wiring and error contract for the new settings: `build_engine` applies `tenant_root` (overriding `scope_root`) and then `retired_scope_root`, and the documented config errors now include a tenant root off the TinyHumans wire and a retired root without a v3 layout. (crates/tinymemory-integrations/src/registry/mod.rs#pub fn build_engine(, crates/tinymemory-integrations/src/registry/mod.rs#pub fn list_engines() -> Vec<EngineDescriptor> {)
  • Modified — Documentation re-rooted from `user:` to `org:`: The layering docs describe `org:<id>` roots, `user:` as the person's actor rather than a scope segment, the hosted tenant root, and the retired-root transition (reads merged by id, forget/erasure covering both, writes to the root only), with the full transitional configuration example in the integration guide. (docs/architecture/cortex-layout.md#A root is `type:id` segments of the types CortexDB's hosted API admits, docs/architecture/cortex-layout.md#node, and erasing one node never takes a sibling kind with it:, docs/architecture/cortex-layout.md#An engine with no scope root uses it, so existing memory stays where it is., docs/integration.md#actor header and the headers the HTTP stack sets, so the credential stays, docs/specs/memory-v2.md#credentialed cleartext non-loopback endpoint, all as `Error::Config`., README.md#route has no keyword/vector switch, so both wires declare hybrid fetch only. See, crates/tinymemory-integrations/src/cortex/README.md#app:tinymemory/team:acme/agent:writer/app:learnings a team m)

Tests

  • unit — An item ranked in two scopes is returned once, at its best rank, by the recall interleaver.: Directly pins the dedup behaviour that keeps an item held below both roots a single hit; would fail if the `seen` set were removed. (crates/tinymemory-integrations/src/cortex/engine/fetch_tests.rs#fn a_pack_budget_fits_each_event_whole_up_to_a_ceiling() {)
  • unit — The tenant layout names no root, parses paths with a tenant prefix stripped, and treats the legacy tree as foreign.: Pins the hosted tenant-root path rendering and parsing; review noted a low-confidence edge in the tenant-prefix heuristic and canonical-path validation. (crates/tinymemory-integrations/src/cortex/envelope/layout_tests.rs#fn a_namespace_repeating_the_root_is_flagged() {)
  • unit — A retired root doubles reads (`paths`, `roots`, `node_prefixes`), parses both roots, and is refused when invalid or the root itself.: Covers the layout-level transition semantics and the configuration guards. (crates/tinymemory-integrations/src/cortex/envelope/layout_tests.rs#fn a_namespace_repeating_the_root_is_flagged() {)
  • integration (in-process doubles) — A hosted tenant engine sends no root segment, never an empty `prefix=`, and lists the whole tenant.: Exercises the hosted wire against a double; the critique lane noted the request assertion could also reject other malformed prefixes. (crates/tinymemory-integrations/src/cortex/engine/mod_retired_root_tests.rs)
  • integration (in-process doubles) — A hosted tenant engine reads the retired `user:` root too, and an exact reach returns both roots' items; without the retired root the old path is no longer read.: Pins the transition read behaviour on the hosted wire. (crates/tinymemory-integrations/src/cortex/engine/mod_retired_root_tests.rs)
  • integration (in-process doubles) — Reads merge both roots by item id and writes go only to the new root, with the root's owner registered as the person's actor.: Exercises dedup and the write-target guarantee against a double; the critique lane asked for an explicit negative assertion that no write targets the retired root. (crates/tinymemory-integrations/src/cortex/engine/mod_retired_root_tests.rs)
  • integration (in-process doubles) — A forget by id and by filter removes from both roots, and an erasure removes both roots' scopes.: Covers the forget and erasure transition semantics; the critique lane flagged erasure scope enumeration as a remaining finding. (crates/tinymemory-integrations/src/cortex/engine/mod_retired_root_tests.rs)
  • integration (in-process doubles) — An item held in both roots is listed once across pages, with the cursor keeping the twin from returning on a later page.: Pins pagination-level dedup across roots. (crates/tinymemory-integrations/src/cortex/engine/mod_retired_root_tests.rs)

Findings

  • high · critique · Skip the hosted tenant prefix when parsing tenant paths — This configures the hosted engine with a root containing the retired user segment, but the test does not establish that the hosted tenant prefix is removed before the path is parse (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:100)
  • high · critique · Include retired roots when collecting scopes for erasure — This only verifies the count and later checks that two expected paths appeared in the fake's erasure log. It does not exercise a retired root containing a scope that is absent from (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:253)
  • high · critique · Use the root occurrence when validating the canonical path — The exact-reach assertion checks only the number of returned items. It does not verify the canonical namespace/path associated with each root occurrence, so a path-normalization bu (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:120)
  • high · critique · Parse the complete hosted tenant prefix — For the tenant layout, this skips only the first segment whenever it starts with `org:`. A hosted path whose backend prefix contains more than one segment, such as `org:42/dept:sha (crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs:375)
  • medium · critique · Select a deterministic source for duplicate items — Both copies of the duplicate item are byte-for-byte identical, so the assertions only prove that one item is returned, not which root supplied its metadata or other conflicting fie (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:154)
  • medium · critique · Reject every empty tenant prefix query — This assertion misses an empty `prefix` when it is the final query parameter, such as `...?prefix=`; it only detects the form followed by `&`. The test can therefore pass even thou (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:91)
  • medium · critique · Disambiguate overlapping retired and active roots — The retired root is always tried first, but hosted parsing searches for the last occurrence of each root. Valid configurations can overlap: with active root `org:42/user:42` and re (crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs:271)
  • medium · critique · Exercise the transition against a live CortexDB — The transition tests run against `direct_double`, so they validate only the test router's emulation of scope listing, merging, forgetting, and erasure. They do not catch incompatib (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:153)
  • medium · critique · Assert that transition writes never target the retired root — Checking that added scopes start with `org:42` only catches writes recorded as scope events and does not prove that no transition operation targets `user:42`; a malformed or separa (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:175)
  • medium · critique · Show a complete configuration for the retired user root — The test only supplies the retired root string and relies on the helper's defaults for its ownership and routing configuration. That leaves the configuration contract under-specifi (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs:64)
  • high · security · Use the matched root occurrence when validating the canonical path — For a hosted path such as `org:tenant/user:42/app:conversations`, `start` points at the matched `user:42` occurrence. Subtracting the root length moves the slice back to the tenant (crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs:406)
  • high · security · Only shadow retired items after confirming the active copy is complete — This treats any event found in the active scope as sufficient to shadow the retired copy. During a partial transition, the active scope can contain only some events for a conversat (crates/tinymemory\-integrations/src/cortex/engine/list\.rs:237)
  • medium · tests · Drive the tenant-root and retired-root settings against a live CortexDB — Still standing from the earlier review: all the new behaviour is exercised against in-process doubles (`direct_double`, `hosted_double`). The wire-level details this change depends (crates/tinymemory\-integrations/src/registry/mod\_tests\.rs:254)

Resolved this pass

  • Choose a deterministic source when duplicate scopes contain an item
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Choose a deterministic source when duplicate scopes contain an item
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Use the root occurrence when validating the canonical path
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Drive the tenant-root and retired-root settings against a live CortexDB
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths
  • Strip the hosted tenant prefix before parsing tenant-root paths
  • Assert that transition writes never target the retired root
  • Show a complete configuration for the retired user root
  • Choose a deterministic source when duplicate scopes contain an item
  • Use the root occurrence when validating the canonical path
  • Include retired roots when collecting scopes for erasure
  • Skip the hosted tenant prefix when parsing tenant paths

Before merge

  • Address Skip the hosted tenant prefix when parsing tenant paths (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs).
  • Address Include retired roots when collecting scopes for erasure (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs).
  • Address Use the root occurrence when validating the canonical path (crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs).
  • Address Parse the complete hosted tenant prefix (crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs).
  • Address Use the matched root occurrence when validating the canonical path (crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs).
  • Address Only shadow retired items after confirming the active copy is complete (crates/tinymemory\-integrations/src/cortex/engine/list\.rs).
Agent review details

critique

  • Conclusion: Failure
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 8 files; 10 findings. _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Skip the hosted tenant prefix when parsing tenant paths
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Include retired roots when collecting scopes for erasure
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Use the root occurrence when validating the canonical path
  • Evidence: crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs — Parse the complete hosted tenant prefix
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Select a deterministic source for duplicate items
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Reject every empty tenant prefix query
  • Evidence: crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs — Disambiguate overlapping retired and active roots
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Exercise the transition against a live CortexDB
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Assert that transition writes never target the retired root
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/mod\_retired\_root\_tests\.rs — Show a complete configuration for the retired user root

security

  • Conclusion: Failure
  • Scope reviewed: all assigned evidence
  • Positive: The security lane raised the tenant-prefix parsing issue and canonical-path validation, and recorded no other sensitive-data finding.
  • Lane summary: Reviewed 7 files; 2 findings. 1 file was not security-reviewed: docs/integration.md (prose or tabular data). _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: crates/tinymemory\-integrations/src/cortex/envelope/layout\.rs — Use the matched root occurrence when validating the canonical path
  • Evidence: crates/tinymemory\-integrations/src/cortex/engine/list\.rs — Only shadow retired items after confirming the active copy is complete

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The behavioural branches of the change — item-id dedup in get and recall, `KindScope::read`, the doubled scope listing, the empty-prefix request, and the cross-page dedup — are each exercised by a test that would fail if the behaviour regressed.
  • Positive: The transition is driven end to end against engine doubles for the hosted and direct wires, covering writes, reads, forgets, erasures and pagination.
  • Lane summary: This revision lands the `org:`/tenant-root layout plus the retired-root transition, and the tests now earn their keep: `mod_retired_root_tests.rs` drives writes, reads, forgets, erasures and pagination against engine doubles, and the layout and interleave unit tests pin the prefix-stripping, dual-root parsing and dedup behaviour. Six of the eight earlier findings are fixed in code with tests that would fail on regression; the live-CortexDB coverage finding still stands. One new, low-confidence edge in the tenant-prefix heuristic is noted below. _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: crates/tinymemory\-integrations/src/registry/mod\_tests\.rs — Drive the tenant-root and retired-root settings against a live CortexDB

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The revision resolves the earlier blocking findings: tenant prefixes are stripped before parsing, writes never target the retired root and are asserted not to, duplicates across roots merge deterministically (root's copy first in get, best rank in recall, root's copy listed and the twin shadowed), erasure covers both roots, and the docs now show the full transitional configuration. What remains is that the new behaviour is exercised only against in-process doubles; no finding blocks merging. (1 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

e2e

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The transition behaviour (tenant root, retired root, merged reads, deterministic duplicate choice, hosted prefix stripping) is now implemented and unit-tested against doubles, and all earlier findings except live end-to-end coverage are resolved. The new settings and wire behaviour still reach no running CortexDB: the lexical candidates in integration/cortexdb are comments and compose boilerplate, and no workflow triggers the harness, so the change is otherwise safe to merge with that coverage gap recorded. (1 already reported on an earlier push) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
Evidence and run details
  • Models: gpt-5.6-luna, glm-5.3-flash
  • Spend: $0.035225
  • Tokens: 740822 input · 48161 output · 57571 cached · 0 embedding
  • Continuity: summary cache chain restarted at the storage ceiling.
Head State Pass summary
e7c229bec877 changes requested 8 active finding(s), 0 resolved finding(s) (at 1791480946)
76ed6312ce1d changes requested 13 active finding(s), 105 resolved finding(s) (at 1791484445)

tinysweeper 0.1.0

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 18 billable files and costs up to $4.50.

Or wait 35 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bbd78681-ca79-4826-bf4a-e121ef2d8e40
📥 Commits

Reviewing files that changed from the base of the PR and between fb817fe and 76ed631.

📒 Files selected for processing (18)
  • README.md
  • crates/tinymemory-integrations/src/config/mod.rs
  • crates/tinymemory-integrations/src/cortex/README.md
  • crates/tinymemory-integrations/src/cortex/engine/fetch.rs
  • crates/tinymemory-integrations/src/cortex/engine/fetch_tests.rs
  • crates/tinymemory-integrations/src/cortex/engine/items.rs
  • crates/tinymemory-integrations/src/cortex/engine/list.rs
  • crates/tinymemory-integrations/src/cortex/engine/mod.rs
  • crates/tinymemory-integrations/src/cortex/engine/mod_retired_root_tests.rs
  • crates/tinymemory-integrations/src/cortex/engine/scopes.rs
  • crates/tinymemory-integrations/src/cortex/envelope/layout.rs
  • crates/tinymemory-integrations/src/cortex/envelope/layout_tests.rs
  • crates/tinymemory-integrations/src/cortex/log/read.rs
  • crates/tinymemory-integrations/src/registry/mod.rs
  • crates/tinymemory-integrations/src/registry/mod_tests.rs
  • docs/architecture/cortex-layout.md
  • docs/integration.md
  • docs/specs/memory-v2.md
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T18:32:31.472760Z 76ed631 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7c229bec8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tinymemory-integrations/src/cortex/engine/scopes.rs
Comment thread crates/tinymemory-integrations/src/cortex/engine/scopes.rs
Comment thread crates/tinymemory-integrations/src/cortex/envelope/layout.rs
Comment thread crates/tinymemory-integrations/src/cortex/engine/items.rs

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0392 · 843,253 in / 41,159 out · 75,894 cached (9%)  · gpt-5.6-luna, glm-5.3-flash, deepseek-v4.1-flash
critique:    $0.0242 · 484,583 in / 23,553 out · 48,943 cached (10%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0144 · 261,460 in / 11,189 out · 23,367 cached (9%)  · gpt-5.6-luna
tests:       $0.0002 · 23,658 in  / 598 out    · 1,856 cached (8%)   · glm-5.3-flash
description: $0.0001 · 23,603 in  / 1,590 out  · 0 cached (0%)       · glm-5.3-flash
e2e:         $0.0002 · 25,763 in  / 708 out    · 1,728 cached (7%)   · glm-5.3-flash

Comment thread crates/tinymemory-integrations/src/cortex/envelope/layout.rs
Comment thread docs/integration.md
Comment thread crates/tinymemory-integrations/src/cortex/engine/items.rs
Comment thread crates/tinymemory-integrations/src/cortex/envelope/layout.rs
Comment thread crates/tinymemory-integrations/src/config/mod.rs
@tinysweeper tinysweeper Bot added the priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. label Oct 8, 2026
senamakel and others added 5 commits October 8, 2026 23:55
Fetching items from the engine now keeps the stored payload intact instead of
dropping it during envelope decoding, so callers receive the full item data
rather than a stripped-down record.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Move the item lookup and formatting helpers out of the list engine into a
dedicated items module so both can be reused by other engine operations.
No behaviour change.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add tests asserting that an item ranked under both the active and retired
root is listed once at its best rank across pages, and that writes in the
transitional layout only land below the new root. Also cover parsing of
hosted tenant paths with a prefix, including retired-root paths behind it.
Reformat two call sites in the list engine with no behaviour change.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The scope root section now explains that memory written below an earlier
`user:<id>` root stays readable and forgettable only while the engine names
that root as well, so both roots must be configured during the move. Writes
go to the new root only, and `retired_scope_root` can be dropped once the
memory has moved.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76ed6312ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +271 to +274
retired
.as_deref()
.and_then(|retired| parse_below(retired, *prefixed, path))
.or_else(|| parse_below(root, *prefixed, path))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match the retired root only at the tenant boundary

With a tenant-root layout retiring user:42, an active namespace such as ws:main/user:42 is valid, but this retired-first call delegates to parse_below(..., prefixed = true), which finds user:42 anywhere in the path. It therefore misparses org:<tenant>/ws:main/user:42/app:learnings as a retired-root scope at the root namespace; subtree reads at ws:main then omit it, while exact list reads can classify the active scope as retired and shadow its own items. Restrict retired-root matching to the first segment after the hosted org: prefix.

Useful? React with 👍 / 👎.

Comment on lines +171 to +173
found
.entry(ItemId::new(id.clone()))
.or_insert_with(|| hit(&id, &item, 0.0));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve active-root priority in get lookups

For a hosted tenant-root request at ws:main, known sorts the retired path user:42/ws:main/... before the active path ws:main/...; this first-write-wins insertion therefore retains the retired copy, and the outer loop stops before querying the active scope once all requested IDs are found. If the copies diverge during migration, get returns stale or partial retired data even though the new ordered assembly path now prefers the active copy. Consume scopes in active-then-retired order, using the retired item only as a fallback.

Useful? React with 👍 / 👎.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0352 · 740,822 in / 48,161 out · 57,571 cached (8%)  · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0207 · 408,638 in / 24,124 out · 39,567 cached (10%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0135 · 217,594 in / 15,230 out · 18,004 cached (8%)  · gpt-5.6-luna
tests:       $0.0003 · 27,352 in  / 3,069 out  · 0 cached (0%)       · glm-5.3-flash
description: $0.0002 · 27,301 in  / 1,411 out  · 0 cached (0%)       · glm-5.3-flash
e2e:         $0.0003 · 29,588 in  / 1,278 out  · 0 cached (0%)       · glm-5.3-flash

#[tokio::test]
async fn a_hosted_tenant_engine_reads_the_retired_user_segment_too() {
let (endpoint, state) = hosted_double().await;
let old = hosted_engine(&endpoint)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique confident

Skip the hosted tenant prefix when parsing tenant paths

This configures the hosted engine with a root containing the retired user segment, but the test does not establish that the hosted tenant prefix is removed before the path is parsed. A parser that treats the hosted prefix as part of the namespace could still make this test pass through the double's broad routing behavior. Add an assertion on the actual request path or configure a concrete hosted tenant prefix and verify the resulting Cortex path is relative to the tenant root.

[RULE] hosted-path-parsing ·

)))
.await
.unwrap();
assert_eq!(report.erased_scopes, 2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique likely

Include retired roots when collecting scopes for erasure

This only verifies the count and later checks that two expected paths appeared in the fake's erasure log. It does not exercise a retired root containing a scope that is absent from the new root's scope listing, so an implementation that collects erasure scopes only from the active root can still pass. Add a retired-only scope and assert that its exact path is erased.

[RULE] retired-root-erasure ·

reach: Some(Reach::exact("ws:main".parse().unwrap())),
..MetaFilter::default()
};
let exact = new.list(ListRequest::new(at_main, 10)).await.unwrap();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique likely

Use the root occurrence when validating the canonical path

The exact-reach assertion checks only the number of returned items. It does not verify the canonical namespace/path associated with each root occurrence, so a path-normalization bug can still return two items while assigning them to the wrong root or metadata. Assert the returned metadata/path for the retired and active items separately.

[RULE] root-path-validation ·

// Hosted, the backend may answer a tenant-root path behind the
// tenant's own `org:<id>`. `org` is never a namespace segment, so a
// leading `org:` segment is that prefix.
usize::from(prefixed && parts.first().is_some_and(|part| part.starts_with("org:")))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique likely

Parse the complete hosted tenant prefix

For the tenant layout, this skips only the first segment whenever it starts with org:. A hosted path whose backend prefix contains more than one segment, such as org:42/dept:shared/ws:main/app:conversations, is then parsed from dept:shared, producing the wrong namespace (or failing canonical validation) instead of parsing ws:main. The surrounding v3 parser already treats hosted prefixes as variable-length by locating the root occurrence; the empty-root case needs equivalent prefix handling based on the backend's actual tenant-prefix contract rather than dropping exactly one segment.

[RULE] incorrect-prefix-parsing ·

#[tokio::test]
async fn reads_merge_both_roots_by_item_id_and_writes_go_only_to_the_new_one() {
let (endpoint, state) = direct_double().await;
let shared = learning("held in both", "ws:main");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Select a deterministic source for duplicate items

Both copies of the duplicate item are byte-for-byte identical, so the assertions only prove that one item is returned, not which root supplied its metadata or other conflicting fields. If the roots contain the same id with different metadata, an unordered merge could still pass these tests while returning nondeterministic data. Store distinct metadata in the two copies and assert the documented precedence explicitly.

[RULE] insufficient-test-oracle ·

// The transitional write adds scopes below the new root only.
let added: Vec<_> = written.difference(&before).collect();
assert!(!added.is_empty(), "{written:?}");
assert!(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Assert that transition writes never target the retired root

Checking that added scopes start with org:42 only catches writes recorded as scope events and does not prove that no transition operation targets user:42; a malformed or separately logged write can evade this assertion. Assert directly over all write requests/registrations that no target path equals or is below the retired root, while retaining the positive new-root assertions.

[RULE] write-target-validation ·

direct_engine(endpoint)
.with_scope_root("org:42", Some("user:42"))
.unwrap()
.with_retired_root("user:42")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique likely

Show a complete configuration for the retired user root

The test only supplies the retired root string and relies on the helper's defaults for its ownership and routing configuration. That leaves the configuration contract under-specified: an implementation can ignore the retired-root owner or derive it incorrectly while still satisfying the double. Configure and assert the complete retired-root settings, including the root's owner/actor and the resulting registration.

[RULE] retired-root-configuration ·

Comment on lines +406 to +410
let own = if root.is_empty() {
parts[start..].join("/")
} else {
parts[start - root.split('/').count()..].join("/")
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high security confident

Use the matched root occurrence when validating the canonical path

For a hosted path such as org:tenant/user:42/app:conversations, start points at the matched user:42 occurrence. Subtracting the root length moves the slice back to the tenant prefix, so it can never equal render(root, ...), which starts at user:42. Consequently hosted reads, listing, and erasure parsing for non-empty scope roots are rejected even when the path is canonical. Compare from start itself so the canonical validation covers the matched root occurrence.

Suggested change
let own = if root.is_empty() {
parts[start..].join("/")
} else {
parts[start - root.split('/').count()..].join("/")
};
let own = parts[start..].join("/");

[RULE] canonical-path-validation ·

.collect::<HashSet<_>>()
.into_iter()
.collect();
let active = KindScope::new(&self.layout, scope.namespace.clone(), scope.kind);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high security confident

Only shadow retired items after confirming the active copy is complete

This treats any event found in the active scope as sufficient to shadow the retired copy. During a partial transition, the active scope can contain only some events for a conversation or chunked document while the retired scope still contains the complete item. The listing then suppresses the retired occurrence, and resolve attempts assembly from the incomplete active events and returns no item, losing it from listings and exports. Check that the active events rebuild successfully as a whole before marking the id shadowed; otherwise retain the retired occurrence as the fallback.

[RULE] incomplete-fallback ·

..EngineSettings::default()
};
assert!(build_engine("cortexdb", &direct, key()).is_ok());
let legacy = EngineSettings {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium tests likely

Drive the tenant-root and retired-root settings against a live CortexDB

Still standing from the earlier review: all the new behaviour is exercised against in-process doubles (direct_double, hosted_double). The wire-level details this change depends on — that the hosted backend really bounds an unprefixed scope listing to the caller's tenant, refuses an empty prefix=, and pins org:<id> — are asserted only against the doubles' own assumptions. An integration run against a real CortexDB/TinyHumans endpoint would pin the contract the doubles encode; until then the doubles could agree with the client and disagree with the backend. No code change suggested; this is a coverage gap, not a known defect.

[RULE] test-coverage ·

@senamakel
senamakel merged commit 0260ec6 into main Oct 8, 2026
40 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant