diff --git a/README.md b/README.md index 08d53c7..47d6436 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,8 @@ claude mcp add ledger -- ledger mcp See [docs/mcp.md](docs/mcp.md) for the tool table, action-provenance contract, remote mode, and the official-registry listing. +See the [Evaluator Guide](docs/EVALUATOR_GUIDE.md) for the three-tier architecture +and the boundary between context, memory, and provenance. ```python from ledger_agent import Meter diff --git a/docs/EVALUATOR_GUIDE.md b/docs/EVALUATOR_GUIDE.md new file mode 100644 index 0000000..7958450 --- /dev/null +++ b/docs/EVALUATOR_GUIDE.md @@ -0,0 +1,75 @@ +# Perseus evaluator guide + +Perseus is a three-tier local-first platform. The tiers are composable, but they +have different responsibilities and can be evaluated independently. + +## 1. Perseus Context Engine + +**Role:** workspace fact scanner and pre-session context renderer. + +It resolves operator-selected workspace sources and renders a bounded Markdown +artifact, normally `AGENTS.md`, before an agent session starts. It describes the +current workspace: project instructions, repository state, selected files, and +other explicitly allowed facts. The output is an input artifact for an agent or +MCP host; it is not an inference engine. + +Evaluate it by running `perseus quickstart` in a temporary workspace, then +rendering `.perseus/context.md` to `AGENTS.md`, and inspecting the bounded output. + +## 2. Perseus Vault + +**Role:** durable local memory. + +Perseus Vault is an encrypted Rust memory engine backed by embedded SQLite and +FTS5. It stores and recalls durable workspace facts, with optional local dense +embeddings and hybrid retrieval. Its primary agent integration is MCP over stdio; +it does not require a Perseus-hosted service for the local path. + +For an LLM host, use the lean advertisement profile to keep tool selection focused: + +```bash +mkdir -p /tmp/verify_vault +perseus-vault serve --profile lean --db /tmp/verify_vault/perseus-vault.db +``` + +The lean profile advertises the core memory operations (`remember`, `recall`, +`forget`, `correct`, `context`, `perseus_vault_workspace_status`, and `health`). +In lean mode, `perseus_vault_workspace_status` is scoped to the transport-stamped +MCP `clientInfo.name` and does not disclose other profile/workspace bindings. The +full registry remains available under the default/all profile. Evaluate the +installed binary and the MCP `initialize`/`tools/list`/`tools/call` flow rather +than relying on a registry count copied from another release. + +## 3. Perseus Ledger + +**Role:** tamper-evident event provenance. + +Perseus Ledger is a stdlib `http.server`-based threaded audit server and Python +package. It records events in a hash-chained append-only history, supports +verification and receipts, and exports OSCAL-compatible evidence. It is the +provenance layer for actions and resource/accounting events; it is not the memory +store and does not replace the Context Engine or Vault. + +Evaluate it with the repository's `uv run pytest` suite, then exercise a temporary +SQLite database, append a record, verify the chain, and inspect an OSCAL export. + +## Boundary: what Perseus is not + +Perseus is **not** an LLM, model provider, inference API, or prompt-generation +service. It does not select or host a model for the operator. Perseus is also +**not a required cloud SaaS dependency**: the Context Engine, Vault's local stdio +path, and Ledger's local deployment can run on operator-controlled machines. +Optional remote transports, provider integrations, and hosted deployment choices +are explicit configuration boundaries, not prerequisites for the local product. + +## Recommended evaluation order + +1. Run the Context Engine quickstart in `/tmp/verify_perseus`, render its context +to `AGENTS.md`, and inspect the result. +2. Start Vault with `--profile lean` and verify its advertised MCP tools and a + write/recall round trip using a temporary database. +3. Run Ledger's tests and verify one append, chain check, and OSCAL export against + a temporary database. +4. Keep results labeled by tier, repository revision, feature profile, and test + command; do not combine retrieval, context-rendering, and provenance results + into one product metric. diff --git a/legacy/README.md b/legacy/README.md new file mode 100644 index 0000000..da9038a --- /dev/null +++ b/legacy/README.md @@ -0,0 +1,6 @@ +# Archived legacy assets + +`legacy/` contains historical artifacts retained for provenance only. They are +not shipped as the current Perseus Ledger application or public product surface. +The active implementation and package use the canonical `ledger_agent` namespace, +the `ledger` CLI, and the **Perseus Ledger** product name. diff --git a/pyproject.toml b/pyproject.toml index eda2634..2742907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,20 @@ invarium = ["invarium>=0.3"] all = ["stripe>=7.0,<14", "reportlab>=4.0,<5", "boto3>=1.28", "sentry-sdk>=2.0,<3", "invarium>=0.3; python_version >= '3.10'", "jsonschema>=4.18,<5"] dev = ["stripe>=7.0,<14", "reportlab>=4.0,<5", "boto3>=1.28", "pytest>=7.0,<10", "sentry-sdk>=2.0,<3", "invarium>=0.3; python_version >= '3.10'", "jsonschema>=4.18,<5"] +# ``uv run`` installs the default ``dev`` group, so the documented local test +# gate is self-provisioning. Keep this separate from runtime dependencies and +# mirror the optional ``dev`` extra above for pip consumers. +[dependency-groups] +dev = [ + "stripe>=7.0,<14", + "reportlab>=4.0,<5", + "boto3>=1.28", + "pytest>=7.0,<10", + "sentry-sdk>=2.0,<3", + "invarium>=0.3; python_version >= '3.10'", + "jsonschema>=4.18,<5", +] + [project.urls] Homepage = "https://perseus.observer/ledger/" Repository = "https://github.com/Perseus-Computing-LLC/ledger" diff --git a/tests/test_evaluator_guide.py b/tests/test_evaluator_guide.py new file mode 100644 index 0000000..646f3ea --- /dev/null +++ b/tests/test_evaluator_guide.py @@ -0,0 +1,14 @@ +"""Evaluator-guide facts stay aligned with the Ledger implementation.""" +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def test_evaluator_guide_describes_the_stdlib_ledger_server(): + guide = (ROOT / "docs" / "EVALUATOR_GUIDE.md").read_text(encoding="utf-8") + app = (ROOT / "ledger_agent" / "server" / "app.py").read_text(encoding="utf-8") + + assert "stdlib `http.server`" in guide + assert "FastAPI" not in guide + assert "http.server" in app diff --git a/tests/test_uv_default_test_dependency.py b/tests/test_uv_default_test_dependency.py new file mode 100644 index 0000000..a72df9c --- /dev/null +++ b/tests/test_uv_default_test_dependency.py @@ -0,0 +1,18 @@ +"""The documented plain ``uv run pytest`` gate must be self-provisioning.""" +from __future__ import annotations + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def test_plain_uv_run_provisions_pytest_from_the_default_dev_group(): + text = (ROOT / "pyproject.toml").read_text(encoding="utf-8") + group = re.search( + r"(?ms)^\[dependency-groups\]\s+dev\s*=\s*\[(.*?)\]", + text, + ) + assert group, "pyproject.toml must declare a default uv development group" + assert re.search(r"(?i)pytest(?:[<>=!~]|\s|\")", group.group(1)) diff --git a/uv.lock b/uv.lock index 51c0bc4..8a7776a 100644 --- a/uv.lock +++ b/uv.lock @@ -275,6 +275,17 @@ stripe = [ { name = "stripe" }, ] +[package.dev-dependencies] +dev = [ + { name = "boto3" }, + { name = "invarium" }, + { name = "jsonschema" }, + { name = "pytest" }, + { name = "reportlab" }, + { name = "sentry-sdk" }, + { name = "stripe" }, +] + [package.metadata] requires-dist = [ { name = "boto3", marker = "extra == 'all'", specifier = ">=1.28" }, @@ -300,6 +311,17 @@ requires-dist = [ ] provides-extras = ["stripe", "sentry", "pdf", "fetchers", "invarium", "all", "dev"] +[package.metadata.requires-dev] +dev = [ + { name = "boto3", specifier = ">=1.28" }, + { name = "invarium", marker = "python_full_version >= '3.10'", specifier = ">=0.3" }, + { name = "jsonschema", specifier = ">=4.18,<5" }, + { name = "pytest", specifier = ">=7.0,<10" }, + { name = "reportlab", specifier = ">=4.0,<5" }, + { name = "sentry-sdk", specifier = ">=2.0,<3" }, + { name = "stripe", specifier = ">=7.0,<14" }, +] + [[package]] name = "pillow" version = "12.3.0"