diff --git a/docs/context-map.md b/docs/context-map.md new file mode 100644 index 000000000..0546dcc01 --- /dev/null +++ b/docs/context-map.md @@ -0,0 +1,132 @@ +# OriginWeave Context Map + +This map records bounded-context ownership and allowed dependency direction. Protected `main` remains shipment truth. A context marked **active PR** exists only on the referenced branch until it is integrated. + +## Subdomains + +| Subdomain | Classification | Current owner | Responsibility | +|---|---|---|---| +| Browser authority contracts | Core | `originweave-core` | Stable no-I/O values for origin identity, action intent, capability, risk, approval scope, action requests, and policy context | +| Policy decision | Core | `originweave-policy` | Deterministic fail-closed authorization over typed action requests and explicit context | +| Destination authority | Core | `originweave-destination` | Resolved-address classification, approved resolution snapshots, redirect authorization, and DNS-rebinding resistance | +| Direct transport authority | Supporting | `originweave-network` | Single-use direct TCP plans and exact operating-system peer proof | +| TLS service identity | Supporting | `originweave-tls` | WebPKI authentication for an already verified direct TCP stream | +| Resource governance | Supporting | `originweave-resource` | Bounded task budgets and deterministic mitigation plans | +| Evidence contracts | Supporting | `originweave-evidence` | Value-redacted network evidence and source-bound provenance records | +| Browser Agent Protocol | Supporting | `originweave-bap` | Browser-agent protocol contracts and lifecycle vocabulary | +| MCP protocol adapter | Generic integration | `originweave-mcp` (**active PR #272**) | MCP version/method/tool discovery, routing integrity, and translation into typed OriginWeave action contracts; grants no execution authority | + +## Product bounded contexts + +The product vocabulary below names stable responsibility boundaries even when the final implementation has not reached protected `main`. A planned or active-PR context is not shipment truth and must not be used to claim implemented browser behavior. + +| Bounded Context | Classification | Current implementation status | Authority boundary | +|---|---|---|---| +| Browser Session | Core | **planned / active-PR work**, not integrated on protected-main | Owns browser-process/session lifetime and attachment identity; session existence does not grant action, network, profile, secret, or evidence authority | +| Profile/Identity Boundary | Core | **planned / active-PR work**, not integrated on protected-main | Owns explicit profile selection, isolation, and identity attachment; the user default profile is never ambient agent authority | +| Semantic Observation | Core | **planned / active-PR work**, not integrated on protected-main | Owns source-bound semantic observation and node identity; observed page text remains untrusted input and does not become an instruction | +| Typed Action Execution | Core | **planned / active-PR work**, not integrated on protected-main | Owns execution of already-authorized typed browser actions; protocol or selector validity alone does not authorize execution | +| Resource Governance | Supporting | `originweave-resource` contracts are on protected-main; wider browser/process enforcement remains incremental | Owns bounded resource budgets and deterministic mitigation; budget availability does not confer browser or policy authority | +| Scraping/Extraction | Supporting | **planned / active-PR work**, not integrated as a complete bounded context on protected-main | Owns extraction semantics and crawl-specific constraints; extracted content is data, not trusted control input | +| Provenance | Supporting | `originweave-evidence` contracts are on protected-main; durable WARC/PROV persistence remains planned | Owns source/evidence identity and redaction-safe provenance; an evidence record proves only its declared invariant | +| Extension Policy | Supporting | **planned / active-PR work**, not integrated as a complete bounded context on protected-main | Owns extension/native-messaging admission and isolation; extension installation or connection does not inherit session authority | +| Secret Broker | Supporting | policy contracts for broker-handle use exist; complete broker lifecycle remains planned | Owns opaque secret-handle lifecycle and delivery; secret values never enter model-visible context and handles grant only their explicit scope | +| Agent Integration | Generic integration | BAP contracts are on protected-main; MCP ownership repair is **active PR #272**; complete MCP/WebMCP runtime remains planned | Owns external agent-protocol anti-corruption adapters; protocol success does not transfer policy, browser, network, secret, approval, or evidence authority | + +These contexts may share published contracts, but cross-context success **does not transfer authority**. New implementation must land in the context that owns the invariant instead of using `originweave-core`, an adapter, or another convenient technical package as a temporary dumping ground. + +Planned contexts such as HTTP, proxy, WebDriver BiDi/CDP adapters, WARC/PROV persistence, and release benchmarks remain planned until code reaches protected `main`. Their planned names do not grant ownership to unrelated current crates. + +## Context relationships + +```text +External MCP client + | + v ++--------------------+ +| MCP adapter | generic integration / ACL +| originweave-mcp | ++---------+----------+ + | route/action equality; mismatch stays adapter-owned + | accepted routes enter ordinary policy evaluation + v ++--------------------+ +----------------------+ +| Policy decision |------->| Browser authority | +| originweave-policy | | originweave-core | ++--------------------+ +----------------------+ + ^ ^ + | | + +------ typed request ------+ + | + +-------------------+-------------------+ + | | | + +-------+--------+ +-------+--------+ +-------+--------+ + | Destination | | Evidence | | Resource | + | authority | | contracts | | governance | + +-------+--------+ +----------------+ +----------------+ + | + v + +-------+--------+ + | Direct TCP | + | authority | + +-------+--------+ + | + v + +-------+--------+ + | TLS identity | + +----------------+ +``` + +The arrows show allowed contract dependence, not runtime authority transfer. A downstream context must still validate its own invariants. In particular, an origin grant does not authorize a DNS result, a destination approval does not prove the connected peer, a verified peer does not prove TLS service identity, and an MCP route does not authorize an action. + +## Relationship contracts + +### MCP adapter → browser authority contracts + +Relationship: **Anti-Corruption Layer / conformist at the protocol edge**. + +The MCP adapter may depend on stable OriginWeave action vocabulary from `originweave-core`. External MCP method names, tool names, protocol versions, cursors, and request metadata remain adapter types. They must not be promoted into `originweave-core` domain entities. The adapter maps a bounded, validated MCP request to an existing typed action kind and does not invent capabilities or approvals. + +### MCP adapter → policy decision + +Relationship: **published contract consumption**. + +A route/action mismatch remains adapter-owned as `McpRouteRejection::ActionMismatch`; it is not a policy `DenialReason` and never enters policy evaluation. After the adapter proves MCP route/action equality, it may call the protocol-independent policy API published by `originweave-policy`. Policy owns authorization invariants and depends only on stable OriginWeave domain contracts; it does not import MCP DTOs, `ValidatedMcpToolCall`, MCP-specific denial reasons, or other adapter vocabulary. MCP owns protocol validation and the protocol-to-policy bridge. Neither context may absorb the other's responsibility. + +### Destination authority → browser authority contracts + +Relationship: **customer/supplier through stable values**. + +Destination policy consumes canonical origin identity but owns address classification, resolution snapshots, rebinding checks, and redirect destination decisions. `Origin` is a logical web identity, not an SSRF decision. + +### Direct transport authority → destination authority + +Relationship: **customer/supplier through an approved snapshot**. + +The network boundary accepts only a concrete address authorized by an origin-bound `ResolutionSnapshot`. It performs no DNS resolution and cannot reinterpret an origin grant as socket authority. + +### TLS service identity → direct transport and destination authority + +Relationship: **customer/supplier through verified transport evidence**. + +TLS consumes the already connected direct stream, the canonical HTTPS origin, and explicit trust material. It may not reconnect, re-resolve, or replace exact peer evidence with certificate success. + +### Evidence and resource contexts + +Relationship: **published contracts, no shared mutable kernel**. + +Evidence and resource governance expose bounded value contracts. Browser/protocol adapters may produce telemetry or evidence inputs, but these contexts do not acquire browser, network, policy, or secret authority from those callers. + +## Dependency rules + +1. Domain/security contracts must not import MCP, BiDi, CDP, Chromium SDK, HTTP client, persistence, UI, or provider DTOs. +2. Protocol adapters may depend inward on stable OriginWeave contracts and policy ports; stable contracts and policy must not depend outward on protocol adapters. +3. Policy authorization remains centralized in `originweave-policy`; adapters cannot duplicate or weaken it. Protocol-integrity failures remain owned by the adapter that can interpret them. +4. Cross-context calls use public crate APIs or explicit application ports. Direct access to another context's internals is forbidden. +5. No context may infer authority from another context's success. Each boundary emits evidence specific to the invariant it proves. +6. Shared Kernel additions require an accepted ADR and must be smaller than the contexts that consume them. `originweave-core` is not a dumping ground for integration DTOs. +7. Planned contexts remain separate responsibilities even before their crates exist. New work must not be parked temporarily in `originweave-core` merely because the final context has not yet been created. + +## Machine-checkable fitness + +`tests/test_repository_contract.py` enforces the first MCP ownership slice introduced with PR #272: `originweave-mcp` is a workspace package, MCP routing does not live under `originweave-core`, the adapter depends inward on `originweave-core` and `originweave-policy`, and policy depends only on `originweave-core` without MCP protocol or adapter vocabulary. The adapter owns route/action integrity rejection and delegates only matching routes to the ordinary policy evaluator. `tests/test_ddd_documentation_contract.py` binds this Context Map to the same ownership language and the explicit product bounded-context vocabulary above. Additional context relationships should gain equivalent import/dependency fitness checks when their production boundaries land. diff --git a/docs/tactical-ddd-map.md b/docs/tactical-ddd-map.md new file mode 100644 index 000000000..2f2d3d6e2 --- /dev/null +++ b/docs/tactical-ddd-map.md @@ -0,0 +1,70 @@ +# OriginWeave Tactical DDD Map + +This document classifies the tactical DDD building blocks that actually exist in OriginWeave. It is deliberately conservative: a type is not called an Entity, Aggregate, Repository, or Domain Event merely because that pattern may be useful later. Protected `main` is shipment truth; active PR #272 is identified separately; planned work is not promoted into the current model. + +## Classification rules + +- **Value Object**: immutable domain/security meaning is carried by validated values and equality, not by a mutable lifecycle identity. +- **Entity**: identity persists while domain state changes through an owned lifecycle. +- **Aggregate / Aggregate Root**: one root owns a consistency boundary and is the only mutation entry point for the aggregate invariant. +- **Domain Service**: protocol-independent domain logic that does not naturally belong to one Value Object or Entity. +- **Repository**: an inward-facing domain/application port for loading and persisting an Aggregate or other explicitly durable domain state. A database client, table, object-store SDK, or adapter is not itself the domain Repository contract. +- **Domain Event**: a domain-owned fact emitted after a domain state transition. External protocol events such as WebDriver BiDi notifications are integration events until an adapter translates and admits them into a domain-owned fact. +- **Invariant**: a condition that must remain true at the owning boundary and must fail closed when it cannot be established. + +## Current tactical model + +| Building block | Current code/API truth | Status | Required interpretation | +|---|---|---|---| +| Value Object | `originweave_core::Origin`, `BrowserSessionId`, `BrowsingContextId`, `DocumentEpoch`, `ObservedNodeHandle`, `ActionRequest`, `PolicyContext`, `ApprovalScope` | protected-main | Validated immutable authority/security values. Their identifiers do not by themselves imply mutable Entity semantics. | +| Value Object | `originweave_policy::SensitiveDataAuthority`, `SensitiveDataRequest`, `DisclosureScope`, `SensitiveValueHandleScope`, `HandleUseRequest`, `Decision`, `DisclosureDecision`, `HandleUseDecision` | protected-main | Purpose-bound authority and deterministic decision values; none stores protected field bytes or owns broker persistence. | +| Entity | No explicit mutable domain Entity is owned by protected-main core/policy or active PR #272 | current truth | `BrowserSessionId` and `BrowsingContextId` are identities, not Entities. Do not call a future browser process/session object an Entity until its lifecycle state and invariants are implemented behind an owned API. | +| Aggregate / Aggregate Root | No explicit Aggregate Root is implemented by protected-main core/policy or active PR #272 | current truth | The planned Browser Session bounded context may eventually own an Aggregate Root, but the name is not a shipment claim. Protocol adapters must not become substitute aggregate roots. | +| Domain Service | `originweave_policy::evaluate`, `evaluate_disclosure`, `evaluate_handle_use` | protected-main | Pure deterministic policy services. They perform no browser/network/storage I/O and return explicit fail-closed decisions. | +| Repository | No domain Repository contract is introduced by protected-main core/policy or active PR #272 | current truth | Durable WARC/PROV, tenant persistence, KMS/object storage, retention, legal hold, deletion, and offline replay remain issue #199 work. When persistence lands, inward repository ports must remain provider-neutral and storage adapters must depend inward on them. | +| Domain Event | No explicit OriginWeave domain-event type is introduced by protected-main core/policy or active PR #272 | current truth | WebDriver BiDi `browsingContext.navigationCommitted`, MCP requests, HTTP messages, and other wire events are adapter-owned integration events. They cannot be placed in core or treated as domain authority without translation, correlation, and invariant admission. | +| Invariant | Canonical origin admission, authority-bound observed-node reuse, deterministic action policy, purpose-bound sensitive-data scope, and fail-closed approval/secret rules | protected-main | Constructors/evaluators own these conditions. Callers cannot recover authority from a failed or ambiguous validation. | +| Invariant | MCP method/tool/action correlation and route/action equality | active PR #272 | This is adapter-owned protocol integrity. `McpRouteRejection` does not become a policy denial and the adapter delegates only an admitted typed action to protocol-independent policy. | + +## Aggregate and Entity admission rule + +The first real Chromium Agent Task vertical must not manufacture an Aggregate merely to satisfy a diagram. When browser-session lifecycle code reaches its owning bounded context, classify it as an Entity or Aggregate Root only if all of the following are true in code and tests: + +1. a stable domain identity survives meaningful state transitions; +2. one owning API controls those transitions; +3. the consistency boundary is explicit, including session/context/origin/document-epoch relationships; +4. teardown and crash recovery close the lifecycle rather than leaving adapter-owned ambient state; +5. external BiDi/CDP/MCP identifiers remain ACL/integration values and cannot mutate the domain object directly; +6. tests prove stale epoch, wrong context, wrong origin, replay, teardown, and crash paths fail closed. + +Until then, current identifiers and immutable authority values remain Value Objects. + +## Domain-service boundary + +`originweave-policy` is the current clearest Domain Service boundary. Its evaluators accept complete typed domain values and return deterministic decisions. They do not open sockets, call Chromium, parse MCP wire DTOs, resolve secrets, persist records, or invoke an LLM. Browser/network/secret/evidence adapters may consume a decision, but successful adapter I/O cannot reverse or widen that decision. + +MCP remains an external protocol ACL. Active PR #272 may translate MCP method/tool vocabulary into an existing typed `ActionRequest` and call the published policy API. Core and policy must not import MCP, WebDriver BiDi, CDP, Chromium SDK, HTTP-client, provider, persistence, or UI DTOs. + +## Repository and database truth + +There is no OriginWeave-owned production database or domain Repository introduced by PR #272/#273. That absence is intentional and machine-visible; documentation must not draw repository boxes or ERD tables as if they were shipped. + +Issue #199 owns the durable extraction/evidence direction. When that work introduces persistence: + +- the domain/application Repository port is defined independently of PostgreSQL, object-storage, KMS, WARC transport, or cloud-provider SDK types; +- adapters implement the port and preserve tenant, retention, legal-hold, deletion, replay, and cryptographic-evidence invariants; +- database objects use multi-word `snake_case` names and represent only data owned by the bounded context; +- cross-context authoritative tables are not copied into OriginWeave and cross-service SQL is not used as an integration contract; +- `context-graph-contracts` and `enterprise-architecture-core` remain the owners of shared provenance/context/identity/architecture contracts when those shared contracts are required. + +## Domain-event boundary + +A protocol notification is not a Domain Event. In particular, WebDriver BiDi `browsingContext.navigationCommitted` is external evidence from a browser adapter. It becomes usable domain state only after the adapter correlates the event to the pinned session/context, establishes the canonical origin/document epoch transition, applies bounds, and invokes the owning domain/application contract. A command ACK is likewise not a post-condition event. + +If OriginWeave later publishes domain events, each event must have an owning bounded context, typed payload, version, causation/correlation identity, explicit invariant that was established before emission, and tests proving that wire/protocol DTOs do not leak into the event contract. Shared event/provenance schemas should reuse released `context-graph-contracts` contracts rather than duplicate them locally. + +## Fitness and traceability + +`tests/test_ddd_documentation_contract.py` keeps this tactical classification explicit alongside `docs/context-map.md` and `docs/ubiquitous-language.md`. `tests/test_repository_contract.py` enforces the MCP dependency direction introduced by active PR #272. Rust unit/integration tests remain the authority for the constructors and deterministic evaluators named above; documentation tests do not substitute for production behavior, exact-head CI, coverage, security, or a real Chromium E2E. + +Any future code change that introduces an Entity, Aggregate Root, Repository, or Domain Event must update this file, the Context Map, relevant ADR/API/ERD material, and executable fitness tests in the same change. Planned work must remain marked planned until it reaches protected `main`. \ No newline at end of file diff --git a/docs/traceability/mcp-authority-route.md b/docs/traceability/mcp-authority-route.md index 45068334e..4a4cfe1bf 100644 --- a/docs/traceability/mcp-authority-route.md +++ b/docs/traceability/mcp-authority-route.md @@ -3,7 +3,7 @@ - **`tools/call` capability maturity:** `IMPLEMENTED_ON_PROTECTED_MAIN` - **`tools/list` capability maturity:** `IMPLEMENTED_ON_PROTECTED_MAIN` - **Protected-main owning work:** merged PR #168 (`tools/call`) and PR #170 (`tools/list`) -- **Active architecture repair:** PR #272 (`originweave-mcp` adapter boundary) +- **Active architecture repair:** active PR #272 (`originweave-mcp` adapter boundary) - **Complete MCP adapter status:** `PLANNED` - **Governing decision:** ADR 0107 @@ -13,7 +13,11 @@ Protected `main@c789b802fc98a8d7fd8c09d9327f36828054d2a1` contains the bounded R A successful MCP routing value proves protocol integrity only. It grants no capability, origin, approval, secret, browser, tenant, persistence, network, evidence, or ambient execution authority. Browser and policy authority remain in their OriginWeave bounded contexts. -PR #272 is an active DDD repair that moves the external MCP protocol surface into `originweave-mcp` while preserving inward dependency direction: the adapter may consume stable core contracts and the protocol-independent policy API, but core and policy must not depend outward on MCP transport types. The move is active-PR evidence, not protected-main shipment. +PR #272 is an active DDD repair that moves the external MCP protocol surface into `originweave-mcp` while preserving inward dependency direction: the adapter may consume stable core contracts and the protocol-independent policy API, but core and policy must not depend outward on MCP transport types. The move is active-PR evidence, not protected-main behavior. + +## Bounded-context correction + +Protected main still owns the integrated foundation at `crates/originweave-core/src/mcp.rs`. Active PR #272 moves the protocol implementation to `crates/originweave-mcp/src/routing.rs`; `originweave_mcp::evaluate_mcp` retains route/action mismatch as adapter-owned `McpRouteRejection` and calls `originweave_policy::evaluate` only after protocol integrity is established. Core remains the stable action and authority vocabulary, while policy remains protocol-independent. ## Final 2026-07-28 per-request envelope diff --git a/docs/ubiquitous-language.md b/docs/ubiquitous-language.md new file mode 100644 index 000000000..739ccae7e --- /dev/null +++ b/docs/ubiquitous-language.md @@ -0,0 +1,55 @@ +# OriginWeave Ubiquitous Language + +This glossary defines terms that must mean the same thing in code, tests, ADRs, APIs, issues, and product documentation. When an external protocol uses a conflicting term, the adapter translates it at the boundary instead of changing the OriginWeave meaning. + +| Term | Meaning in OriginWeave | Ownership / invariant | +|---|---|---| +| Origin | Canonical logical web identity: scheme, host, and effective port | `originweave-core`; never means a resolved IP address or an SSRF authorization | +| Destination | Concrete network address proposed or approved for connection | `originweave-destination`; must be classified and explicitly authorized | +| Resolution Snapshot | Non-empty, origin-bound set of approved resolver results | `originweave-destination`; later DNS answers may contract but not expand without reauthorization | +| Connection Plan | Bounded, single-use authority to attempt one or more exact direct TCP connections | `originweave-network`; contains no hostname-resolution authority | +| Direct TCP Connection | Connected stream whose operating-system peer has been checked against the requested canonical socket | `originweave-network`; proves peer equality, not TLS identity | +| TLS Service Identity | WebPKI-authenticated service identity for the canonical HTTPS origin on an existing verified TCP stream | `originweave-tls`; cannot reconnect or re-resolve | +| Session Mode | Human, Assist, Agent Task, or Crawler execution posture | `originweave-core`; determines the authority model, not just UI presentation | +| Execution Purpose | Explicit reason for a governed execution, such as a user-delegated task or public crawl | `originweave-core`; must agree with the allowed session mode | +| Capability | Explicit permission category required by an `ActionKind` | `originweave-core`; absence is denial, never inferred from ambient browser state | +| Action Kind | Closed typed operation that an agent may request | `originweave-core`; production agents do not receive unrestricted JavaScript as a standard action | +| Action Request | Complete typed request binding action, source/target origin, instruction source, secret-delivery mode, and intent digest | `originweave-core`; input to policy, not proof of authorization | +| Action Intent Digest | Immutable digest of the canonical complete user/enterprise intent relevant to approval | `originweave-core`; approval must bind to it exactly | +| Approval Scope | Exact action + target origin + intent digest tuple to which approval applies | `originweave-core`; scopes are not fungible across actions or destinations | +| Approval Evidence | User or enterprise evidence authorizing one exact `ApprovalScope` | `originweave-core`; stale or mismatched evidence fails closed | +| Policy Decision | `Allow`, `Deny`, or `RequireApproval` result for a typed request and explicit context | `originweave-policy`; adapters do not manufacture this decision | +| Instruction Source | Provenance category describing where an instruction originated | `originweave-core`; web/page content is data and cannot promote itself to trusted authority | +| Secret Delivery | Whether an action receives no secret or an opaque broker handle | `originweave-core`; raw secret values must not enter model context or generic action payloads | +| Risk Class | R0–R5 action-risk classification used by policy | `originweave-core`; R5 is non-delegable under the current contract | +| Evidence | Bounded, credential-free or value-redacted proof emitted by a boundary about the invariant it actually checked | `originweave-evidence` and producing contexts; evidence from one boundary cannot stand in for another | +| Resource Budget | Explicit bounded allowance for task/resource consumption | `originweave-resource`; hard pressure must reduce the active consumer and reject unsafe admission | +| Mitigation Plan | Deterministic cumulative response to observed resource pressure | `originweave-resource`; may combine actions rather than collapse simultaneous pressures into one flag | +| MCP Route | Validated MCP protocol-version/method/tool binding that maps to an OriginWeave `ActionKind` | `originweave-mcp` active PR #272; proves routing consistency only, not authorization | +| MCP Route Rejection | Fail-closed adapter outcome when a validated MCP route and the typed `ActionRequest` disagree about the action | `originweave-mcp`; `McpRouteRejection` is protocol-integrity evidence, not a policy `DenialReason` or authorization decision | +| MCP Tool | External MCP-visible name for one supported typed OriginWeave action | `originweave-mcp`; unknown or malformed names fail closed | +| Browser Agent Protocol (BAP) | OriginWeave browser-agent protocol contract and lifecycle vocabulary | `originweave-bap`; it is not a synonym for MCP, WebDriver BiDi, or CDP | +| Adapter | Boundary translating an external protocol/runtime representation into OriginWeave contracts | Protocol/infrastructure context; must not leak provider DTOs into domain contracts | +| Anti-Corruption Layer | Translation boundary that prevents an external model or foreign bounded context from redefining OriginWeave domain terms | Required at protocol/provider edges when vocabularies or authority models differ | +| Shared Kernel | Deliberately tiny, versioned contract surface shared by bounded contexts | `originweave-core` is the current kernel; additions require stable cross-context meaning, not convenience | +| Protected-main truth | Behavior and documentation integrated into protected `main` with its required governance evidence | Open PRs, predecessor checks, model comments, and planned modules are not protected-main truth | +| Active-PR evidence | Code, tests, checks, or documentation that exists on an open PR exact head but is not yet shipped | Must remain labeled as active until protected integration | + +## Terms that must not be collapsed + +These distinctions are security and product invariants, not editorial preferences: + +- **Origin ≠ destination ≠ TCP peer ≠ TLS service identity.** Each is proven at a different boundary. +- **MCP route ≠ policy decision ≠ browser execution.** Protocol validity cannot grant capability or approval. +- **MCP route rejection ≠ policy denial.** A protocol-integrity mismatch is owned by the MCP adapter and must not enter the policy domain as an MCP-specific denial reason. +- **Capability ≠ approval.** Possessing the capability to request an action does not satisfy approval requirements for its risk class. +- **Action request ≠ observed post-condition.** Dispatch success is not proof that the intended browser state was reached. +- **Evidence ≠ authority.** Evidence records what a boundary proved; possession of an evidence object does not create new permission. +- **Crawler policy ≠ access authorization.** Robots decisions govern crawl behavior but do not create authentication or data-access rights. +- **Planned context ≠ current implementation.** A name in the architecture roadmap is not shipped ownership until code and tests reach protected `main`. + +## Naming rules + +Public Rust types, modules, tests, API fields, database objects, and documentation should use these terms directly where they express the same concept. External names such as MCP `tools/call`, WebDriver BiDi command names, CDP domains, provider SDK objects, Chromium internals, or persistence DTOs stay at their adapters. Do not rename a domain concept merely to match an external provider vocabulary. + +Generic names such as `utils`, `helpers`, `common`, `services`, `models`, `misc`, `legacy`, or an unqualified `data`/`security`/`browser` directory do not establish domain ownership. A new package or module should name the stable responsibility it owns or live inside the already accepted bounded context that owns that responsibility. diff --git a/tests/test_ddd_documentation_contract.py b/tests/test_ddd_documentation_contract.py new file mode 100644 index 000000000..a463a0a26 --- /dev/null +++ b/tests/test_ddd_documentation_contract.py @@ -0,0 +1,137 @@ +"""Architectural documentation fitness for bounded-context ownership.""" + +from __future__ import annotations + +import pathlib +import tomllib +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +class DddDocumentationContractTests(unittest.TestCase): + """Keep the Context Map and Ubiquitous Language executable as repository contracts.""" + + def test_context_map_records_mcp_as_protocol_adapter_not_shared_kernel(self) -> None: + context_map = (ROOT / "docs/context-map.md").read_text(encoding="utf-8") + self.assertIn("originweave-mcp", context_map) + self.assertIn("Anti-Corruption Layer", context_map) + self.assertIn("MCP adapter", context_map) + self.assertIn("originweave-core", context_map) + self.assertIn("originweave-policy", context_map) + self.assertIn("must not depend outward on protocol adapters", context_map) + self.assertIn("MCP adapter → policy decision", context_map) + self.assertNotIn("Policy decision → MCP adapter", context_map) + self.assertIn("policy depends only on `originweave-core`", context_map) + self.assertIn("route/action mismatch remains adapter-owned", context_map) + + def test_cargo_dependency_direction_enforces_mcp_anti_corruption_layer(self) -> None: + manifests: dict[str, dict[str, object]] = {} + for crate in ("originweave-core", "originweave-policy", "originweave-mcp"): + manifest_path = ROOT / "crates" / crate / "Cargo.toml" + manifests[crate] = tomllib.loads(manifest_path.read_text(encoding="utf-8")) + + core_dependencies = manifests["originweave-core"].get("dependencies", {}) + policy_dependencies = manifests["originweave-policy"].get("dependencies", {}) + mcp_dependencies = manifests["originweave-mcp"].get("dependencies", {}) + + self.assertNotIn("originweave-mcp", core_dependencies) + self.assertNotIn("originweave-mcp", policy_dependencies) + self.assertEqual( + policy_dependencies.get("originweave-core"), + {"path": "../originweave-core"}, + ) + self.assertEqual( + mcp_dependencies.get("originweave-core"), + {"path": "../originweave-core"}, + ) + self.assertEqual( + mcp_dependencies.get("originweave-policy"), + {"path": "../originweave-policy"}, + ) + + def test_context_map_names_product_bounded_contexts_explicitly(self) -> None: + context_map = (ROOT / "docs/context-map.md").read_text(encoding="utf-8") + for bounded_context in ( + "Browser Session", + "Profile/Identity Boundary", + "Semantic Observation", + "Typed Action Execution", + "Resource Governance", + "Scraping/Extraction", + "Provenance", + "Extension Policy", + "Secret Broker", + "Agent Integration", + ): + with self.subTest(bounded_context=bounded_context): + self.assertIn(bounded_context, context_map) + self.assertIn("protected-main", context_map) + self.assertIn("active PR", context_map) + self.assertIn("planned", context_map) + self.assertIn("does not transfer authority", context_map) + + def test_tactical_ddd_map_keeps_patterns_truthful_and_protocol_independent(self) -> None: + tactical_map = (ROOT / "docs/tactical-ddd-map.md").read_text(encoding="utf-8") + for pattern in ( + "Value Object", + "Entity", + "Aggregate / Aggregate Root", + "Domain Service", + "Repository", + "Domain Event", + "Invariant", + ): + with self.subTest(pattern=pattern): + self.assertIn(pattern, tactical_map) + + self.assertIn("No explicit mutable domain Entity", tactical_map) + self.assertIn("No explicit Aggregate Root", tactical_map) + self.assertIn("No domain Repository contract", tactical_map) + self.assertIn("No explicit OriginWeave domain-event type", tactical_map) + self.assertIn("originweave_policy::evaluate", tactical_map) + self.assertIn("browsingContext.navigationCommitted", tactical_map) + self.assertIn("integration events", tactical_map) + self.assertIn("A command ACK is likewise not a post-condition event", tactical_map) + self.assertIn("multi-word `snake_case`", tactical_map) + self.assertIn("context-graph-contracts", tactical_map) + self.assertIn("must not import MCP, WebDriver BiDi, CDP", tactical_map) + + def test_ubiquitous_language_keeps_authority_terms_distinct(self) -> None: + glossary = (ROOT / "docs/ubiquitous-language.md").read_text(encoding="utf-8") + for term in ( + "Origin", + "Destination", + "Resolution Snapshot", + "Direct TCP Connection", + "TLS Service Identity", + "Action Request", + "Approval Scope", + "Policy Decision", + "MCP Route", + "MCP Route Rejection", + "Anti-Corruption Layer", + "Protected-main truth", + ): + with self.subTest(term=term): + self.assertIn(term, glossary) + self.assertIn("Origin ≠ destination ≠ TCP peer ≠ TLS service identity", glossary) + self.assertIn("MCP route ≠ policy decision ≠ browser execution", glossary) + + def test_mcp_traceability_names_current_and_active_ownership_separately(self) -> None: + trace = (ROOT / "docs/traceability/mcp-authority-route.md").read_text( + encoding="utf-8" + ) + self.assertIn("IMPLEMENTED_ON_PROTECTED_MAIN", trace) + self.assertIn("active PR #272", trace) + self.assertIn("crates/originweave-core/src/mcp.rs", trace) + self.assertIn("crates/originweave-mcp/src/routing.rs", trace) + self.assertIn("originweave_mcp::evaluate_mcp", trace) + self.assertIn("McpRouteRejection", trace) + self.assertIn("originweave_policy::evaluate", trace) + self.assertIn("protocol-independent", trace) + self.assertIn("active-PR evidence, not protected-main behavior", trace) + + +if __name__ == "__main__": + unittest.main()