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
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,13 @@ non-File providers remain `NOT_ACTIVE`; see
[ADR-0068](./docs/decisions/0068-activate-loopback-dogfood-runtime.md).

Once the bounded API is running, the maintainer caller and Quality runner are
documented in [`eval/README.md`](./eval/README.md). They call only the frozen
resolve HTTP operation; the evaluation report remains separate from the
security release gate.
separate surfaces. Use the read-only
[`context-engine-context`](./docs/operations/maintainer-context-cli.md) CLI for
fresh questions and untrusted Package inspection. Evaluation replay remains in
[`eval/README.md`](./eval/README.md). Both callers use only the frozen resolve
HTTP operation; the maintainer CLI has no Control, ActionPlane, model, or
promotion authority, and evaluation remains separate from the security release
gate.

### Run the worker

Expand Down Expand Up @@ -519,7 +523,7 @@ engine/ The sealed core — no HTTP, no vendor SDKs
adapters/ Everything that touches the outside world
http/ FastAPI ingress, authentication, transport limits, routes
parsers/ format parsers (PDF / Markdown / Office)
applications/ Thin process entry points (~200 LOC total)
applications/ Thin process entry points and operator CLIs — wiring only
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
api.py `context-engine-api`
worker.py `context-engine-worker`
bot_delivery/ M2 trusted Bot process (TypeScript); generated-SDK caller
Expand All @@ -535,13 +539,14 @@ PLAN.md vision, principles, roadmap, non-goals

Two structural facts worth noticing:

- **Thin entry points, thick core.** `applications/` is roughly 200 lines. All
behavior lives in `engine/`, which is what makes "the production composition
root cannot substitute, skip, or wire a no-op `AuthorizationKernel`" an
enforceable property rather than a slogan.
- **Tests outweigh implementation ~3:1.** Roughly 21k lines under `engine/`
against roughly 68k lines under `tests/`. For a project whose central claim is
a security invariant, the executable evidence *is* the product.
- **Thin entry points, thick core.** Every module in `applications/` only parses
arguments and wires a composition; no authorization, retrieval, or delivery
behavior lives there. All of it lives in `engine/`, which is what makes "the
production composition root cannot substitute, skip, or wire a no-op
`AuthorizationKernel`" an enforceable property rather than a slogan.
- **Tests outweigh implementation ~3:1.** The `tests/` tree is several times the
size of `engine/`. For a project whose central claim is a security invariant,
the executable evidence *is* the product.
Comment on lines +547 to +549

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the numeric repository-size claim from both READMEs.

Both changed sections retain an approximate 3:1 test-to-engine ratio, although the PR removes volatile README counts. Use a non-numeric qualitative statement in both languages.

  • README.md#L534-L536: remove ~3:1.
  • README.zh-CN.md#L246-L247: remove “约为实现量的 3 倍”.
📍 Affects 2 files
  • README.md#L534-L536 (this comment)
  • README.zh-CN.md#L246-L247
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 534 - 536, Remove the numeric test-to-engine ratio
from README.md lines 534-536 and README.zh-CN.md lines 246-247, replacing each
with a qualitative non-numeric statement while preserving the surrounding
meaning.


### What is pluggable, and what is not

Expand Down
15 changes: 8 additions & 7 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ engine/ sealed 内核——不含 HTTP,不含厂商 SDK
adapters/ 一切与外部世界接触的部分
http/ FastAPI ingress、认证、传输限制、路由
parsers/ 格式解析器(PDF / Markdown / Office)
applications/ 极薄的进程入口(合计约 200 行)
applications/ 极薄的进程入口与运维 CLI(只做组装)
api.py `context-engine-api`
worker.py `context-engine-worker`
bot_delivery/ M2 受信 Bot 进程(TypeScript),generated-SDK 调用方
Expand All @@ -232,17 +232,18 @@ sdk/typescript/ 由 OpenAPI 生成的 HTTP client
eval/ golden set、切片门禁、裁判、安全目录
migrations/ Alembic 迁移
tests/ unit / integration / catalog / process 套件
docs/ 实现权威、60 篇 ADR、威胁模型、PRD、研究
docs/ 实现权威、编号 ADR、威胁模型、PRD、研究
CONTEXT.md 领域术语表(只有术语,不含实现)
PLAN.md 愿景、原则、路线图、Non-goals
```

有两个结构事实值得注意:

- **薄入口,厚内核。** `applications/` 只有约 200 行,全部行为都在 `engine/`
里。这正是「生产 composition root 不能替换、跳过或装配 no-op
`AuthorizationKernel`」能成为一条**可强制执行的性质**、而不只是一句口号的原因。
- **测试量约为实现量的 3 倍。** `engine/` 约 2.1 万行,`tests/` 约 6.8 万行。
- **薄入口,厚内核。** `applications/` 里的每个模块都只做参数解析与组装,不含任何
授权、检索或投递行为;这些行为全部在 `engine/` 里。这正是「生产 composition
root 不能替换、跳过或装配 no-op `AuthorizationKernel`」能成为一条**可强制执行的
性质**、而不只是一句口号的原因。
- **测试量约为实现量的 3 倍。** `tests/` 的体量远大于 `engine/`。
对一个核心主张是安全不变量的项目来说,**可执行的证据本身就是产品**。

### 什么可插拔,什么不可
Expand Down Expand Up @@ -289,7 +290,7 @@ IM 交付由 `BotDelivery` 这个受信深模块完成。它从 M2 起作为独
| [CONTEXT.md](./CONTEXT.md) | 领域术语表——身份、安全、内容与生命周期术语的仓库权威 |
| [PLAN.md](./PLAN.md) | 愿景、不可谈判的设计原则、路线图、明确的 Non-goals |
| [STATUS.md](./STATUS.md) | 逐 Issue 的能力激活台账与证据边界 |
| [ADR 索引](./docs/decisions/README.md) | 60 篇决策记录:边界、依赖方向、禁止捷径、重访触发器 |
| [ADR 索引](./docs/decisions/README.md) | 编号决策记录:边界、依赖方向、禁止捷径、重访触发器 |
| [实现设计](./docs/design/2026-07-18-context-engine-implementation-design.md) | 集成后的实现权威与里程碑边界 |
| [威胁模型](./docs/security/context-engine-threat-model.md) | 资产、信任边界、威胁与 hard oracles |
| [Program PRD](./docs/agents/prd-contextengine-implementation.md) · [Epic Tech Spec](./docs/specs/2026-07-19-context-engine-implementation-epic.md) | 需求、100 条 user story、contract shape、work package |
Expand Down
21 changes: 21 additions & 0 deletions STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ designed, and deliberately not active:
| General / multi-user Source and Resource ACLs | Dogfood uses current mirrored File access plus Membership field rights. A separate deterministic Feishu twin proves bounded source-native Article ACL ingestion, but no live or general administration carrier is active |
| General content retrieval | Only the loopback File pgvector dogfood `Acquire` carrier is active |
| Local consumer expansion | The repo-local Claude Code skill and spawn-per-session local stdio MCP `Acquire` translator are active; pi and every broader MCP carrier remain `NOT_ACTIVE` |
| Maintainer Context CLI expansion | `context-engine-context` activates only loopback File `Acquire` and untrusted public Package inspection; citation open, evaluation replay, Control, ActionPlane, models, effects, and promotion are absent |
| Capability-aware agent operation discovery | ADR-0098 accepts a future generated short-lived `AgentOperationManifest`; no discovery endpoint, contract version, SDK method, or active manifest carrier exists |
| `Continue` carrier | The bounded dogfood composition keeps Continue unavailable; its private File `OpenCitation` carrier is active for UI citation closure |
| Structured acquisition / live surface context | ADR-0099 selects one exact bounded collaboration conversation as the first design workload; structured-family terms, provider carrier, live network, mixed-family Evidence, and surface attachments remain `NOT_ACTIVE` |
Expand Down Expand Up @@ -90,6 +91,26 @@ authentication, a second human, network exposure beyond the maintainer machine,
group/public audience, `Continue`, hybrid retrieval, non-File providers, and
external/network query embeddings remain `NOT_ACTIVE` pending issue #217.

### Read-only maintainer Context CLI

`context-engine-context query` sends one fresh closed `Acquire` through the
same loopback HTTP and environment-held bearer composition. Human output keeps
Package purpose/as-of/expiry, coverage, budget usage, every Block, and exact
Evidence/citation lineage together. Strict JSON is the validated public wire
document without a CLI wrapper; its one deliberate substitution is the
redeemable `egressGrant` value, which this non-egress caller never emits or
persists. `inspect` treats file or stdin JSON as untrusted and validates the
whole closed Package, digest, lineage, accounting, lifetime, and expiry before
rendering; the capture never becomes authority or reusable context.

Stable exits distinguish success, explicit refusal, service unavailable,
malformed Package, expired Package, and invalid local configuration. Empty
authorized context is an explicit non-enumerating refusal, not a corpus answer.
Dogfood `OpenCitation`, `Continue`, evaluation replay, remote/multi-user
operation, Control, ActionPlane, models, effects, and promotion remain absent or
`NOT_ACTIVE`; see the
[operator guide](./docs/operations/maintainer-context-cli.md).

### Repo-local Claude Code consumer

| ADR | Activates |
Expand Down
117 changes: 85 additions & 32 deletions adapters/http/dogfood_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import json
import os
from collections.abc import Mapping
from dataclasses import dataclass, field
from dataclasses import dataclass, field, fields, is_dataclass
from typing import Final, cast
from urllib.parse import urlsplit

Expand All @@ -19,6 +19,7 @@
DOGFOOD_SECRET_ENV: Final = "CONTEXT_ENGINE_DOGFOOD_SECRET"
MAX_QUERY_CHARACTERS: Final = 4_096
MAX_RESPONSE_BYTES: Final = 16 * 1024 * 1024
CALLER_REJECTED_STATUSES: Final = frozenset({400, 401, 422})


class DogfoodEvaluationUnavailable(RuntimeError):
Expand All @@ -29,6 +30,14 @@ class DogfoodSecretExclusionUnavailable(DogfoodEvaluationUnavailable):
"""The caller cannot keep configured secret material out of its output."""


class DogfoodCallerRejected(DogfoodEvaluationUnavailable):
"""The served composition refused this caller's credential or request.

Only the closed transport status is read; no response detail is retained,
so the distinction stays content-free while remaining actionable locally.
"""


def _require_exact_text(name: str, value: object, *, maximum: int = 512) -> str:
if (
type(value) is not str
Expand All @@ -48,12 +57,68 @@ def _require_opaque_ref(name: str, value: object) -> str:
return result


def validate_dogfood_query(value: object) -> str:
"""Validate one untrusted query before constructing a request."""

return _require_exact_text(
"dogfood query",
value,
maximum=MAX_QUERY_CHARACTERS,
)


def validate_dogfood_request_id(value: object) -> str:
"""Validate one non-secret request correlation reference."""

return _require_opaque_ref("dogfood request_id", value)


def _as_object(value: object, name: str) -> dict[str, object]:
if type(value) is not dict or any(type(key) is not str for key in value):
raise DogfoodEvaluationUnavailable(f"{name} is unavailable")
return cast(dict[str, object], value)


def _package_from_outcome(outcome: dict[str, object]) -> dict[str, object]:
package = _as_object(outcome.get("package"), "ContextPackage")
if type(package.get("blocks")) is not list or type(
package.get("evidence")
) is not list:
raise DogfoodEvaluationUnavailable("ContextPackage is unavailable")
return package


def reject_secret_material(secret: object, value: object) -> None:
"""Reject any value containing the given secret material.

Local consumers scan without a transport destination; only the exclusion
itself is required here, never a loopback URL or a bearer shape.
"""

if type(secret) is not str or not secret or secret.isspace():
raise DogfoodEvaluationUnavailable("secret material is unavailable")
pending: list[object] = [value]
while pending:
current = pending.pop()
if type(current) is str:
if secret in current:
raise DogfoodSecretExclusionUnavailable(
"dogfood secret exclusion is unavailable"
)
continue
if type(current) is dict:
document = cast(dict[object, object], current)
pending.extend(document.keys())
pending.extend(document.values())
continue
if type(current) in {list, tuple}:
sequence = cast(list[object] | tuple[object, ...], current)
pending.extend(sequence)
continue
if is_dataclass(current) and not isinstance(current, type):
pending.extend(getattr(current, item.name) for item in fields(current))


@dataclass(frozen=True, slots=True)
class DogfoodHttpConfiguration:
"""Loopback-only destination and redacted bearer secret."""
Expand Down Expand Up @@ -94,23 +159,7 @@ def __post_init__(self) -> None:
def reject_secret_material(self, value: object) -> None:
"""Reject any decoded value containing the configured bearer."""

pending = [value]
while pending:
current = pending.pop()
if type(current) is str:
if self.secret in current:
raise DogfoodSecretExclusionUnavailable(
"dogfood secret exclusion is unavailable"
)
continue
if type(current) is dict:
document = cast(dict[object, object], current)
pending.extend(document.keys())
pending.extend(document.values())
continue
if type(current) in {list, tuple}:
sequence = cast(list[object] | tuple[object, ...], current)
pending.extend(sequence)
reject_secret_material(self.secret, value)

@classmethod
def load(
Expand Down Expand Up @@ -144,11 +193,7 @@ def __init__(self, configuration: DogfoodHttpConfiguration) -> None:
def resolve_acquire(self, *, query: str, request_id: str) -> dict[str, object]:
"""Invoke one Acquire without collapsing a closed refusal outcome."""

query = _require_exact_text(
"dogfood query",
query,
maximum=MAX_QUERY_CHARACTERS,
)
query = validate_dogfood_query(query)
self._configuration.reject_secret_material(query)
return self.resolve_acquire_document(
acquire={"kind": "acquire", "need": {"query": query}},
Expand Down Expand Up @@ -207,9 +252,18 @@ async def resolve_acquire_document_async(
return self._validated_outcome(bytes(raw))
except DogfoodEvaluationUnavailable:
raise
except httpx.HTTPStatusError as error:
if error.response.status_code in CALLER_REJECTED_STATUSES:
raise DogfoodCallerRejected(
"dogfood resolve rejected this caller"
) from None
raise DogfoodEvaluationUnavailable(
"dogfood resolve is unavailable"
) from None
except (
httpx.HTTPError,
OSError,
RecursionError,
ValueError,
UnicodeDecodeError,
ValidationError,
Expand All @@ -224,7 +278,7 @@ def _validated_request(
acquire: dict[str, object],
request_id: str,
) -> tuple[bytes, str]:
request_id = _require_opaque_ref("dogfood request_id", request_id)
request_id = validate_dogfood_request_id(request_id)
try:
validated_acquire = AcquireWire.model_validate(acquire)
except ValidationError:
Expand Down Expand Up @@ -263,24 +317,23 @@ def _validated_outcome(self, raw: bytes) -> dict[str, object]:
)
outcome = _as_object(json.loads(raw), "dogfood resolve response")
self._configuration.reject_secret_material(outcome)
if outcome.get("kind") not in {"resolved", "request_not_available"}:
kind = outcome.get("kind")
if type(kind) is not str or kind not in {
"resolved",
"request_not_available",
}:
raise DogfoodEvaluationUnavailable("dogfood resolve outcome is unavailable")
return outcome

def acquire(self, *, query: str, request_id: str) -> dict[str, object]:
"""Invoke one Acquire and require a ContextPackage for evaluation."""

outcome = self.resolve_acquire(query=query, request_id=request_id)
package = outcome.get("package")
if (
outcome.get("kind") != "resolved"
or type(package) is not dict
or type(package.get("blocks")) is not list
or type(package.get("evidence")) is not list
):
if outcome.get("kind") != "resolved":
raise DogfoodEvaluationUnavailable(
"dogfood resolve did not return a ContextPackage"
)
_package_from_outcome(outcome)
return outcome

def reject_secret_material(self, value: object) -> None:
Expand Down
38 changes: 4 additions & 34 deletions applications/dogfood_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
from adapters.http.dogfood_client import (
MAX_QUERY_CHARACTERS,
MAX_RESPONSE_BYTES,
_as_object,
_package_from_outcome,
_require_exact_text,
_require_opaque_ref,
)
from adapters.http.dogfood_client import (
DogfoodEvaluationUnavailable as DogfoodEvaluationUnavailable,
Expand All @@ -44,25 +48,6 @@
MAX_GOLDEN_CASES: Final = 50


def _require_exact_text(name: str, value: object, *, maximum: int = 512) -> str:
if (
type(value) is not str
or not value
or value.isspace()
or value != value.strip()
or len(value) > maximum
):
raise DogfoodEvaluationUnavailable(f"{name} is unavailable")
return value


def _require_opaque_ref(name: str, value: object) -> str:
result = _require_exact_text(name, value)
if any(character.isspace() for character in result):
raise DogfoodEvaluationUnavailable(f"{name} is unavailable")
return result


def _require_relative_path(value: object) -> str:
path = _require_exact_text("golden expected path", value, maximum=1_024)
parsed = PurePosixPath(path)
Expand Down Expand Up @@ -217,12 +202,6 @@ def _golden_set_document(golden_set: GoldenSet) -> dict[str, object]:
}


def _as_object(value: object, name: str) -> dict[str, object]:
if type(value) is not dict or any(type(key) is not str for key in value):
raise DogfoodEvaluationUnavailable(f"{name} is unavailable")
return value


def _closed_object(
value: object,
name: str,
Expand Down Expand Up @@ -363,15 +342,6 @@ class ResolveCaller(Protocol):
def acquire(self, *, query: str, request_id: str) -> dict[str, object]: ...


def _package_from_outcome(outcome: dict[str, object]) -> dict[str, object]:
package = _as_object(outcome.get("package"), "ContextPackage")
if type(package.get("blocks")) is not list or type(
package.get("evidence")
) is not list:
raise DogfoodEvaluationUnavailable("ContextPackage is unavailable")
return package


def _observed_evidence(
outcome: dict[str, object],
) -> tuple[EvidenceIdentity, ...]:
Expand Down
Loading
Loading