From d2dc11a9ad49b266d27c33456e0498fe3b09e752 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 10 Sep 2026 17:06:52 +0900 Subject: [PATCH 1/7] docs(mcp): record merged tools/list as protected-main truth README and CHANGELOG still described merged PR #170 as active-PR-only while protected main already ships McpToolsListPage and mcp_tools_list_page with cache tests. Update buyer docs to shipped wording and add a failing-first contract test. Verified: 156 Python tests, cargo fmt/check/test/clippy/doc green. --- CHANGELOG.md | 2 +- README.md | 6 ++-- tests/test_mcp_shipped_contract.py | 45 ++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/test_mcp_shipped_contract.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f747adeae..68fd28143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Canonical HTTPS and loopback-origin boundary with case-normalized schemes and hosts, default-port normalization, IPv4/IPv6 handling, browser-special numeric-host rejection, and explicit malformed-input errors. - Typed browser actions, capabilities, risk classes, execution modes, robots decisions, secret-delivery contracts, immutable canonical action-intent digests, and intent-bound approval scopes. - Protected main now contains deterministic MCP `2026-07-28` stateless `tools/call` routing with bounded method/tool names, a single reviewed tool-to-action registry shared by routing and discovery metadata, and fail-closed policy binding that grants no ambient authority. The complete MCP adapter, transport serialization, discovery response handling, OAuth, browser I/O, and persistence remain planned. -- Active PR #170 adds conservative MCP `2026-07-28` `tools/list` discovery metadata derived from that protected-main catalog, with `resultType = complete`, zero freshness, private cache scope, no continuation cursor, per-request protocol/client-capability admission, and bounded protocol-version and method metadata validated before cross-field comparison. This remains active-PR evidence only and grants no browser, network, secret, approval, or Agent authority. +- Protected main now contains conservative MCP `2026-07-28` `tools/list` discovery metadata derived from that protected-main catalog (merged PR #170), with `resultType = complete`, zero freshness, private cache scope, no continuation cursor, per-request protocol/client-capability admission, and bounded protocol-version and method metadata validated before cross-field comparison. This grants no browser, network, secret, approval, or Agent authority. - Deterministic fail-closed policy evaluation for untrusted instructions, origin grants, crawler restrictions, execution-mode and purpose consistency, approvals, and brokered secrets. - Fail-closed resolved-destination policy with IPv4/IPv6 special-purpose and reviewed cloud-platform endpoint classification, IPv4-mapped canonicalization, explicit class grants, non-empty origin-bound DNS snapshots capped at 256 resolver addresses, concrete connection pinning, DNS-set expansion detection, and per-hop redirect reauthorization. - Bounded resolution-freshness authority with trusted monotonic approval time, capped non-zero validity, half-open use windows, non-expanding revalidation, and credential-free authorization timestamps. diff --git a/README.md b/README.md index 0942976cf..bf7b7c12a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ OriginWeave is a Chromium-compatible, Rust-first control plane for governed AI agents on the web. It is designed to let an agent observe, extract, and act without turning untrusted page content into authority, exposing secrets to a model, connecting to an unapproved network destination, accepting an unauthenticated web service, or losing the evidence required to explain what happened. -> Project status: pre-alpha. The current protected repository contains independently reusable safety, resolved-destination, direct TCP peer-binding, authenticated TLS service-identity, and bounded MCP `2026-07-28` stateless `tools/call` routing/policy foundations. Chromium, WebDriver BiDi, CDP, complete MCP, HTTP, proxy, WARC, and persistent provenance adapters are planned but not yet shipped. Active PR #170 implements only conservative `tools/list` discovery metadata on top of the protected-main MCP catalog; it remains non-shipped active-PR evidence and does not make the complete MCP adapter available. +> Project status: pre-alpha. The current protected repository contains independently reusable safety, resolved-destination, direct TCP peer-binding, authenticated TLS service-identity, bounded MCP `2026-07-28` stateless `tools/call` routing/policy foundations, and conservative `tools/list` discovery metadata on top of the protected-main MCP catalog. Chromium, WebDriver BiDi, CDP, complete MCP, HTTP, proxy, WARC, and persistent provenance adapters are planned but not yet shipped. The shipped `tools/list` boundary returns only conservative discovery metadata and does not make the complete MCP adapter available. ## Why OriginWeave @@ -40,7 +40,7 @@ The repository is organized as independently consumable Rust crates: - `originweave-resource`: task-level RAM, VRAM, thread, and frame-time budgets with cumulative mitigation plans. - `originweave-evidence`: universally value-redacted network evidence and source-bound provenance records. -Protected main additionally contains an `originweave-core` MCP routing registry and `originweave-policy` binding for the MCP `2026-07-28` `tools/call` boundary. That shipped foundation validates and maps an explicit tool name to an existing typed action while preserving normal OriginWeave policy. Active PR #170 adds non-shipped conservative `tools/list` discovery metadata derived from the same reviewed catalog. Neither boundary implements transport parsing, OAuth, browser control, secret materialization, persistence, or ambient authority. +Protected main additionally contains an `originweave-core` MCP routing registry and `originweave-policy` binding for the MCP `2026-07-28` `tools/call` boundary, and conservative `tools/list` discovery metadata derived from the same reviewed catalog. The shipped `tools/call` foundation validates and maps an explicit tool name to an existing typed action while preserving normal OriginWeave policy. The shipped `tools/list` boundary returns only conservative discovery metadata. Neither boundary implements transport parsing, OAuth, browser control, secret materialization, persistence, or ambient authority. See [ARCHITECTURE.md](ARCHITECTURE.md) and the [architecture decision records](docs/adr/) for binding design decisions. @@ -99,7 +99,7 @@ isolated Chromium session → redacted provenance bundle ``` -Subsequent work connects the live Chromium network service, adds explicit proxy and download policy, WARC/PROV persistence, completes the MCP and Browser Agent Protocol adapters beyond the protected-main `tools/call` foundation and active `tools/list` refinement, expands extension compatibility testing, adds GPU/RAM telemetry and prompt-injection benchmarks, and builds an accessible approval interface. See [docs/product-roadmap.md](docs/product-roadmap.md). +Subsequent work connects the live Chromium network service, adds explicit proxy and download policy, WARC/PROV persistence, completes the MCP and Browser Agent Protocol adapters beyond the protected-main `tools/call` and `tools/list` foundations, expands extension compatibility testing, adds GPU/RAM telemetry and prompt-injection benchmarks, and builds an accessible approval interface. See [docs/product-roadmap.md](docs/product-roadmap.md). ## Hourly product-development loop diff --git a/tests/test_mcp_shipped_contract.py b/tests/test_mcp_shipped_contract.py new file mode 100644 index 000000000..18d3ab65a --- /dev/null +++ b/tests/test_mcp_shipped_contract.py @@ -0,0 +1,45 @@ +"""Regression contract: protected-main MCP tools/list shipment must be documented as shipped.""" + +from __future__ import annotations + +import pathlib +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +class McpShippedContractTests(unittest.TestCase): + """Buyer-facing docs must not describe merged tools/list code as active-PR only.""" + + def test_tools_list_code_is_present_on_protected_main(self) -> None: + """The shipped tools/list boundary must exist in tree before docs claim it.""" + mcp_source = (ROOT / "crates/originweave-core/src/mcp.rs").read_text( + encoding="utf-8" + ) + self.assertIn("McpToolsListPage", mcp_source) + self.assertIn("mcp_tools_list_page", mcp_source) + self.assertTrue( + (ROOT / "crates/originweave-core/tests/mcp_tools_list_cache.rs").is_file() + ) + + def test_readme_does_not_claim_merged_tools_list_is_active_pr_only(self) -> None: + """README must not retain the pre-merge Active PR #170 wording after #170 merged.""" + readme = (ROOT / "README.md").read_text(encoding="utf-8") + self.assertNotIn("Active PR #170", readme) + self.assertNotIn("active `tools/list` refinement", readme) + + def test_readme_describes_shipped_tools_list_boundary(self) -> None: + """README must describe both tools/call and tools/list as protected-main behavior.""" + readme = (ROOT / "README.md").read_text(encoding="utf-8") + self.assertIn("tools/call", readme) + self.assertIn("tools/list", readme) + self.assertIn("Protected main", readme) + + def test_changelog_does_not_claim_merged_tools_list_is_active_pr_only(self) -> None: + """CHANGELOG must not retain the pre-merge Active PR #170 wording after #170 merged.""" + changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8") + self.assertNotIn("Active PR #170", changelog) + + +if __name__ == "__main__": + unittest.main() From beaecc70108a5cb81569ab036c44fd03d13d72ef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 28 Sep 2026 15:40:54 +0900 Subject: [PATCH 2/7] docs: align buyer docs with merged foundation maturity Protected main already contains the merged MCP tools/list boundary, resolution and revocation freshness primitives, extension-grant proofs, the controlled Agent Task fixture, and credential-free handle evidence. Buyer documents still described several of those lanes as active PRs. Record the live split: those merged foundations stay on protected main, open #46, #50, #55, #61, and #64 stay active, and closed unmerged #54 supplies no implementation. Contract tests lock that wording. --- docs/DOCUMENTATION_FITNESS.md | 8 +-- .../0107-browser-protocol-adapter-strategy.md | 8 +-- docs/doctoring.md | 2 +- docs/product-technical-gap-baseline.md | 2 +- docs/traceability/README.md | 22 +++---- docs/traceability/mcp-authority-route.md | 16 ++--- .../resolution-freshness-authority.md | 22 +++---- .../tls-revocation-freshness-authority.md | 12 ++-- tests/test_documentation_fitness_contract.py | 17 ++++++ tests/test_freshness_traceability_contract.py | 60 ++++++++++++++++++- tests/test_mcp_shipped_contract.py | 8 +++ tests/test_product_documentation_contract.py | 52 ++++++++++++++++ 12 files changed, 180 insertions(+), 49 deletions(-) diff --git a/docs/DOCUMENTATION_FITNESS.md b/docs/DOCUMENTATION_FITNESS.md index 69f603252..5a8f3751c 100644 --- a/docs/DOCUMENTATION_FITNESS.md +++ b/docs/DOCUMENTATION_FITNESS.md @@ -92,13 +92,13 @@ An early audit incorrectly called resource-pressure and hourly-automation views ### 3.8 Resolution freshness authority -Active #47 → #50 → #54 progressively binds approved resolution state to first-party network planning and rechecks freshness immediately before socket I/O under trusted monotonic time. +Merged #47 provides the protected-main freshness primitive; open #50 proposes first-party network planning and closed, unmerged #54 proposed rechecking freshness immediately before socket I/O under trusted monotonic time. The complete resolution-to-socket boundary remains partial. **Resolution:** this refines Accepted ADR 0004 rather than introducing a resolver service, proxy/PAC authority, wall-clock authority, persistence owner or new deployed component. ### 3.9 TLS revocation-material freshness -Active #48 provides a bounded freshness primitive for already verified revocation material. +Merged #48 provides a protected-main bounded freshness primitive for already verified revocation material; acquisition, authenticity, cache, and TLS composition remain unimplemented. **Resolution:** this is not OCSP/CRL acquisition, signature/path validation, cache operation or an unrevoked-certificate claim. No fictitious revocation-service topology is added. @@ -146,7 +146,7 @@ Active #61 gives the page main world and the MV3 content script the same JavaScr ### 3.17 Extension proposal authority and secret approval composition -Active #62/#63 exercise two sides of one architectural separator. #62 first proves the exact extension/session/context `ProposeTypedAction` grant is present and then requires ordinary Agent policy to reject origin/capability/instruction/secret widening. #63 gives the Agent context its independent `FillSecret` capability and broker-handle delivery request, but still requires the ordinary high-risk result `RequireApproval(RiskClass::R3)`. +Merged #62/#63 exercise two sides of one architectural separator. #62 first proves the exact extension/session/context `ProposeTypedAction` grant is present and then requires ordinary Agent policy to reject origin/capability/instruction/secret widening. #63 gives the Agent context its independent `FillSecret` capability and broker-handle delivery request, but still requires the ordinary high-risk result `RequireApproval(RiskClass::R3)`. **Resolution:** extension proposal permission can neither mint Agent capability/origin/secret authority nor manufacture approval. These are regression proofs over existing boundaries, not a secret broker, browser adapter, approval service or new trust domain. Proposed ADR 0013 already captures the relevant permission-vs-Agent-authority decision. @@ -158,7 +158,7 @@ Active #64 makes a successful action-outcome value require existing verified pro ### 3.19 Controlled Agent Task fixture -Active #65 supplies a deterministic synthetic local web fixture with a labelled semantic input, submit control, same-document post-condition and explicitly hidden/untrusted prompt-injection text. The fixture contains no credential collection surface and requires no live third-party site. +Merged #65 supplies a deterministic synthetic local web fixture with a labelled semantic input, submit control, same-document post-condition and explicitly hidden/untrusted prompt-injection text. The fixture contains no credential collection surface and requires no live third-party site. **Resolution:** the fixture makes the future real Chromium vertical slice reproducible without turning a third-party site into a test dependency. It is not a browser adapter, semantic extractor, input dispatcher, policy engine, trusted clock, process-attribution source or proof of real Chromium execution. diff --git a/docs/adr/0107-browser-protocol-adapter-strategy.md b/docs/adr/0107-browser-protocol-adapter-strategy.md index fb1bf2e17..ef144a2a3 100644 --- a/docs/adr/0107-browser-protocol-adapter-strategy.md +++ b/docs/adr/0107-browser-protocol-adapter-strategy.md @@ -38,11 +38,11 @@ MCP version negotiation is independent of the OriginWeave Protocol version. As o The complete MCP adapter remains **Planned**. Protected main now contains the narrower bounded Rust `tools/call` routing/action-policy foundation merged through PR #168. That protected-main foundation validates the `2026-07-28` stateless `tools/call` routing envelope presented to this boundary, bounds and syntax-checks both untrusted method fields and both untrusted tool-name fields before cross-field correlation, derives one of the existing typed `ActionKind` values from a deterministic reviewed registry, exposes discovery metadata from that same registry, and requires the resulting action to pass the ordinary OriginWeave policy evaluator. The method boundary accepts only nonempty ASCII method names up to 64 bytes using the reviewed routing alphabet, while the tool-name boundary accepts only nonempty ASCII names up to 128 bytes using its narrower reviewed alphabet. The catalog and validated route grant no capability, approval, origin, secret, browser, persistence, or evidence authority by themselves. -Active PR #170 is a separate non-shipped refinement on top of that protected-main catalog. It adds one conservative typed `tools/list` request/result contract: both protocol-version fields are required and bounded before comparison, client-capability metadata must be present without becoming authority, both routing/body methods are syntax-bounded before correlation, only exact `tools/list` is admitted, and every caller-supplied cursor is rejected because the current fixed catalog issues none. The result is one complete page with zero freshness, private cache scope, and no continuation cursor. +Merged PR #170 adds a conservative typed `tools/list` request/result contract to the protected-main catalog: both protocol-version fields are required and bounded before comparison, client-capability metadata must be present without becoming authority, both routing/body methods are syntax-bounded before correlation, only exact `tools/list` is admitted, and every caller-supplied cursor is rejected because the current fixed catalog issues none. The result is one complete page with zero freshness, private cache scope, and no continuation cursor. -Neither protected main nor PR #170 implements Streamable HTTP transport parsing, JSON-RPC/HTTP serialization, OAuth, browser I/O, WebMCP/BiDi/CDP translation, secret delivery, persistence, general pagination/subscription state, or a complete OriginWeave Protocol adapter. Those remain separate adapter/runtime work. Protected `main` may therefore describe only the bounded merged `tools/call` foundation as implemented; the full MCP adapter remains planned, and the `tools/list` refinement remains active-PR evidence until separately integrated. +Neither protected main nor PR #170 implements Streamable HTTP transport parsing, JSON-RPC/HTTP serialization, OAuth, browser I/O, WebMCP/BiDi/CDP translation, secret delivery, persistence, general pagination/subscription state, or a complete OriginWeave Protocol adapter. Those remain separate adapter/runtime work. Protected `main` may therefore describe only the bounded merged `tools/call` and `tools/list` foundations as implemented; the full MCP adapter remains planned. -The version boundary is explicit: the protected-main routing foundation and active discovery refinement accept only MCP `2026-07-28`; neither infers compatibility with later protocol generations. OriginWeave Protocol versioning remains independent and cannot be changed by MCP metadata. +The version boundary is explicit: the protected-main routing and discovery foundations accept only MCP `2026-07-28`; neither infers compatibility with later protocol generations. OriginWeave Protocol versioning remains independent and cannot be changed by MCP metadata. ## Consequences @@ -60,7 +60,7 @@ Protocol validation occurs before messages influence policy. Tool/page-provided Require version-negotiation tests, schema/property tests, malformed-message tests, BiDi/CDP semantic parity tests for shared capabilities, WebMCP prompt-injection tests, MCP authority-separation and version-change tests, browser-version compatibility matrices, and end-to-end proof that unsupported capabilities fail without side effects. -For the protected-main `tools/call` foundation, acceptance includes deterministic method and tool-name bounds/syntax, exact header/body method and tool-name correlation only after both sides are bounded, explicit invalid-method/invalid-tool-name/unknown-tool rejection, one unambiguous tool-to-action registry, independent capability/risk expectations, route/action mismatch denial before ordinary policy evaluation, exact 100% owned-production coverage, and integrated review evidence from PR #168. For active PR #170, exact-current acceptance additionally requires bounded protocol metadata before cross-field comparison, required client-capabilities presence, bounded `tools/list` method correlation, rejection of unissued cursors, deterministic result/cache semantics, exact 100% owned-production coverage, and unchanged-head CI/security/review evidence. These checks do not substitute for complete transport or adapter conformance. +For the protected-main `tools/call` foundation, acceptance includes deterministic method and tool-name bounds/syntax, exact header/body method and tool-name correlation only after both sides are bounded, explicit invalid-method/invalid-tool-name/unknown-tool rejection, one unambiguous tool-to-action registry, independent capability/risk expectations, route/action mismatch denial before ordinary policy evaluation, exact 100% owned-production coverage, and integrated review evidence from PR #168. For merged PR #170, acceptance additionally required bounded protocol metadata before cross-field comparison, required client-capabilities presence, bounded `tools/list` method correlation, rejection of unissued cursors, deterministic result/cache semantics, and exact owned-production coverage. These checks do not substitute for complete transport or adapter conformance. ## Migration and rollback diff --git a/docs/doctoring.md b/docs/doctoring.md index ec51daaf3..c01d546cd 100644 --- a/docs/doctoring.md +++ b/docs/doctoring.md @@ -8,7 +8,7 @@ This document records external evidence that changes OriginWeave architecture, t The 1 June 2026 WebDriver BiDi Working Draft defines a bidirectional remote-control protocol, events, commands, and user contexts. Because it remains a W3C Working Draft, OriginWeave places BiDi behind a versioned adapter and Web Platform Tests-derived contract tests rather than make it the internal authority model. -The final Model Context Protocol `2026-07-28` specification defines the currently reviewed MCP generation. Its stateless request model carries protocol metadata per request and standard Streamable HTTP routing metadata for MCP operations; its Tools surface defines bounded, case-sensitive tool names and requires clients to treat tool annotations as untrusted unless supplied by a trusted server. OriginWeave therefore keeps MCP outside the product authority model. Active PR #168 implements only a bounded Rust `tools/call` routing/action-policy foundation for that exact generation; the complete transport, request-metadata, discovery, OAuth, browser, secret, and persistence adapter remains planned and cannot be inferred from the core routing primitive. +The final Model Context Protocol `2026-07-28` specification defines the currently reviewed MCP generation. Its stateless request model carries protocol metadata per request and standard Streamable HTTP routing metadata for MCP operations; its Tools surface defines bounded, case-sensitive tool names and requires clients to treat tool annotations as untrusted unless supplied by a trusted server. OriginWeave therefore keeps MCP outside the product authority model. Protected main contains only the bounded Rust `tools/call` routing/action-policy foundation merged through PR #168 and the conservative `tools/list` discovery boundary merged through PR #170 for that exact generation; the complete transport, JSON-RPC/HTTP serialization, OAuth, browser, secret, and persistence adapter remains planned and cannot be inferred from those bounded core primitives. ### Browser origin equivalence diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 8a702c75f..a5a2f8bbc 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -43,7 +43,7 @@ Representative active workstreams at this snapshot were: | Release artifact identity | #218 and #219 | Ready/non-draft fail-closed benchmark release decision and canonical release manifest binding; Strix provider-failure reruns completed green on both heads | | Schema-bound extraction and BAP lifecycle | #209 and #208 | Ready/non-draft schema-bound extraction contract and resumable task-lifecycle kernel; #209 Strix rerun green, #208 rerun re-dispatched after a further provider failure | | WebDriver BiDi transport | #188 through #205 | Active stack whose top #205 merged into its prerequisite branch, not protected `main`; it exercises framed `locateNodes` exchange over a bounded WebSocket opening path, but authenticated browser-process provenance, semantic task execution, and protected-main shipment remain unproven | -| MCP adapter | (#168 merged) and #170 | Typed MCP routing foundations are protected-main behavior since 2026-08-24; conservative `tools/list` cache metadata remains active-PR evidence with a Strix rerun in flight | +| MCP adapter | (#168 and #170 merged) | Typed MCP `tools/call` routing and conservative `tools/list` cache metadata are protected-main behavior; complete transport, OAuth, browser, persistence, and runtime adapter semantics remain open | | Workflow-registry audit | #124 | Real Strix finding vuln-0001 (Unicode homoglyph path confusion, MEDIUM) remediated on head `30cc458b` with regression contract tests; fresh exact-head checks and review re-running | | Controlled Chromium and recovery | #65, #70-#73, #100, #105, #142-#152 and descendants | Real pinned-browser fixture, semantic location, resource, crash, and teardown evidence exists on active stacks; evidence does not transfer across heads or prerequisites | | Durable WARC/PROV evidence | #210, #217 | Bounded WARC resource records and PROV JSON-LD binding are draft active-PR foundations; durable ownership, replay, retention/deletion, and browser side-effect reconciliation remain open | diff --git a/docs/traceability/README.md b/docs/traceability/README.md index e30b9eda1..10bc3a456 100644 --- a/docs/traceability/README.md +++ b/docs/traceability/README.md @@ -25,7 +25,7 @@ Lower layers may define future direction but cannot override current protected i Transient implementation evidence that materially tightens an existing authority boundary is kept in explicit active-PR traceability rather than silently changing protected-main maturity: -- [`resolution-freshness-authority.md`](resolution-freshness-authority.md) — PR #47 bounds the lifetime of validated destination-resolution authority; the direct socket consumer still must require that fresh authority before the overall DNS-rebinding/TOCTOU interval can be called implemented on protected main. +- [`resolution-freshness-authority.md`](resolution-freshness-authority.md) — merged PR #47 bounds the lifetime of validated destination-resolution authority; the direct socket consumer still must require that fresh authority before the overall DNS-rebinding/TOCTOU interval can be called implemented on protected main. - [`tls-revocation-freshness-authority.md`](tls-revocation-freshness-authority.md) — PR #48 classifies independently verified revocation material for freshness only; it does not fetch or authenticate OCSP/CRL material and does not create an unrevoked-certificate claim. These dossiers are evidence indexes, not substitute ADRs. A new ADR is required only when a durable architecture/trust/deployment decision changes. @@ -55,16 +55,16 @@ ADR lifecycle is separate and remains `Proposed`, `Accepted`, `Superseded`, `Dep | Page content is data, never instruction authority | IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0002; `ARCHITECTURE.md`; `docs/TRD.md` | `originweave-core` + `originweave-policy` tests | | Typed actions instead of default arbitrary JavaScript | PARTIAL | PRD-ACT-001..004; ADR 0002 | Typed core/policy foundations are on main; complete browser action adapter remains Planned | | logical origin != resolved destination | IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0004; TRD-INV-002 | `originweave-destination`; destination governance tests | -| Bounded resolution freshness is explicit before destination authority is consumed | IMPLEMENTED_ON_ACTIVE_PR | ADR 0004; [`resolution-freshness-authority.md`](resolution-freshness-authority.md) | PR #47 implements the deterministic freshness primitive; protected-main socket planning can still bypass it, so the overall resolution-to-socket TOCTOU boundary remains PARTIAL | +| Bounded resolution freshness is explicit before destination authority is consumed | IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0004; [`resolution-freshness-authority.md`](resolution-freshness-authority.md) | Merged PR #47 implements the deterministic freshness primitive; protected-main socket planning can still bypass it, so the overall resolution-to-socket TOCTOU boundary remains PARTIAL | | resolved destination != TCP peer | IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0005; TRD Section 6 | `originweave-network`; loopback/peer tests | | TCP peer != TLS service identity | IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0006; TRD Section 6 | `originweave-tls`; rustls integration tests | -| Revocation-material freshness is separate from revocation authenticity/non-revocation | IMPLEMENTED_ON_ACTIVE_PR | ADR 0006/0008 boundary; [`tls-revocation-freshness-authority.md`](tls-revocation-freshness-authority.md) | PR #48 adds a freshness classifier only; protected main still records revocation as NotConfigured and makes no unrevoked claim | +| Revocation-material freshness is separate from revocation authenticity/non-revocation | IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0006/0008 boundary; [`tls-revocation-freshness-authority.md`](tls-revocation-freshness-authority.md) | Merged PR #48 adds a freshness classifier only; protected main still records revocation as NotConfigured and makes no unrevoked claim | | Proxy/PAC route authority must be explicit | PARTIAL | PRD-NET-005; TRD Section 6.3 | Protected-main direct-route authority exists; PAC evaluation/proxy transport/CONNECT remain incomplete | | Bounded HTTP semantics require an authenticated governed connection and resource bounds | IMPLEMENTED_ON_ACTIVE_PR | PRD-NET-006; issue #9; active PR #37 | `originweave-http` replacement exists on active PR #37; historical PR #11 is SUPERSEDED implementation lineage and is not current evidence; no protected-main HTTP claim yet | | Node handles bind session/context/origin/document lifetime | PARTIAL | ADR 0010; PRD-OBS-001/002; TRD Section 5 | Core opaque session/context/document/node authority is on protected main; active PR #40 owns the protocol-ID registry and remains non-shipped evidence | | Semantic observations retain OriginWeave node authority and explicit source-channel provenance | IMPLEMENTED_ON_ACTIVE_PR | PRD-OBS-001/003/005; ADR 0010; structured-observation architecture | Active PR #52, stacked on #40, implements a bounded `SemanticNodeObservation` value primitive that rejects missing evidence-channel provenance. It is not a browser observation adapter; channels and advertised node actions are descriptive evidence and grant no execution authority | | Raw secrets never enter model context | PARTIAL | PRD-DATA-001; ADR 0002; TRD Section 9 | Core secret-delivery policy exists; trusted broker/runtime completion remains Planned | -| Sensitive disclosure is purpose- and classification-bound | PARTIAL | ADR 0007; PRD-DATA-002; issue #10 | Purpose-bound policy/evidence foundations are on protected main; active PR #45 adds credential-free handle-lifecycle evidence and #46 adds bounded in-process authoritative use reservation, while trusted storage/revocation/value resolution/cross-process lifecycle/model-disclosure remain open | +| Sensitive disclosure is purpose- and classification-bound | PARTIAL | ADR 0007; PRD-DATA-002; issue #10 | Purpose-bound policy/evidence foundations are on protected main; merged PR #45 adds credential-free handle-lifecycle evidence and merged PR #53 adds bounded in-process revocation state. Open PR #46 adds authoritative use reservation and #55 adds audience binding, while trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open | | Evidence/provenance are product outputs, not debug leftovers | PARTIAL | ADR 0003; PRD Section 9.6 | `originweave-evidence` foundations exist; complete durable Evidence Trail/WARC/PROV adapters remain Planned | | Human interaction outranks inference/background collection | PARTIAL | `ARCHITECTURE.md`; PRD-RES-002 | Deterministic resource mitigation/CPU-worker admission foundations exist; platform telemetry/actuation remain Planned | | Structured observation precedes raw HTML/screenshot fallback | ACCEPTED_ARCHITECTURE | PRD-OBS-003; TRD Section 7 | Active PR #52 supplies a non-shipped bounded semantic value primitive; real browser observation and fallback adapters remain Planned | @@ -84,10 +84,10 @@ ADR lifecycle is separate and remains `Proposed`, `Accepted`, `Superseded`, `Dep | Canonical origin / action / approval | `originweave-core` | crate tests; ADR 0002 | IMPLEMENTED_ON_PROTECTED_MAIN | | Deterministic action policy | `originweave-policy` | policy/security-review tests | IMPLEMENTED_ON_PROTECTED_MAIN | | Destination/rebinding/redirect | `originweave-destination` | destination tests; ADR 0004 | IMPLEMENTED_ON_PROTECTED_MAIN | -| Resolution freshness authority | active `originweave-destination` work in PR #47 | [`resolution-freshness-authority.md`](resolution-freshness-authority.md); active exact-head tests/coverage | IMPLEMENTED_ON_ACTIVE_PR | +| Resolution freshness authority | merged `originweave-destination` primitive from PR #47; open consumer remains in PR #50 | [`resolution-freshness-authority.md`](resolution-freshness-authority.md); protected-main primitive tests plus active consumer evidence | IMPLEMENTED_ON_PROTECTED_MAIN (primitive) / PARTIAL (full path) | | Exact direct socket/peer | `originweave-network` | real loopback + error tests; ADR 0005 | IMPLEMENTED_ON_PROTECTED_MAIN | | TLS identity | `originweave-tls` | real rustls integration; ADR 0006 | IMPLEMENTED_ON_PROTECTED_MAIN | -| TLS revocation-material freshness | active `originweave-tls` work in PR #48 | [`tls-revocation-freshness-authority.md`](tls-revocation-freshness-authority.md); active exact-head tests/coverage | IMPLEMENTED_ON_ACTIVE_PR | +| TLS revocation-material freshness | merged `originweave-tls` primitive from PR #48; complete revocation path remains planned | [`tls-revocation-freshness-authority.md`](tls-revocation-freshness-authority.md); protected-main primitive tests and planned adapter evidence | IMPLEMENTED_ON_PROTECTED_MAIN (primitive) / PARTIAL (full path) | | Resource budgets/mitigations | `originweave-resource` | crate tests | PARTIAL | | Redacted evidence/provenance | `originweave-evidence` | crate tests; ADR 0003 | PARTIAL | | Bounded HTTP/1.1 | active `originweave-http` replacement in PR #37 | issue #9; active-PR unit/integration/coverage evidence | IMPLEMENTED_ON_ACTIVE_PR | @@ -108,9 +108,9 @@ ADR lifecycle is separate and remains `Proposed`, `Accepted`, `Superseded`, `Dep | PRD-COMP-001, Chromium compatibility kernel | ADR 0001 (Accepted) | | PRD-ACT-001, PRD-ACT-005, PRD-CRAWL-001, trust-source boundary | ADR 0002 (Accepted) | | PRD-EVD-001, PRD-EVD-002, PRD-EVD-005 | ADR 0003 (Accepted) | -| PRD-NET-001, PRD-NET-002, redirect/rebinding/freshness boundary | ADR 0004 (Accepted); active PR #47 tightens the existing boundary without creating a new deployed component or trust owner | +| PRD-NET-001, PRD-NET-002, redirect/rebinding/freshness boundary | ADR 0004 (Accepted); merged PR #47 tightens the existing boundary without creating a new deployed component or trust owner | | PRD-NET-003 | ADR 0005 (Accepted) | -| PRD-NET-004 | ADR 0006 (Accepted); active PR #48 adds revocation-material freshness only and does not define a complete revocation architecture | +| PRD-NET-004 | ADR 0006 (Accepted); merged PR #48 adds revocation-material freshness only and does not define a complete revocation architecture | | Purpose-bound sensitive-data authority | ADR 0007 (Accepted); trusted broker/storage/lifecycle still issue #10 | | TLS delegated-task leaf-validity horizon | ADR 0008 (Accepted) | | Session/context/document/node binding | ADR 0010 (Accepted); active registry implementation #40 remains non-shipped | @@ -147,7 +147,7 @@ Material claims should update `docs/doctoring.md` with current primary evidence | Diagram | Requirements represented / maturity | |---|---| | UML component/bounded-context view | Product family, Chromium/Rust ownership, adapter boundaries | -| Network authority sequence | PRD-NET-001..007; TRD-INV-002; HTTP remains active-PR until #37 integrates; resolution freshness remains an active lower-layer primitive until the socket consumer requires it | +| Network authority sequence | PRD-NET-001..007; TRD-INV-002; HTTP remains active-PR until #37 integrates; the merged resolution-freshness primitive remains distinct from the unshipped socket consumer | | Observation/action sequence | PRD-OBS, PRD-ACT, PRD-DATA, trust separation; active #52 makes the bounded semantic-observation value/provenance contract explicit without establishing browser I/O or action dispatch | | Delegated-task state machine | session lifecycle, approval, resource pause, cancellation/recovery, post-condition truth | | Deployment topology | renderer trust, orchestrator/model/store boundaries | @@ -177,8 +177,8 @@ Repository contracts should fail when canonical PRD/TRD/ADR/UML/ERD/traceability ## 10. Open traceability work -- **Open:** active PR #47 must reach unchanged exact-head CI/security/100% coverage, then the first-party socket consumer must require the fresh resolution authority before the resolution-to-socket TOCTOU interval can become protected-main implemented evidence. -- **Open:** active PR #48 remains freshness classification only; define and review revocation-material acquisition/authenticity/cache/failure/composition before any protected-main revocation-enforcement or unrevoked claim. +- **Open:** the merged PR #47 primitive is protected-main evidence; the first-party socket consumer must require fresh resolution authority, and delayed socket use must recheck it, before the resolution-to-socket TOCTOU interval can become protected-main implemented evidence. +- **Open:** merged PR #48 remains freshness classification only; define and review revocation-material acquisition/authenticity/cache/failure/composition before any protected-main revocation-enforcement or unrevoked claim. - **Open:** after #37 integrates, move bounded HTTP from `IMPLEMENTED_ON_ACTIVE_PR` into protected-main evidence and close historical PR #11 only after unique-work preservation and protected-main verification are proven. - **Open:** after #43 integrates, move bounded MV3 downloads from `IMPLEMENTED_ON_ACTIVE_PR` into the protected-main compatibility evidence inventory while issue #27 remains open for the complete matrix. - **Open:** after #40 stabilizes/integrates, map its registry API and tests without presenting raw BiDi/CDP identifiers as durable authority. diff --git a/docs/traceability/mcp-authority-route.md b/docs/traceability/mcp-authority-route.md index 94f181ed4..db0c15285 100644 --- a/docs/traceability/mcp-authority-route.md +++ b/docs/traceability/mcp-authority-route.md @@ -1,9 +1,9 @@ # MCP 2026-07-28 authority-route traceability - **`tools/call` capability maturity:** `IMPLEMENTED_ON_PROTECTED_MAIN` -- **`tools/list` capability maturity:** `IMPLEMENTED_ON_ACTIVE_PR` +- **`tools/list` capability maturity:** `IMPLEMENTED_ON_PROTECTED_MAIN` - **Protected-main owning work:** merged PR #168 `feat(mcp): bind stateless tool routing to typed actions` -- **Active follow-on:** PR #170 `feat(mcp): expose conservative tools list cache contract` +- **Protected-main follow-on:** merged PR #170 `feat(mcp): expose conservative tools list cache contract` - **Complete MCP adapter status:** `PLANNED` - **Governing decision:** ADR 0107 @@ -13,13 +13,13 @@ Protected main at `b05d5acca82b9d916ada2c8e82f59f92a89817e1` contains the bounde A successful `ValidatedMcpToolCall` proves routing integrity only. It grants no capability, origin, approval, secret, browser, tenant, persistence, network, or evidence authority. `originweave_policy::evaluate_mcp` still delegates to the ordinary policy evaluator after the route/action match. -Active PR #170 builds on that protected-main catalog with a conservative typed `tools/list` request/result boundary. Its current branch requires matching MCP protocol metadata, required client-capability presence, bounded and syntax-validated routing/body methods, exact `tools/list` routing, and no caller-supplied cursor because the fixed catalog issues none. Its result is one complete page with zero freshness, private cache scope, and no continuation cursor. This active-PR slice remains non-shipped until it reaches protected main and does not grant any OriginWeave action authority. +Protected main now also contains the conservative typed `tools/list` request/result boundary merged through PR #170. It requires matching MCP protocol metadata, required client-capability presence, bounded and syntax-validated routing/body methods, exact `tools/list` routing, and no caller-supplied cursor because the fixed catalog issues none. Its result is one complete page with zero freshness, private cache scope, and no continuation cursor. It does not grant any OriginWeave action authority. ## Product-status reconciliation -`docs/PRD.md` PRD-INT-004 and `docs/TRD.md` Section 12 intentionally remain **Planned** at the complete-adapter level. That status is not contradicted by the bounded `tools/call` foundation now on protected main or by active PR #170: both are reusable control-plane contracts below the complete product adapter. `README.md` and `CHANGELOG.md` distinguish protected-main routing from the active discovery refinement, and ADR 0107 records the protocol/version and authority boundary. +`docs/PRD.md` PRD-INT-004 and `docs/TRD.md` Section 12 intentionally remain **Planned** at the complete-adapter level. That status is not contradicted by the bounded `tools/call` and `tools/list` foundations now on protected main: both are reusable control-plane contracts below the complete product adapter. `README.md` and `CHANGELOG.md` distinguish these shipped foundations from the complete adapter, and ADR 0107 records the protocol/version and authority boundary. -The following remain outside protected main and PR #170 and must not be inferred from either: +The following remain outside protected main and must not be inferred from either shipped foundation: - Streamable HTTP transport parsing and header materialization; - JSON-RPC/HTTP response serialization of the typed discovery page; @@ -49,10 +49,10 @@ Protected-main PR #168 production/test surfaces include: - `crates/originweave-policy/src/lib.rs` — `evaluate_mcp` route/action guard before normal policy evaluation; and - `crates/originweave-policy/tests/mcp_route_binding.rs` — confused-deputy and policy-preservation evidence. -Active PR #170 additionally exercises its discovery contract in `crates/originweave-core/tests/mcp_tools_list_cache.rs`, including result/cache semantics, required protocol/client metadata, bounded protocol and method validation, routing correlation, cursor rejection, and public error contracts. +Protected-main PR #170 additionally exercises its discovery contract in `crates/originweave-core/tests/mcp_tools_list_cache.rs`, including result/cache semantics, required protocol/client metadata, bounded protocol and method validation, routing correlation, cursor rejection, and public error contracts. -Exact current-head CI/security/review evidence must be regenerated after every branch mutation. Protected-main evidence proves only the merged `tools/call` foundation; predecessor or protected-main results are not current-head proof for active PR #170. +Protected-main evidence proves the merged `tools/call` and `tools/list` foundations only; it does not prove the complete MCP adapter or any future branch behavior. ## Promotion rule -The bounded `tools/call` routing foundation is already `IMPLEMENTED_ON_PROTECTED_MAIN`. The `tools/list` discovery refinement may change to `IMPLEMENTED_ON_PROTECTED_MAIN` only after PR #170 reaches protected `main` under live governance and exact-head acceptance. Neither promotion makes the complete MCP adapter implemented; each remaining transport/runtime boundary requires its own integrated evidence. +The bounded `tools/call` and `tools/list` foundations are `IMPLEMENTED_ON_PROTECTED_MAIN`. Neither promotion makes the complete MCP adapter implemented; each remaining transport/runtime boundary requires its own integrated evidence. diff --git a/docs/traceability/resolution-freshness-authority.md b/docs/traceability/resolution-freshness-authority.md index edb91e4bb..39a4a6de6 100644 --- a/docs/traceability/resolution-freshness-authority.md +++ b/docs/traceability/resolution-freshness-authority.md @@ -2,7 +2,7 @@ - **Documentation status:** Active-PR traceability - **Protected-main capability status:** **PARTIAL** -- **Primitive implementation lane:** PR #47, `feat/resolution-freshness-authority-main` +- **Primitive implementation lane:** merged PR #47, `feat/resolution-freshness-authority-main` - **First-party planning consumer lane:** PR #50, `feat/network-consume-resolution-freshness` - **Socket-use freshness lane:** PR #54, `fix/network-resolution-freshness-at-use` - **Governing existing decision boundary:** ADR 0004 and the protected-main destination/rebinding authority model @@ -10,15 +10,15 @@ ## Truth boundary -Protected `main` already classifies, approves, pins, and non-expansively revalidates resolved destination addresses. It does **not** yet require a time-bounded resolution authority through the entire first-party direct-socket path. +Protected `main` already classifies, approves, pins, and non-expansively revalidates resolved destination addresses, including the time-bounded `FreshResolutionSnapshot` primitive merged through PR #47. It does **not** yet require that fresh authority through the entire first-party direct-socket path. -PR #47 exact head `6b5ed4dcea281b505f67db6180bb14c3bc95b392` contains the reusable production `FreshResolutionSnapshot` primitive and has terminal successful CI/security/SAST/exact-coverage evidence. That primitive is therefore **IMPLEMENTED_ON_ACTIVE_PR** evidence only; it is not protected-main truth. +PR #47 exact head `6b5ed4dcea281b505f67db6180bb14c3bc95b392` contains the reusable production `FreshResolutionSnapshot` primitive and has terminal successful CI/security/SAST/exact-coverage evidence. PR #47 is merged, so this bounded primitive is **IMPLEMENTED_ON_PROTECTED_MAIN**; its presence does not close the later planning or socket-use gap. PR #50 exact head `f8b43bc94444986ab23aa4ef3086e446a0b39295` implements the dependent first-party planning boundary. It keeps the untimed `ConnectionPlan` internal to `originweave-network`, exposes `FreshConnectionPlan` as the ordinary direct-socket planner, requires a `FreshResolutionSnapshot` plus caller-supplied trusted monotonic current time, rejects expired authority at plan authorization, and migrates existing TLS integration helpers through that same fresh boundary. Exact-head CI run `31408474576` passes repository contracts, formatting, workspace check/tests, strict Clippy, rustdoc and exact owned production function/line/region/branch coverage; CodeRabbit exact-head status is success. -PR #54 exact head `ec81031c537f2b662910c1ce78c7ae0e0bfc9c1e` closes a later plan-to-connect TOCTOU discovered after #50: freshness checked only when the plan was created could expire before socket I/O. The active lane retains the exact `FreshResolutionSnapshot` in the single-use plan, exposes `connect_at(current_time)` to re-run freshness immediately before socket use under the caller's trusted monotonic clock domain, and keeps the legacy `connect()` surface fail-closed by adding process-local monotonic elapsed time to the original authorization checkpoint before delegating to `connect_at`. CI run `31418337788` passes repository contracts, formatting, workspace checks/tests, strict Clippy, rustdoc and exact owned production function/line/region/branch coverage; CodeRabbit exact-head status is successful. +PR #54 exact head `ec81031c537f2b662910c1ce78c7ae0e0bfc9c1e` proposed closing a later plan-to-connect TOCTOU discovered after #50: freshness checked only when the plan was created could expire before socket I/O. The closed, unmerged lane retained the exact `FreshResolutionSnapshot` in the single-use plan, exposed `connect_at(current_time)` to re-run freshness immediately before socket use under the caller's trusted monotonic clock domain, and kept the legacy `connect()` surface fail-closed by adding process-local monotonic elapsed time to the original authorization checkpoint before delegating to `connect_at`. CI run `31418337788` passed repository contracts, formatting, workspace checks/tests, strict Clippy, rustdoc and exact owned production function/line/region/branch coverage; this remains branch evidence only. -PRs #47, #50 and #54 remain **IMPLEMENTED_ON_ACTIVE_PR**, not shipped. #50 remains dependency-gated on #47 and #54 remains dependency-gated on #50. The overall protected-main resolution-to-socket interval therefore remains **PARTIAL** until dependency-ordered integration and fresh protected-main acceptance prove the same authority chain without an untimed planning or delayed-use bypass. +PR #47 is **IMPLEMENTED_ON_PROTECTED_MAIN** for the bounded freshness primitive. PR #50 remains open and dependency-gated on that primitive, while #54 is closed without merge and therefore supplies no protected-main implementation. The overall protected-main resolution-to-socket interval remains **PARTIAL** until the planning and delayed-use boundaries are integrated and receive fresh protected-main acceptance without an untimed planning or delayed-use bypass. ## Current exact-head RCA @@ -28,7 +28,7 @@ The first production-complete PR #47 head reached all ordinary Rust contracts an That was a realistic DNS-rebinding case rather than an impossible instrumentation artifact. The branch added a focused one-address expansion regression requiring `ResolutionSetExpanded`, retained the two-address expansion case, and exact head `6b5ed4dcea281b505f67db6180bb14c3bc95b392` subsequently passed CI including exact production function/line/region/branch coverage, Security Scan, and SAST Semgrep. -The freshness ceiling is executable active-PR evidence rather than an aspirational requirement. `crates/originweave-destination/src/resolution.rs` owns `MAX_RESOLUTION_VALIDITY: Duration = Duration::from_secs(30)`. `FreshResolutionSnapshot::approve` rejects `Duration::ZERO` and any interval above that constant with `DestinationError::InvalidResolutionValidity`; `crates/originweave-destination/tests/resolution_freshness.rs::fresh_resolution_rejects_invalid_or_overflowing_validity` verifies both the zero and greater-than-30-second boundaries plus approval-time overflow. This evidence remains active-PR-only until PR #47 integrates. +The freshness ceiling is executable protected-main evidence rather than an aspirational requirement. `crates/originweave-destination/src/resolution.rs` owns `MAX_RESOLUTION_VALIDITY: Duration = Duration::from_secs(30)`. `FreshResolutionSnapshot::approve` rejects `Duration::ZERO` and any interval above that constant with `DestinationError::InvalidResolutionValidity`; `crates/originweave-destination/tests/resolution_freshness.rs::fresh_resolution_rejects_invalid_or_overflowing_validity` verifies both the zero and greater-than-30-second boundaries plus approval-time overflow. The first-party planning and socket-use consumers remain active-PR-only. ### PR #50 planning consumer @@ -46,9 +46,9 @@ The accepted remedy is therefore realized on the active branch: ordinary first-p PR #54 follows #50 because a plan authorized within the resolution window could be retained until that window expired and then connected. The first failing boundary was therefore no longer public planner construction; it was the time between plan authorization and the exact operating-system connect operation. -The accepted active-branch remedy keeps the admitted freshness snapshot with the non-cloneable single-use plan and revalidates it at the socket-use boundary. `connect_at(current_time)` is the explicit deterministic path and rejects both expiry and an authorization-time regression using the existing destination error taxonomy. The compatibility `connect()` path does not freeze the old authorization timestamp: it anchors a process-local monotonic `Instant` at plan construction, adds actual elapsed time to the admitted authorization time, and delegates to `connect_at`, so delayed legacy callers cannot replay stale authority indefinitely. +The proposed active-branch remedy keeps the admitted freshness snapshot with the non-cloneable single-use plan and revalidates it at the socket-use boundary. `connect_at(current_time)` is the explicit deterministic path and rejects both expiry and an authorization-time regression using the existing destination error taxonomy. The compatibility `connect()` path does not freeze the old authorization timestamp: it anchors a process-local monotonic `Instant` at plan construction, adds actual elapsed time to the admitted authorization time, and delegates to `connect_at`, so delayed legacy callers cannot replay stale authority indefinitely. -The regression suite proves explicit success, deadline expiry, trusted-time regression, unchanged connection-parameter validation, and expiry of the compatibility path with a deliberately short real monotonic interval. Current exact head `ec81031c537f2b662910c1ce78c7ae0e0bfc9c1e` passes CI run `31418337788`. This remains active-PR evidence and does not add DNS lookup, a wall-clock authority, proxy/PAC, or a resolver service. +The regression suite proves explicit success, deadline expiry, trusted-time regression, unchanged connection-parameter validation, and expiry of the compatibility path with a deliberately short real monotonic interval. Current exact head `ec81031c537f2b662910c1ce78c7ae0e0bfc9c1e` passed CI run `31418337788`. This remains closed-branch evidence and does not add DNS lookup, a wall-clock authority, proxy/PAC, or a resolver service. ## Deterministic authority contract @@ -88,9 +88,9 @@ The durable network-authority sequence is now `resolver answer -> destination/or ## Required follow-through -- keep PR #47 as active/non-shipped evidence until repository governance integrates it; -- keep PR #50 Draft and dependency-gated while #47 remains active; do not transfer its green evidence to protected main; -- keep PR #54 Draft and dependency-gated while #50 remains active; do not transfer its green evidence to #50 or protected main; +- keep the merged PR #47 primitive distinct from the still-unshipped first-party planning and socket-use consumers; +- keep PR #50 open and dependency-gated while its protected-main consumer integration is absent; do not transfer its green evidence to protected main; +- keep the closed PR #54 branch evidence separate from protected-main truth; do not transfer its green evidence to #50 or protected main; - preserve the structural invariant that ordinary first-party direct planning cannot import an untimed `ConnectionPlan`; - preserve the socket-use invariant that a delayed call cannot reuse plan-time freshness without a new trusted monotonic use-time check; - keep PRD/TRD/traceability from calling the DNS-rebinding/TOCTOU interval closed while any prerequisite remains active; diff --git a/docs/traceability/tls-revocation-freshness-authority.md b/docs/traceability/tls-revocation-freshness-authority.md index a5bfb98c0..b8a88cf98 100644 --- a/docs/traceability/tls-revocation-freshness-authority.md +++ b/docs/traceability/tls-revocation-freshness-authority.md @@ -2,7 +2,7 @@ - **Documentation status:** Active-PR traceability - **Protected-main capability status:** **PARTIAL** -- **Active implementation lane:** PR #48, `feat/tls-revocation-freshness-main` +- **Primitive implementation lane:** merged PR #48, `feat/tls-revocation-freshness-main` - **Governing existing boundary:** protected-main TLS service-identity authority, ADR 0006, ADR 0008, and the revocation-distribution/freshness roadmap gap - **Buyer-visible gap:** prevent stale independently verified revocation material from being treated as current authority while preserving the fact that OriginWeave does not yet make an unrevoked-certificate claim @@ -10,7 +10,7 @@ Protected `main` authenticates the requested HTTPS service over the already verified TCP stream, but its TLS evidence records revocation as `NotConfigured`. It does not fetch, parse, validate, cache, or enforce OCSP/CRL material and it does not claim that a certificate is unrevoked. -PR #48 adds a reusable **freshness primitive** for revocation material only. The primitive can classify independently verified material as usable inside its signed `thisUpdate` to `nextUpdate` interval. That active-PR implementation is not protected-main truth, and passing the freshness check does not prove signature validity, path validity, responder authority, non-revocation, successful distribution, or complete TLS authentication policy. +PR #48 adds a reusable **freshness primitive** for revocation material only. The primitive can classify independently verified material as usable inside its signed `thisUpdate` to `nextUpdate` interval. PR #48 is merged, so this bounded classifier is protected-main primitive evidence; passing the freshness check does not prove signature validity, path validity, responder authority, non-revocation, successful distribution, or complete TLS authentication policy. The complete revocation path therefore remains **PARTIAL** until a separately reviewed adapter acquires and cryptographically verifies revocation material, composes freshness into the authentication decision, defines failure/cache/recovery semantics, and proves the resulting behavior on protected main. @@ -19,7 +19,7 @@ The complete revocation path therefore remains **PARTIAL** until a separately re The bounded primitive is expected to preserve these properties: 1. a higher-layer adapter may construct `RevocationMaterialFreshness` only after independent cryptographic verification has supplied both signed `thisUpdate` and `nextUpdate`; because RFC 6960 permits an OCSP `SingleResponse` to omit `nextUpdate`, absence must fail closed in that adapter before construction and must never be converted into an invented timestamp; -2. the active PR #48 primitive deliberately accepts mandatory `u64` `this_update_unix_seconds` and `next_update_unix_seconds`, so a missing `nextUpdate` has no representable successful state in the primitive; any future parser/adapter must expose a typed missing-`nextUpdate` error or a separately reviewed bounded fallback contract before calling freshness approved; +2. the merged PR #48 primitive deliberately accepts mandatory `u64` `this_update_unix_seconds` and `next_update_unix_seconds`, so a missing `nextUpdate` has no representable successful state in the primitive; any future parser/adapter must expose a typed missing-`nextUpdate` error or a separately reviewed bounded fallback contract before calling freshness approved; 3. the signed interval is non-empty and ordered; 4. the usable interval is half-open: `thisUpdate <= trusted_time < nextUpdate`; 5. trusted time before `thisUpdate` and at/after `nextUpdate` fails closed with typed bounded errors; @@ -28,18 +28,18 @@ The bounded primitive is expected to preserve these properties: ## Architecture and ADR assessment -The active primitive tightens an existing TLS evidence/policy concern without introducing a new deployed component, persistence owner, wire protocol, network path, or secret boundary. A new ADR is therefore not required merely because the helper type exists. +The merged primitive tightens an existing TLS evidence/policy concern without introducing a new deployed component, persistence owner, wire protocol, network path, or secret boundary. A new ADR is therefore not required merely because the helper type exists. A new or superseding ADR becomes appropriate if OriginWeave later chooses a concrete revocation architecture that changes trust ownership—for example, stapled OCSP versus independently fetched OCSP/CRL, cache authority and freshness policy, hard-fail versus explicitly bounded degraded behavior, responder/path validation ownership, or a separate revocation service. -No new physical ERD object is justified by this active in-memory primitive. UML should change only when the executable TLS/revocation data or control path changes materially. +No new physical ERD object is justified by this in-memory primitive. UML should change only when the executable TLS/revocation data or control path changes materially. ## Evidence progression | Evidence state | Allowed maturity claim | |---|---| | Protected main records `RevocationStatus::NotConfigured` | `PARTIAL`; no revocation enforcement or unrevoked claim | -| Active PR freshness primitive with exact-head tests/coverage | `IMPLEMENTED_ON_ACTIVE_PR` for freshness classification only | +| Merged PR #48 freshness primitive with exact-head tests/coverage | `IMPLEMENTED_ON_PROTECTED_MAIN` for freshness classification only | | Protected-main freshness primitive without verified material acquisition/composition | `PARTIAL` | | Protected-main adapter verifies responder/material authenticity, requires or safely bounds missing `nextUpdate`, enforces freshness, cache/failure policy, and binds the result into TLS authentication | implementation evidence for the chosen bounded revocation policy | | Protected-main integration/recovery/operational tests prove the complete path | required additional release evidence; not implied by the helper primitive | diff --git a/tests/test_documentation_fitness_contract.py b/tests/test_documentation_fitness_contract.py index 33aefed22..3dca2af22 100644 --- a/tests/test_documentation_fitness_contract.py +++ b/tests/test_documentation_fitness_contract.py @@ -177,6 +177,23 @@ def test_current_replacement_lanes_are_not_promoted_to_protected_main(self) -> N self.assertIn("IMPLEMENTED_ON_PROTECTED_MAIN", traceability) self.assertIn("Active-PR behavior is never protected-main truth", traceability) + def test_resolution_freshness_fitness_matches_live_maturity(self) -> None: + """The current fitness baseline must separate merged and unmerged freshness lanes.""" + assessment = (DOCS_ROOT / "DOCUMENTATION_FITNESS.md").read_text(encoding="utf-8") + self.assertIn("Merged #47 provides the protected-main freshness primitive", assessment) + self.assertIn("open #50 proposes first-party network planning", assessment) + self.assertIn("closed, unmerged #54 proposed rechecking freshness", assessment) + self.assertIn("complete resolution-to-socket boundary remains partial", assessment) + self.assertNotIn("Active #47 → #50 → #54", assessment) + + def test_merged_policy_and_fixture_lanes_match_live_maturity(self) -> None: + """Merged policy and fixture evidence must not remain labeled active-only.""" + assessment = (DOCS_ROOT / "DOCUMENTATION_FITNESS.md").read_text(encoding="utf-8") + self.assertIn("Merged #62/#63 exercise two sides", assessment) + self.assertIn("Merged #65 supplies a deterministic synthetic local web fixture", assessment) + self.assertNotIn("Active #62/#63 exercise two sides", assessment) + self.assertNotIn("Active #65 supplies a deterministic synthetic local web fixture", assessment) + def test_semantic_observation_lane_stays_non_shipped_and_provenance_bound(self) -> None: """The semantic observation value object must stay active-only and distinct from browser I/O.""" appendix = (DOCS_ROOT / "evidence" / "2026-08-10-active-pr-maturity.md").read_text( diff --git a/tests/test_freshness_traceability_contract.py b/tests/test_freshness_traceability_contract.py index a9e0e4f01..9dd5502a3 100644 --- a/tests/test_freshness_traceability_contract.py +++ b/tests/test_freshness_traceability_contract.py @@ -31,10 +31,19 @@ def test_active_freshness_traces_preserve_protected_main_maturity(self) -> None: ): text = (TRACEABILITY / filename).read_text(encoding="utf-8") with self.subTest(filename=filename): - self.assertIn("Active-PR traceability", text) self.assertIn("Protected-main capability status:** **PARTIAL", text) - self.assertIn("IMPLEMENTED_ON_ACTIVE_PR", text) - self.assertIn("not protected-main truth", text) + if filename == "resolution-freshness-authority.md": + self.assertIn("active-PR", text) + self.assertIn( + "first-party planning and socket-use consumers remain active-PR-only", + text, + ) + else: + self.assertIn("Active-PR traceability", text) + self.assertTrue( + "not protected-main truth" in text + or "protected-main primitive evidence" in text + ) def test_resolution_trace_requires_socket_use_freshness_not_only_plan_time(self) -> None: """The DNS freshness trace must retain the delayed-use boundary added by PR #54.""" @@ -45,6 +54,51 @@ def test_resolution_trace_requires_socket_use_freshness_not_only_plan_time(self) self.assertIn("delayed call cannot reuse plan-time freshness", text) self.assertIn("#47 + #50 + #54", text) + def test_resolution_trace_matches_live_pr_maturity(self) -> None: + """Merged primitive and unmerged consumers must not share one maturity label.""" + text = (TRACEABILITY / "resolution-freshness-authority.md").read_text(encoding="utf-8") + self.assertIn("merged PR #47", text) + self.assertIn("IMPLEMENTED_ON_PROTECTED_MAIN", text) + self.assertIn("PR #50 remains open", text) + self.assertIn("#54 is closed without merge", text) + self.assertIn("overall protected-main resolution-to-socket interval remains **PARTIAL**", text) + self.assertNotIn("PR #47, #50 and #54 remain **IMPLEMENTED_ON_ACTIVE_PR**", text) + + def test_traceability_index_matches_merged_resolution_primitive(self) -> None: + """The canonical index must not leave merged PR #47 in active-only status.""" + index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") + self.assertIn("merged PR #47 bounds the lifetime", index) + self.assertIn("| Bounded resolution freshness is explicit before destination authority is consumed | IMPLEMENTED_ON_PROTECTED_MAIN |", index) + self.assertIn("merged `originweave-destination` primitive from PR #47", index) + self.assertIn("ADR 0004 (Accepted); merged PR #47 tightens the existing boundary", index) + self.assertIn("the merged resolution-freshness primitive remains distinct from the unshipped socket consumer", index) + self.assertNotIn("active `originweave-destination` work in PR #47", index) + self.assertNotIn("active PR #47 tightens the existing boundary", index) + self.assertNotIn("resolution freshness remains an active lower-layer primitive", index) + + def test_merged_revocation_primitive_matches_live_maturity(self) -> None: + """Current TLS freshness docs must distinguish merged #48 from the incomplete path.""" + trace = (TRACEABILITY / "tls-revocation-freshness-authority.md").read_text(encoding="utf-8") + index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") + fitness = (ROOT / "docs" / "DOCUMENTATION_FITNESS.md").read_text(encoding="utf-8") + self.assertIn("merged PR #48", trace) + self.assertIn("IMPLEMENTED_ON_PROTECTED_MAIN", trace) + self.assertIn("Merged PR #48 adds a freshness classifier only", index) + self.assertIn("IMPLEMENTED_ON_PROTECTED_MAIN", index) + self.assertIn("Merged #48 provides a protected-main bounded freshness primitive", fitness) + self.assertNotIn("active PR #48", index) + self.assertNotIn("Active #48 provides", fitness) + + def test_sensitive_disclosure_row_matches_live_maturity(self) -> None: + """The current index must distinguish merged evidence from open handle lanes.""" + index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") + self.assertIn("merged PR #45 adds credential-free handle-lifecycle evidence", index) + self.assertIn("merged PR #53 adds bounded in-process revocation state", index) + self.assertIn("Open PR #46 adds authoritative use reservation", index) + self.assertIn("#55 adds audience binding", index) + self.assertIn("trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open", index) + self.assertNotIn("active PR #45 adds", index) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_mcp_shipped_contract.py b/tests/test_mcp_shipped_contract.py index 18d3ab65a..a995c38a8 100644 --- a/tests/test_mcp_shipped_contract.py +++ b/tests/test_mcp_shipped_contract.py @@ -40,6 +40,14 @@ def test_changelog_does_not_claim_merged_tools_list_is_active_pr_only(self) -> N changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8") self.assertNotIn("Active PR #170", changelog) + def test_doctoring_does_not_claim_merged_mcp_foundations_are_active_pr_only(self) -> None: + """The standards doctoring record must not retain pre-merge MCP maturity wording.""" + doctoring = (ROOT / "docs/doctoring.md").read_text(encoding="utf-8") + self.assertNotIn("Active PR #168", doctoring) + self.assertIn("merged through PR #168", doctoring) + self.assertIn("`tools/list` discovery boundary merged through PR #170", doctoring) + self.assertNotIn("request-metadata, discovery, OAuth", doctoring) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_product_documentation_contract.py b/tests/test_product_documentation_contract.py index f192aaa4d..1c81c4f49 100644 --- a/tests/test_product_documentation_contract.py +++ b/tests/test_product_documentation_contract.py @@ -248,6 +248,58 @@ def test_stale_node_adr_defines_action_linearization_race(self) -> None: with self.subTest(phrase=phrase): self.assertIn(phrase, adr) + def test_bap_lifecycle_documents_durability_as_future_work(self) -> None: + """The in-memory lifecycle must not be presented as durable runtime support.""" + adr = (ROOT / "docs/adr/0016-bap-task-lifecycle-authority.md").read_text( + encoding="utf-8" + ) + api_contract = (ROOT / "docs/API_CONTRACT.md").read_text(encoding="utf-8") + for phrase in ( + "it is not a durable task repository", + "does not claim atomic persistence", + "Bind durable idempotency receipts", + ): + with self.subTest(phrase=phrase): + self.assertIn(phrase, adr) + self.assertIn("This is a contract baseline, not a claim", api_contract) + + def test_mcp_tools_list_traceability_matches_protected_main(self) -> None: + """Maturity documents must not retain stale active-PR status after merge.""" + traceability = (ROOT / "docs/traceability/mcp-authority-route.md").read_text( + encoding="utf-8" + ) + baseline = (ROOT / "docs/product-technical-gap-baseline.md").read_text( + encoding="utf-8" + ) + self.assertIn( + "`tools/list` capability maturity:** `IMPLEMENTED_ON_PROTECTED_MAIN`", + traceability, + ) + self.assertIn("**Protected-main follow-on:** merged PR #170", traceability) + self.assertNotIn("IMPLEMENTED_ON_ACTIVE_PR", traceability) + self.assertIn("(#168 and #170 merged)", baseline) + self.assertIn("complete transport, OAuth, browser, persistence", baseline) + adr = (ROOT / "docs/adr/0107-browser-protocol-adapter-strategy.md").read_text( + encoding="utf-8" + ) + self.assertIn("Merged PR #170", adr) + self.assertNotIn("Active PR #170 is", adr) + + def test_browser_vertical_slice_remains_explicitly_unshipped(self) -> None: + """Buyer documentation must not promote browser prerequisites into runtime truth.""" + baseline = (ROOT / "docs/product-technical-gap-baseline.md").read_text( + encoding="utf-8" + ) + traceability = (ROOT / "docs/traceability/README.md").read_text( + encoding="utf-8" + ) + prd = (ROOT / "docs/PRD.md").read_text(encoding="utf-8") + self.assertIn("Phase 1 is **in progress**, not shipped.", baseline) + self.assertIn("complete browser action adapter remains Planned", traceability) + self.assertIn("| PRD-INT-004 |", prd) + self.assertIn("| PRD-INT-004 | MCP integrates", prd) + self.assertIn("| Planned |", prd[prd.index("| PRD-INT-004 |") :]) + def test_hourly_automation_adr_requires_exit_sweep(self) -> None: """Automation closure must re-sweep all actionable lanes instead of stopping after one result.""" adr = ( From e79a8bf0bdeb2662a11b05b692583b628cad82d7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 30 Sep 2026 11:43:46 +0900 Subject: [PATCH 3/7] docs: correct merged MCP and freshness lineage Co-Authored-By: Claude Code --- CHANGELOG.md | 1 + docs/traceability/mcp-authority-route.md | 2 +- docs/traceability/resolution-freshness-authority.md | 8 ++++---- tests/test_freshness_traceability_contract.py | 4 ++-- tests/test_product_documentation_contract.py | 4 +++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68fd28143..e1ddc8020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Refreshed the product-gap queue to 126 open pull requests (54 ready, 72 draft) after #190, #188, #185, #192, #182, #184, #115, #181, #116, #117, #118, #183, #114, #127, #112, #109, #186, #110, #108, #111, #174, and #113 were merged into their immediate stacked prerequisites. PRs #147, #146, #145, #144, #143, #142, #141, #139, #136, #132, #129, and #128 moved to ready after exact-head checks and thread review; these are queue-consolidation results, not protected-main shipment. ### Added +- Clarified the shipped MCP discovery version boundary and separated open resolution-planning work from closed, unmerged socket-use evidence. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. - Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge. diff --git a/docs/traceability/mcp-authority-route.md b/docs/traceability/mcp-authority-route.md index db0c15285..8a9c21d17 100644 --- a/docs/traceability/mcp-authority-route.md +++ b/docs/traceability/mcp-authority-route.md @@ -32,7 +32,7 @@ The following remain outside protected main and must not be inferred from either ## Version boundary -The protected-main routing foundation and active discovery refinement accept only protocol generation `2026-07-28`. MCP versioning is independent of the OriginWeave Protocol. A later MCP revision does not silently change OriginWeave action, risk, capability, approval, secret, origin, tenant, browser, or evidence semantics. +The protected-main routing and discovery foundations accept only protocol generation `2026-07-28`. MCP versioning is independent of the OriginWeave Protocol. A later MCP revision does not silently change OriginWeave action, risk, capability, approval, secret, origin, tenant, browser, or evidence semantics. The reviewed primary source is: diff --git a/docs/traceability/resolution-freshness-authority.md b/docs/traceability/resolution-freshness-authority.md index 39a4a6de6..649deccb1 100644 --- a/docs/traceability/resolution-freshness-authority.md +++ b/docs/traceability/resolution-freshness-authority.md @@ -28,7 +28,7 @@ The first production-complete PR #47 head reached all ordinary Rust contracts an That was a realistic DNS-rebinding case rather than an impossible instrumentation artifact. The branch added a focused one-address expansion regression requiring `ResolutionSetExpanded`, retained the two-address expansion case, and exact head `6b5ed4dcea281b505f67db6180bb14c3bc95b392` subsequently passed CI including exact production function/line/region/branch coverage, Security Scan, and SAST Semgrep. -The freshness ceiling is executable protected-main evidence rather than an aspirational requirement. `crates/originweave-destination/src/resolution.rs` owns `MAX_RESOLUTION_VALIDITY: Duration = Duration::from_secs(30)`. `FreshResolutionSnapshot::approve` rejects `Duration::ZERO` and any interval above that constant with `DestinationError::InvalidResolutionValidity`; `crates/originweave-destination/tests/resolution_freshness.rs::fresh_resolution_rejects_invalid_or_overflowing_validity` verifies both the zero and greater-than-30-second boundaries plus approval-time overflow. The first-party planning and socket-use consumers remain active-PR-only. +The freshness ceiling is executable protected-main evidence rather than an aspirational requirement. `crates/originweave-destination/src/resolution.rs` owns `MAX_RESOLUTION_VALIDITY: Duration = Duration::from_secs(30)`. `FreshResolutionSnapshot::approve` rejects `Duration::ZERO` and any interval above that constant with `DestinationError::InvalidResolutionValidity`; `crates/originweave-destination/tests/resolution_freshness.rs::fresh_resolution_rejects_invalid_or_overflowing_validity` verifies both the zero and greater-than-30-second boundaries plus approval-time overflow. The first-party planning remains on open PR #50; socket-use remains closed, unmerged PR #54 branch evidence. ### PR #50 planning consumer @@ -46,13 +46,13 @@ The accepted remedy is therefore realized on the active branch: ordinary first-p PR #54 follows #50 because a plan authorized within the resolution window could be retained until that window expired and then connected. The first failing boundary was therefore no longer public planner construction; it was the time between plan authorization and the exact operating-system connect operation. -The proposed active-branch remedy keeps the admitted freshness snapshot with the non-cloneable single-use plan and revalidates it at the socket-use boundary. `connect_at(current_time)` is the explicit deterministic path and rejects both expiry and an authorization-time regression using the existing destination error taxonomy. The compatibility `connect()` path does not freeze the old authorization timestamp: it anchors a process-local monotonic `Instant` at plan construction, adds actual elapsed time to the admitted authorization time, and delegates to `connect_at`, so delayed legacy callers cannot replay stale authority indefinitely. +The closed, unmerged branch's proposed remedy keeps the admitted freshness snapshot with the non-cloneable single-use plan and revalidates it at the socket-use boundary. `connect_at(current_time)` is the explicit deterministic path and rejects both expiry and an authorization-time regression using the existing destination error taxonomy. The compatibility `connect()` path does not freeze the old authorization timestamp: it anchors a process-local monotonic `Instant` at plan construction, adds actual elapsed time to the admitted authorization time, and delegates to `connect_at`, so delayed legacy callers cannot replay stale authority indefinitely. The regression suite proves explicit success, deadline expiry, trusted-time regression, unchanged connection-parameter validation, and expiry of the compatibility path with a deliberately short real monotonic interval. Current exact head `ec81031c537f2b662910c1ce78c7ae0e0bfc9c1e` passed CI run `31418337788`. This remains closed-branch evidence and does not add DNS lookup, a wall-clock authority, proxy/PAC, or a resolver service. ## Deterministic authority contract -The active stack proves one continuous destination-to-socket authority chain with all of the following properties: +The merged primitive, open planning branch, and closed socket-use branch separately provide evidence for the following intended authority chain; they do not prove a continuous protected-main path: 1. approval time is explicit and supplied from one trusted monotonic clock domain; 2. validity is non-zero and capped by the active implementation's repository-owned `MAX_RESOLUTION_VALIDITY` safety budget (30 seconds on PR #47 exact head), with shorter caller-selected intervals permitted; @@ -81,7 +81,7 @@ The durable network-authority sequence is now `resolver answer -> destination/or | Active PR #50 hides the untimed planner and exact compile evidence finds stale first-party consumers | valid structural remedy with migration still incomplete | | Active PR #50 exact head `f8b43bc...` migrates first-party consumers and passes exact CI/coverage | `IMPLEMENTED_ON_ACTIVE_PR` for planning; delayed socket-use freshness still requires #54 | | Active PR #54 exact head `ec81031c...` rechecks freshness immediately before socket I/O and passes exact CI/coverage | `IMPLEMENTED_ON_ACTIVE_PR` for socket-use freshness; dependency-gated and non-shipped | -| PR #47 + #50 + #54 exact heads are individually gate-clean but none are on protected main | active-PR evidence only; no shipped claim | +| Merged PR #47 and individually gate-clean but unmerged #50/#54 branches | shipped primitive only; planning and socket-use remain branch evidence | | Protected-main primitive/planner, but delayed socket use can outlive freshness | `PARTIAL` | | Protected-main direct socket path requires exact fresh authority and rechecks it at use, with tests proving pre-approval/expiry/rebinding/delay behavior | `IMPLEMENTED_ON_PROTECTED_MAIN` for the bounded resolution-to-socket interval | | Browser/network adapter proves the same clock and authority chain under real navigation | additional integration/release evidence; not implied by lower-layer primitives | diff --git a/tests/test_freshness_traceability_contract.py b/tests/test_freshness_traceability_contract.py index 9dd5502a3..e4cbd89cf 100644 --- a/tests/test_freshness_traceability_contract.py +++ b/tests/test_freshness_traceability_contract.py @@ -33,9 +33,9 @@ def test_active_freshness_traces_preserve_protected_main_maturity(self) -> None: with self.subTest(filename=filename): self.assertIn("Protected-main capability status:** **PARTIAL", text) if filename == "resolution-freshness-authority.md": - self.assertIn("active-PR", text) + self.assertIn("PR #50 remains open", text) self.assertIn( - "first-party planning and socket-use consumers remain active-PR-only", + "first-party planning remains on open PR #50; socket-use remains closed, unmerged PR #54 branch evidence", text, ) else: diff --git a/tests/test_product_documentation_contract.py b/tests/test_product_documentation_contract.py index 1c81c4f49..707e38ae8 100644 --- a/tests/test_product_documentation_contract.py +++ b/tests/test_product_documentation_contract.py @@ -277,6 +277,7 @@ def test_mcp_tools_list_traceability_matches_protected_main(self) -> None: ) self.assertIn("**Protected-main follow-on:** merged PR #170", traceability) self.assertNotIn("IMPLEMENTED_ON_ACTIVE_PR", traceability) + self.assertNotIn("active discovery refinement", traceability) self.assertIn("(#168 and #170 merged)", baseline) self.assertIn("complete transport, OAuth, browser, persistence", baseline) adr = (ROOT / "docs/adr/0107-browser-protocol-adapter-strategy.md").read_text( @@ -298,7 +299,8 @@ def test_browser_vertical_slice_remains_explicitly_unshipped(self) -> None: self.assertIn("complete browser action adapter remains Planned", traceability) self.assertIn("| PRD-INT-004 |", prd) self.assertIn("| PRD-INT-004 | MCP integrates", prd) - self.assertIn("| Planned |", prd[prd.index("| PRD-INT-004 |") :]) + row = next(row for row in prd.splitlines() if row.startswith("| PRD-INT-004 |")) + self.assertEqual("Planned", row.split("|")[3].strip()) def test_hourly_automation_adr_requires_exit_sweep(self) -> None: """Automation closure must re-sweep all actionable lanes instead of stopping after one result.""" From 10507542b7966dcc0c662193d2feb7bafb863e87 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 30 Sep 2026 15:40:03 +0900 Subject: [PATCH 4/7] test(docs): remove stale combined freshness assertion Co-Authored-By: Claude Code --- CHANGELOG.md | 2 +- tests/test_freshness_traceability_contract.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ddc8020..9eea304d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Refreshed the product-gap queue to 126 open pull requests (54 ready, 72 draft) after #190, #188, #185, #192, #182, #184, #115, #181, #116, #117, #118, #183, #114, #127, #112, #109, #186, #110, #108, #111, #174, and #113 were merged into their immediate stacked prerequisites. PRs #147, #146, #145, #144, #143, #142, #141, #139, #136, #132, #129, and #128 moved to ready after exact-head checks and thread review; these are queue-consolidation results, not protected-main shipment. ### Added -- Clarified the shipped MCP discovery version boundary and separated open resolution-planning work from closed, unmerged socket-use evidence. +- Clarified the shipped MCP discovery version boundary, separated open resolution-planning work from closed, unmerged socket-use evidence, and aligned the documentation checks with those distinct states. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. - Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge. diff --git a/tests/test_freshness_traceability_contract.py b/tests/test_freshness_traceability_contract.py index e4cbd89cf..7c02a4733 100644 --- a/tests/test_freshness_traceability_contract.py +++ b/tests/test_freshness_traceability_contract.py @@ -52,7 +52,6 @@ def test_resolution_trace_requires_socket_use_freshness_not_only_plan_time(self) self.assertIn("connect_at(current_time)", text) self.assertIn("rechecks the retained freshness authority immediately before socket I/O", text) self.assertIn("delayed call cannot reuse plan-time freshness", text) - self.assertIn("#47 + #50 + #54", text) def test_resolution_trace_matches_live_pr_maturity(self) -> None: """Merged primitive and unmerged consumers must not share one maturity label.""" From 03c24d5bba86f11026c5b295488c8e17f58474cd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 2 Oct 2026 05:15:02 +0900 Subject: [PATCH 5/7] docs: distinguish stacked revocation from protected main --- CHANGELOG.md | 1 + docs/traceability/README.md | 2 +- tests/test_freshness_traceability_contract.py | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eea304d8..e4020e3e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Added - Clarified the shipped MCP discovery version boundary, separated open resolution-planning work from closed, unmerged socket-use evidence, and aligned the documentation checks with those distinct states. +- Corrected the sensitive-disclosure trace to distinguish protected-main lifecycle evidence from PR #53's stacked-only revocation implementation; regression checks reject its promotion to protected-main enforcement. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. - Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge. diff --git a/docs/traceability/README.md b/docs/traceability/README.md index 10bc3a456..f817d8780 100644 --- a/docs/traceability/README.md +++ b/docs/traceability/README.md @@ -64,7 +64,7 @@ ADR lifecycle is separate and remains `Proposed`, `Accepted`, `Superseded`, `Dep | Node handles bind session/context/origin/document lifetime | PARTIAL | ADR 0010; PRD-OBS-001/002; TRD Section 5 | Core opaque session/context/document/node authority is on protected main; active PR #40 owns the protocol-ID registry and remains non-shipped evidence | | Semantic observations retain OriginWeave node authority and explicit source-channel provenance | IMPLEMENTED_ON_ACTIVE_PR | PRD-OBS-001/003/005; ADR 0010; structured-observation architecture | Active PR #52, stacked on #40, implements a bounded `SemanticNodeObservation` value primitive that rejects missing evidence-channel provenance. It is not a browser observation adapter; channels and advertised node actions are descriptive evidence and grant no execution authority | | Raw secrets never enter model context | PARTIAL | PRD-DATA-001; ADR 0002; TRD Section 9 | Core secret-delivery policy exists; trusted broker/runtime completion remains Planned | -| Sensitive disclosure is purpose- and classification-bound | PARTIAL | ADR 0007; PRD-DATA-002; issue #10 | Purpose-bound policy/evidence foundations are on protected main; merged PR #45 adds credential-free handle-lifecycle evidence and merged PR #53 adds bounded in-process revocation state. Open PR #46 adds authoritative use reservation and #55 adds audience binding, while trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open | +| Sensitive disclosure is purpose- and classification-bound | PARTIAL | ADR 0007; PRD-DATA-002; issue #10 | Purpose-bound policy/evidence foundations are on protected main; merged PR #45 adds credential-free handle-lifecycle evidence, not authoritative revocation enforcement. PR #53 merged into the #46 stack, not protected main; authoritative revocation remains unshipped. Open PR #46 adds authoritative use reservation and #55 adds audience binding, while trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open | | Evidence/provenance are product outputs, not debug leftovers | PARTIAL | ADR 0003; PRD Section 9.6 | `originweave-evidence` foundations exist; complete durable Evidence Trail/WARC/PROV adapters remain Planned | | Human interaction outranks inference/background collection | PARTIAL | `ARCHITECTURE.md`; PRD-RES-002 | Deterministic resource mitigation/CPU-worker admission foundations exist; platform telemetry/actuation remain Planned | | Structured observation precedes raw HTML/screenshot fallback | ACCEPTED_ARCHITECTURE | PRD-OBS-003; TRD Section 7 | Active PR #52 supplies a non-shipped bounded semantic value primitive; real browser observation and fallback adapters remain Planned | diff --git a/tests/test_freshness_traceability_contract.py b/tests/test_freshness_traceability_contract.py index 7c02a4733..75631b17b 100644 --- a/tests/test_freshness_traceability_contract.py +++ b/tests/test_freshness_traceability_contract.py @@ -92,7 +92,9 @@ def test_sensitive_disclosure_row_matches_live_maturity(self) -> None: """The current index must distinguish merged evidence from open handle lanes.""" index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") self.assertIn("merged PR #45 adds credential-free handle-lifecycle evidence", index) - self.assertIn("merged PR #53 adds bounded in-process revocation state", index) + self.assertIn("PR #53 merged into the #46 stack, not protected main", index) + self.assertIn("authoritative revocation remains unshipped", index) + self.assertNotIn("merged PR #53 adds bounded in-process revocation state", index) self.assertIn("Open PR #46 adds authoritative use reservation", index) self.assertIn("#55 adds audience binding", index) self.assertIn("trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open", index) From 61bb4d4cd75cdd2ab88d3b785ddc185700c2a763 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 2 Oct 2026 07:34:07 +0900 Subject: [PATCH 6/7] docs: align lifecycle trace with protected main --- CHANGELOG.md | 2 +- docs/traceability/README.md | 4 ++-- tests/test_freshness_traceability_contract.py | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4020e3e8..e16976425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Added - Clarified the shipped MCP discovery version boundary, separated open resolution-planning work from closed, unmerged socket-use evidence, and aligned the documentation checks with those distinct states. -- Corrected the sensitive-disclosure trace to distinguish protected-main lifecycle evidence from PR #53's stacked-only revocation implementation; regression checks reject its promotion to protected-main enforcement. +- Corrected the sensitive-disclosure trace to distinguish protected-main lifecycle evidence from PR #53's stacked-only revocation implementation; regression checks reject its promotion to protected-main enforcement. Row-scoped checks also keep merged PR #45 lifecycle evidence distinct from open PR #46 reservation work in the requirement and remaining-work traces. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. - Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge. diff --git a/docs/traceability/README.md b/docs/traceability/README.md index f817d8780..581a730fa 100644 --- a/docs/traceability/README.md +++ b/docs/traceability/README.md @@ -96,7 +96,7 @@ ADR lifecycle is separate and remains `Proposed`, `Accepted`, `Superseded`, `Dep | Semantic observation value authority/provenance | active `originweave-core` work in PR #52, stacked on #40 | `semantic_node_observation` tests; PRD-OBS-001/003/005; issue #28 | IMPLEMENTED_ON_ACTIVE_PR | | Manifest V3 compatibility evidence | `scripts/ci/run_mv3_compatibility.py` + controlled MV3 fixture; active downloads lane #43 | issue #27; real-browser contracts | PARTIAL | | Extension-to-Agent authority | protected-main core authority kernel + Proposed ADR 0013 | issue #27; extension authority UML | PARTIAL | -| Purpose-bound sensitive-data policy/evidence | `originweave-policy` + evidence foundations; active lifecycle/reservation work #45/#46 | ADR 0007; issue #10 | PARTIAL | +| Purpose-bound sensitive-data policy/evidence | `originweave-policy` + evidence foundations; merged PR #45 records credential-free handle-lifecycle evidence; open PR #46 adds authoritative use reservation | ADR 0007; issue #10 | PARTIAL | | Trusted sensitive-data broker/storage/lifecycle | future bounded service/crate | issue #10; PRD/TRD/data governance | PLANNED | | BiDi/CDP/WebMCP/MCP | future/versioned adapter crates; registry prerequisite active in #40 | protocol compatibility tests required | PLANNED | | WARC/PROV persistence | persistence/export adapters | doctoring + future conformance tests | PLANNED | @@ -183,7 +183,7 @@ Repository contracts should fail when canonical PRD/TRD/ADR/UML/ERD/traceability - **Open:** after #43 integrates, move bounded MV3 downloads from `IMPLEMENTED_ON_ACTIVE_PR` into the protected-main compatibility evidence inventory while issue #27 remains open for the complete matrix. - **Open:** after #40 stabilizes/integrates, map its registry API and tests without presenting raw BiDi/CDP identifiers as durable authority. - **Open:** after stacked #52 stabilizes/integrates behind #40, reclassify only its bounded semantic-observation value/provenance primitive; keep real browser observation I/O, action dispatch, mutation invalidation and post-condition evidence under issue #28 until implemented. -- **Open:** after #45/#46 integrate, reclassify their narrow lifecycle/reservation primitives while keeping durable trusted-broker storage/revocation/value-resolution/model-disclosure boundaries under issue #10 until implemented. +- **Open:** PR #45 lifecycle evidence is on protected main; PR #46 reservation remains open. Reclassify only the reservation primitive after #46 integrates, while keeping durable trusted-broker storage/revocation/value-resolution/model-disclosure boundaries under issue #10 until implemented. - **Open:** attach concrete release profiles and quantitative benchmark thresholds after reproducible benchmark evidence exists. - **Open:** map every future public OriginWeave Protocol operation to risk/capability/authority and conformance tests. - **Open:** map enterprise controls to exact SOC 2/CSAP-oriented control evidence without claiming certification. diff --git a/tests/test_freshness_traceability_contract.py b/tests/test_freshness_traceability_contract.py index 75631b17b..dbd94d49d 100644 --- a/tests/test_freshness_traceability_contract.py +++ b/tests/test_freshness_traceability_contract.py @@ -88,6 +88,30 @@ def test_merged_revocation_primitive_matches_live_maturity(self) -> None: self.assertNotIn("active PR #48", index) self.assertNotIn("Active #48 provides", fitness) + def test_sensitive_requirement_and_open_work_match_merged_lifecycle_evidence(self) -> None: + """Each sensitive-data trace must separate merged evidence from open reservation work.""" + index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") + rows = [ + line + for line in index.splitlines() + if line.startswith("| Purpose-bound sensitive-data policy/evidence |") + ] + self.assertEqual(len(rows), 1) + self.assertIn("merged PR #45 records credential-free handle-lifecycle evidence", rows[0]) + self.assertIn("open PR #46 adds authoritative use reservation", rows[0]) + self.assertIn("PARTIAL", rows[0]) + self.assertNotIn("active lifecycle/reservation work #45/#46", rows[0]) + open_items = [ + line + for line in index.splitlines() + if line.startswith("- **Open:**") and "trusted-broker" in line + ] + self.assertEqual(len(open_items), 1) + self.assertIn("PR #45 lifecycle evidence is on protected main", open_items[0]) + self.assertIn("PR #46 reservation remains open", open_items[0]) + self.assertIn("trusted-broker storage/revocation/value-resolution/model-disclosure", open_items[0]) + self.assertNotIn("after #45/#46 integrate", open_items[0]) + def test_sensitive_disclosure_row_matches_live_maturity(self) -> None: """The current index must distinguish merged evidence from open handle lanes.""" index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") From 54bbea576158da50187d7d2c0a256d6d8a41f853 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 2 Oct 2026 08:17:25 +0900 Subject: [PATCH 7/7] test(docs): reject relocated sensitive maturity claims --- CHANGELOG.md | 2 +- docs/traceability/README.md | 2 +- tests/test_freshness_traceability_contract.py | 52 ++++++++++++++++--- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16976425..6930d7ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Added - Clarified the shipped MCP discovery version boundary, separated open resolution-planning work from closed, unmerged socket-use evidence, and aligned the documentation checks with those distinct states. -- Corrected the sensitive-disclosure trace to distinguish protected-main lifecycle evidence from PR #53's stacked-only revocation implementation; regression checks reject its promotion to protected-main enforcement. Row-scoped checks also keep merged PR #45 lifecycle evidence distinct from open PR #46 reservation work in the requirement and remaining-work traces. +- Corrected the sensitive-disclosure trace to distinguish protected-main lifecycle evidence from PR #53's stacked-only revocation implementation; regression checks reject its promotion to protected-main enforcement. Row-scoped checks also keep merged PR #45 lifecycle evidence distinct from open PR #46 reservation work in the requirement and remaining-work traces. The sensitive-disclosure decision check now rejects false, missing, or duplicate current rows even when correct historical wording appears elsewhere. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. - Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge. diff --git a/docs/traceability/README.md b/docs/traceability/README.md index 581a730fa..913b82b88 100644 --- a/docs/traceability/README.md +++ b/docs/traceability/README.md @@ -173,7 +173,7 @@ This rule intentionally prevents chat history from becoming a shadow architectur ## 9. Documentation drift checks -Repository contracts should fail when canonical PRD/TRD/ADR/UML/ERD/traceability artifacts disappear, lifecycle/index status diverges, an active PR is promoted to protected-main truth, or core maturity/authority vocabulary is removed. Active freshness dossiers must remain discoverable from this index so lower-layer primitives cannot silently become over-broad shipped claims. More semantic checks should be added when a specific drift has caused a real defect; avoid brittle tests that merely duplicate prose. +Repository contracts should fail when canonical PRD/TRD/ADR/UML/ERD/traceability artifacts disappear, lifecycle/index status diverges, an active PR is promoted to protected-main truth, or core maturity/authority vocabulary is removed. Active freshness dossiers must remain discoverable from this index so lower-layer primitives cannot silently become over-broad shipped claims. Maturity assertions must select exactly one current decision row: missing or duplicate rows and false shipment labels must fail even when the correct historical wording appears elsewhere. More semantic checks should be added when a specific drift has caused a real defect; avoid brittle tests that merely duplicate prose. ## 10. Open traceability work diff --git a/tests/test_freshness_traceability_contract.py b/tests/test_freshness_traceability_contract.py index dbd94d49d..633d28a66 100644 --- a/tests/test_freshness_traceability_contract.py +++ b/tests/test_freshness_traceability_contract.py @@ -4,6 +4,7 @@ import pathlib import unittest +from unittest import mock ROOT = pathlib.Path(__file__).resolve().parents[1] TRACEABILITY = ROOT / "docs" / "traceability" @@ -112,17 +113,52 @@ def test_sensitive_requirement_and_open_work_match_merged_lifecycle_evidence(sel self.assertIn("trusted-broker storage/revocation/value-resolution/model-disclosure", open_items[0]) self.assertNotIn("after #45/#46 integrate", open_items[0]) + def test_sensitive_disclosure_contract_rejects_relocated_or_duplicate_rows(self) -> None: + """Unrelated historical prose cannot satisfy the current decision-row contract.""" + index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") + rows = [ + line + for line in index.splitlines() + if line.startswith("| Sensitive disclosure is purpose- and classification-bound |") + ] + self.assertEqual(len(rows), 1) + row = rows[0] + unrelated = "\nHistorical quotation only: " + row + "\n" + incorrect = ( + "| Sensitive disclosure is purpose- and classification-bound | " + "IMPLEMENTED_ON_PROTECTED_MAIN | ADR 0007 | " + "Authoritative revocation and trusted broker are complete |" + ) + variants = { + "false_shipment_with_correct_text_elsewhere": index.replace(row, incorrect) + unrelated, + "missing_row_with_correct_text_elsewhere": index.replace(row, "") + unrelated, + "duplicate_row": index.replace(row, row + "\n" + row), + } + for name, text in variants.items(): + with self.subTest(variant=name): + with mock.patch.object(pathlib.Path, "read_text", return_value=text): + with self.assertRaises(AssertionError): + self.test_sensitive_disclosure_row_matches_live_maturity() + def test_sensitive_disclosure_row_matches_live_maturity(self) -> None: """The current index must distinguish merged evidence from open handle lanes.""" index = (TRACEABILITY / "README.md").read_text(encoding="utf-8") - self.assertIn("merged PR #45 adds credential-free handle-lifecycle evidence", index) - self.assertIn("PR #53 merged into the #46 stack, not protected main", index) - self.assertIn("authoritative revocation remains unshipped", index) - self.assertNotIn("merged PR #53 adds bounded in-process revocation state", index) - self.assertIn("Open PR #46 adds authoritative use reservation", index) - self.assertIn("#55 adds audience binding", index) - self.assertIn("trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open", index) - self.assertNotIn("active PR #45 adds", index) + rows = [ + line + for line in index.splitlines() + if line.startswith("| Sensitive disclosure is purpose- and classification-bound |") + ] + self.assertEqual(len(rows), 1) + row = rows[0] + self.assertIn("| PARTIAL |", row) + self.assertIn("merged PR #45 adds credential-free handle-lifecycle evidence", row) + self.assertIn("PR #53 merged into the #46 stack, not protected main", row) + self.assertIn("authoritative revocation remains unshipped", row) + self.assertNotIn("merged PR #53 adds bounded in-process revocation state", row) + self.assertIn("Open PR #46 adds authoritative use reservation", row) + self.assertIn("#55 adds audience binding", row) + self.assertIn("trusted storage/value resolution/cross-process lifecycle/model-disclosure remain open", row) + self.assertNotIn("active PR #45 adds", row) if __name__ == "__main__":