Skip to content

feat: stand up the TinyMemory workspace — contract, driver registry, shared mandatory families, TinyCortex adapter - #1

Merged
senamakel merged 35 commits into
mainfrom
tinymemory-vendor
Aug 10, 2026
Merged

feat: stand up the TinyMemory workspace — contract, driver registry, shared mandatory families, TinyCortex adapter#1
senamakel merged 35 commits into
mainfrom
tinymemory-vendor

Conversation

@senamakel

@senamakel senamakel commented Aug 10, 2026

Copy link
Copy Markdown
Member

What

Turns the rust-template scaffold into the real TinyMemory workspace: the engine-neutral memory layer that OpenHuman (and any other host) performs memory operations through, with TinyCortex as the default engine behind it rather than as the thing hosts talk to directly.

api/                 tinymemory-api — the contract
src/registry/        driver admission
src/mandatory/       the three mandatory families, shared across engines
adapters/tinycortex/ the TinyCortex seam
vendor/tinycortex/   the engine, pinned as a submodule

The pieces

api/ — the contract, moved from tinycortex-api. Every type name, serde representation and enum wire string is byte-identical: MemoryTaint's serde form and chunks::chunk_id's derivation are persisted on disk, and MemoryTaint::from_db_str is security-critical and fails closed. Only the crate identifier and the prose claiming the contract belongs to one engine changed. A contract named after one engine is not a contract a second engine can enter through.

src/registry/ — driver admission, lifted out of OpenHuman's memory/binding.rs. These are the rules with the same correct answer for every host:

  • a built-in id's class is fixed, and an explicit class line may confirm it but never override it. Without that, driver = "null" plus class = "embedded" builds the real engine and persists memory under the id documented as /dev/null;
  • an unknown id is refused, not guessed, so a typo surfaces in status instead of silently running the default engine under an invented name;
  • an external driver is fail-closed on trust, and the transport refusal carries a distinct message so a trust test cannot pass for the wrong reason.

Refusals are operator-facing and logged, so DriverEntry deliberately carries only class and trust_state — never an endpoint or a credential reference. A refusal structurally cannot leak a secret.

src/mandatory/ — the piece with the most leverage. MemoryCore, MemoryRecall and MemoryPortability are supertraits of MemoryProvider, so every driver implements them. For a backend that already implements Memory most of that is mechanical — and the parts that are not are the parts each backend gets wrong the same way. They live here once, each with a test that reproduces the trap:

Trap What goes wrong
store via store_with_taint, never store store hard-codes Internal, laundering externally-sourced content into internal-trust content
list(None, ..) spans every namespace a backend normalising None to the global namespace returns one namespace and calls it "everything"
a scoped recall is refused ignoring it leaks invisibly; post-filtering lets limit be eaten by rows the caller may not see
import keeps each record's taint re-stamping upgrades the trust of every synced record in a restore

MemoryTraitProvider makes any Memory backend a bindable driver advertising exactly the mandatory three, so its optional surface is absent rather than present-and-failing. audit_provider pins the advertised set against the reachable accessors.

adapters/tinycortex/ — the seam. TinyCortex's contract and TinyMemory's describe the same values but are distinct crates, so something must convert; one small audited crate beats a conversion scattered across a host. Every conversion destructures exhaustively rather than using ..: two contracts that may drift will drift, and a lenient conversion drops the new field silently, whereas a full destructuring pattern is a compile error naming it. The enums match every variant for the same reason.

Notes for review

  • Adapters name their engine by version requirement, not path. A host that already pins its own TinyCortex checkout unifies onto one copy through its [patch.crates-io]; the workspace root patches it to vendor/ for standalone builds. A path dep would hand a host two engines with two incompatible Memory traits.
  • Policy is deliberately absent. Tier enforcement, scope predicates, taint stamping, redaction, egress and audit stay in a decorator the host owns. A driver that could be swapped for one that skips enforcement is the entire reason that layer exists.
  • Lints are scoped to the facade package; api/ is held byte-identical rather than reformatted to a stricter lint set.

Testing

cargo test --workspace — 183 tests + doctests green. cargo clippy --workspace --all-targets -- -D warnings clean. cargo fmt --all --check clean.

Contract purity is guarded in the forward form (cargo tree -p tinymemory-api …), since cargo tree -i discards the -p scope and exits 0 even when the scoped crate is the culprit.

Summary by CodeRabbit

  • New Features

    • Introduced TinyMemory as an engine-neutral memory platform with a stable contract for storage, recall, portability, health, capabilities, and provider integration.
    • Added capability negotiation, auditing, version compatibility, source tracking, memory goals, tool-scoped memory, hierarchical trees, and maintenance operations.
    • Added TinyCortex integration for storage, recall, listing, deletion, health checks, and data portability.
    • Added driver registration, validation, trust handling, and safe fallback reporting.
    • Added a reference null provider and comprehensive documentation.
  • Documentation

    • Replaced template documentation with TinyMemory architecture, layout, and development guidance.
    • Removed obsolete example plans, roadmap content, and template examples.

senamakel and others added 20 commits August 10, 2026 16:58
…apabilities_tests.rs,api/src/ch

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed an unused import statement from the API library's root module to clean up the code and eliminate a compiler warning.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed an unused import statement from the API library's root module to clean up the code and eliminate a compiler warning.

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

The project has been renamed from the generic "rust-template" to "tinymemory" and restructured as a workspace with the root package and a new `api` sub-crate. The root package now depends on `tinymemory-api` via a path dependency, making the contract types available through a single dependency. All metadata, descriptions, and documentation links have been updated to reflect the new project identity, and the license has been changed from GPL-3.0-only to MIT.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When looking up a class by name in the registry, the code now returns an appropriate error instead of panicking if the class is not found. This ensures the registry behaves predictably for missing entries rather than crashing the caller.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the test helper function to return an empty string instead of panicking when the package name is empty, ensuring robustness against edge cases in test data.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…driver registry

Converts the rust-template scaffold into a Cargo workspace and moves the
engine-neutral memory layer into it.

- api/: the contract, moved verbatim from tinycortex-api. Every type name,
  serde representation and enum wire string is byte-identical -- MemoryTaint's
  serde form and chunks::chunk_id's derivation are persisted on disk, and
  MemoryTaint::from_db_str fails closed. Only the crate identifier and the
  prose claiming the contract belongs to one engine changed.
- src/registry/: driver admission, lifted from OpenHuman's memory/binding.rs.
  Reserved ids, the confirm-never-override rule for a built-in id's class, and
  the fail-closed external-driver trust gate. Pure, so the rules stay testable
  without booting a host.
- src/lib.rs: re-exports the contract wholesale so a host takes one dependency
  and tinymemory::provider::MemoryProvider is tinymemory_api's same type.

Policy stays host-side by design: a driver that could be swapped for one that
skips enforcement is exactly why the guard lives in the host.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
The mandatory module now correctly processes empty input instead of panicking or returning an error. This change ensures that the function gracefully handles edge cases where no data is provided, improving robustness and user experience.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a fallback in the mandatory module to handle cases where no provider is configured, preventing a panic and returning a clear error instead. This improves robustness when the system is used without a mandatory provider setup.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the test assertion to properly validate the mandatory field behavior, ensuring the test correctly reflects the expected validation logic. The previous assertion was incorrectly checking for an error case that did not match the actual implementation.

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

MemoryCore, MemoryRecall and MemoryPortability are supertraits of
MemoryProvider, so every driver must implement them. For a backend that
already implements the Memory storage trait almost all of that is mechanical
-- and the parts that are not are the parts each backend gets wrong the same
way. They live here once now, with a test that reproduces each trap:

- store routes through store_with_taint, never store, which hard-codes
  Internal and would launder externally-sourced content into internal-trust
  content;
- list(None, ..) spans every namespace, because a backend that normalises a
  None namespace to the global one returns one namespace and calls it
  everything;
- a scoped recall is refused rather than silently answered in full or
  post-filtered, both of which leak;
- import persists the taint each record carries instead of re-stamping it.

MemoryTraitProvider makes any Memory backend a bindable driver advertising
exactly the mandatory three, so its optional surface is absent rather than
present-and-failing -- audit_provider pins the two halves together.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
This change introduces the tinycortex library as a vendored dependency by adding it as a git submodule and updating the Cargo.toml in the adapters directory to reference it, enabling the project to use its functionality without relying on an external package registry.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When converting a TinyCortex configuration, the code now gracefully handles the case where the cortex version field is absent by defaulting to a sensible fallback value instead of failing with an error. This ensures backward compatibility with older configuration files that do not explicitly specify the version.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Fix a bug where memory read and write operations could fail or produce incorrect results when the address was not aligned to the natural boundary of the data type. The change adds explicit alignment handling to ensure correct behavior for all memory access patterns.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the project dependencies to their latest compatible versions, ensuring the build remains reproducible and benefiting from recent bug fixes and improvements in the dependency tree.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…as a submodule

TinyCortex now lives under vendor/ as a nested submodule, and
adapters/tinycortex is the single seam between its contract and TinyMemory's.
The two describe the same values but are distinct crates, so something has to
convert; one small audited crate is far better than a conversion scattered
across every call site in a host.

Every conversion destructures exhaustively rather than using  or building
onto a Default. Two contracts that may drift will drift, and a lenient
conversion drops the new field silently -- a full destructuring pattern turns
that into a compile error naming the field. The enums match every variant for
the same reason: a new category or a third taint level cannot fall into a
catch-all and be quietly downgraded.

TinycortexMemory overrides store_with_taint rather than inheriting the trait
default, which drops the taint -- that default is how externally-sourced
content would get laundered into internal-trust content. Tests cover it in
both directions and end-to-end across two engines via export/import.

The adapter names tinycortex by version requirement, not path, so a host that
already pins its own checkout unifies onto one copy through its patch table;
the workspace root patches it to vendor/ for standalone builds. A path dep
would give a host two engines with two incompatible Memory traits.

Also finishes the template conversion: README, AGENTS.md, issue templates.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@senamakel, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c4a148ff-46a9-41e2-b15c-fee3351309f9

📥 Commits

Reviewing files that changed from the base of the PR and between c58ca49 and 4232c35.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • Cargo.toml
  • adapters/tinycortex/src/convert.rs
  • adapters/tinycortex/src/lib.rs
  • adapters/tinycortex/src/memory_test.rs
  • api/src/chunks.rs
  • api/src/chunks_tests.rs
  • api/src/traits.rs
  • api/src/tree.rs
  • api/src/types.rs
  • rust_out
  • src/mandatory/mod.rs
  • src/registry/class.rs
  • src/registry/mod.rs
  • src/registry/test.rs
  • tmp
📝 Walkthrough

Walkthrough

TinyMemory replaces the Rust template with an engine-neutral Cargo workspace. It adds public API contracts, provider capabilities, mandatory storage and portability composition, driver admission, a TinyCortex adapter, tests, documentation, CI updates, and local submodule integration.

Changes

TinyMemory workspace

Layer / File(s) Summary
Workspace and repository setup
.github/*, .gitmodules, AGENTS.md, Cargo.toml, README.md, clippy.toml, api/Cargo.toml, adapters/tinycortex/Cargo.toml
The repository now defines the TinyMemory workspace, local engine patches, recursive submodule checkout, project guidance, and package metadata.

Engine-neutral API

Layer / File(s) Summary
API contracts and domain types
api/src/*
The API crate defines capabilities, memory types, chunks, recall options, health, errors, goals, tool memory, trees, versioning, and storage traits.
Provider contracts and reference provider
api/src/provider/*, api/src/null*
Provider traits cover mandatory and optional capability families. Auditing compares advertised capabilities with reachable trait objects. The null provider implements mandatory operations and typed unsupported responses.

Runtime composition

Layer / File(s) Summary
Mandatory provider composition
src/lib.rs, src/mandatory/*
The root crate re-exports the API and composes storage, recall, namespace listing, cursor-based export, batch import, health, and provider behavior.
Driver admission registry
src/registry/*
The registry parses driver classes, reserves built-in identifiers, checks trust for external drivers, rejects unsupported transport, and returns structured fallback reasons.

TinyCortex integration

Layer / File(s) Summary
TinyCortex adapter
adapters/tinycortex/*, vendor/tinycortex
The adapter converts TinyCortex and TinyMemory values, delegates the Memory trait, exposes a provider binding, and tests taint, recall, namespaces, portability, and debug output.

Estimated code review effort: 5 (Critical) | ~120 minutes

Poem

I’m a rabbit with bytes in my burrow tonight,
TinyMemory contracts now fit just right.
Taints hop safely from store into stream,
Drivers choose paths like a well-planned dream.
TinyCortex joins with a whisker-bright cheer,
And tests guard the carrots throughout the year.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the workspace conversion and the main contract, registry, mandatory-family, and TinyCortex adapter changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 15

🧹 Nitpick comments (11)
README.md (1)

74-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the documented validation commands match CI.

This block omits cargo build --all-targets --all-features, and the test command does not enable all features. Use the same validation commands as .github/workflows/ci.yml Lines 37-50.

Proposed update
 cargo fmt --all -- --check
-cargo test --workspace
-cargo clippy --workspace --all-targets -- -D warnings
+cargo clippy --all-targets --all-features -- -D warnings
+cargo build --all-targets --all-features
+cargo test --all-features

Based on learnings, run all four contract commands from the repository root: cargo fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings, cargo build --all-targets --all-features, and cargo test --all-features.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 74 - 79, Update the README validation command block
to match CI’s four repository-root commands: keep cargo fmt, add --all-features
to cargo clippy, add cargo build --all-targets --all-features, and run cargo
test with --all-features.

Source: Learnings

adapters/tinycortex/src/memory_test.rs (1)

9-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an end-to-end recall test through the adapter.

This file covers store, get, list, forget, namespaces, and export/import against the real engine, but it never calls recall. MemoryRecall is not imported. The adapter's recall at adapters/tinycortex/src/memory.rs Lines 105-118 is the least mechanical method in the seam: it chains two local bindings and takes a borrow through (&engine_owned).into() so the owned conversion outlives the engine's borrowing RecallOpts.

adapters/tinycortex/src/convert_test.rs Lines 143-160 test recall_opts_to_tinycortex in isolation. Nothing proves the filters reach the engine and narrow a real query. Add a test that stores entries in two namespaces, then recalls with OwnedRecallOpts { namespace: Some(..), .. } and asserts the result set is narrowed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/tinycortex/src/memory_test.rs` around lines 9 - 18, Add an
end-to-end recall test in the existing memory adapter tests, importing
MemoryRecall and using the real engine from engine(). Store entries across two
namespaces, invoke recall with OwnedRecallOpts containing one namespace, and
assert only entries from that namespace are returned, exercising the adapter’s
recall path and filter propagation.
src/mandatory/mod.rs (1)

365-377: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log the partial import counts before the batch aborts.

A backend write failure returns Err at Line 375. The records already written stay in the target store, and outcome is dropped, so an operator cannot tell how far the restore got. The trait signature fixes the return type, so the counts can only be preserved in a log line.

♻️ Proposed change to keep the partial counts diagnosable
-        memory
+        if let Err(error) = memory
             .store_with_taint(
                 &entry.namespace,
                 &entry.key,
                 &entry.content,
                 entry.category,
                 entry.session_id.as_deref(),
                 record.taint,
             )
             .await
-            .map_err(engine_error)?;
+        {
+            log::error!(
+                "[tinymemory:mandatory] import aborted after imported={} failed={}",
+                outcome.imported,
+                outcome.failed
+            );
+            return Err(engine_error(error));
+        }
         outcome.imported = outcome.imported.saturating_add(1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mandatory/mod.rs` around lines 365 - 377, Before propagating the error
from the store_with_taint call in the import loop, log the current partial
import counts from outcome so successfully written records remain diagnosable
when a batch aborts. Preserve the existing engine_error mapping and return
behavior after emitting the log.
src/mandatory/test.rs (1)

73-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a Memory double that fails a write, and cover the import_records abort path.

VecMemory::store_with_taint always returns Ok, so no test reaches the abort in import_records at src/mandatory/mod.rs Line 375. That path is a documented contract: a backend write failure must abort the batch instead of reporting a partial restore as a success. The coding guidelines require tests to cover failure paths.

Add a fail_writes flag to VecMemory, return an anyhow error from store_with_taint when it is set, and assert that import_records returns MemoryError::Other.

As per coding guidelines: "cover failure paths and test every new error variant".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mandatory/test.rs` around lines 73 - 98, Extend VecMemory with a
fail_writes flag and make store_with_taint return an anyhow error when the flag
is enabled. Add an import_records test that configures this failing double,
triggers a backend write failure, and asserts the result is MemoryError::Other,
covering the batch-abort path without changing successful imports.

Source: Coding guidelines

api/src/tool_memory_tests.rs (1)

73-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the UUID encoding test deterministic.

Lines 73-79 assert that two random v4 values differ. A UUID collision can fail CI without a product regression. Extract the UUID-to-ID encoding into a private helper and test that helper with fixed UUID bytes. Keep generate_id covered by shape validation.

As per coding guidelines, “Tests must be deterministic and independent of network, wall-clock time, and execution order.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/tool_memory_tests.rs` around lines 73 - 79, Update
rule_generate_id_produces_unique_values so it no longer asserts inequality
between random generate_id results. Extract the UUID-to-ID encoding from
ToolMemoryRule::generate_id into a private helper, test that helper using fixed
UUID bytes and expected deterministic output, and retain generate_id coverage
through the existing prefix and character-shape assertions.

Source: Coding guidelines

api/src/provider/mod.rs (1)

55-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider narrowing the submodule visibility to a single public path.

Every type here has two public paths: provider::mandatory::MemoryCore and provider::MemoryCore. A third-party driver can then depend on either, which doubles the surface you must keep stable. If the flat re-exports are the intended path, change the submodules to pub(crate) mod and keep the pub use lines.

♻️ Proposed change
-pub mod audit;
-pub mod content;
-pub mod driver;
-pub mod knowledge;
-pub mod mandatory;
-pub mod records;
-pub mod types;
+pub(crate) mod audit;
+pub(crate) mod content;
+pub(crate) mod driver;
+pub(crate) mod knowledge;
+pub(crate) mod mandatory;
+pub(crate) mod records;
+pub(crate) mod types;

If you intend both paths, no change is needed.

As per coding guidelines: "Keep the public surface minimal: default to private and export deliberately from src/lib.rs".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/provider/mod.rs` around lines 55 - 73, Keep the flat re-export paths
in provider, but narrow the module declarations for audit, content, driver,
knowledge, mandatory, records, and types from public to crate-visible
(`pub(crate) mod`). Preserve the existing `pub use` exports so consumers
continue using paths such as `provider::MemoryCore` without exposing parallel
submodule paths.

Source: Coding guidelines

api/src/chunks.rs (1)

306-310: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

The comment does not describe what the code guards.

text.chars().count() returns usize. The as u32 cast truncates by wrapping before saturating_add runs, so saturating_add guards only the +3, not the input length. Use the same accumulate-in-u64-then-clamp form that conservative_token_estimate already uses at Line 334.

♻️ Proposed change
 /// Approximate token count (GPT-family heuristic: 1 token ≈ 4 chars).
 pub fn approx_token_count(text: &str) -> u32 {
-    // saturating_add guards against absurdly long inputs
-    let chars = text.chars().count() as u32;
-    chars.saturating_add(3) / 4
+    // Count in u64 and clamp, so an absurdly long input saturates instead of
+    // wrapping through an `as u32` cast.
+    let chars = text.chars().count() as u64;
+    let tokens = chars.div_ceil(4);
+    tokens.min(u64::from(u32::MAX)) as u32
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/chunks.rs` around lines 306 - 310, Update approx_token_count to avoid
casting the character count directly to u32 before addition; accumulate in u64
and clamp the final result to u32 using the same pattern as
conservative_token_estimate. Revise the nearby comment to accurately describe
the overflow protection.
api/src/goals_tests.rs (1)

15-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the empty-id and empty-text rejection branch.

GoalsDoc::parse rejects an item when the id or the text is empty (api/src/goals.rs lines 75-77). No test exercises that branch. The coding guidelines require tests to cover failure paths.

💚 Proposed test
+#[test]
+fn parse_rejects_an_empty_id_or_an_empty_text() {
+    let doc = GoalsDoc::parse("- [] has no id\n- [g2]\n- [g3] kept\n");
+    assert_eq!(doc.items.len(), 1);
+    assert_eq!(doc.items[0].id, "g3");
+}

As per coding guidelines: "cover failure paths and test every new error variant".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/goals_tests.rs` around lines 15 - 22, Extend the goals parsing tests
around parse_ignores_non_item_lines with a case containing item syntax whose id
or text is empty, and assert that GoalsDoc::parse rejects or omits it according
to its existing behavior. Ensure the test exercises the empty-id/empty-text
validation branch in GoalsDoc::parse without changing production code.

Source: Coding guidelines

api/src/lib.rs (1)

1-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a short runnable example to the crate docs.

The crate documentation gives an overview and a module map, but no runnable example. A doctest here also proves that the primary entry points compile against the published surface. Add a small example that builds a driver and reads its capabilities and health.

//! ```
//! use tinymemory_api::{null::NullMemoryProvider, provider::MemoryProvider};
//!
//! let driver = NullMemoryProvider::new();
//! assert_eq!(driver.driver_id(), "null");
//! assert_eq!(driver.capabilities(), tinymemory_api::capabilities::Capabilities::mandatory());
//! ```

As per coding guidelines, "src/lib.rs must provide a crate-level overview, primary entry points, and a short runnable example."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/lib.rs` around lines 1 - 60, Add a runnable doctest to the
crate-level documentation in src/lib.rs, near the overview or module map,
importing NullMemoryProvider and MemoryProvider. Construct the null driver,
assert its driver_id is "null", and verify its capabilities equal
Capabilities::mandatory(); also read the driver’s health as requested by the
comment, using only the published API surface.

Source: Coding guidelines

api/src/provider/types.rs (1)

98-102: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid the per-candidate allocation in allows_source_id.

The closure builds a new String for every allowed id on every call. Recall paths evaluate this predicate per row, so the allocation count scales with rows × allowed ids. strip_prefix gives the same equality-or-prefix semantics with no allocation.

♻️ Proposed refactor
     pub fn allows_source_id(&self, source_id: &str) -> bool {
-        self.allow.iter().any(|allowed| {
-            source_id == allowed || source_id.starts_with(&format!("mem_src:{allowed}:"))
-        })
+        let attributed = source_id.strip_prefix("mem_src:");
+        self.allow.iter().any(|allowed| {
+            source_id == allowed
+                || attributed.is_some_and(|rest| {
+                    rest.strip_prefix(allowed.as_str())
+                        .is_some_and(|tail| tail.starts_with(':'))
+                })
+        })
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/provider/types.rs` around lines 98 - 102, Update
Provider::allows_source_id to avoid constructing a formatted String for each
allowed id; preserve the exact and mem_src:{allowed}: prefix matching semantics
by using strip_prefix or equivalent allocation-free checks within the iterator.
api/src/traits.rs (1)

89-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add # Errors sections to the remaining fallible methods.

store documents its failure mode. The other fallible methods do not: store_with_taint, recall, recall_relevant_by_vector, get, list, forget, namespace_summaries, and count. Each returns anyhow::Result. Add a short # Errors section to each one. The module note that a returned Err is opaque belongs in those sections too, so implementors read it at the method they are writing.

As per coding guidelines, "Document a # Errors section on every public fallible function and a # Panics section on anything that can panic."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/traits.rs` around lines 89 - 146, Add concise Rustdoc # Errors
sections to the public fallible methods store_with_taint, recall,
recall_relevant_by_vector, get, list, forget, namespace_summaries, and count.
Describe that Err indicates an opaque backend failure, while preserving each
method’s existing success and not-found semantics; do not alter behavior or
signatures.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/tinycortex/src/convert.rs`:
- Around line 21-28: Update the rustdoc link in the module documentation for
taint conversion to reference the in-scope `tm::MemoryTaint` symbol instead of
the unresolved bare `MemoryTaint` link, leaving the surrounding security
guidance unchanged.

In `@adapters/tinycortex/src/memory_test.rs`:
- Around line 183-196: Bound the paging loop in the test around export_page with
a page counter, following the existing pattern in mandatory test paging logic.
Increment the counter per iteration and assert or fail once a reasonable maximum
is exceeded, while preserving normal cursor advancement and the final
records.len() assertion.

In `@api/Cargo.toml`:
- Line 4: Update the edition setting in api/Cargo.toml to edition = "2021" so it
matches the workspace and sibling crates; do not use Rust 2024 unless explicitly
approved.

In `@api/src/capabilities.rs`:
- Around line 395-397: Move module-local tests into dedicated test.rs files and
wire each owner with #[cfg(test)] mod test;. In api/src/capabilities.rs lines
395-397 and api/src/error.rs lines 102-104 and api/src/version.rs lines 89-91,
replace path-based wiring; move capabilities_tests.rs to
api/src/capabilities/test.rs, chunks_tests.rs to api/src/chunks/test.rs,
error_tests.rs to api/src/error/test.rs, tool_memory_tests.rs to
api/src/tool_memory/test.rs, tree_tests.rs to api/src/tree/test.rs,
types_tests.rs to api/src/types/test.rs, version_tests.rs to
api/src/version/test.rs, and provider/types_tests.rs to
api/src/provider/types/test.rs, updating each owning module accordingly.

In `@api/src/chunks_tests.rs`:
- Around line 7-12: Update chunk_id_is_deterministic to assert the fixed
historical digest for the specified input, rather than only comparing two newly
computed values. Retain the length assertion if useful, and use the pre-move
contract’s exact 32-character golden value to detect changes to field ordering,
separators, hashing, or truncation.

In `@api/src/chunks.rs`:
- Around line 40-48: Add a Rustdoc `# Errors` section to both public `parse`
functions, `SourceKind::parse` and `DataSource::parse`, describing that they
return an error when the input string does not match a supported
source/data-source kind. Preserve their existing parsing behavior.

In `@api/src/traits.rs`:
- Around line 58-79: Update the default implementation of the MemoryStore
trait’s store_with_taint method to fail closed for MemoryTaint::ExternalSync and
other external taints instead of delegating to store and losing provenance;
return an error unless the backend overrides this method, while preserving the
existing store delegation only for MemoryTaint::Internal if appropriate.

In `@api/src/tree.rs`:
- Around line 190-201: Update node_id_to_path to validate node_id before
constructing any PathBuf, accepting only "root" or slash-separated numeric time
components. Reject traversal segments, absolute paths, and any other malformed
identifiers using the function’s existing error-handling contract, and perform
level_from_node_id only after validation.
- Around line 145-148: Update estimate_tokens so converting text.len() to u32
saturates at u32::MAX instead of wrapping, then retain the existing div_ceil(4)
calculation.

In `@api/src/types.rs`:
- Around line 135-165: Update MemoryCategory::from_str so the exact value
"custom:" deserializes to Custom(String::new()) instead of falling through to
the generic custom-value branch. Preserve the existing prefixed parsing for
non-empty custom names and the built-in category handling.

In `@Cargo.toml`:
- Line 11: Remove the manual Cargo.toml version change and restore the package
version to its pre-PR value, leaving version updates under release-workflow
control. Do not modify the version field elsewhere unless the release workflow
itself needs the initial transition.

In `@src/mandatory/mod.rs`:
- Around line 22-27: Remove the redundant explicit intra-doc link targets while
preserving the shorthand links: in src/mandatory/mod.rs lines 22-27, update the
GLOBAL_NAMESPACE link because it is imported; in adapters/tinycortex/src/lib.rs
lines 16-18, update the MemoryTraitProvider link because it is imported. Keep
the other explicit links in src/mandatory/mod.rs unchanged.

In `@src/registry/class.rs`:
- Around line 58-64: Replace the String error returned by DriverClass::parse
with a documented DriverClassParseError type, including an Unknown { raw: String
} variant for unrecognized input. Update parse and the DriverClass FromStr
implementation to return this typed error and set Self::Err =
DriverClassParseError, preserving successful parsing through Self::ALL.

In `@src/registry/mod.rs`:
- Around line 173-175: Update DriverRegistry::with_reserved in
src/registry/mod.rs:173-175 to prevent overwriting an existing reservation,
either preserving the original class or returning the established typed
registration-conflict error. First add failing reproducer coverage in
src/registry/test.rs:188-200 for re-reserving NULL_DRIVER_ID and
TINYCORTEX_DRIVER_ID with different DriverClass values, then implement the
smallest fix that preserves the fixed-class invariant.
- Around line 229-230: Update the class parsing branch in
src/registry/mod.rs:229-230 to map DriverClass::parse errors to a generic
refusal reason instead of passing the error through refuse(&e), ensuring invalid
raw input is never published. Add a regression test in
src/registry/test.rs:215-232 using a secret-shaped class value such as
https://user:token@host and assert that the resulting FallbackReason.reason does
not contain that input.

---

Nitpick comments:
In `@adapters/tinycortex/src/memory_test.rs`:
- Around line 9-18: Add an end-to-end recall test in the existing memory adapter
tests, importing MemoryRecall and using the real engine from engine(). Store
entries across two namespaces, invoke recall with OwnedRecallOpts containing one
namespace, and assert only entries from that namespace are returned, exercising
the adapter’s recall path and filter propagation.

In `@api/src/chunks.rs`:
- Around line 306-310: Update approx_token_count to avoid casting the character
count directly to u32 before addition; accumulate in u64 and clamp the final
result to u32 using the same pattern as conservative_token_estimate. Revise the
nearby comment to accurately describe the overflow protection.

In `@api/src/goals_tests.rs`:
- Around line 15-22: Extend the goals parsing tests around
parse_ignores_non_item_lines with a case containing item syntax whose id or text
is empty, and assert that GoalsDoc::parse rejects or omits it according to its
existing behavior. Ensure the test exercises the empty-id/empty-text validation
branch in GoalsDoc::parse without changing production code.

In `@api/src/lib.rs`:
- Around line 1-60: Add a runnable doctest to the crate-level documentation in
src/lib.rs, near the overview or module map, importing NullMemoryProvider and
MemoryProvider. Construct the null driver, assert its driver_id is "null", and
verify its capabilities equal Capabilities::mandatory(); also read the driver’s
health as requested by the comment, using only the published API surface.

In `@api/src/provider/mod.rs`:
- Around line 55-73: Keep the flat re-export paths in provider, but narrow the
module declarations for audit, content, driver, knowledge, mandatory, records,
and types from public to crate-visible (`pub(crate) mod`). Preserve the existing
`pub use` exports so consumers continue using paths such as
`provider::MemoryCore` without exposing parallel submodule paths.

In `@api/src/provider/types.rs`:
- Around line 98-102: Update Provider::allows_source_id to avoid constructing a
formatted String for each allowed id; preserve the exact and mem_src:{allowed}:
prefix matching semantics by using strip_prefix or equivalent allocation-free
checks within the iterator.

In `@api/src/tool_memory_tests.rs`:
- Around line 73-79: Update rule_generate_id_produces_unique_values so it no
longer asserts inequality between random generate_id results. Extract the
UUID-to-ID encoding from ToolMemoryRule::generate_id into a private helper, test
that helper using fixed UUID bytes and expected deterministic output, and retain
generate_id coverage through the existing prefix and character-shape assertions.

In `@api/src/traits.rs`:
- Around line 89-146: Add concise Rustdoc # Errors sections to the public
fallible methods store_with_taint, recall, recall_relevant_by_vector, get, list,
forget, namespace_summaries, and count. Describe that Err indicates an opaque
backend failure, while preserving each method’s existing success and not-found
semantics; do not alter behavior or signatures.

In `@README.md`:
- Around line 74-79: Update the README validation command block to match CI’s
four repository-root commands: keep cargo fmt, add --all-features to cargo
clippy, add cargo build --all-targets --all-features, and run cargo test with
--all-features.

In `@src/mandatory/mod.rs`:
- Around line 365-377: Before propagating the error from the store_with_taint
call in the import loop, log the current partial import counts from outcome so
successfully written records remain diagnosable when a batch aborts. Preserve
the existing engine_error mapping and return behavior after emitting the log.

In `@src/mandatory/test.rs`:
- Around line 73-98: Extend VecMemory with a fail_writes flag and make
store_with_taint return an anyhow error when the flag is enabled. Add an
import_records test that configures this failing double, triggers a backend
write failure, and asserts the result is MemoryError::Other, covering the
batch-abort path without changing successful imports.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7dacc9ed-05df-48d1-b596-91136bd34b19

📥 Commits

Reviewing files that changed from the base of the PR and between b3ddba4 and c58ca49.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (65)
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/workflows/ci.yml
  • .gitmodules
  • AGENTS.md
  • Cargo.toml
  • README.md
  • ROADMAP.md
  • adapters/tinycortex/Cargo.toml
  • adapters/tinycortex/src/convert.rs
  • adapters/tinycortex/src/convert_test.rs
  • adapters/tinycortex/src/lib.rs
  • adapters/tinycortex/src/memory.rs
  • adapters/tinycortex/src/memory_test.rs
  • api/Cargo.toml
  • api/src/capabilities.rs
  • api/src/capabilities_tests.rs
  • api/src/chunks.rs
  • api/src/chunks_tests.rs
  • api/src/error.rs
  • api/src/error_tests.rs
  • api/src/goals.rs
  • api/src/goals_tests.rs
  • api/src/health.rs
  • api/src/health_tests.rs
  • api/src/lib.rs
  • api/src/null.rs
  • api/src/null_tests.rs
  • api/src/provider/audit.rs
  • api/src/provider/audit_tests.rs
  • api/src/provider/content.rs
  • api/src/provider/driver.rs
  • api/src/provider/knowledge.rs
  • api/src/provider/mandatory.rs
  • api/src/provider/mod.rs
  • api/src/provider/records.rs
  • api/src/provider/types.rs
  • api/src/provider/types_tests.rs
  • api/src/recall.rs
  • api/src/recall_tests.rs
  • api/src/tool_memory.rs
  • api/src/tool_memory_tests.rs
  • api/src/traits.rs
  • api/src/tree.rs
  • api/src/tree_tests.rs
  • api/src/types.rs
  • api/src/types_tests.rs
  • api/src/version.rs
  • api/src/version_tests.rs
  • clippy.toml
  • docs/plans/example-retry-policy.md
  • docs/specs/example-retry-policy.md
  • examples/basic.rs
  • src/error/mod.rs
  • src/error/test.rs
  • src/greeting/mod.rs
  • src/greeting/test.rs
  • src/lib.rs
  • src/mandatory/mod.rs
  • src/mandatory/provider.rs
  • src/mandatory/test.rs
  • src/registry/class.rs
  • src/registry/mod.rs
  • src/registry/test.rs
  • tests/public_api.rs
  • vendor/tinycortex
💤 Files with no reviewable changes (9)
  • examples/basic.rs
  • src/error/test.rs
  • ROADMAP.md
  • src/greeting/mod.rs
  • tests/public_api.rs
  • docs/specs/example-retry-policy.md
  • docs/plans/example-retry-policy.md
  • src/greeting/test.rs
  • src/error/mod.rs

Comment thread adapters/tinycortex/src/convert.rs
Comment thread adapters/tinycortex/src/memory_test.rs
Comment thread api/Cargo.toml
Comment thread api/src/capabilities.rs
Comment thread api/src/chunks_tests.rs
Comment thread Cargo.toml
Comment thread src/mandatory/mod.rs
Comment thread src/registry/class.rs Outdated
Comment thread src/registry/mod.rs
Comment thread src/registry/mod.rs Outdated
Auto-committed-on: dragonfly
The idna_adapter dependency was downgraded from version 1.2.1 to 1.1.0, which replaces the ICU-based normalization and properties crates with simpler unicode-bidi and unicode-normalization alternatives. This change removes a large set of ICU-related transitive dependencies including icu_normalizer, icu_properties, and their supporting crates such as displaydoc, yoke, zerofrom, and zerovec, significantly reducing the dependency tree.

Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
The binary file has been reduced from approximately 4.15 MB to 3.49 MB, saving about 0.66 MB of disk space.

Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
Updated doc comments in the tinycortex adapter to use fully qualified paths for `MemoryTaint` and `MemoryTraitProvider`, making the documentation clearer and more consistent with Rust conventions for cross-crate references.

Auto-committed-on: dragonfly
Introduce a `custom:` memory category that accepts an empty label, add proper error types for driver class parsing, and harden several parsing functions against invalid input. The `estimate_tokens` function now safely handles overflow, `node_id_to_path` rejects malformed node IDs, and the default `store_with_taint` implementation validates taint support. A test guard prevents infinite export loops, and the registry's `with_reserved` method avoids overwriting existing reservations.

Auto-committed-on: dragonfly
Reformatted two method chains that exceeded the line length limit, splitting them across multiple lines to improve code readability without changing any behaviour.

Auto-committed-on: dragonfly
Removed the `thiserror` dependency from `DriverClassParseError` by replacing the derive macro with manual implementations of `Display` and `Error`. This eliminates an external dependency while preserving the same error message and behavior, and updates the test assertion to compare against the error type directly.

Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
The `raw` field in the `DriverClassParseError::Unknown` variant now carries a documentation comment explaining its purpose, and the test assertion for the error message is updated to match the current output format.

Auto-committed-on: dragonfly
The chunk_id_is_deterministic test now also asserts the exact expected hash value, making the test more specific and ensuring the hash algorithm produces the correct output rather than just checking consistency between calls.

Auto-committed-on: dragonfly
Auto-committed-on: dragonfly
@senamakel
senamakel merged commit 829d007 into main Aug 10, 2026
9 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants