Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
42 changes: 21 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ jobs:
name: Rust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
# This job executes repository code (cargo build/test); don't persist
# the token in git config.
persist-credentials: false
submodules: recursive

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Check formatting
run: cargo fmt --all -- --check
Expand Down Expand Up @@ -132,18 +132,18 @@ jobs:
name: Feature powerset and coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
submodules: recursive
persist-credentials: false

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
components: llvm-tools-preview

- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- uses: taiki-e/install-action@v2
- uses: taiki-e/install-action@5b4d68e2e660441203ab128a23676f1e4faf1532 # v2
with:
tool: cargo-hack,cargo-llvm-cov

Expand Down Expand Up @@ -217,14 +217,14 @@ jobs:
- name: all features
features: --all-features
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
submodules: recursive

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable

- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

# Scoped to the facade: these are *its* features, and it is what a host
# compiles against. An engine's own suite runs in the main job.
Expand All @@ -243,16 +243,16 @@ jobs:
name: Module (own workspace)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
submodules: recursive

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Check formatting
run: cargo fmt --manifest-path crates/tinymemory-module/Cargo.toml --all -- --check
Expand Down Expand Up @@ -305,14 +305,14 @@ jobs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
submodules: recursive

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable

- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Build documentation
env:
Expand All @@ -323,7 +323,7 @@ jobs:
name: Minimum supported Rust version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
submodules: recursive
Expand All @@ -340,11 +340,11 @@ jobs:
fi
echo "version=$msrv" >> "$GITHUB_OUTPUT"

- uses: dtolnay/rust-toolchain@master
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master
with:
toolchain: ${{ steps.msrv.outputs.version }}

- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2

- name: Build with the declared MSRV
run: cargo build --all-targets --all-features
Expand All @@ -353,12 +353,12 @@ jobs:
name: Supply chain
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
submodules: recursive

- name: Check advisories, licenses, bans, and sources
uses: EmbarkStudios/cargo-deny-action@v2
uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2
with:
command: check all
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