From 8463350b71f5440dde04f68af2f64e2403320bf1 Mon Sep 17 00:00:00 2001 From: Bo Wu Date: Sat, 5 Sep 2026 19:19:02 -0700 Subject: [PATCH] Harden Wiki runtime boundary --- docker/runtime/Dockerfile.dockerignore | 1 + docs/architecture/system-architecture.md | 6 ++++++ prompts/orchestrator.md | 8 ++++++-- tests/test_container_runtime_contract.py | 2 ++ wiki-service/README.md | 7 +++++++ wiki-service/src/search.mjs | 2 ++ wiki-service/test/wiki-service.test.mjs | 2 ++ 7 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docker/runtime/Dockerfile.dockerignore b/docker/runtime/Dockerfile.dockerignore index 36dfcbe..e3cc1df 100644 --- a/docker/runtime/Dockerfile.dockerignore +++ b/docker/runtime/Dockerfile.dockerignore @@ -6,6 +6,7 @@ benchmarks client evaluation prod-mcp +wiki-service/catalog patch.txt model_api_key.txt .env diff --git a/docs/architecture/system-architecture.md b/docs/architecture/system-architecture.md index 3db9ea4..d58020d 100644 --- a/docs/architecture/system-architecture.md +++ b/docs/architecture/system-architecture.md @@ -627,6 +627,12 @@ citations cannot grant filesystem, network, repository, session, or production-operation authority. Source consolidation must never be interpreted as data consolidation or credential sharing. +Wiki result scores express lexical relevance only. They cannot substantiate +aggregate claims such as repository usage, popularity, dependency weight, or +confidence, and the query interface does not provide aggregation. A caller +must rely on an explicitly cited synthesized page or a separate authoritative +read operation for such a claim, and otherwise report insufficient evidence. + Canonical knowledge remains directly auditable Markdown. Personal vaults use the existing `LLM Wiki/index.md`, synthesized-page, Obsidian-link, graph, raw source, and private `LLM Wiki/system/` conventions. Organization knowledge is diff --git a/prompts/orchestrator.md b/prompts/orchestrator.md index 957cfb1..7d376f2 100644 --- a/prompts/orchestrator.md +++ b/prompts/orchestrator.md @@ -43,8 +43,12 @@ inspect it only in the recovery workflow. | Independently assess a decision, request, diff, receipt, or claim | reviewer/verifier | Run `wiki-query` when the task needs organization-wide knowledge or repository -discovery. Treat its cited result as routing evidence, not authority. A confined -role may use `multiagent ops read --request-file PATH` for a live capability +discovery. Treat its cited result as routing evidence, not authority. Wiki +scores are lexical relevance only: never reinterpret them or repository-name +mentions as usage, popularity, dependency weight, or confidence. If an +aggregate claim is not explicitly supported by a cited Wiki page or fresh +authoritative evidence, report that the available evidence is insufficient. A +confined role may use `multiagent ops read --request-file PATH` for a live capability advertised as non-mutating read/materialize with no approval roles. Only write/execute/mutating external operations belong to ops and the reviewed runbook lifecycle. No role calls provider endpoints directly or receives diff --git a/tests/test_container_runtime_contract.py b/tests/test_container_runtime_contract.py index 61b04ca..177aa0f 100644 --- a/tests/test_container_runtime_contract.py +++ b/tests/test_container_runtime_contract.py @@ -20,9 +20,11 @@ def test_state_parent_is_traversable_by_isolated_roles_but_not_writable(self): def test_runtime_exposes_only_the_wiki_query_command(self): dockerfile = (ROOT / "docker/runtime/Dockerfile").read_text() + dockerignore = (ROOT / "docker/runtime/Dockerfile.dockerignore").read_text() self.assertIn("wiki-service/bin/wiki-query.mjs /usr/local/bin/wiki-query", dockerfile) self.assertNotIn("npm install --global /opt/multiagent/wiki-service", dockerfile) self.assertNotIn("WIKI_ROOT=/var/lib/wiki", dockerfile) + self.assertIn("wiki-service/catalog", dockerignore.splitlines()) def test_wiki_image_is_independent_and_unprivileged(self): dockerfile = (ROOT / "docker/wiki-service/Dockerfile").read_text() diff --git a/wiki-service/README.md b/wiki-service/README.md index 80be40a..da4d89a 100644 --- a/wiki-service/README.md +++ b/wiki-service/README.md @@ -173,6 +173,13 @@ enough matching results, it scans a deterministic, configured bound of remaining Markdown pages. Results include path, content SHA-256, bounded excerpt, score, and whether the page came through the catalog or fallback. +The score means lexical relevance to this query only. It is not usage, +popularity, confidence, dependency weight, or business importance. The query +API does not aggregate repository activity. Questions such as "top used +repositories" require a cited Wiki topic that already contains that aggregate +or a separate authoritative external read; agents must report insufficient +evidence when neither exists. + Important bounds can be configured with `WIKI_MAX_REQUEST_BYTES`, `WIKI_MAX_CORPUS_FILES`, `WIKI_MAX_CORPUS_BYTES`, `WIKI_MAX_FALLBACK_FILES`, and `WIKI_MAX_FALLBACK_BYTES`. Agent requests diff --git a/wiki-service/src/search.mjs b/wiki-service/src/search.mjs index e9adb29..85e38c3 100644 --- a/wiki-service/src/search.mjs +++ b/wiki-service/src/search.mjs @@ -89,6 +89,8 @@ export function searchCorpus(corpus, request, bounds) { })), retrieval: Object.freeze({ mode: needFallback ? "index+fallback" : "index", + scoreMeaning: "lexical-relevance-only", + supportsAggregation: false, indexedCandidates: corpus.indexed.length, fallbackFilesScanned: fallbackFiles, fallbackBytesScanned: fallbackBytes, diff --git a/wiki-service/test/wiki-service.test.mjs b/wiki-service/test/wiki-service.test.mjs index 105c929..a5d3595 100644 --- a/wiki-service/test/wiki-service.test.mjs +++ b/wiki-service/test/wiki-service.test.mjs @@ -123,6 +123,8 @@ test("index-first query locates InternalServices architecture with citation", as assert.match(body.results[0].excerpt, /owns Kubernetes clusters/); assert.match(body.results[0].sha256, /^[a-f0-9]{64}$/); assert.equal(body.retrieval.mode, "index"); + assert.equal(body.retrieval.scoreMeaning, "lexical-relevance-only"); + assert.equal(body.retrieval.supportsAggregation, false); assert.equal(body.retrieval.fallbackFilesScanned, 0); }); });