diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..2a1036f --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,18 @@ +cff-version: 1.2.0 +message: "If TROVE supports your work, please cite the software using these metadata." +title: "TROVE" +type: software +authors: + - name: "TROVE contributors" +repository-code: "https://github.com/JNHFlow21/trove" +url: "https://github.com/JNHFlow21/trove" +license: Apache-2.0 +version: 1.0.0 +date-released: 2026-08-09 +keywords: + - agent memory + - MCP + - local-first + - privacy-preserving + - evidence retrieval + - WeChat diff --git a/PRIVACY.md b/PRIVACY.md index 8eab1fb..ed362bb 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,7 +1,9 @@ # Open-source privacy boundary -The source repository contains product code, public documentation, schemas, and -synthetic tests. It must not contain any user's real data. +The public source repository contains product code, public documentation, +schemas, and synthetic tests. It must not contain any user's real data—not in +the current tree, generated artifacts, Git objects, branches, tags, or deleted +history. ## Never commit @@ -17,21 +19,35 @@ synthetic tests. It must not contain any user's real data. Fixtures must use unmistakably synthetic labels such as `Sample Contact`, `wxid_fixturea`, and `acct-a`. Synthetic conversations should test only the -minimum behavior required by the contract; they should not paraphrase a real -conversation or profile. +minimum behavior required by the contract; they must not quote or paraphrase a +real conversation or profile. + +## Public-history rule + +Never make a private development repository public merely by changing its +visibility. Deleted files remain recoverable from Git history. The public +repository must contain only reviewed, source-safe commits; private branches, +tags, reflogs, release evidence, and development artifacts are not publication +inputs. ## Local storage Runtime data belongs in a Vault outside the repository. The default product location is `$HOME/Trove/trove-vault`; callers may instead pass `--vault` or set -`TROVE_VAULT_ROOT`. +`TROVE_VAULT_ROOT`. Keep the Vault and release acceptance evidence owner-only. + +Provider credentials belong in Agent Switch's private secret store. TROVE +configuration may reference secret **names**, never secret values. ## Required checks ```bash ./scripts/trove-python scripts/privacy_scan.py . ./scripts/trove-python scripts/check.py contract +gitleaks git --redact ``` -The scanner is a guardrail, not proof that content is non-personal. Review test -fixtures and documentation as data before publishing. +CI performs both the project scanner and a full-history Gitleaks scan. The +scanners are guardrails, not proof that content is non-personal. Review fixtures, +documentation, binary artifacts, Git history, and screenshots as data before +publishing. diff --git a/README.md b/README.md index 425dbda..545e130 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,206 @@ -# TROVE +
+ English · 简体中文 +
+ ++ Local-first, privacy-preserving memory and cited evidence for AI agents. +
+ ++ A macOS runtime that gives Codex, Claude Code, and other MCP clients bounded access to a local evidence Vault—without turning personal data into a cloud service. +
+ + + + + +TROVE is not a general-purpose autonomous agent and it is not a hosted chat +database. It is a local capability runtime: external agents ask for recall, +search, context, or a controlled operation; TROVE returns a typed, size-bounded +result with citations and coverage metadata. + +The product is **TROVE**. WeChat is one optional source Provider, not the +product identity. + +## Why TROVE + +| Common approach | TROVE | +| --- | --- | +| Copy whole conversations into an agent prompt | Return only bounded evidence needed for the current task | +| Let every client open the database directly | One owner-only daemon coordinates each canonical Vault | +| Treat retrieved text as instructions | Treat messages, filenames, OCR, and transcripts as untrusted evidence | +| Give an agent ambient write or send authority | Separate requests from human approval and delivery policy | +| Hide partial retrieval behind a confident answer | Return citations, coverage, cursors, and typed errors | + +## Architecture + +```mermaid +flowchart LR + A["Codex / Claude Code / MCP client"] --> M["trove-mcp"] + O["Local operator"] --> C["trove CLI"] + M --> L["Shared client"] + C --> L + L -->|"trove/1 over owner-only Unix socket"| D["troved"] + D --> K["Capability catalog + dispatcher"] + K --> V["Private local Vault + indexes"] + K --> P["Verified source Provider"] + P --> W["Optional WeChat source"] + D --> R["Optional Reply Runtime — off by default"] + O -->|"exact local decision"| R + R --> P +``` + +There is no public network listener. One canonical Vault maps to one daemon. +The CLI and MCP adapter use the same protocol, catalog, validation, and +dispatcher, so the recovery path and agent path cannot silently diverge. + +## Core capabilities -TROVE is a macOS-only, local-private capability runtime. It returns bounded -cited evidence to external Agents and can optionally run a local Reply Runtime -that generates, reviews, and delivers replies through verified source -Providers. Reply delivery is disabled by default. MCP is the primary Agent -interface and the CLI is the recovery and operator interface. +- **Bounded recall and search** — result limits, response budgets, opaque + cursors, coverage metadata, and stable citations. +- **Local-first storage** — Vault data, indexes, caches, and operation journals + stay under an owner-controlled path outside this repository. +- **Agent-native MCP** — cumulative `standard`, `operations`, and `admin` packs; + use the smallest pack that completes the task. +- **Typed failure semantics** — retry only when `error.retryable` is true; + ambiguity and incomplete coverage are explicit. +- **Provider boundary** — source integrations implement a verified contract; + WeChat support is independently packaged. +- **Human-controlled actions** — approval decisions require an interactive + controlling terminal. MCP and background jobs cannot approve themselves. +- **Privacy gates** — synthetic fixture rules, current-tree scanning, full Git + history scanning with Gitleaks, and CI checks on every push and pull request. -The product is **TROVE**. WeChat support is an optional source Provider rather -than the product identity. +## Quick start from source -## Install +### Requirements -From a verified release artifact directory: +- macOS +- Python 3.11 or newer +- Git + +Clone the public source and install the lightweight base runtime: ```bash -python3 -m venv "$HOME/.local/share/trove/runtime" -"$HOME/.local/share/trove/runtime/bin/pip" install ./trove_runtime-1.0.0-py3-none-any.whl ./trove_provider_*.whl -export PATH="$HOME/.local/share/trove/runtime/bin:$PATH" -trove version +git clone https://github.com/JNHFlow21/trove.git +cd trove +TROVE_RUNTIME_INSTALL_EXTRAS="" bash scripts/bootstrap_runtime.sh ``` -Keep the artifact directory and Vault owner-only. Create or select a Vault, -then run the redacted health check. The explicit path avoids hidden discovery. +Create an owner-only Vault **outside** the source checkout and run the redacted +health check: ```bash export TROVE_VAULT_ROOT="$HOME/Trove/trove-vault" mkdir -p "$TROVE_VAULT_ROOT" chmod 700 "$TROVE_VAULT_ROOT" -trove --vault "$TROVE_VAULT_ROOT" doctor +.venv/bin/trove --vault "$TROVE_VAULT_ROOT" doctor ``` -## Connect MCP +The default macOS bootstrap additionally supports +`local-vision,local-embedding,zvec`. See [testing](docs/testing.md) before +installing optional local ASR, VLM, key-capture, or cloud-retrieval extras. + +### Connect an MCP client -Register `trove-mcp` through Agent Switch with these arguments: +Register the installed `trove-mcp` through +[Agent Switch](https://github.com/JNHFlow21/agent-switch) with: ```text --pack standard --vault $TROVE_VAULT_ROOT ``` -Run `agent-switch doctor` before changing its central configuration and -`agent-switch reconcile` afterward. Do not hand-edit generated client configs. -The standard pack is sufficient for ordinary recall and search. - -## First call - -Ask the Agent to call `trove_recall`, or use the exact CLI fallback: +Run `agent-switch doctor` before changing central tool configuration and +`agent-switch reconcile` afterward. Do not copy credentials into native client +configuration. Ask the agent to call `trove_recall`, or use the recovery CLI: ```bash -trove --vault "$TROVE_VAULT_ROOT" recall --target "Example person" --limit 50 +.venv/bin/trove --vault "$TROVE_VAULT_ROOT" recall \ + --target "Example person" --limit 50 ``` -The JSON envelope states `ok`, typed errors, citations, and coverage. Follow an -opaque cursor only when the requested coverage needs another page. +Follow a returned cursor only when the task needs more coverage. Stop on +complete coverage, `no_results`, or a terminal error. -## Failure path +## Privacy and safety boundary -Run `trove --vault "$TROVE_VAULT_ROOT" doctor`. Retry only when -`error.retryable` is true. For `ambiguous_target`, select one returned account. -For `approval_required`, stop: an Agent can request or inspect approval but only -a human at the controlling terminal can decide it. +The public repository contains source code, schemas, public documentation, and +synthetic tests only. It must never contain real chats, contacts, account IDs, +media, transcripts, OCR, provider payloads, local Vaults, logs, credentials, +machine-specific paths, or evidence from real runs. -See [MCP](docs/mcp.md), [operations](docs/operations.md), and the generated -[capability reference](docs/capability-map.md). Provider setup is separate; -see the [installed source Provider](docs/providers/wechat.md). The optional -Reply Runtime architecture and safety model are documented in -[Reply Runtime](docs/architecture/reply-runtime.md). +```bash +./scripts/trove-python scripts/privacy_scan.py . +./scripts/trove-python scripts/check.py contract +gitleaks git --redact +``` -## Privacy boundary +The scanners are guardrails, not a substitute for human review. See +[Open-source privacy](PRIVACY.md) and the [Security Policy](SECURITY.md). + +> [!IMPORTANT] +> The optional Reply Runtime is disabled by default. An agent may request or +> inspect approval, but only a human at the controlling terminal can decide an +> exact action. Live delivery requires a separate, explicit policy grant. + +## Repository map + +| Path | Responsibility | +| --- | --- | +| `packages/trove_protocol` | Versioned `trove/1` schemas and wire contracts | +| `packages/trove_core` | Capability catalog, application services, search, Vault, safety boundaries | +| `packages/trove_daemon` | One local daemon per canonical Vault | +| `packages/trove_client` | Shared client used by every adapter | +| `packages/trove_mcp` | Primary stdio MCP interface for external agents | +| `packages/trove_cli` | Operator, recovery, diagnostics, and explicit approval interface | +| `packages/trove_provider_wechat` | Optional independently packaged WeChat Provider | +| `skills` | Outcome-oriented agent Skills and generated manifest | +| `scripts` | Build, test, privacy, release, benchmark, and migration gates | + +## Project activity + +| Public signal | Live or latest owner-visible value | +| --- | ---: | +| Stars / forks / commits | Live badges above | +| README visits | Public counter above; may include bots and repeat visits | +| Unique repository visitors | **0** in the rolling 14-day GitHub Traffic window | +| Unique Git cloners | **15** (**21** total clones) in the rolling 14-day window | + +Traffic snapshot: 2026-08-10. GitHub exposes clone and unique-visitor analytics only to maintainers, so those values are a dated, transparent snapshot rather than a token-backed public badge. + +## Documentation + +- [Architecture](docs/architecture.md) +- [MCP packs and trust boundary](docs/mcp.md) +- [Capability reference](docs/capability-map.md) +- [Protocol](docs/protocol.md) +- [Provider SDK](docs/provider-sdk.md) +- [WeChat Provider](docs/providers/wechat.md) +- [Operations and recovery](docs/operations.md) +- [Testing](docs/testing.md) +- [Release model](docs/release.md) +- [Roadmap](docs/roadmap.md) -Real chat databases, exports, media, transcripts, provider payloads, secrets, -logs, and local Vault data do not belong in this repository. Tests and checked-in -evidence must be synthetic or explicitly source-safe. Run the privacy scanner -before every commit. +## Contributing -See [open-source privacy](PRIVACY.md) and -[security policy](SECURITY.md). +Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. Privacy +regressions and changes that weaken the application or approval boundaries will +not be accepted. Please report vulnerabilities through GitHub private +vulnerability reporting rather than a public issue. -## Contributing +## License -See [CONTRIBUTING.md](CONTRIBUTING.md). TROVE is licensed under -[Apache License 2.0](LICENSE). +[Apache License 2.0](LICENSE) © 2026 TROVE contributors diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..78a011a --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,169 @@ ++ English · 简体中文 +
+ ++ 面向 AI Agent 的本地优先、隐私保护型记忆与引用证据运行时。 +
+ ++ 在 macOS 本地为 Codex、Claude Code 和其他 MCP 客户端提供有边界的证据 Vault 访问能力,而不是把个人数据变成云端服务。 +
+ + + + + +TROVE 不是通用自治 Agent,也不是托管式聊天数据库。外部 Agent 请求回忆、 +搜索、上下文或受控操作;TROVE 返回带引用、覆盖范围和大小边界的类型化结果。 + +产品名称是 **TROVE**。微信只是一个可选数据源 Provider,并不是产品本身。 + +## 为什么使用 TROVE + +| 常见做法 | TROVE | +| --- | --- | +| 把整段对话复制进 Agent 提示词 | 只返回当前任务需要的有边界证据 | +| 让每个客户端直接打开数据库 | 每个规范 Vault 只由一个 owner-only daemon 协调 | +| 把检索文本当成指令 | 消息、文件名、OCR 和转写只能作为不可信证据 | +| 给 Agent 默认写入或发送权限 | 把请求、人类审批与实际发送策略彻底分离 | +| 用自信答案掩盖检索不完整 | 明确返回引用、覆盖范围、游标和类型化错误 | + +## 架构 + +```mermaid +flowchart LR + A["Codex / Claude Code / MCP 客户端"] --> M["trove-mcp"] + O["本地操作者"] --> C["trove CLI"] + M --> L["共享客户端"] + C --> L + L -->|"owner-only Unix socket 上的 trove/1"| D["troved"] + D --> K["能力目录与调度器"] + K --> V["本地私有 Vault 与索引"] + K --> P["已验证的数据源 Provider"] + P --> W["可选微信数据源"] + D --> R["默认关闭的 Reply Runtime"] + O -->|"精确的本地决定"| R + R --> P +``` + +TROVE 不开放公网监听。CLI 和 MCP 适配器使用同一套协议、能力目录、验证与 +调度逻辑,因此恢复路径与 Agent 路径不会静默分叉。 + +## 核心能力 + +- **有边界的回忆与搜索**:结果上限、响应预算、不透明游标、覆盖元数据和稳定引用。 +- **本地优先存储**:Vault、索引、缓存和操作日志都保存在仓库外的 owner-controlled 路径。 +- **Agent 原生 MCP**:提供递增的 `standard`、`operations`、`admin` 能力包,应始终使用满足任务的最小能力包。 +- **类型化失败语义**:只有 `error.retryable` 为真时才重试;歧义和覆盖不完整会明确返回。 +- **Provider 边界**:数据源接入必须实现已验证合约;微信支持单独打包。 +- **人类控制的操作**:审批决定必须来自交互式控制终端,MCP 与后台任务无法自行批准。 +- **隐私门禁**:合成夹具规则、当前树扫描、Gitleaks 全 Git 历史扫描和 CI 检查。 + +## 从源码开始 + +要求:macOS、Python 3.11+、Git。 + +```bash +git clone https://github.com/JNHFlow21/trove.git +cd trove +TROVE_RUNTIME_INSTALL_EXTRAS="" bash scripts/bootstrap_runtime.sh + +export TROVE_VAULT_ROOT="$HOME/Trove/trove-vault" +mkdir -p "$TROVE_VAULT_ROOT" +chmod 700 "$TROVE_VAULT_ROOT" +.venv/bin/trove --vault "$TROVE_VAULT_ROOT" doctor +``` + +默认的 macOS bootstrap 还支持 `local-vision,local-embedding,zvec`。 +其他本地 ASR、VLM、key-capture 或 cloud-retrieval 扩展请先阅读 +[测试文档](docs/testing.md)。 + +### 连接 MCP 客户端 + +通过 [Agent Switch](https://github.com/JNHFlow21/agent-switch) 注册已安装的 +`trove-mcp`: + +```text +--pack standard --vault $TROVE_VAULT_ROOT +``` + +变更中央工具配置前运行 `agent-switch doctor`,变更后运行 +`agent-switch reconcile`。不要把密钥复制进原生客户端配置。 + +## 隐私与安全边界 + +公开仓库只包含源码、Schema、公开文档和合成测试。真实聊天、联系人、账号 +标识、媒体、转写、OCR、Provider payload、本地 Vault、日志、密钥、机器路径和 +真实运行证据都不得进入当前目录、生成产物或 Git 历史。 + +```bash +./scripts/trove-python scripts/privacy_scan.py . +./scripts/trove-python scripts/check.py contract +gitleaks git --redact +``` + +扫描器只是门禁,不替代人工检查。详见[开源隐私边界](PRIVACY.md)和 +[安全策略](SECURITY.md)。Reply Runtime 默认关闭;Agent 可以请求或查看审批, +但只有控制终端上的人类才能决定精确操作。 + +## 仓库结构 + +| 路径 | 职责 | +| --- | --- | +| `packages/trove_protocol` | 版本化 `trove/1` Schema 与 wire contract | +| `packages/trove_core` | 能力目录、应用服务、搜索、Vault 与安全边界 | +| `packages/trove_daemon` | 每个规范 Vault 对应的本地 daemon | +| `packages/trove_client` | 所有适配器共用的客户端 | +| `packages/trove_mcp` | 面向外部 Agent 的主要 stdio MCP 接口 | +| `packages/trove_cli` | 操作、恢复、诊断与显式审批接口 | +| `packages/trove_provider_wechat` | 独立打包的可选微信 Provider | +| `skills` | 面向结果的 Agent Skills 与清单 | +| `scripts` | 构建、测试、隐私、发行、性能和迁移门禁 | + +## 项目数据 + +| 公开指标 | 实时数据或最近一次维护者可见数据 | +| --- | ---: | +| Star / Fork / Commit | 见上方实时徽章 | +| README 访问量 | 见上方公开计数器;可能包含机器人和重复访问 | +| 仓库独立访客 | GitHub Traffic 最近 14 天滚动窗口内为 **0** | +| 独立克隆者 | 最近 14 天滚动窗口内为 **15**(共 **21** 次克隆) | + +数据快照日期:2026-08-10。GitHub 只向仓库维护者提供克隆与独立访客数据,因此这里采用注明日期的透明快照,而不是需要私密 Token 的公开徽章。 + +## 文档 + +- [架构](docs/architecture.md) +- [MCP 能力包与信任边界](docs/mcp.md) +- [能力参考](docs/capability-map.md) +- [协议](docs/protocol.md) +- [Provider SDK](docs/provider-sdk.md) +- [微信 Provider](docs/providers/wechat.md) +- [运维与恢复](docs/operations.md) +- [测试](docs/testing.md) +- [发行模型](docs/release.md) +- [路线图](docs/roadmap.md) + +## 参与贡献 + +提交 PR 前请阅读 [CONTRIBUTING.md](CONTRIBUTING.md)。削弱隐私、应用边界或 +审批边界的变更不会被接受。安全漏洞请通过 GitHub 私密漏洞报告提交,不要创建 +公开 Issue。 + +## 许可证 + +[Apache License 2.0](LICENSE) © 2026 TROVE contributors diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..3402ede --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,34 @@ +# TROVE roadmap + +TROVE's roadmap is organized around invariants rather than promised dates. +Issues and pull requests should preserve the local-first, bounded-evidence, +typed-protocol, Provider, and human-approval boundaries. + +## Current line: `trove/1` + +- Maintain stable bounded recall, search, context, profile, file, and media + contracts. +- Keep the CLI and MCP adapter on the same generated capability catalog. +- Harden source-Provider verification, upgrade/rollback, and privacy gates. +- Improve local indexing quality and latency without changing coverage claims. +- Expand synthetic interoperability and failure-path fixtures. + +## Candidate directions + +- Additional independently packaged source Providers that satisfy the public + Provider SDK and trust boundary. +- Clearer contributor tooling for synthetic Vault creation and contract tests. +- Better operator-visible diagnostics, citation inspection, and storage health. +- Reproducible, signed release artifacts after the release gate proves the exact + source, dependency, protocol, catalog, and Provider set. + +## Explicit non-goals + +- A hosted service that receives users' private Vault contents. +- Silent upload of chats, contacts, media, embeddings, or diagnostic evidence. +- Ambient send authority for agents or approval decisions through MCP. +- Treating retrieved personal content as trusted instructions. +- Compatibility claims for platforms that are not covered by release gates. + +See [Architecture](architecture.md), [Release](release.md), and +[Open-source privacy](../PRIVACY.md) for the invariants behind this roadmap. diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..d891c6c --- /dev/null +++ b/llms.txt @@ -0,0 +1,37 @@ +# TROVE + +> TROVE is a macOS-only, local-first, privacy-preserving evidence and memory runtime for AI agents. It exposes bounded, cited local retrieval through MCP and keeps personal source data outside the public repository. + +Canonical repository: https://github.com/JNHFlow21/trove +License: Apache-2.0 +Platform: macOS; Python 3.11+ +Protocol: trove/1 over an owner-only local Unix socket + +## Product boundary + +- TROVE is a local capability runtime, not a general autonomous agent or a hosted chat database. +- `trove-mcp` is the primary agent interface; `trove` is the operator and recovery CLI. +- One canonical Vault maps to one local daemon. There is no public network listener. +- Results are bounded and carry typed errors, citations, coverage, and opaque continuation cursors. +- Retrieved messages, files, OCR, transcripts, and Provider fields are untrusted evidence, never control instructions. +- WeChat is an optional independently packaged source Provider, not the TROVE product identity. +- The optional Reply Runtime is disabled by default. Approval decisions require a human at the controlling terminal. + +## Privacy boundary + +- Real chats, contacts, account identifiers, media, transcripts, OCR, Provider payloads, Vaults, logs, credentials, machine paths, and real-run evidence never belong in the public repository or Git history. +- Tests use unmistakably synthetic identities and content. +- CI runs the project privacy scanner and a full-history Gitleaks scan. +- Provider credentials should remain in Agent Switch's private secret store; configuration refers to secret names only. + +## Documentation + +- README: https://github.com/JNHFlow21/trove#readme +- Architecture: https://github.com/JNHFlow21/trove/blob/main/docs/architecture.md +- MCP and trust boundary: https://github.com/JNHFlow21/trove/blob/main/docs/mcp.md +- Protocol: https://github.com/JNHFlow21/trove/blob/main/docs/protocol.md +- Provider SDK: https://github.com/JNHFlow21/trove/blob/main/docs/provider-sdk.md +- WeChat Provider: https://github.com/JNHFlow21/trove/blob/main/docs/providers/wechat.md +- Operations: https://github.com/JNHFlow21/trove/blob/main/docs/operations.md +- Privacy: https://github.com/JNHFlow21/trove/blob/main/PRIVACY.md +- Security: https://github.com/JNHFlow21/trove/blob/main/SECURITY.md diff --git a/pyproject.toml b/pyproject.toml index 29b5bf0..c0b9f03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,34 @@ [project] name = "trove-runtime" version = "1.0.0" -description = "Local private Agent capability runtime" +description = "Local-first, privacy-preserving memory and cited evidence runtime for AI agents" requires-python = ">=3.11" readme = "README.md" license = { text = "Apache-2.0" } authors = [{ name = "TROVE contributors" }] +keywords = ["agent-memory", "mcp", "local-first", "privacy", "evidence-retrieval", "wechat"] +classifiers = [ + "Environment :: MacOS X", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Topic :: Database :: Database Engines/Servers", + "Topic :: Scientific/Engineering :: Artificial Intelligence", +] dependencies = [ "cryptography>=42,<50", "mcp>=1.0,<2", "zstandard>=0.23,<1", ] +[project.urls] +Homepage = "https://github.com/JNHFlow21/trove" +Documentation = "https://github.com/JNHFlow21/trove#readme" +Issues = "https://github.com/JNHFlow21/trove/issues" +Repository = "https://github.com/JNHFlow21/trove" + [project.optional-dependencies] local-asr = [ "faster-whisper>=1.1,<2", diff --git a/tests/test_documentation_contract.py b/tests/test_documentation_contract.py index 5283ec1..3fa0ed2 100644 --- a/tests/test_documentation_contract.py +++ b/tests/test_documentation_contract.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import posixpath from pathlib import Path, PurePosixPath import re import subprocess @@ -23,6 +24,7 @@ 'docs/providers/wechat.md', 'docs/testing.md', 'docs/release.md', + 'docs/roadmap.md', ) HISTORICAL_PREFIXES = ('docs/perf/', 'docs/plans/', 'docs/release-notes/') REMOVED_ACTIVE_DOCS = frozenset({ @@ -50,7 +52,7 @@ def repository_snapshot() -> dict[str, str]: - needed = set(ACTIVE_DOCS) | {'skills/manifest.json'} + needed = set(ACTIVE_DOCS) | {'README.zh-CN.md', 'skills/manifest.json'} needed.update({ f'skills/{name}/SKILL.md' for name in ( @@ -91,7 +93,7 @@ def _relative_link(source: str, target: str) -> str: clean = target.split('#', 1)[0] if source == 'README.md': return PurePosixPath(clean).as_posix() - return (PurePosixPath(source).parent / clean).as_posix() + return posixpath.normpath((PurePosixPath(source).parent / clean).as_posix()) class DocumentationContractTests(unittest.TestCase): @@ -119,17 +121,22 @@ def test_formal_documentation_is_minimal_and_legacy_free(self): def test_readme_is_ordered_artifact_walkthrough(self): readme = self.active['README.md'] - headings = ('## Install', '## Connect MCP', '## First call', '## Failure path') + headings = ( + '## Why TROVE', '## Architecture', '## Core capabilities', + '## Quick start from source', '## Privacy and safety boundary', + '## Repository map', '## Documentation', + ) positions = [readme.index(item) for item in headings] self.assertEqual(positions, sorted(positions)) for token in ( - 'trove_runtime-1.0.0', 'trove version', 'trove-mcp', - 'trove --vault "$TROVE_VAULT_ROOT" doctor', 'trove_recall', - 'error.retryable', 'approval_required', + 'trove-mcp', '.venv/bin/trove --vault "$TROVE_VAULT_ROOT" doctor', + 'trove_recall', 'error.retryable', 'controlling terminal', + 'Unique Git cloners', 'packages/trove_protocol', ): self.assertIn(token, readme) - for source_detail in ('cd ', './scripts/', '-m trove_', 'packages/'): - self.assertNotIn(source_detail, readme) + chinese = self.files['README.zh-CN.md'] + self.assertIn('简体中文', chinese) + self.assertIn('README.zh-CN.md', readme) def test_generated_reference_is_byte_identical_to_catalog(self): self.assertEqual( @@ -141,14 +148,17 @@ def test_links_resolve_inside_formal_or_historical_docs(self): for source, text in self.active.items(): for target in LINK.findall(text): with self.subTest(source=source, target=target): - self.assertFalse(target.startswith(('http:', 'https:', '/'))) + if target.startswith(('http:', 'https:')): + continue + self.assertFalse(target.startswith('/')) resolved = _relative_link(source, target) self.assertIn(resolved, self.files) def test_command_examples_use_current_entrypoints(self): allowed = { 'python3', 'export', 'mkdir', 'chmod', 'trove', 'trove-mcp', - 'agent-switch', './scripts/trove-python', + 'agent-switch', './scripts/trove-python', '.venv/bin/trove', + 'git', 'cd', 'bash', 'gitleaks', } for name, content in self.active.items(): for block in FENCE.findall(content): @@ -156,7 +166,10 @@ def test_command_examples_use_current_entrypoints(self): line = raw.strip() if not line or line.startswith(('#', '--')): continue - first = line.split()[0] + parts = line.split() + while parts and re.fullmatch(r'[A-Z][A-Z0-9_]*=.*', parts[0]): + parts.pop(0) + first = parts[0] if first.startswith('"$HOME/'): first = 'python3' with self.subTest(name=name, line=line): @@ -177,7 +190,7 @@ def test_protocol_skill_and_document_budgets(self): versions = set(re.findall(r'trove/[0-9]+', text)) with self.subTest(name=name): self.assertLessEqual(versions, {'trove/1'}) - self.assertLessEqual(len(self.active['README.md'].encode()), 4_000) + self.assertLessEqual(len(self.active['README.md'].encode()), 12_000) self.assertLessEqual(len(self.active['docs/mcp.md'].encode()), 4_000) manifest = json.loads(self.files['skills/manifest.json'])