Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions adapters/tinycortex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ anyhow = "1"
# The behavioural contract suite, run against this crate's drivers
# (issue #18 §E1, acceptance criterion 5).
tinymemory-conformance = { path = "../../conformance" }
# The full-provider conformance target opens a real workspace store.
tempfile = "3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

[lints.rust]
Expand Down
5 changes: 2 additions & 3 deletions adapters/tinycortex/src/conformance_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
//! needs a `MemoryClient` — which needs the host's process-global seams
//! (`set_embedding_host` and friends) installed before it will open. A test
//! that installs a process global is order-dependent, which `AGENTS.md` rules
//! out, so covering it needs its own integration target that owns the global
//! for the whole binary. That is not written yet, and criterion 5 is not
//! complete until it is.
//! out, so it is covered in `tests/full_provider_conformance.rs`: an
//! integration target that owns the global for its whole binary.
//!
//! Running against `InMemoryMemoryStore` rather than a SQLite workspace is
//! deliberate and is also the sharper test: it is the engine's simplest
Expand Down
7 changes: 4 additions & 3 deletions adapters/tinycortex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
//! field added to one contract had to be added to the other and to the
//! conversion — three places, or the value was silently dropped. Since
//! issue #18 §A1 `tinycortex-api` re-exports `tinymemory-api` rather than
//! redefining it, so both sides name one type and `convert` is gone. What
//! remains is the trait shape: the engine's storage trait and the contract's
//! are still separate traits over the same values.
//! redefining it, so both sides name one type and `convert` is gone — and
//! since the contract re-export landed upstream, `tinycortex::memory::Memory`
//! *is* `tinymemory_api::traits::Memory`: one trait, one set of values. What
//! this crate adds on top is composition, not translation.
//!
//! ## What is here
//! - [`TinycortexMemory`] — wraps any TinyCortex [`tinycortex::memory::Memory`]
Expand Down
120 changes: 120 additions & 0 deletions adapters/tinycortex/tests/full_provider_conformance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//! The conformance suite over the FULL eighteen-family driver (#18 §E1/§E3).
//!
//! `conformance_test.rs` (in-lib) covers `crate::provider` — the mandatory
//! three families over any engine backend. This target covers
//! [`tinymemory_tinycortex::engine::TinycortexProvider`], which the in-lib
//! test cannot: the provider needs a `MemoryClient`, and a `MemoryClient`
//! needs the host's process-global embedding seam installed. A process global
//! makes tests order-dependent inside a shared binary, so this lives in its
//! own integration target that owns the global for its whole lifetime — the
//! arrangement the in-lib test's module doc promised.
//!
//! The seam is the same noop shape the §B5 acceptance test uses: recall
//! quality is not under test here, contract shape is.

// A panic in a test IS the failure report — same allowance the in-lib
// conformance test carries.
#![allow(clippy::expect_used)]

use std::sync::Arc;

use tinymemory_tinycortex::engine::{EngineRuntimeConfig, TinycortexProvider};

/// The one piece of host wiring `MemoryClient` requires.
#[derive(Debug)]
struct NoopEmbeddingHost;

impl tinymemory_api::host::EmbeddingHost for NoopEmbeddingHost {
fn resolve_api_key(&self, _provider: &str) -> Option<String> {
None
}

fn ollama_base_url(&self) -> String {
"http://127.0.0.1:1".into()
}

fn default_embedding_provider(&self) -> Arc<dyn tinymemory_api::host::EmbeddingProvider> {
Arc::new(tinymemory_api::host::NoopEmbedding)
}

fn create_embedding_provider_with_credentials(
&self,
_provider: &str,
_model: &str,
_dims: usize,
_api_key: &str,
_custom_endpoint: Option<&str>,
) -> Result<Box<dyn tinymemory_api::host::EmbeddingProvider>, String> {
Ok(Box::new(tinymemory_api::host::NoopEmbedding))
}

fn model_supports_dimensions(&self, _model: &str) -> bool {
false
}

fn cloud_embedding_provider(
&self,
_model: &str,
_dims: usize,
) -> Result<Box<dyn tinymemory_api::host::EmbeddingProvider>, String> {
Ok(Box::new(tinymemory_api::host::NoopEmbedding))
}

fn default_cloud_embedding_model(&self) -> &str {
"noop"
}

fn default_cloud_embedding_dimensions(&self) -> usize {
8
}

fn ollama_embedding_provider(
&self,
_base_url: &str,
_model: &str,
_dims: usize,
) -> Result<Box<dyn tinymemory_api::host::EmbeddingProvider>, String> {
Ok(Box::new(tinymemory_api::host::NoopEmbedding))
}
}

fn provider_over(workspace: &std::path::Path) -> TinycortexProvider {
tinymemory_core::embedding_host::set_embedding_host(Arc::new(NoopEmbeddingHost));
let client = Arc::new(
tinymemory_core::store::MemoryClient::from_workspace_dir(workspace.to_path_buf())
.expect("open the workspace store"),
);
let config = EngineRuntimeConfig {
workspace_dir: workspace.to_path_buf(),
config_path: workspace.join("config.toml"),
memory: Default::default(),
memory_tree: Default::default(),
scheduler_gate: Default::default(),
local_ai: Default::default(),
embeddings_provider: None,
memory_provider: None,
default_model: None,
default_temperature: 0.2,
output_language: None,
memory_sources: serde_json::Value::Null,
};
TinycortexProvider::new("tinycortex".into(), config, client)
}

#[tokio::test(flavor = "multi_thread")]
async fn the_full_tinycortex_provider_upholds_the_contract() {
let workspace = tempfile::tempdir().expect("workspace");
let provider = provider_over(workspace.path());
tinymemory_conformance::assert_provider(Arc::new(provider)).await;
}

#[tokio::test(flavor = "multi_thread")]
async fn the_full_provider_actually_retains() {
let workspace = tempfile::tempdir().expect("workspace");
let provider = provider_over(workspace.path());
assert!(
tinymemory_conformance::retains_writes(&provider).await,
"the workspace store must retain writes, or the suite above asserts \
almost nothing"
);
}
16 changes: 13 additions & 3 deletions scripts/ci/engine-containment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,23 @@ set -euo pipefail
cd "$(dirname "$0")/../.."

# Strip comment lines (`//`, `///`, `//!`) before matching so prose cannot
# trip it; then require the crate name in path position.
# trip it; then require the crate name in path position. The audit probed the
# first version of this regex and found three bypasses, each closed below:
# `extern crate tinycortex;` (no `::`), whitespace between the crate name and
# the path separator (`tinycortex ::memory`), and a `//` inside a string
# literal on the same line eating a real use (`let u="//x"; use tinycortex::A;`
# — comment-stripping must not fire inside quotes). Block comments can still
# yield false POSITIVES (prose inside `/* */` is not stripped), which fails
# safe: a human looks, nothing slips through.
offenders="$(
grep -rln --include='*.rs' 'tinycortex' core/src \
| grep -v '^core/src/engine/' \
| while read -r f; do
if sed -E 's://.*$::' "$f" \
| grep -Eq '(^|[^A-Za-z0-9_])(use[[:space:]]+tinycortex\b|tinycortex::)'; then
# Strip string literals first (so a `//` inside one cannot hide the
# rest of the line), then line comments; then match path positions.
if sed -E 's:"([^"\\]|\\.)*"::g' "$f" \
| sed -E 's://.*$::' \
| grep -Eq '(^|[^A-Za-z0-9_])(use[[:space:]]+tinycortex\b|extern[[:space:]]+crate[[:space:]]+tinycortex\b|tinycortex[[:space:]]*::)'; then
echo "$f"
fi
done
Expand Down
10 changes: 10 additions & 0 deletions sources/src/readers/github/git_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@ use std::process::Command;

/// Run `git` with the given args in `cwd`, asserting success and returning
/// stdout as a string.
///
/// The developer's own git configuration is neutralised: a global
/// `commit.gpgsign = true` would otherwise park `git commit` on a pinentry
/// prompt and hang the whole test binary — on exactly the machines most
/// likely to run these tests. `GIT_CONFIG_GLOBAL`/`GIT_CONFIG_SYSTEM` point
/// at nothing, and signing is off explicitly for good measure.
fn git_ok(cwd: &Path, args: &[&str]) -> String {
let out = Command::new("git")
.env("GIT_CONFIG_GLOBAL", "/dev/null")
.env("GIT_CONFIG_SYSTEM", "/dev/null")
.env("GIT_CONFIG_NOSYSTEM", "1")
.args(["-c", "commit.gpgsign=false", "-c", "tag.gpgsign=false"])
.args(args)
.current_dir(cwd)
.output()
Expand Down
Loading