From a658900ffc0dba3353f78b7ad158efd5aae2e855 Mon Sep 17 00:00:00 2001 From: RNT56 Date: Sun, 12 Jul 2026 04:53:01 +0200 Subject: [PATCH] docs: mark phase 15 complete Reconcile the canonical provider architecture and task queue with the merged P15-T13 eval suite and the shipped native drop-on-decode safety boundary. --- CHANGELOG.md | 2 ++ docs/ARCHITECTURE.md | 2 +- docs/ROADMAP-PROVIDERS.md | 15 ++++++++------- docs/TASKS.md | 29 +++++++++++++++++------------ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c8e88c..20f0af5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ On a release, the maintainer moves the accumulated `[Unreleased]` entries into a ## [Unreleased] +- **P15-completion-docs** — Reconcile the canonical task, provider-roadmap, and architecture docs after P15-T13: Phase 15 is complete, the hermetic golden eval is shipped, and the documented I7 boundary now matches the implemented native drop-on-decode plus client-side untrusted-data fence. _Owns:_ `docs/{TASKS,ROADMAP-PROVIDERS,ARCHITECTURE}.md`, `CHANGELOG.md`. _(Phase 15 documentation)_ + - **P15-T13** — Add a hermetic, golden-transcript provider compatibility evaluation covering custom OpenAI-compatible endpoints, reasoning token caps, strict JSON Schema output, OpenRouter routing/extras/attribution, native search rendering and drop-on-decode containment, plus injection-flagged and untrusted-data-fenced client search fallback. _Owns:_ `eval/provider-compat/`, `CHANGELOG.md`. _(Phase 15)_ - **frontdoor-parity** — Centralize the native runtime capabilities shared by run/watch/resume/swarm, chat/TUI, and serve (advisor budget, repo orientation, proactive context window, live code intelligence, memory/lessons, and trusted steering) behind one configurator, with a constructor-level parity test across all three real builders so a one-door omission cannot ship inert again. _Owns:_ `cmd/nilcore/{native_runtime.go,native_runtime_test.go,chat.go,main.go}`, `CHANGELOG.md`. _(architecture hardening)_ diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index d470938..15b64f9 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -128,7 +128,7 @@ The native loop, Codex, and Claude Code are interchangeable behind this. Adding **Agentic browser + computer use (Phase 14 / Phase CU, additive, opt-in, contract-untouched).** Two GUI-agent loops drive a *persistent* session over many turns — observe → plan → act → verify — atop the same native loop, egress proxy, sandbox, capguard Rule-of-Two gate, and verifier. **Browser** (`nilcore browse`, `docs/ROADMAP-BROWSER-USE.md`): an accessibility **Set-of-Marks** snapshot (`internal/cdp` stamps `data-nilref`) the model acts on by ref; the browser runs in ONE long-lived in-sandbox `nilcore-browser --serve` Exec (I4) reached over a file-queue on `/work`; `internal/browsersession` version-stamps refs (stale-ref guard) and does host-side `{{secret}}` substitution so a credential never enters the model context (I3); `record_finding` writes typed `artifact.Claim`s re-verified in-box (I2). **Desktop** (`nilcore desktop`, `docs/ROADMAP-COMPUTER-USE.md`): a contained virtual X11 desktop driven through a **3-rung Set-of-Marks ladder** (AT-SPI refs → SoM-marked screenshot → raw coordinate), the fat driver shelling to image-baked tools (`internal/{desktopwire,desktopsession,desktopagent,som,desktop}`). Two paths: generic **Path B** (default, provider-agnostic) and native Anthropic **Path A** (opt-in, via the lone `model.Tool.Builtin` contract addition — byte-identical for every normal tool). A **native-macOS** tier (`docs/ROADMAP-COMPUTER-USE-DARWIN.md`) adds a pure-Go darwin driver (`screencapture`+`cliclick`, Rungs 2/3) and a `--mac-host` host-control mode — the one path where I4's sandbox boundary is *deliberately* relaxed, behind a separate opt-in + forced gate + kill-switch/allowlist (see CLAUDE.md §2 I4). Both default to byte-identical when their env gate is unset; **no module added** (I6). -**Provider upgrade — OpenAI / OpenRouter / openai-compatible (Phase 15, additive, contract-untouched).** The `model.Provider` seam now backs a configurable-BaseURL OpenAI adapter + a generic **`openai-compatible`** vendor (vLLM/Ollama/Groq/Fireworks/Azure/…) with an auth descriptor (Bearer/Azure/None), a single-key `max_tokens`/`max_completion_tokens` marshal (gpt-5.x/o-series), SOTA request fields (reasoning effort, structured outputs, tool_choice, service tier), OpenRouter typed routing/`models[]`/plugins + attribution headers, a typed `model.APIError` (terminal-vs-retryable + `Retry-After`) that `resilience.go` consumes, and prompt-cache/reasoning-token + authoritative-cost accounting in the meter. Anti-exfiltration (I3): a compat BaseURL **rejects** a canonical key-env with a key-free error. Every new request field is `omitempty` set once in `newRequest`, so a zero-valued config is **byte-identical** (`docs/ROADMAP-PROVIDERS.md`). `backend.CodingBackend` untouched (I1); stdlib-only (I6). Web search (native `BuiltinTool` + the client-side fenced fallback) is the remaining Phase-15 wave. +**Provider upgrade — OpenAI / OpenRouter / openai-compatible (Phase 15, complete, contract-untouched).** The `model.Provider` seam now backs a configurable-BaseURL OpenAI adapter + a generic **`openai-compatible`** vendor (vLLM/Ollama/Groq/Fireworks/Azure/…) with an auth descriptor (Bearer/Azure/None), a single-key `max_tokens`/`max_completion_tokens` marshal (gpt-5.x/o-series), SOTA request fields (reasoning effort, structured outputs, tool_choice, service tier), OpenRouter typed routing/`models[]`/plugins + attribution headers, a typed `model.APIError` (terminal-vs-retryable + `Retry-After`) that `resilience.go` consumes, and prompt-cache/reasoning-token + authoritative-cost accounting in the meter. Anti-exfiltration (I3): a compat BaseURL **rejects** a canonical key-env with a key-free error. Every new request field is `omitempty` set once in `newRequest`, so a zero-valued config is **byte-identical**. Native web search rides `BuiltinTool`; Anthropic raw server-tool blocks are dropped at decode while client fallback output stays `guard.Wrap`-fenced. `eval/provider-compat/` pins these request and safety contracts with hermetic golden transcripts (`docs/ROADMAP-PROVIDERS.md`). `backend.CodingBackend` untouched (I1); stdlib-only (I6). **Earned backend selection — `-backend auto` + preferred-backend (additive, Phase 13).** The default backend is no longer hard-wired to native. `-backend auto` (and config `backend: auto`, mapped by `applyConfigDefaults`) resolves to a single concrete backend at run time via `resolveAutoBackend`: `availableBackends(cfg, cred)` reports which of {native, codex, claude-code} actually have their CLI + key present on the host, and the survivors are ordered PREFERENCE-FIRST — the one-run `-prefer-backend` flag, else durable config `preferred_backend` (a CONCRETE backend, validated never to be "auto") — then re-ordered by the verifier-judged Trust Ledger (`trust.Replay` → `Order`), so a cold install honors the stated preference and accumulated evidence overtakes it. `-backends auto` expands the same available set into the multi-backend race (the `Selector` seam above). The selector and the preference only ORDER which backend RUNS; the verifier still judges every result (I2), and per-backend creds resolve through the SecretStore seam (I3). diff --git a/docs/ROADMAP-PROVIDERS.md b/docs/ROADMAP-PROVIDERS.md index 1f754ed..a5b6f01 100644 --- a/docs/ROADMAP-PROVIDERS.md +++ b/docs/ROADMAP-PROVIDERS.md @@ -8,7 +8,7 @@ without adding a Go module, and keeping the default binary byte-identical until Read order: `CLAUDE.md` §2 (invariants) → `docs/ARCHITECTURE.md` (providers) → this file → `docs/TASKS.md` (the Phase-15 queue rows + specs). -> **Status: SHIPPED.** The provider upgrade's non-web waves (T01–T06, T10–T12) merged in PR #61 (this Phase-15 DAG runs T01–T14 — there is no T15/T16). +> **Status: COMPLETE.** The provider upgrade's non-web waves (T01–T06, T10–T12) merged in PR #61 (this Phase-15 DAG runs T01–T14 — there is no T15/T16). > **Web search (T07 · T08 · T09) is now shipped too** — the `model.BuiltinTool` foundation it > depended on landed via the computer-use work (#60). Native render: Anthropic tools-entry > (`web_search_20250305`), OpenAI top-level `web_search_options`, OpenRouter `web` plugin — @@ -18,8 +18,9 @@ Read order: `CLAUDE.md` §2 (invariants) → `docs/ARCHITECTURE.md` (providers) > both. The **I7 fence** holds by construction — provider web results are the model's own > synthesized text (OpenAI) or distinct non-text blocks dropped from re-injection (Anthropic), > and the client path stays `guard.Wrap`'d; raw provider result blocks never re-enter as trusted -> instructions. Remaining: the optional `eval/provider-compat` coverage harness (T13) and a live -> end-to-end check against real provider keys (hermetic shape/byte-identity tests gate it today). +> instructions. The hermetic `eval/provider-compat` golden-transcript suite (T13, PR #105) now gates generic +> endpoints, reasoning, structured output, OpenRouter extras, and both web-search safety paths without network +> access. A live check against real provider keys remains an operator-only validation, never a CI requirement. --- @@ -192,8 +193,8 @@ independently. Sequence the web-search waves after the `BuiltinTool` foundation | **T10** | Onboarding config + wizard for compat vendor | `internal/onboard/*` (+tests) | T02 | M | | **T11** | Metering/pricing for new ids + authoritative `usage.cost` | `meter/pricer.go` (+test) | T03 | M | | **T12** | Egress allowlist extensibility (sandbox only) | `policy/egress.go` (+test) | — | S | -| **T13** | Eval coverage (compat, reasoning, structured, native search) — **NOT BUILT** (`eval/provider-compat/` does not exist; hermetic shape/byte-identity tests gate the feature today) | `eval/provider-compat/` | T06,T08,T09 | M | -| **T14** | 📄 Docs: PREREQUISITES + ARCHITECTURE + TASKS *(contract, serialised)* | `docs/{PREREQUISITES,ARCHITECTURE,TASKS}.md` | T02,T03,T06,T08,T09,T10,T11,T12 | M | +| **T13** | Eval coverage (compat, reasoning, structured, native search) — **SHIPPED in #105** (hermetic per-provider servers + golden transcripts) | `eval/provider-compat/` | T06,T08,T09 | M | +| **T14** | 📄 Docs: PREREQUISITES + ARCHITECTURE + TASKS *(contract, serialised)* | `docs/{PREREQUISITES,ARCHITECTURE,TASKS}.md` | T02,T03,T06,T08,T09,T10,T11,T12,T13 | M | ## 7. Parallel execution waves @@ -205,9 +206,9 @@ Each wave's `Owns` sets are pairwise-disjoint; every dependency resolves to a st | 2 | T02 · T04 | `provider.go` ‖ new `openai_maxtokens.go` | | 3 | T05 *(alone)* | sole owner of `openai.go` | | 4 | T06 · T07 · T10 · T11 | `openrouter_extras.go` ‖ `builtin.go`+`openai_websearch.go` ‖ `onboard/*` ‖ `meter/*` | -| 5 | T08 *(alone)* | co-edits `openai.go`+`native.go`+`model.go` | +| 5 | T08 *(alone)* | `provider/anthropic.go` (+tests), as shipped | | 6 | T09 *(alone)* | new `cmd/nilcore/webcap.go` | -| 7 | T13 *(alone — NOT BUILT)* | new `eval/provider-compat/` | +| 7 | T13 *(alone — shipped #105)* | `eval/provider-compat/` | | 8 | T14 *(alone)* | serialised contract docs | **Critical path:** T01 → T05 → T07 → T08 → T09 → T13 → T14. Peak fan-out: Wave 1 (3) and Wave 4 (4). diff --git a/docs/TASKS.md b/docs/TASKS.md index 31be5ae..0090b17 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -144,7 +144,7 @@ Pick the lowest-ID task whose dependencies are all **Done** and whose `Owns` set > **First wave:** only `P0-T01` and `P0-T02` are eligible at the start, and `P0-T02` is solo (it may touch the whole tree to get the build green). Once `P0-T02` is Done, the tree opens up: `P0-T03`, `P1-T01`, `P2-T01`, `P2-T06`, `P4-T01` become eligible in parallel. -> **Later phases:** Phases 0–6 (56 tasks) shipped at `[0.1.0]`. **Phase 7** (portability — host-native namespace + Landlock sandbox) shipped; its specs are in the section below. **Phase 8** (full multi-agent concurrency) is **shipped** — dynamic-wave async dispatch, tracked in its own design doc (`docs/CONCURRENCY.md`). **Phase 9** (behavioral verification & event-driven autonomy, promoted from `docs/UPGRADE-PATH.md` Tier 1) is **shipped**. **Phase 10** (context depth, trusted steering & distribution, promoted from Tier 2) is **shipped**. **Phase 11** (verifier-backed artifact factory) is **shipped** (`docs/ROADMAP-EVIDENCE-ARTIFACTS.md`). **Phase 12** (verified swarm mode — the bounded in-process `nilcore swarm` surface over the Phase-11 spine) is **shipped**, tracked in its own design doc (`docs/SWARM.md`). **Phase 13** (model-driven work-routing + earned multi-backend Trust-Ledger selection) is **shipped** (`[1.1.0]`). **Phase 14** (agentic browser use — `nilcore browse`) and **Phase CU** (desktop computer use — `nilcore desktop`, Path B + native Path A, plus the native-macOS host-control tier) are **shipped**, tracked in `docs/ROADMAP-BROWSER-USE.md` · `docs/ROADMAP-COMPUTER-USE.md` · `docs/ROADMAP-COMPUTER-USE-DARWIN.md`. **Phase 15** (OpenAI/OpenRouter/openai-compatible provider upgrade) is **shipped**, including the web-search wave (PR #65) (`docs/ROADMAP-PROVIDERS.md`); the one unbuilt task is **P15-T13** (the `eval/provider-compat/` eval coverage — no such directory exists), so a "completes Phase 15" claim is slightly ahead of reality. The external-infrastructure tier (`EXT-01..08`) is registered under "External infrastructure — GATED" below — it is **not** eligible work and stays blocked behind the thesis gate in `docs/ROADMAP-EXTERNAL-INFRA.md` §0. `docs/UPGRADE-PATH.md` holds the deep rationale + file:line sourcing for Phases 9–10 and the gated tier. +> **Later phases:** Phases 0–6 (56 tasks) shipped at `[0.1.0]`. **Phase 7** (portability — host-native namespace + Landlock sandbox) shipped; its specs are in the section below. **Phase 8** (full multi-agent concurrency) is **shipped** — dynamic-wave async dispatch, tracked in its own design doc (`docs/CONCURRENCY.md`). **Phase 9** (behavioral verification & event-driven autonomy, promoted from `docs/UPGRADE-PATH.md` Tier 1) is **shipped**. **Phase 10** (context depth, trusted steering & distribution, promoted from Tier 2) is **shipped**. **Phase 11** (verifier-backed artifact factory) is **shipped** (`docs/ROADMAP-EVIDENCE-ARTIFACTS.md`). **Phase 12** (verified swarm mode — the bounded in-process `nilcore swarm` surface over the Phase-11 spine) is **shipped**, tracked in its own design doc (`docs/SWARM.md`). **Phase 13** (model-driven work-routing + earned multi-backend Trust-Ledger selection) is **shipped** (`[1.1.0]`). **Phase 14** (agentic browser use — `nilcore browse`) and **Phase CU** (desktop computer use — `nilcore desktop`, Path B + native Path A, plus the native-macOS host-control tier) are **shipped**, tracked in `docs/ROADMAP-BROWSER-USE.md` · `docs/ROADMAP-COMPUTER-USE.md` · `docs/ROADMAP-COMPUTER-USE-DARWIN.md`. **Phase 15** (OpenAI/OpenRouter/openai-compatible provider upgrade) is **complete**, including the web-search wave (PR #65) and hermetic provider compatibility evals (P15-T13, PR #105) (`docs/ROADMAP-PROVIDERS.md`). The external-infrastructure tier (`EXT-01..08`) is registered under "External infrastructure — GATED" below — it is **not** eligible work and stays blocked behind the thesis gate in `docs/ROADMAP-EXTERNAL-INFRA.md` §0. `docs/UPGRADE-PATH.md` holds the deep rationale + file:line sourcing for Phases 9–10 and the gated tier. > **Deferred items D1–D4 shipped:** the four formerly-deferred items in `docs/IMPLEMENTATION-PLANS.md` are now implemented + merged — **D1** behavioral verification (sandboxed headless browser, the `browser_view` tool + the pure-Go `nilcore-browser` driver, composite verifier opt-in via `NILCORE_BROWSER_VERIFY`), **D2** semantic code search (content-hash-cached pure-Go HNSW, opt-in via `NILCORE_EMBED_KEY`), **D3** multi-language code intelligence (a language-parser seam + pure-Go, CGO-free Python, TS/JS, and Rust backends — not tree-sitter; the seam was broadened to four backends / nine extensions by R2), and **D4** the gated draft PR (`watch --open-pr` / `schedule --open-pr` via `internal/forge`, only after the human gate). All four are additive, opt-in, and pure stdlib — no new module was added (HNSW, the multi-language parser backends, the embedder, the browser driver incl. its pure-Go CDP client, and forge are all stdlib), so the core dependency count in the default binary stays at exactly **two** (pure-Go SQLite + `golang.org/x/sys`); the Charm TUI stack (3 modules) still links **only** under `make tui`. I6 holds; `CGO_ENABLED=0` across the release matrix. Invariants I1–I7 all hold unchanged. @@ -878,8 +878,8 @@ design + feature matrix + rollout in [`docs/ROADMAP-PROVIDERS.md`](ROADMAP-PROVI > `Provider.Complete` signature and `backend.CodingBackend` are untouched; I3 keys via env-name/SecretStore, > per-request header only, never logged — and a compat endpoint must use a **dedicated `NILCORE_COMPAT_API_KEY`** > (a canonical vendor key on a compat BaseURL is rejected with a key-free error); I4 the client-side web search -> stays sandboxed; I6 stdlib only; I7 provider-returned web-search citations are `guard.Wrap`'d untrusted DATA, -> never a text block, never via `emitReasoning`. +> stays sandboxed; I6 stdlib only; I7 raw provider server-tool result blocks are dropped at decode and never +> re-enter the loop, while client-side search output is `guard.Wrap`'d untrusted DATA. | ID | Phase | Title | Depends on | Owns | Note | |---|---|---|---|---|---| @@ -890,13 +890,13 @@ design + feature matrix + rollout in [`docs/ROADMAP-PROVIDERS.md`](ROADMAP-PROVI | P15-T05 | 15 | SOTA request fields + widened usage/model decode | P15-T01, P15-T03, P15-T04 | `internal/provider/openai.go` (+tests) | sole `openai.go` owner this wave | | P15-T06 | 15 | OpenRouter typed extras (routing, `models[]`, transforms, headers) | P15-T05 | `internal/provider/openrouter_extras.go` (+test) | | | P15-T07 | 15 | Web-search `BuiltinTool` variant + adapter render | P15-T05 | `internal/model/builtin.go`, `internal/provider/openai_websearch.go` (+tests) | needs `BuiltinTool` in `main` | -| P15-T08 | 15 | I7 fence: `web_search_result` block + `guard.Wrap` + native.go handler | P15-T05, P15-T07 | `internal/model/model.go`, `internal/provider/openai.go`, `internal/backend/native.go` (+tests) | solo wave | +| P15-T08 | 15 | I7 fence: drop native server-tool result blocks at decode | P15-T05, P15-T07 | `internal/provider/anthropic.go` (+tests) | solo wave; shipped shape | | P15-T09 | 15 | Exactly-one-web-tool capability switch | P15-T07, P15-T08 | `cmd/nilcore/webcap.go` (+test) | mutual-exclusion dispatch invariant | | P15-T10 | 15 | Onboarding config + wizard for compat vendor | P15-T02 | `internal/onboard/` (+tests) | | | P15-T11 | 15 | Metering/pricing for new ids + authoritative `usage.cost` | P15-T03 | `internal/meter/pricer.go` (+test) | | | P15-T12 | 15 | Egress allowlist extensibility (sandbox only) | — | `internal/policy/egress.go` (+test) | small; ∥ Wave 1 | | P15-T13 | 15 | Eval coverage (compat, reasoning, structured, native search) | P15-T06, P15-T08, P15-T09 | `eval/provider-compat/` | hermetic httptest fixtures | -| P15-T14 | 15 | Docs: PREREQUISITES + ARCHITECTURE + TASKS specs | P15-T02, P15-T03, P15-T06, P15-T08, P15-T09, P15-T10, P15-T11, P15-T12 | `docs/PREREQUISITES.md`, `docs/ARCHITECTURE.md`, `docs/TASKS.md` | **contract**, serialised, last | +| P15-T14 | 15 | Docs: PREREQUISITES + ARCHITECTURE + TASKS specs | P15-T02, P15-T03, P15-T06, P15-T08, P15-T09, P15-T10, P15-T11, P15-T12, P15-T13 | `docs/PREREQUISITES.md`, `docs/ARCHITECTURE.md`, `docs/TASKS.md` | **contract**, serialised, last | **Waves (Owns disjoint within each; deps in strictly earlier waves):** W1 `T01·T03·T12` → W2 `T02·T04` → W3 `T05` → W4 `T06·T07·T10·T11` → W5 `T08` → W6 `T09` → W7 `T13` → W8 `T14`. @@ -944,10 +944,12 @@ search-capable models only). `TestNormalToolByteIdentical` covers nil-Builtin AN **Depends on the `model.BuiltinTool` seam being in `main`.** `make verify` green. **Status (shipped, #65):** the feature landed but **not** as the planned standalone `internal/provider/openai_websearch.go` — that source file does **not** exist. The adapter render lives in `internal/provider/openai.go` (`splitWebSearch` + the `web_search_options` request field) atop the `model.BuiltinTool` seam in `internal/model/builtin.go`; only `internal/provider/openai_websearch_test.go` carries the `openai_websearch` name. The `Owns` path above is the plan artifact, not a shipped-file guarantee. -### P15-T08 — I7 fence: `web_search_result` block + `guard.Wrap` at decode + native.go re-entry -Additive `Block{Type:"web_search_result"}` carrying the `guard.Wrap`'d citation payload; decoders never produce a text -block from citations; `native.go` runs `guard.Suspicious` + `injection_flagged` and appends as fenced DATA only (never -via `emitReasoning`; `textBlocks` drops it). No double-fence. Injection-string fixture proves it. `make verify` green. +### P15-T08 — I7 fence: drop native server-tool result blocks at decode +**Status (shipped, #65):** the originally planned `Block{Type:"web_search_result"}` and native-loop handler were +not built. The Anthropic adapter tolerantly decodes and drops `server_tool_use` / `web_search_tool_result` blocks, +preserving only the model's synthesized text (or a fixed body-free marker for a server-tool-only pause turn). Raw +provider result content therefore never re-enters conversation history. Client-side search remains independently +`guard.Wrap`-fenced. The injection fixture in `eval/provider-compat/` pins both boundaries. `make verify` green. ### P15-T09 — Exactly-one-web-tool capability switch `resolveWeb` in cmd selects exactly one path: native (server-side `BuiltinTool`, citations) or the sandboxed client-side @@ -972,13 +974,16 @@ egress edit). `make verify` green. ### P15-T13 — Eval coverage for compat endpoints, reasoning, structured output, native search `eval/provider-compat/` fixtures exercise a generic compat endpoint, `max_completion_tokens` reasoning model, -`json_schema` output, OpenRouter extras, and the native web-search-result fence (assert `injection_flagged` + fenced -data end-to-end). Per-provider `httptest.Server`, golden fixtures, no network. `make verify` green. **Status: NOT built** — no `eval/provider-compat/` directory exists (`eval/` has only `browse`, `desktop`, `self`). Phase-15 web search itself shipped (#65); this eval-coverage task did not, so any "completes Phase 15" claim overstates it until P15-T13 lands. +`json_schema` output, OpenRouter extras, and both web-search safety paths. Per-provider `httptest.Server`, golden +transcripts, no network. The native fixture proves an injected raw server-tool result is detected by the audit +predicate and absent from model-visible content; the client fallback proves the same payload remains inside an +escaped `guard.Wrap` fence. The suite also rejects duplicate token-cap keys and secret-bearing goldens. +**Status (shipped, #105):** `make verify`, repeated race runs, lint, TUI verification, and vulnerability scanning green. ### P15-T14 — Docs: PREREQUISITES + ARCHITECTURE providers section + Phase-15 specs (contract, serialised) `PREREQUISITES.md` documents the dedicated compat key-env (hard requirement), auth schemes, BaseURL-as-full-prefix, tool/vision as per-model capability flags, and egress-governs-sandbox-not-host. `ARCHITECTURE.md` providers section -reflects the new vendor, the web-search `BuiltinTool` variant, the `web_search_result` fenced block, and the +reflects the new vendor, the web-search `BuiltinTool` variant, the native drop-on-decode/client-fence boundary, and the `model.APIError`/resilience semantics (consistent with T03). `docs/TASKS.md` Phase-15 specs finalised. `make verify` green. ---