From 14f9a241dbffd7828b9f51995c3e9e08306ddd62 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:25:54 -0700 Subject: [PATCH 01/33] Add full-surface documentation refresh spec --- ...2026-08-19-documentation-refresh-design.md | 632 ++++++++++++++++++ 1 file changed, 632 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-19-documentation-refresh-design.md diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md new file mode 100644 index 000000000..11e6b6d1d --- /dev/null +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -0,0 +1,632 @@ +# Documentation Refresh (Full Surface) + +**Date:** 2026-08-19 +**Status:** Draft, pending review +**Scope:** Documentation and doc tooling only. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18). + +## Context + +Trusted Server's documentation spans four surfaces: the VitePress site +(`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap +help, JSDoc), and configuration templates (`trusted-server.example.toml`, +`fastly.toml`, `edgezero.toml`, `.env.example`). A four-track audit of `main` +found systemic drift in every surface. The failures fall into five categories: + +1. **Fabricated or dead content presented as real.** The API reference + documents `GET /first-party/ad` and `POST /third-party/ad`; neither exists + anywhere in `crates/` (the real client auction endpoint is `POST /auction`). + `docs/guide/ad-serving.md` documents an Equativ ad server and an + `[ad_servers.equativ]` config key with zero code presence. + `docs/guide/architecture.md:97-104` shows a `RequestWrapper` trait that does + not exist. Sidebar-linked pages exist for `gam` ("Target Release: Q1 2026", + now past) and `kargo` integrations that have no implementation. + `crates/trusted-server-core/src/auction/README.md` cites a route table in + `main.rs` with invented line numbers (routes moved to `app.rs`), a + `providers/` directory that does not exist, and an APS `mock` config key + that was removed. +2. **Incomplete references.** Five of fifteen `Settings` sections + (`tinybird`, `consent`, `tester_cookie`, `image_optimizer`, `rewrite`) are + absent from both `trusted-server.example.toml` and + `docs/guide/configuration.md`. The API reference omits `POST /auction`, + `/_ts/page-bids`, `/health`, `/_ts/debug/ja4`, the EC partner API, and all + integration endpoints except three. `docs/guide/cli.md` omits + `ts config diff` and the entire `ts dev` subtree. + `docs/guide/integrations-overview.md` compares 7 of 14 integration IDs. + `docs/guide/architecture.md` describes 4 of 10 workspace crates. +3. **Missing coverage.** No pages exist for: Cloudflare/Spin/Axum deployment + (only Fastly has a setup guide, and it is orphaned from the nav), EdgeZero + platform layer, auction telemetry/Tinybird (a 17-file `tinybird/` directory + with no operator path to a working config), the tsjs module system, GPT + slot handoff, cross-adapter parity testing, `testlight` (the canonical + reference integration), and `adserver_mock`. Seven of ten crates have no + README, including all four adapters and the CLI. +4. **Publishing and policy hygiene.** All 75 internal spec/plan files under + `docs/superpowers/` are built and published to the public GitHub Pages site + (no `srcExclude` in `docs/.vitepress/config.mts`), along with internal + onboarding, an internal epic, and an ops runbook. `docs/public/CNAME` + contains the literal placeholder `your-custom-domain.com`. `fastly.toml` + carries a real personal email (`authors`, line 4) and a real Fastly service + id (line 10) against the repo's own sensitive-data policy, and unlabeled + base64 key fixtures that read as credentials. +5. **No enforcement.** `cargo doc` never runs in CI; the two existing + doctests never execute (core is tested only cross-compiled, which skips + doctests); no `missing_docs` or `rustdoc::*` lints are enabled; the docs + PR workflow runs lint and Prettier but never `vitepress build`, so dead + links are only caught after merge when the deploy breaks the live site; + `eslint-plugin-jsdoc` is installed but has zero rules enabled. + +Full finding indexes with `file:line` citations are in Appendix E. + +## Decision + +Treat documentation as a product surface with a defined source of truth per +artifact, fix the audit findings in eight independently shippable work +packages ordered by risk, and add CI enforcement so the same drift cannot +silently recur. Every claim in the refreshed docs must be verifiable against +code on `main`; anything aspirational must be labeled as such or removed. + +The source-of-truth map: + +| Artifact | Truth source | Consumers | +| ------------------ | --------------------------------------------------------------------------------------------------------- | ----------------------- | +| HTTP API reference | Adapter route tables (`adapter-*/src/app.rs`) + core handlers | Publishers, partners | +| Config reference | `Settings` in `crates/trusted-server-core/src/settings.rs` (`deny_unknown_fields` makes parity checkable) | Operators | +| CLI reference | clap definitions in `crates/trusted-server-cli/src/run.rs` and command modules | Operators | +| Integration pages | `builders()` in `core/src/integrations/mod.rs` + registry capabilities | Publishers, integrators | +| Deployment guides | `edgezero.toml` adapter blocks + per-adapter manifests | Operators | +| Architecture | `Cargo.toml` workspace members + `core/src/platform/` | Contributors | +| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | + +## Goals + +- Every endpoint, config key, command, flag, crate name, and code path named + in documentation exists in the code at `main`, with adapter-specific + availability stated where routes differ (several are Fastly-only). +- Every shipped, operator- or publisher-visible surface has documentation: + all 14 integration IDs, all 15 config sections, all 4 deployment targets, + all `ts` commands, the telemetry pipeline, and the tsjs module system. +- The public docs site publishes only intended pages: internal specs, plans, + epics, onboarding, and runbooks are excluded from the build. +- Sensitive real-world values are removed from source-controlled config per + the repo policy in `CLAUDE.md`. +- CI gates catch documentation regressions: docs build (dead links) on PRs, + rustdoc build with broken-intra-doc-link denial, doctests actually running. +- Root markdown (`README`, `CONTRIBUTING`, `TESTING`, `CHANGELOG`) accurately + describes the current workspace, build system, and test matrix. + +## Non-goals + +- No changes to runtime behavior, routes, config schema, or code structure. + Where the audit exposed code issues (e.g. `ts --version` missing, Tinybird + access logging config present but not wired, internal spec references + leaking into vendored `edgezero-cli` help text), this spec records them as + follow-up issues, not in-scope work. +- No new documentation toolchains. VitePress, rustdoc, and clap help remain + the three delivery mechanisms. No TypeDoc, no docs.rs publishing. +- No rewrite of `docs/roadmap.md` content strategy or + `docs/business-use-cases.md` marketing copy beyond factual corrections + (crate names, adapter maturity). +- No release-management policy changes. The CHANGELOG's 10-month untagged + `[Unreleased]` backlog and the governance doc's unfulfilled commitments are + flagged for maintainers, with only mechanical repairs in scope. +- Not chasing 100% rustdoc item coverage. In-code doc work targets module + orientation (`//!`) and the highest-traffic public surfaces, not a + `missing_docs` blanket. + +## Work packages + +Each package is one PR, independently reviewable and landable. WP1 and WP2 +are corrective and should land first; WP3-WP6 are completion work; WP7-WP8 +are quality and enforcement. Later packages assume earlier ones only where +noted. + +### WP1: Publishing and policy hygiene + +Smallest package, highest urgency. + +- Add `srcExclude: ['superpowers/**', 'internal/**', 'epics/**', 'README.md']` + to `docs/.vitepress/config.mts` so internal material stays in the repo but + out of the published site. Verify with a local `vitepress build` that the + dist no longer contains those paths. +- Resolve `docs/public/CNAME`: it currently ships the placeholder + `your-custom-domain.com` into every Pages deploy while `base` is set to + `/trusted-server` (the two are mutually inconsistent). Default action: + delete the CNAME and keep the project-path deploy; revisit if a custom + domain is actually provisioned. Update `docs/README.md:138` and `:172` + accordingly. +- `fastly.toml`: replace the personal email in `authors` with a team alias or + empty list; remove or externalize the hardcoded `service_id` (confirm the + deploy workflow's expectations first; the CHANGELOG already claims this + removal happened); add `# local test fixture, not a real key` labels to the + `[local_server]` secret/JWKS entries; add one-line comments to the four KV + store declarations; remove the orphaned reference to the deleted + `scripts/test-prebid-eids.sh` (line 38). +- `.github/pull_request_template.md`: fix `tracing` to `log` (line 40); add + Cloudflare, Spin, and parity gates to the test-plan checkboxes. +- Fill `docs/guide/index.md` (currently 0 bytes, renders a blank page) with a + short guide landing page linking to Getting Started, Architecture, + Configuration, and the integration index. +- Align `.claude/commands/{check-ci,verify,test-all,test-crate}.md` with the + canonical gate list in `CLAUDE.md` (all currently omit Spin and + `clippy-cloudflare-wasm`; `test-crate.md` uses an untargeted + `cargo test -p`, the exact pattern `AGENTS.md` warns will fail). Add the + missing Spin/cloudflare-wasm gates to `AGENTS.md`'s fallback list. + +Acceptance: `vitepress build` output contains no `superpowers/`, `internal/`, +or `epics/` pages; no real personal emails or service ids in tracked config; +every command file lists the same gates as `CLAUDE.md`. + +### WP2: Remove fabricated and dead content + +Truth pass over existing pages. Nothing new is written here beyond minimal +replacement prose; the goal is that nothing documented is false. + +- `docs/guide/api-reference.md`: delete `GET /first-party/ad` and + `POST /third-party/ad` sections (endpoints do not exist). The full + replacement reference is WP4; in this package, add a stub for + `POST /auction` so the primary endpoint is not undocumented in the interim. +- `docs/guide/integrations-overview.md:46-48`: remove the same dead routes. +- `docs/guide/ad-serving.md`: remove the Equativ section, the + `[ad_servers.equativ]` block, the top-level `[prebid]` block (real section + is `[integrations.prebid]`), and the placeholder `trackImpression` API. + Rewrite the page as a short, accurate description of the real flow: + creative opportunities matched during HTML processing, server-side auction, + creative rewriting to the first-party proxy, GPT handoff. +- `docs/guide/architecture.md`: remove the nonexistent `RequestWrapper` trait + example; replace with the real platform traits from + `core/src/platform/traits.rs` (`PlatformKvStore`, `PlatformConfigStore`, + `PlatformHttpClient`, ...). +- Adapter maturity consistency: `docs/index.md:27`, + `docs/guide/what-is-trusted-server.md:32`, and `docs/roadmap.md:19,34-38` + all describe Cloudflare/Spin as future work while + `docs/guide/architecture.md:154-159` calls them production targets. Settle + on the architecture page's version (all four adapters ship with CI) and fix + the other three. Fix `docs/roadmap.md:21-22` old crate names (`/common/`, + `/cloudflare/`). +- Retire `docs/guide/integrations/gam.md` and `kargo.md` (delete pages, + remove sidebar entries). Neither integration exists; GAM ad serving is + already covered factually via GPT/creative-opportunities docs. If the team + wants to keep roadmap visibility, a one-line entry in `roadmap.md` replaces + each page. +- Retire `FAQ_POC.md`: its headline answer ("NOT ready for use", two-partner + Fastly+Equativ POC) is false on every axis. Delete it (git history + preserves it); fold any still-true answers into + `docs/guide/what-is-trusted-server.md`. +- `crates/trusted-server-core/src/auction/README.md`: point the route table + at `crates/trusted-server-adapter-fastly/src/app.rs` and drop the invented + line numbers (name the tables, `NAMED_ROUTES` / `routes_for_state()`, + instead of line numbers so this cannot rot the same way); remove the + `providers/your_provider.rs` instructions in favor of the real layout + (`auction/provider.rs`, provider registration via + `register_providers` in each integration); delete the APS `mock = true` + sections (field no longer exists). +- `docs/guide/onboarding.md:51,107`: remove or retarget the two links to the + nonexistent root `SEQUENCE.md`. +- `docs/epics/revenue-operations-dashboard.md`: correct its telemetry + sections to reflect the shipped Tinybird pipeline (the epic proposes + BigQuery/Grafana/Prometheus and predates it). It is excluded from the site + by WP1 either way. +- `CHANGELOG.md` mechanical repairs: fix the two dead compare links (no + `v1.2.0` tag exists), the `.rust-analyzer.json` reference (file does not + exist), the retired `synthetic_id`/`x-synthetic-id` naming (subsystem is + now EC), section ordering per Keep-a-Changelog, the "fastly.tom" and + "gogernance" typos, and add the missing entry for #992 (DataDome IP + exclusions and staging bypass, an operator-visible change). +- `.env.example`: remove the `TRUSTED_SERVER__SYNTHETIC__*` keys and the + implication that the `TRUSTED_SERVER__` overlay configures the runtime (the + runtime loads config from the store; the env overlay is test-only). Document + the variables the runtime actually reads (Appendix D) and reference + `.env.example` from the getting-started guide, which today only mentions + `.env.dev`. +- `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section + is `[consent]`. + +Acceptance: grepping the docs tree for `first-party/ad`, `third-party/ad`, +`equativ`, `ad_servers`, `RequestWrapper`, `trackImpression`, `SEQUENCE.md`, +`synthetic_id`, `providers/your_provider`, and `mock = true` (APS context) +returns nothing; no sidebar entry points at a nonexistent integration. + +### WP3: Configuration reference completion + +Bring the two operator-facing config artifacts to parity with `Settings` +(`core/src/settings.rs:1916`, `#[serde(deny_unknown_fields)]`). + +- `trusted-server.example.toml`: add commented, documented example blocks for + the missing sections: `[tinybird]` (all 10 fields, with the note that + `access_enabled` must remain false), `[consent]` (mode, expiration, + jurisdiction, conflict resolution, `consent_store`), `[rewrite]` + (`exclude_domains`, already referenced by a CHANGELOG breaking entry), + `[tester_cookie]`, `[image_optimizer]` (`profile_sets` with one worked + profile), `[[proxy.asset_routes]]` (one worked route with `path_pattern` + and optional S3 SigV4 auth block), `[integrations.osano]`, and the missing + `[auction]` keys (`mediator`, `creative_store`, + `allowed_context_keys`) plus `[debug].inject_adm_for_testing` with its + never-in-production warning. +- `docs/guide/configuration.md`: add the missing `### [consent]`, + `### [tinybird]`, and `### [debug]` sections; extend the Integration + Configurations section from 5 to all 14 IDs (add `aps`, `datadome`, + `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, + `google_tag_manager`, `adserver_mock`), each with its typed config keys + from the integration source. +- Add a parity checklist to the PR description mapping each of the 15 + `Settings` fields to its example-toml block and configuration.md heading + (the table in Appendix B is the worklist). + +Acceptance: every field of `Settings` appears in both +`trusted-server.example.toml` and `docs/guide/configuration.md`; every +integration ID accepted by deploy validation (`core/src/config.rs:29-44`) has +a config subsection. + +### WP4: API reference rebuild + +Rebuild `docs/guide/api-reference.md` from the route inventory (Appendix A). + +- Document every named route: health, discovery/signing endpoints, admin key + rotation (and the deliberately 404-denied legacy `/admin/keys/*` aliases), + EC partner API (`/_ts/api/v1/batch-sync`, `/_ts/api/v1/identify`), tester + cookie endpoints, `POST /auction`, `GET /_ts/page-bids` plus the legacy + `/__ts/page-bids` alias, the four + `/first-party/*` proxy endpoints, `/_ts/debug/ja4`, and the tsjs bundle + endpoint (`/static/tsjs=...`, unified vs deferred vs standalone module + forms with ETag behavior). +- Add an adapter-availability matrix: several routes are Fastly-only + (EC partner API, tester cookies, JA4 debug, real key rotation, Tinybird + telemetry), `/health` is absent on Cloudflare, and Axum returns + `admin_key_management_not_supported`. This distinction exists nowhere in + the docs today. +- Document the fallback dispatch order (tsjs, integration proxy routes, + asset routes, publisher origin proxy) so route-shadowing questions are + answerable from docs. +- Add an Integration Endpoints section generated from the registry proxy + table (Appendix C column "proxy routes") instead of today's three-entry + list. +- State auth expectations per route group: Basic auth handlers covering + `Settings::ADMIN_ENDPOINTS`, Bearer auth on the partner API, `tstoken` + signing on first-party proxy URLs. + +Acceptance: the route list in the reference matches the union of the four +adapter route tables, with per-adapter availability flagged; every documented +route names its handler file. + +### WP5: New coverage pages and navigation repair + +- New deployment guides parallel to `docs/guide/fastly.md`: + `docs/guide/cloudflare.md` (wrangler config, `TRUSTED_SERVER_KV` binding, + `TRUSTED_SERVER_CONFIG` var with blob envelope, missing `/health`), + `docs/guide/spin.md` (component variables encoding, `spin-full-url` + reconstruction, KV store), and `docs/guide/axum-dev.md` (env-var-backed + stores, `PORT`, unsupported admin ops). Group all four under a new + "Deployment" sidebar section and stop orphaning `fastly.md`. +- New `docs/guide/edgezero.md`: the platform layer the app now sits on. The + `edgezero.toml` manifest (app, logical stores, adapter blocks), the config + flow (`trusted-server.toml` validated, pushed as a blob envelope via + `ts config push`, resolved at runtime through `settings_data.rs` including + Fastly chunked storage), and the `ts` lifecycle commands + (auth/build/serve/deploy/provision). Fold the still-relevant parts of + `docs/internal/EDGEZERO_MIGRATION.md` in; the internal runbook itself stays + excluded from the site. +- New `docs/guide/telemetry.md`: auction telemetry from + `[tinybird]` config through `auction_sink_from_settings` to the + `tinybird/` datasources, pipes, and rollups; the operator setup path + (Tinybird tokens in `ts_secrets`); explicit note that access-log telemetry + is not yet wired and `access_enabled` must remain false. New + `tinybird/README.md` covering the `tb` workflow and file layout. +- New `docs/guide/tsjs.md`: the module system (core + immediate vs deferred + integration modules, `JS_ALWAYS` creative module), the build pipeline + (`build-all.mjs`, `build.rs` embedding, runtime concatenation and + hashing), the bundle endpoint forms, the SPA page-bids flow, and the + public `window.tsjs` surface from `crates/trusted-server-js/lib/src/core/types.ts`. +- Document `testlight` in its real context: it is the example/test + integration, so it belongs in the developer-facing + `docs/guide/integration-guide.md` (which already mirrors it) as a named + reference section, not as a partner integration page. Add + `docs/guide/integrations/adserver_mock.md` for the mock auction provider + used in local development, currently unmentioned in all docs. +- Extend `docs/guide/integrations/gpt.md` with the slot handoff mechanism + (edge-injected `gpt_bootstrap.js`, the full shim takeover, targeting, APS + renderer bridge, SPA hook); "handoff" currently appears nowhere in docs. +- `docs/guide/integrations-overview.md`: extend the comparison and + performance tables from 7 to all 14 IDs using the registry capability + matrix (Appendix C). +- Testing docs: rewrite root `TESTING.md` as the test-matrix index (the + aliases from `.cargo/config.toml`, the eight CI test jobs, the parity + suite, `scripts/test-cli.sh`, integration/browser scripts, vitest), and + move its current content, an auction curl runbook, into + `docs/guide/auction-testing.md` cross-linked from the auction README. + Update `docs/guide/testing.md` to cover cloudflare/spin/parity/CLI/browser + suites and replace the fictional two-job CI YAML with the real seven-job + layout. +- `docs/guide/cli.md`: full command reference from the clap tree (Appendix + D), adding `ts config diff` and the `ts dev` subtree with its macOS-only + gating, and linking to `ts-dev-proxy.md`. +- Navigation: add sidebar entries for the three orphaned real integrations + (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages; decide + placement for `onboarding.md` (internal; excluded by WP1 unless moved). +- `docs/guide/architecture.md`: describe all 10 workspace crates and the + platform trait boundary; add the missing Cloudflare adapter section. + +Acceptance: every integration ID is documented and nav-reachable (testlight +via its reference section in the integration guide); every deployment target +has a guide; no real page is orphaned; `vitepress build` passes (dead links +fail the build). + +### WP6: Root markdown and crate READMEs + +- `README.md`: current quick start including the `ts` CLI path + (`ts config init` / `ts serve --adapter ...`) alongside `fastly compute +serve`; link the four deployment guides; refresh the doc-site link table. +- `CONTRIBUTING.md` (untouched since 2026-01): reference the per-target + alias system and full CI gate list, point to `CLAUDE.md`/`AGENTS.md` for + agent workflows, fix the "could be dev/develop/master" boilerplate. +- New crate READMEs (short, orientation-level: what it is, how it builds, + where its docs live) for the seven crates lacking one: + `trusted-server-adapter-fastly`, `-axum`, `-cloudflare`, `-spin`, + `trusted-server-cli`, `trusted-server-js`, `trusted-server-openrtb-codegen`. + Rewrite `crates/trusted-server-core/README.md` as an actual crate overview + (currently covers 2 of ~40 modules), linking to the deep-dive docs. +- New `scripts/README.md` (one line per script). +- `ProjectGovernance.md`: correct the two claims contradicted by repo state + (meeting minutes "maintained within the repository" - none exist; + "continuous releases" - none tagged since v1.1.0) or convert them to + intentions; link it from `CONTRIBUTING.md` so the governance model is + visible at the contribution point. Naming maintainers/CODEOWNERS is a + maintainer decision, flagged as an open question. +- Add `readme = "README.md"` to each crate's `Cargo.toml` once the READMEs + exist. + +Acceptance: `find crates -maxdepth 2 -name README.md` returns one per crate; +README quick start commands all run against `main`. + +### WP7: In-code documentation + +Targeted, not exhaustive. Priorities in order: + +1. `core/src/lib.rs` module index: currently lists 12 of 40+ public modules + and links a `test_support` module; make it complete and grouped + (identity, consent, auction, HTML pipeline, proxy, platform, config). +2. `core/src/platform/` (2/8 files documented): module docs for `traits.rs`, + `types.rs`, `kv.rs`, `http.rs`, `error.rs`. This is the cross-adapter + contract and the highest-value rustdoc gap in the repo. +3. Crate-level `//!` headers for the crates missing them: + `adapter-fastly` (`main.rs`), `adapter-cloudflare`, `trusted-server-js`, + and `trusted-server-cli` (whose `lib.rs` already contains the right prose + as `//` comments; convert to `//!`). +4. Module docs for the undocumented operator/security-relevant core files: + `settings.rs`, `http_util.rs`, `proxy.rs`, `auth.rs`, `tsjs.rs`, + `html_processor.rs` (expand the 3-line header for a 1000-line streaming + rewriter), `integrations/registry.rs`, `integrations/prebid.rs`, and the + `nextjs/` and `datadome/` subtrees. +5. `core/src/constants.rs`: document the 35 undocumented public constants + (cookie and header names are de facto public API). +6. CLI module docs for `commands/audit/*`, `commands/config/*`, `run.rs`. +7. TypeScript: file-header JSDoc for the zero-doc multi-export files + (`core/render.ts`, `shared/globals.ts`, `core/registry.ts`, + `integrations/creative/*`), and complete `core/types.ts` (17/35 exports + documented), which is the public tsjs type surface. Add a header block to + `build-prebid-external.mjs` (401 lines, no header). + +Style follows `CLAUDE.md` documentation standards. `# Examples` sections are +added only where an example compiles as a doctest and earns its keep +(`redacted.rs` is the model); this spec does not attempt examples on all ~589 +public functions. + +Acceptance: `cargo doc --no-deps` builds warning-free for core (native) and +each adapter (per target); every workspace crate and every `pub mod` in core +has a `//!` header. + +### WP8: Enforcement + +Prevent recurrence. All additions gate on existing tooling; no new services. + +- Docs site: add `npm run build` to the `format-docs` job in + `.github/workflows/format.yml` so dead links fail PRs instead of the + post-merge deploy. Align the two workflows' npm cache keys (one keys on + `package.json`, the other on `package-lock.json`). +- Rustdoc: add a CI step running `cargo doc --no-deps` for + `trusted-server-core` plus the adapters on their matching targets with + `RUSTDOCFLAGS="-D warnings"` (this denies `rustdoc::broken_intra_doc_links` + by default). Do not enable `missing_docs`; the existing + `missing_errors_doc`/`missing_panics_doc`/`doc_markdown` clippy trio plus + `-D warnings` stays the item-level gate. +- Doctests: add a native-host `cargo test --doc -p trusted-server-core` step + (doctests are silently skipped today because core is only tested + cross-compiled). +- Add `[lints] workspace = true` to `trusted-server-openrtb-codegen`, the + one crate not inheriting the doc lints. +- Dependabot: add the `github-actions` ecosystem and the Playwright + `browser/package.json` npm root (both currently unmanaged). +- `CLAUDE.md`: update the CI Gates section to the real gate list (it omits + ESLint, the CLI/codegen clippy jobs, the bench compile check, the release + WASM builds, and the entire integration-tests workflow) so agents and the + slash commands stay aligned with reality. Keep `MEMORY.md`-tracked crate + paths out of scope; this spec only touches repo files. +- Optional, decide at review: enable a minimal `jsdoc/*` ESLint rule set + (e.g. `jsdoc/check-alignment`, `jsdoc/check-types`) now that the plugin is + installed; skipped by default to keep WP8 low-noise. + +Acceptance: a PR introducing a dead docs link, a broken intra-doc link, or a +failing doctest fails CI. + +## Sequencing and estimate + +| Order | Package | Size | Depends on | +| ----- | ------------------------ | ---- | ------------------------------- | +| 1 | WP1 hygiene | S | - | +| 2 | WP2 truth pass | M | - | +| 3 | WP3 config reference | M | - | +| 4 | WP4 API reference | M | WP2 | +| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | +| 6 | WP6 root + crate READMEs | M | - | +| 7 | WP7 in-code docs | M | - | +| 8 | WP8 enforcement | S | WP7 (doc build must pass first) | + +WP1-WP3 can proceed in parallel. WP8 lands last so the new gates start green. + +## Verification + +Per PR: `cd docs && npm run lint && npm run format && npm run build`; +`cargo fmt --all -- --check`; the target-matched clippy/test aliases for any +crate whose source files changed (WP7); `cargo doc --no-deps` locally for +rustdoc changes. For WP2-WP4, the acceptance greps listed in each package are +run and their output included in the PR description. For WP1, a local +`vitepress build` listing of `dist/` proves the exclusion set. + +## Open questions + +1. `fastly.toml` `service_id`: removal is policy-correct and the CHANGELOG + claims it already happened, but the deploy path may rely on it. Confirm + how `fastly compute publish` is invoked in practice before removing. +2. `docs/public/CNAME`: delete (recommended, matches the `/trusted-server` + base path) or configure a real custom domain? +3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages: this spec recommends + deletion; confirm no external links depend on them. +4. `docs/guide/onboarding.md`: exclude from the public site (WP1 default) or + keep it published? +5. CHANGELOG: should a release be cut to drain the six breaking entries in + `[Unreleased]`, or should the mechanical repairs land alone? (Mechanical + repairs are in WP2 either way.) +6. Governance: who owns naming maintainers/CODEOWNERS and the meeting-minutes + commitment? Out of scope here but flagged. + +## Follow-up issues to file (code, not docs) + +- `ts --version` does not exist (no `#[command(version)]`). +- Vendored `edgezero-cli` help text leaks internal spec references + ("5.4", "spec 3.3 Model A") into `ts config push --help`; fix upstream at + the `edgezero` repo and bump the pinned tag. +- Tinybird access-log telemetry: config exists but is rejected at runtime; + either wire it or remove the config surface. +- `docs/guide/proxy-signing.md` (oldest page, 2026-01-30) likely needs a + content review against `core/src/proxy.rs` signing; not audited deeply. + +## Appendix A: HTTP route inventory (truth source for WP4) + +No single shared router exists; each adapter registers named routes plus a +publisher fallback. Fastly is the superset. Tables: `adapter-fastly/src/app.rs` +(`NAMED_ROUTES`, `routes_for_state()`), `adapter-axum/src/app.rs` +(`named_routes()`), `adapter-cloudflare/src/app.rs` (`build_router()`), +`adapter-spin/src/app.rs` (`named_fallback_paths()`). + +| Route | Methods | Availability | Handler | +| --------------------------------------------------------------------------------------------- | ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------- | +| `/health` | GET | all except Cloudflare | adapter entry points | +| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | +| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | +| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | +| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly real; Axum/Cloudflare return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | +| `/admin/keys/*` | all | all: deliberately 404-denied legacy aliases | adapter apps | +| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | +| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | +| `/auction` | POST | all | `core/src/auction/endpoints.rs` | +| `/_ts/page-bids` | GET | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | +| `/__ts/page-bids` | GET | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | +| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | +| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | +| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | +| asset route prefixes | GET, HEAD | operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request` | +| everything else | all | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | + +Fallback dispatch order: GPT-diagnostics request prep, EC state build and +integration request filters (DataDome may short-circuit), tsjs, integration +proxy routes, asset routes, publisher proxy. + +## Appendix B: Settings sections (truth source for WP3) + +From `core/src/settings.rs` (`Settings`, line ~1916). Sections marked missing +have no block in `trusted-server.example.toml` today. + +| Section | Struct | Example toml today | +| -------------------------- | ----------------------------- | ------------------------------------------------------------------------ | +| `[publisher]` | `Publisher` | present | +| `[tester_cookie]` | `TesterCookieConfig` | missing | +| `[ec]` | `Ec` + `EcPartner` | present | +| `[integrations.*]` | per-integration typed configs | partial (osano missing; 9 IDs missing from configuration.md) | +| `[[handlers]]` | `Handler` | present | +| `response_headers` | map | present (commented) | +| `[request_signing]` | `RequestSigning` | present | +| `[rewrite]` | `Rewrite` | missing | +| `[auction]` | `AuctionConfig` | present but missing `mediator`, `creative_store`, `allowed_context_keys` | +| `[consent]` | `ConsentConfig` | missing | +| `[proxy]` | `Proxy` | partial; `asset_routes` missing | +| `[creative_opportunities]` | `CreativeOpportunitiesConfig` | present | +| `[image_optimizer]` | `ImageOptimizerSettings` | missing | +| `[tinybird]` | `TinybirdSettings` | missing | +| `[debug]` | `DebugConfig` | partial (`inject_adm_for_testing` missing) | + +## Appendix C: Integration registry (truth source for WP5 overview table) + +From `core/src/integrations/mod.rs` `builders()` and per-integration +registrations. Capabilities: P proxy, AR attribute rewriter, SR script +rewriter, HI head injector, PP html post-processor, RF request filter, +DJS deferred JS, AP auction provider. + +| ID | Capabilities | JS module | Docs page today | +| -------------------- | ---------------------------------- | ------------------ | ------------------------------- | +| `prebid` | P, AR, HI, DJS, AP | yes | in sidebar | +| `aps` | P (renderer), AP, no JS bundle | render helper only | in sidebar | +| `datadome` | P, AR, HI, RF (when protection on) | yes | in sidebar | +| `gpt` | P, AR, HI | yes | orphaned | +| `gpt_diagnostics` | standalone JS on demand | yes | in sidebar | +| `google_tag_manager` | P, AR, SR | yes | orphaned | +| `didomi` | P, HI | yes | in sidebar | +| `sourcepoint` | P, AR, HI | yes | orphaned | +| `osano` | bare registration | yes | in sidebar | +| `permutive` | P, AR | yes | in sidebar (thin) | +| `lockr` | P, AR | yes | in sidebar | +| `nextjs` | SR x2, PP, no JS | no | in sidebar | +| `testlight` | P, AR | yes | none | +| `adserver_mock` | AP only (no registration) | no | none | +| `creative` (JS-only) | always injected (`JS_ALWAYS`) | yes | covered via creative-processing | + +## Appendix D: CLI tree and environment variables + +`ts` commands (from `crates/trusted-server-cli/src/run.rs`): `audit`, +`auth login|logout|status`, `build`, `config init|diff|push|validate`, +`deploy`, `prebid bundle`, `provision`, `serve`, +`dev proxy [ca path|install|uninstall|regenerate]` (macOS only; `ts dev` +lists no subcommands on other hosts). All commands and flags carry help text; +`docs/guide/cli.md` must add `config diff` and the `dev` subtree. + +Runtime environment variables to document (WP2 `.env.example`): +`FASTLY_SERVICE_VERSION`, `FASTLY_IS_STAGING`, `FASTLY_HOSTNAME`, +`FASTLY_REGION`, `EDGEZERO_LOG_LEVEL`, `PORT` (Axum), +`TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` / `TRUSTED_SERVER_SECRET_{STORE}_{KEY}` +(Axum stores), `TRUSTED_SERVER_CONFIG` (Cloudflare var), `EDGEZERO_*` store +overrides, and the build-time `TSJS_SKIP_BUILD`/`TSJS_TEST`. + +## Appendix E: Staleness finding index + +Compact index of audit findings driving WP1/WP2; each was verified against +`main` at `2e85a1cdc`. + +- Dead endpoints documented: `docs/guide/api-reference.md:85` (`/first-party/ad`), + `:190` (`/third-party/ad`); `docs/guide/integrations-overview.md:46-48`. +- Fabricated content: `docs/guide/ad-serving.md:11-18,43,48,77-83` (Equativ, + `[ad_servers]`, `trackImpression`); `docs/guide/architecture.md:97-104` + (`RequestWrapper`); `docs/guide/integration-guide.md:313` (equativ bidder). +- Wrong config names: `docs/guide/getting-started.md:141` (`[gdpr]`). +- Old crate layout: `docs/roadmap.md:21-22` (the only surviving instance). +- Adapter maturity contradictions: `docs/index.md:27`, + `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38` vs + `docs/guide/architecture.md:154-159`. +- Aspirational sidebar pages: `docs/guide/integrations/gam.md` (no such + integration, "Q1 2026" passed), `kargo.md`. +- Auction README: route table file/line rot, nonexistent `providers/` dir, + removed APS `mock` key (`crates/trusted-server-core/src/auction/README.md:269-285,466-473,487-489,543-549,577`). +- Dead links: `docs/guide/onboarding.md:51,107` (`SEQUENCE.md`); + `CHANGELOG.md:51-52` (no `v1.2.0` tag). +- CHANGELOG: retired `synthetic_id` naming (`:24`), nonexistent + `.rust-analyzer.json` (`:51`), missing #992 entry, section order, typos. +- Integration-tests README: wrong CI job name (`:231`), missing environment + files (`:165-177`), missing browser spec (`:141-145`). +- fastly.toml: personal email (`:4`), service id (`:10`), orphaned script + reference (`:38`), unlabeled key fixtures (`:48-74`). +- Publishing: 75 `docs/superpowers/**` files built into the public site (no + `srcExclude`); `docs/public/CNAME` placeholder; empty `docs/guide/index.md`. +- Slash-command drift: `.claude/commands/{check-ci,verify,test-all}.md` omit + Spin/cloudflare-wasm/parity gates; `test-crate.md` untargeted `cargo test`. +- Tooling: no `cargo doc` in CI; doctests never run (cross-compile only); + `format-docs` never runs `vitepress build`; `eslint-plugin-jsdoc` inert; + `openrtb-codegen` missing `[lints] workspace = true`; PR template says + `tracing`. From e4da50ae1a9698f992dcfb601afa2be09303197a Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:48:27 -0700 Subject: [PATCH 02/33] Address spec self-review findings --- ...2026-08-19-documentation-refresh-design.md | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 11e6b6d1d..bb21407df 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -37,8 +37,8 @@ found systemic drift in every surface. The failures fall into five categories: (only Fastly has a setup guide, and it is orphaned from the nav), EdgeZero platform layer, auction telemetry/Tinybird (a 17-file `tinybird/` directory with no operator path to a working config), the tsjs module system, GPT - slot handoff, cross-adapter parity testing, `testlight` (the canonical - reference integration), and `adserver_mock`. Seven of ten crates have no + slot handoff, cross-adapter parity testing, `testlight` (the example/test + integration the integration guide mirrors), and `adserver_mock`. Seven of ten crates have no README, including all four adapters and the CLI. 4. **Publishing and policy hygiene.** All 75 internal spec/plan files under `docs/superpowers/` are built and published to the public GitHub Pages site @@ -134,8 +134,8 @@ Smallest package, highest urgency. delete the CNAME and keep the project-path deploy; revisit if a custom domain is actually provisioned. Update `docs/README.md:138` and `:172` accordingly. -- `fastly.toml`: replace the personal email in `authors` with a team alias or - empty list; remove or externalize the hardcoded `service_id` (confirm the +- `fastly.toml`: replace the personal email in `authors` with an empty list + (matching the workspace `Cargo.toml`); remove or externalize the hardcoded `service_id` (confirm the deploy workflow's expectations first; the CHANGELOG already claims this removal happened); add `# local test fixture, not a real key` labels to the `[local_server]` secret/JWKS entries; add one-line comments to the four KV @@ -168,7 +168,9 @@ replacement prose; the goal is that nothing documented is false. - `docs/guide/integrations-overview.md:46-48`: remove the same dead routes. - `docs/guide/ad-serving.md`: remove the Equativ section, the `[ad_servers.equativ]` block, the top-level `[prebid]` block (real section - is `[integrations.prebid]`), and the placeholder `trackImpression` API. + is `[integrations.prebid]`), and the placeholder `trackImpression` API; + also remove the `equativ` bidder from the example in + `docs/guide/integration-guide.md:313`. Rewrite the page as a short, accurate description of the real flow: creative opportunities matched during HTML processing, server-side auction, creative rewriting to the first-party proxy, GPT handoff. @@ -221,7 +223,8 @@ replacement prose; the goal is that nothing documented is false. - `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section is `[consent]`. -Acceptance: grepping the docs tree for `first-party/ad`, `third-party/ad`, +Acceptance: grepping all tracked markdown and example config (the docs tree +plus root files and crate READMEs) for `first-party/ad`, `third-party/ad`, `equativ`, `ad_servers`, `RequestWrapper`, `trackImpression`, `SEQUENCE.md`, `synthetic_id`, `providers/your_provider`, and `mock = true` (APS context) returns nothing; no sidebar entry points at a nonexistent integration. @@ -277,9 +280,9 @@ Rebuild `docs/guide/api-reference.md` from the route inventory (Appendix A). - Document the fallback dispatch order (tsjs, integration proxy routes, asset routes, publisher origin proxy) so route-shadowing questions are answerable from docs. -- Add an Integration Endpoints section generated from the registry proxy - table (Appendix C column "proxy routes") instead of today's three-entry - list. +- Add an Integration Endpoints section generated from each integration's + `IntegrationProxy::routes()` registration (the integrations are enumerated + in Appendix C) instead of today's three-entry list. - State auth expectations per route group: Basic auth handlers covering `Settings::ADMIN_ENDPOINTS`, Bearer auth on the partner API, `tstoken` signing on first-party proxy URLs. @@ -329,7 +332,8 @@ route names its handler file. performance tables from 7 to all 14 IDs using the registry capability matrix (Appendix C). - Testing docs: rewrite root `TESTING.md` as the test-matrix index (the - aliases from `.cargo/config.toml`, the eight CI test jobs, the parity + aliases from `.cargo/config.toml`, the seven `test.yml` jobs plus the four + integration-test workflow jobs, the parity suite, `scripts/test-cli.sh`, integration/browser scripts, vitest), and move its current content, an auction curl runbook, into `docs/guide/auction-testing.md` cross-linked from the auction README. @@ -365,10 +369,11 @@ serve`; link the four deployment guides; refresh the doc-site link table. Rewrite `crates/trusted-server-core/README.md` as an actual crate overview (currently covers 2 of ~40 modules), linking to the deep-dive docs. - New `scripts/README.md` (one line per script). -- `ProjectGovernance.md`: correct the two claims contradicted by repo state +- `ProjectGovernance.md`: the two claims contradicted by repo state (meeting minutes "maintained within the repository" - none exist; - "continuous releases" - none tagged since v1.1.0) or convert them to - intentions; link it from `CONTRIBUTING.md` so the governance model is + "continuous releases" - none tagged since v1.1.0) become accurate + statements of intent, unless open question 6 resolves them differently; + link it from `CONTRIBUTING.md` so the governance model is visible at the contribution point. Naming maintainers/CODEOWNERS is a maintainer decision, flagged as an open question. - Add `readme = "README.md"` to each crate's `Cargo.toml` once the READMEs From 3bba26cdee17617a0f3fb9a6f8bdc27bf15d975e Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:49:06 -0700 Subject: [PATCH 03/33] Ship all work packages in a single implementation PR --- ...2026-08-19-documentation-refresh-design.md | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index bb21407df..2e15a5c75 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -115,10 +115,10 @@ The source-of-truth map: ## Work packages -Each package is one PR, independently reviewable and landable. WP1 and WP2 -are corrective and should land first; WP3-WP6 are completion work; WP7-WP8 -are quality and enforcement. Later packages assume earlier ones only where -noted. +All eight packages ship in one single implementation PR. Each package is one +commit (or a small commit series) in the order below, so the PR is +reviewable commit-by-commit: WP1 and WP2 are corrective, WP3-WP6 are +completion work, WP7-WP8 are quality and enforcement. ### WP1: Publishing and policy hygiene @@ -251,9 +251,9 @@ Bring the two operator-facing config artifacts to parity with `Settings` `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, `google_tag_manager`, `adserver_mock`), each with its typed config keys from the integration source. -- Add a parity checklist to the PR description mapping each of the 15 - `Settings` fields to its example-toml block and configuration.md heading - (the table in Appendix B is the worklist). +- Add a parity checklist to the implementation PR description mapping each + of the 15 `Settings` fields to its example-toml block and configuration.md + heading (the table in Appendix B is the worklist). Acceptance: every field of `Settings` appears in both `trusted-server.example.toml` and `docs/guide/configuration.md`; every @@ -465,16 +465,18 @@ failing doctest fails CI. | 7 | WP7 in-code docs | M | - | | 8 | WP8 enforcement | S | WP7 (doc build must pass first) | -WP1-WP3 can proceed in parallel. WP8 lands last so the new gates start green. +Commits land in this order within the single implementation PR; WP8 comes +last so the new CI gates turn green on the same PR. ## Verification -Per PR: `cd docs && npm run lint && npm run format && npm run build`; +For the implementation PR: +`cd docs && npm run lint && npm run format && npm run build`; `cargo fmt --all -- --check`; the target-matched clippy/test aliases for any crate whose source files changed (WP7); `cargo doc --no-deps` locally for -rustdoc changes. For WP2-WP4, the acceptance greps listed in each package are -run and their output included in the PR description. For WP1, a local -`vitepress build` listing of `dist/` proves the exclusion set. +rustdoc changes. The acceptance greps listed in WP2-WP4 are run and their +output included in the PR description. For WP1, a local `vitepress build` +listing of `dist/` proves the exclusion set. ## Open questions From 1b937f483603198d2a8f603d523ea9cd6d710065 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:49:57 -0700 Subject: [PATCH 04/33] Fold issues #277 and #341 into the documentation refresh spec --- ...2026-08-19-documentation-refresh-design.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 2e15a5c75..206ac86e0 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -222,11 +222,17 @@ replacement prose; the goal is that nothing documented is false. `.env.dev`. - `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section is `[consent]`. +- Remove the nonexistent `.with_asset(...)` builder method from + `docs/guide/creative-processing.md:808` and + `docs/guide/integration-guide.md:84,248`, replacing it with the real + registration builder API (`with_proxy`, `with_head_injector`, + `with_deferred_js`, ...). Closes #277. Acceptance: grepping all tracked markdown and example config (the docs tree plus root files and crate READMEs) for `first-party/ad`, `third-party/ad`, `equativ`, `ad_servers`, `RequestWrapper`, `trackImpression`, `SEQUENCE.md`, -`synthetic_id`, `providers/your_provider`, and `mock = true` (APS context) +`synthetic_id`, `providers/your_provider`, `with_asset`, and `mock = true` +(APS context) returns nothing; no sidebar entry points at a nonexistent integration. ### WP3: Configuration reference completion @@ -325,6 +331,13 @@ route names its handler file. reference section, not as a partner integration page. Add `docs/guide/integrations/adserver_mock.md` for the mock auction provider used in local development, currently unmentioned in all docs. +- Extend `docs/guide/integration-guide.md` with the script-guard mechanism + (`crates/trusted-server-js/lib/src/shared/script_guard.ts`, the + per-integration guards such as `gpt/script_guard.ts` and + `datadome/script_guard.ts`, and `shared/beacon_guard.ts`): what guards + intercept, when an integration needs one, and how to add one. Today + `script_guard` is mentioned only in `docs/guide/integrations/gpt.md`. + Closes #341. - Extend `docs/guide/integrations/gpt.md` with the slot handoff mechanism (edge-injected `gpt_bootstrap.js`, the full shim takeover, targeting, APS renderer bridge, SPA hook); "handoff" currently appears nowhere in docs. @@ -613,6 +626,10 @@ Compact index of audit findings driving WP1/WP2; each was verified against `[ad_servers]`, `trackImpression`); `docs/guide/architecture.md:97-104` (`RequestWrapper`); `docs/guide/integration-guide.md:313` (equativ bidder). - Wrong config names: `docs/guide/getting-started.md:141` (`[gdpr]`). +- Nonexistent builder method: `.with_asset(...)` in + `docs/guide/creative-processing.md:808`, + `docs/guide/integration-guide.md:84,248` (issue #277). +- Script-guard mechanism absent from the integration guide (issue #341). - Old crate layout: `docs/roadmap.md:21-22` (the only surviving instance). - Adapter maturity contradictions: `docs/index.md:27`, `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38` vs From 9ddf3f466dff7f85d646b5e49f90fdf02ebb57dc Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:54:48 -0700 Subject: [PATCH 05/33] Land spec and work packages in the same PR --- .../specs/2026-08-19-documentation-refresh-design.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 206ac86e0..3ecac19c3 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -115,7 +115,8 @@ The source-of-truth map: ## Work packages -All eight packages ship in one single implementation PR. Each package is one +All eight packages ship in the same single PR as this spec (#1049, branch +`spec-docs-refresh`): the spec commit lands first, then each package as one commit (or a small commit series) in the order below, so the PR is reviewable commit-by-commit: WP1 and WP2 are corrective, WP3-WP6 are completion work, WP7-WP8 are quality and enforcement. @@ -257,7 +258,7 @@ Bring the two operator-facing config artifacts to parity with `Settings` `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, `google_tag_manager`, `adserver_mock`), each with its typed config keys from the integration source. -- Add a parity checklist to the implementation PR description mapping each +- Add a parity checklist to the PR description mapping each of the 15 `Settings` fields to its example-toml block and configuration.md heading (the table in Appendix B is the worklist). @@ -478,12 +479,12 @@ failing doctest fails CI. | 7 | WP7 in-code docs | M | - | | 8 | WP8 enforcement | S | WP7 (doc build must pass first) | -Commits land in this order within the single implementation PR; WP8 comes -last so the new CI gates turn green on the same PR. +Commits land in this order within the single PR, after the spec commit; WP8 +comes last so the new CI gates turn green on the same PR. ## Verification -For the implementation PR: +Before the PR is marked ready: `cd docs && npm run lint && npm run format && npm run build`; `cargo fmt --all -- --check`; the target-matched clippy/test aliases for any crate whose source files changed (WP7); `cargo doc --no-deps` locally for From 82caa0cfc779055031abbd5cdd4afaca2ea99b95 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 10:03:00 -0700 Subject: [PATCH 06/33] Rework documentation refresh spec after pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 774 ++++++++++++------ 1 file changed, 509 insertions(+), 265 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 3ecac19c3..f600b2c0c 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,20 +1,24 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 +**Revised:** 2026-08-20 (addresses pre-implementation review) **Status:** Draft, pending review -**Scope:** Documentation and doc tooling only. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18). +**Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18). ## Context Trusted Server's documentation spans four surfaces: the VitePress site (`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and configuration templates (`trusted-server.example.toml`, -`fastly.toml`, `edgezero.toml`, `.env.example`). A four-track audit of `main` -found systemic drift in every surface. The failures fall into five categories: +`fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track +audit of `main`, hardened by a pre-implementation review, found systemic +drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference documents `GET /first-party/ad` and `POST /third-party/ad`; neither exists anywhere in `crates/` (the real client auction endpoint is `POST /auction`). + The same dead endpoints recur in `docs/guide/error-reference.md:658` and + `docs/guide/integrations/prebid.md:515-531`. `docs/guide/ad-serving.md` documents an Equativ ad server and an `[ad_servers.equativ]` config key with zero code presence. `docs/guide/architecture.md:97-104` shows a `RequestWrapper` trait that does @@ -23,112 +27,184 @@ found systemic drift in every surface. The failures fall into five categories: `crates/trusted-server-core/src/auction/README.md` cites a route table in `main.rs` with invented line numbers (routes moved to `app.rs`), a `providers/` directory that does not exist, and an APS `mock` config key - that was removed. -2. **Incomplete references.** Five of fifteen `Settings` sections - (`tinybird`, `consent`, `tester_cookie`, `image_optimizer`, `rewrite`) are - absent from both `trusted-server.example.toml` and - `docs/guide/configuration.md`. The API reference omits `POST /auction`, + that was removed. Operator instructions reference a nonexistent + `npm run type-check` (`error-reference.md:597`) and a nonexistent + `--validate-config` flag (`error-reference.md:663`), and + `docs/guide/key-rotation.md:301-310` shows an obsolete + `KeyRotationManager::new(...)?` signature (the real constructor returns + `Self`, not a `Result`). +2. **Incomplete references.** `docs/guide/configuration.md` has no + `[consent]`, `[tinybird]`, or `[debug]` sections, and its Integration + Configurations section covers 5 of 14 IDs. `trusted-server.example.toml` + has no `[tinybird]`, `[consent]`, `[rewrite]`, `[tester_cookie]`, or + `[image_optimizer]` blocks. The API reference omits `POST /auction`, `/_ts/page-bids`, `/health`, `/_ts/debug/ja4`, the EC partner API, and all integration endpoints except three. `docs/guide/cli.md` omits `ts config diff` and the entire `ts dev` subtree. `docs/guide/integrations-overview.md` compares 7 of 14 integration IDs. - `docs/guide/architecture.md` describes 4 of 10 workspace crates. -3. **Missing coverage.** No pages exist for: Cloudflare/Spin/Axum deployment - (only Fastly has a setup guide, and it is orphaned from the nav), EdgeZero - platform layer, auction telemetry/Tinybird (a 17-file `tinybird/` directory - with no operator path to a working config), the tsjs module system, GPT - slot handoff, cross-adapter parity testing, `testlight` (the example/test - integration the integration guide mirrors), and `adserver_mock`. Seven of ten crates have no - README, including all four adapters and the CLI. -4. **Publishing and policy hygiene.** All 75 internal spec/plan files under + `docs/guide/architecture.md` describes 4 of 10 workspace crates. The + integration guide's code snippets do not compile against the current API: + `IntegrationProxy::handle` is shown without its `RuntimeServices` argument + (`integration-guide.md:96` vs `registry.rs:282-288`), `proxy_request` is + shown without its `services` argument (`integration-guide.md:132` vs + `proxy.rs:737-742`), and a platform-neutral core example imports + `fastly::http` (`integration-guide.md:134`). +3. **Missing coverage.** No pages exist for: Cloudflare or Spin deployment + (only Fastly has a setup guide, and it is orphaned from the nav), the + EdgeZero platform layer, auction telemetry/Tinybird (a 17-file `tinybird/` + directory with no operator path to a working config), the tsjs module + system, GPT slot handoff, script guards, cross-adapter parity testing, + `testlight` (the example/test integration the integration guide mirrors), + and `adserver_mock`. Seven of ten crates have no README, including all + adapters and the CLI. +4. **A misleading adapter support model.** The docs describe Cloudflare and + Spin inconsistently (in-development on the homepage and roadmap, + production on the architecture page), count Axum as a deployment target + when it is a local-development adapter with no deploy command, and say + nothing about the Spin adapter's actual runtime state: it builds its + settings from the checked-in `trusted-server.example.toml` + (`adapter-spin/src/app.rs:52`), and a startup failure installs a router + that returns 503 for all traffic while `/health` still returns 200 + (`adapter-spin/src/app.rs:404`). No smoke test proves non-health traffic + works under `spin up`. CI compiles the Spin artifact; compilation is not + evidence of production maturity. +5. **Publishing and policy hygiene.** All 75 internal spec/plan files under `docs/superpowers/` are built and published to the public GitHub Pages site - (no `srcExclude` in `docs/.vitepress/config.mts`), along with internal - onboarding, an internal epic, and an ops runbook. `docs/public/CNAME` - contains the literal placeholder `your-custom-domain.com`. `fastly.toml` - carries a real personal email (`authors`, line 4) and a real Fastly service - id (line 10) against the repo's own sensitive-data policy, and unlabeled - base64 key fixtures that read as credentials. -5. **No enforcement.** `cargo doc` never runs in CI; the two existing + (no `srcExclude` in `docs/.vitepress/config.mts`), along with + `docs/guide/onboarding.md` (internal contacts, meetings, access guidance), + an internal epic, and an ops runbook. `docs/public/CNAME` contains the + literal placeholder `your-custom-domain.com`. `fastly.toml` carries a real + personal email (`authors`, line 4) and a real Fastly service id (line 10) + against the repo's own sensitive-data policy, and unlabeled base64 key + fixtures that read as credentials. `docs/package.json` is not `private` + and declares an ISC license in an Apache-2.0 repository. +6. **No enforcement.** `cargo doc` never runs in CI; the two existing doctests never execute (core is tested only cross-compiled, which skips doctests); no `missing_docs` or `rustdoc::*` lints are enabled; the docs PR workflow runs lint and Prettier but never `vitepress build`, so dead links are only caught after merge when the deploy breaks the live site; - `eslint-plugin-jsdoc` is installed but has zero rules enabled. + `eslint-plugin-jsdoc` is installed but has zero rules enabled. Nothing + checks that the docs' hand-maintained copies of routes, config fields, + CLI commands, integration IDs, crate lists, or CI gates match the code, + which is exactly how the drift above accumulated. Full finding indexes with `file:line` citations are in Appendix E. ## Decision Treat documentation as a product surface with a defined source of truth per -artifact, fix the audit findings in eight independently shippable work -packages ordered by risk, and add CI enforcement so the same drift cannot -silently recur. Every claim in the refreshed docs must be verifiable against -code on `main`; anything aspirational must be labeled as such or removed. +artifact, fix the audit findings in eight work packages ordered by risk, and +add enforcement, including executable parity checks, so the same drift is +caught by CI instead of by the next manual audit. Every claim in the +refreshed docs must be verifiable against code on `main`; anything +aspirational must be labeled as such or removed; adapter support claims must +come from an honest, owned support matrix rather than marketing copy. The source-of-truth map: -| Artifact | Truth source | Consumers | -| ------------------ | --------------------------------------------------------------------------------------------------------- | ----------------------- | -| HTTP API reference | Adapter route tables (`adapter-*/src/app.rs`) + core handlers | Publishers, partners | -| Config reference | `Settings` in `crates/trusted-server-core/src/settings.rs` (`deny_unknown_fields` makes parity checkable) | Operators | -| CLI reference | clap definitions in `crates/trusted-server-cli/src/run.rs` and command modules | Operators | -| Integration pages | `builders()` in `core/src/integrations/mod.rs` + registry capabilities | Publishers, integrators | -| Deployment guides | `edgezero.toml` adapter blocks + per-adapter manifests | Operators | -| Architecture | `Cargo.toml` workspace members + `core/src/platform/` | Contributors | -| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | +| Artifact | Truth source | Consumers | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | +| HTTP API reference | Adapter route tables and entry points (`adapter-*/src/app.rs`, `adapter-*/src/main.rs`, `adapter-*/src/platform.rs`) + core handlers | Publishers, partners | +| Config reference | `Settings` in `crates/trusted-server-core/src/settings.rs` (`deny_unknown_fields` makes parity checkable) | Operators | +| CLI reference | clap definitions in `crates/trusted-server-cli/src/run.rs` and command modules | Operators | +| Integration pages | `builders()` in `core/src/integrations/mod.rs` + registry capabilities | Publishers, integrators | +| Integration guide snippets | A compiling sample integration (`testlight` or a doc-tested fixture), never hand-written pseudo-code | Integrators | +| Deployment guides | `edgezero.toml` adapter blocks + per-adapter manifests + adapter support matrix | Operators | +| Architecture | `Cargo.toml` workspace members + `core/src/platform/` | Contributors | +| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | + +## Source sets + +Truth-pass acceptance criteria and parity checks operate on defined source +sets, not "all tracked files" (the refresh spec itself, and archived specs +under `docs/superpowers/`, legitimately contain every retired term): + +- **Active public set:** everything VitePress builds, i.e. `docs/**` + excluding the WP1 `srcExclude` list. This is what site visitors see. +- **Active repo set:** root markdown (`README.md`, `CONTRIBUTING.md`, + `TESTING.md`, `CHANGELOG.md`, `ProjectGovernance.md`, `AGENTS.md`, + `CLAUDE.md`), crate READMEs, config templates + (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, + `.env.example`, `.env.dev`), and `.claude/commands/*.md`. +- **Historical set:** `docs/superpowers/**` (specs, plans, implementation + notes, archive) and shipped `CHANGELOG.md` release entries. Exempt from + retired-term greps; a changelog entry describing a rename may name the + old identifier. ## Goals - Every endpoint, config key, command, flag, crate name, and code path named - in documentation exists in the code at `main`, with adapter-specific - availability stated where routes differ (several are Fastly-only). + in active-set documentation exists in the code at `main`, with + adapter-specific availability stated where behavior differs. - Every shipped, operator- or publisher-visible surface has documentation: - all 14 integration IDs, all 15 config sections, all 4 deployment targets, - all `ts` commands, the telemetry pipeline, and the tsjs module system. + all 14 integration IDs, all 15 config sections, the deployment adapters + (with honest maturity labels), all `ts` commands, the telemetry pipeline, + and the tsjs module system. +- The adapter support model is truthful: three deployment adapters (Fastly + production; Cloudflare; Spin, currently experimental) plus the Axum + local-development adapter, backed by a published support matrix. - The public docs site publishes only intended pages: internal specs, plans, - epics, onboarding, and runbooks are excluded from the build. + epics, onboarding, and runbooks are excluded from the build, and + internal-only details are scrubbed from anything that stays in the public + repository regardless of whether VitePress builds it. - Sensitive real-world values are removed from source-controlled config per the repo policy in `CLAUDE.md`. - CI gates catch documentation regressions: docs build (dead links) on PRs, - rustdoc build with broken-intra-doc-link denial, doctests actually running. + rustdoc build with broken-intra-doc-link denial, doctests actually + running, and executable parity checks for the hand-maintained inventories. - Root markdown (`README`, `CONTRIBUTING`, `TESTING`, `CHANGELOG`) accurately describes the current workspace, build system, and test matrix. ## Non-goals -- No changes to runtime behavior, routes, config schema, or code structure. - Where the audit exposed code issues (e.g. `ts --version` missing, Tinybird +- No changes to runtime behavior, routes, config schema, or code structure, + with one boundary clarification: parity checks added by WP8 may add tests + and scripts, but not alter runtime code. Code defects the audit exposed + (Spin's hardcoded example-config startup, `ts --version` missing, Tinybird access logging config present but not wired, internal spec references - leaking into vendored `edgezero-cli` help text), this spec records them as - follow-up issues, not in-scope work. + leaking into vendored `edgezero-cli` help text) are tracked as follow-up + issues; the Spin one blocks publishing a Spin deployment guide (WP5). - No new documentation toolchains. VitePress, rustdoc, and clap help remain the three delivery mechanisms. No TypeDoc, no docs.rs publishing. -- No rewrite of `docs/roadmap.md` content strategy or - `docs/business-use-cases.md` marketing copy beyond factual corrections - (crate names, adapter maturity). +- No rewrite of `docs/business-use-cases.md` marketing copy. Its uncited + quantitative claims are flagged as an open question (evidence or removal + from primary navigation), not silently rewritten. `docs/roadmap.md` gets a + factual status pass (shipped/active/deferred labels, correct crate names), + not a strategy rewrite. - No release-management policy changes. The CHANGELOG's 10-month untagged `[Unreleased]` backlog and the governance doc's unfulfilled commitments are flagged for maintainers, with only mechanical repairs in scope. - Not chasing 100% rustdoc item coverage. In-code doc work targets module orientation (`//!`) and the highest-traffic public surfaces, not a `missing_docs` blanket. +- Operational changes to deployment selection. Removing or externalizing the + `fastly.toml` `service_id` changes which service a deploy targets; it is + an operationally owned follow-up with its own replacement plan, staging + test, and rollback instructions, not part of this refresh. ## Work packages All eight packages ship in the same single PR as this spec (#1049, branch `spec-docs-refresh`): the spec commit lands first, then each package as one commit (or a small commit series) in the order below, so the PR is -reviewable commit-by-commit: WP1 and WP2 are corrective, WP3-WP6 are -completion work, WP7-WP8 are quality and enforcement. +reviewable commit-by-commit. WP1 and WP2 are corrective, WP3-WP6 are +completion work, WP7-WP8 are quality and enforcement. (The reviewer +recommended splitting at least WP1 into its own PR for urgent publishing +containment; the single-PR delivery is a deliberate owner decision, recorded +in open question 7.) ### WP1: Publishing and policy hygiene Smallest package, highest urgency. -- Add `srcExclude: ['superpowers/**', 'internal/**', 'epics/**', 'README.md']` - to `docs/.vitepress/config.mts` so internal material stays in the repo but - out of the published site. Verify with a local `vitepress build` that the - dist no longer contains those paths. +- Add `srcExclude` to `docs/.vitepress/config.mts` covering + `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, and + `README.md`, and move `docs/guide/onboarding.md` to + `docs/internal/onboarding.md` after scrubbing internal contacts, meeting, + and access details. Exclusion from the build is not sufficient on its own: + the repository is public, so source-sensitive details are scrubbed even + from excluded files. Verify with a local `vitepress build` that the dist + no longer contains those paths. - Resolve `docs/public/CNAME`: it currently ships the placeholder `your-custom-domain.com` into every Pages deploy while `base` is set to `/trusted-server` (the two are mutually inconsistent). Default action: @@ -136,17 +212,19 @@ Smallest package, highest urgency. domain is actually provisioned. Update `docs/README.md:138` and `:172` accordingly. - `fastly.toml`: replace the personal email in `authors` with an empty list - (matching the workspace `Cargo.toml`); remove or externalize the hardcoded `service_id` (confirm the - deploy workflow's expectations first; the CHANGELOG already claims this - removal happened); add `# local test fixture, not a real key` labels to the - `[local_server]` secret/JWKS entries; add one-line comments to the four KV - store declarations; remove the orphaned reference to the deleted - `scripts/test-prebid-eids.sh` (line 38). + (matching the workspace `Cargo.toml`); add `# local test fixture, not a +real key` labels to the `[local_server]` secret/JWKS entries; add one-line + comments to the four KV store declarations; remove the orphaned reference + to the deleted `scripts/test-prebid-eids.sh` (line 38). The `service_id` + removal is out of scope here (see Non-goals and open question 1). +- `docs/package.json`: set `"private": true` and align the license with the + repository (Apache-2.0, currently ISC). - `.github/pull_request_template.md`: fix `tracing` to `log` (line 40); add Cloudflare, Spin, and parity gates to the test-plan checkboxes. - Fill `docs/guide/index.md` (currently 0 bytes, renders a blank page) with a - short guide landing page linking to Getting Started, Architecture, - Configuration, and the integration index. + guide landing page organized by reader journey (evaluator, operator, + integrator, contributor), and retarget the top-nav Guide link + (`config.mts:61`, currently `/guide/getting-started`) at it. - Align `.claude/commands/{check-ci,verify,test-all,test-crate}.md` with the canonical gate list in `CLAUDE.md` (all currently omit Spin and `clippy-cloudflare-wasm`; `test-crate.md` uses an untargeted @@ -154,19 +232,37 @@ Smallest package, highest urgency. missing Spin/cloudflare-wasm gates to `AGENTS.md`'s fallback list. Acceptance: `vitepress build` output contains no `superpowers/`, `internal/`, -or `epics/` pages; no real personal emails or service ids in tracked config; -every command file lists the same gates as `CLAUDE.md`. +`epics/`, or onboarding pages; no real personal emails in tracked config; no +internal contacts or access instructions anywhere in the repo; every command +file lists the same gates as `CLAUDE.md`. -### WP2: Remove fabricated and dead content +### WP2: Truth pass over existing content -Truth pass over existing pages. Nothing new is written here beyond minimal -replacement prose; the goal is that nothing documented is false. +Nothing new is written here beyond minimal replacement prose; the goal is +that nothing in the active sets is false. The pass starts from a complete +page inventory: every page in the active public set gets an explicit +disposition, verified, rewrite, or retire, recorded in the PR description. +Token greps establish that retired names are gone; they cannot validate +commands, APIs, auth, or behavior, so each "verified" disposition means the +page's commands and examples were actually checked against code. - `docs/guide/api-reference.md`: delete `GET /first-party/ad` and `POST /third-party/ad` sections (endpoints do not exist). The full replacement reference is WP4; in this package, add a stub for `POST /auction` so the primary endpoint is not undocumented in the interim. -- `docs/guide/integrations-overview.md:46-48`: remove the same dead routes. +- Remove the same dead endpoints from `docs/guide/integrations-overview.md:46-48`, + `docs/guide/error-reference.md:658`, and + `docs/guide/integrations/prebid.md:515-531`. +- `docs/guide/error-reference.md`: remove or replace the nonexistent + `npm run type-check` (line 597) and `--validate-config` (line 663) + instructions with commands that exist. +- `docs/guide/key-rotation.md`: rewrite the Rust API examples against + `core/src/request_signing/rotation.rs` (`KeyRotationManager::new` returns + `Self`, not a `Result`) and add the Basic-auth requirement to the curl + examples for admin endpoints. +- `docs/guide/proxy-signing.md`: full content review against + `core/src/proxy.rs` signing (promoted from a follow-up; a + security-relevant page cannot sit outside a documentation audit). - `docs/guide/ad-serving.md`: remove the Equativ section, the `[ad_servers.equativ]` block, the top-level `[prebid]` block (real section is `[integrations.prebid]`), and the placeholder `trackImpression` API; @@ -179,22 +275,33 @@ replacement prose; the goal is that nothing documented is false. example; replace with the real platform traits from `core/src/platform/traits.rs` (`PlatformKvStore`, `PlatformConfigStore`, `PlatformHttpClient`, ...). -- Adapter maturity consistency: `docs/index.md:27`, - `docs/guide/what-is-trusted-server.md:32`, and `docs/roadmap.md:19,34-38` - all describe Cloudflare/Spin as future work while - `docs/guide/architecture.md:154-159` calls them production targets. Settle - on the architecture page's version (all four adapters ship with CI) and fix - the other three. Fix `docs/roadmap.md:21-22` old crate names (`/common/`, - `/cloudflare/`). +- Adapter support consistency: `docs/index.md:27`, + `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38`, and + `docs/guide/architecture.md:154-159` currently give four different + answers. Align all of them with the WP5 support matrix: Fastly production, + Cloudflare deployable, Spin experimental (see Context item 4), Axum local + development only. Fix `docs/roadmap.md:21-22` old crate names (`/common/`, + `/cloudflare/`) and give roadmap line items shipped/active/deferred + status labels. - Retire `docs/guide/integrations/gam.md` and `kargo.md` (delete pages, remove sidebar entries). Neither integration exists; GAM ad serving is - already covered factually via GPT/creative-opportunities docs. If the team - wants to keep roadmap visibility, a one-line entry in `roadmap.md` replaces - each page. + already covered factually via GPT/creative-opportunities docs. Before + deletion, inventory inbound links (site-internal grep plus a GitHub search + for the public URLs) and leave a client-side redirect stub for any + previously published URL with known inbound references; `vitepress build` + catches surviving internal links but not bookmarks or external links. - Retire `FAQ_POC.md`: its headline answer ("NOT ready for use", two-partner Fastly+Equativ POC) is false on every axis. Delete it (git history preserves it); fold any still-true answers into - `docs/guide/what-is-trusted-server.md`. + `docs/guide/what-is-trusted-server.md`. Same inbound-link inventory as + above before deletion. +- Remove the nonexistent `.with_asset(...)` builder method from + `docs/guide/creative-processing.md:808` and + `docs/guide/integration-guide.md:84,248`, replacing it with the real + registration builder API (`with_proxy`, `with_head_injector`, + `with_deferred_js`, ...). Closes #277. (The deeper integration-guide + signature drift is fixed in WP5 by switching snippets to a compiling + source.) - `crates/trusted-server-core/src/auction/README.md`: point the route table at `crates/trusted-server-adapter-fastly/src/app.rs` and drop the invented line numbers (name the tables, `NAMED_ROUTES` / `routes_for_state()`, @@ -204,109 +311,136 @@ replacement prose; the goal is that nothing documented is false. `register_providers` in each integration); delete the APS `mock = true` sections (field no longer exists). - `docs/guide/onboarding.md:51,107`: remove or retarget the two links to the - nonexistent root `SEQUENCE.md`. + nonexistent root `SEQUENCE.md` (as part of the WP1 move). - `docs/epics/revenue-operations-dashboard.md`: correct its telemetry sections to reflect the shipped Tinybird pipeline (the epic proposes BigQuery/Grafana/Prometheus and predates it). It is excluded from the site by WP1 either way. - `CHANGELOG.md` mechanical repairs: fix the two dead compare links (no `v1.2.0` tag exists), the `.rust-analyzer.json` reference (file does not - exist), the retired `synthetic_id`/`x-synthetic-id` naming (subsystem is - now EC), section ordering per Keep-a-Changelog, the "fastly.tom" and - "gogernance" typos, and add the missing entry for #992 (DataDome IP - exclusions and staging bypass, an operator-visible change). -- `.env.example`: remove the `TRUSTED_SERVER__SYNTHETIC__*` keys and the - implication that the `TRUSTED_SERVER__` overlay configures the runtime (the - runtime loads config from the store; the env overlay is test-only). Document - the variables the runtime actually reads (Appendix D) and reference - `.env.example` from the getting-started guide, which today only mentions - `.env.dev`. + exist), the retired `synthetic_id`/`x-synthetic-id` naming in the + `[Unreleased]` entry (subsystem is now EC; shipped historical entries keep + their original wording), section ordering per Keep-a-Changelog, the + "fastly.tom" and "gogernance" typos, and add the missing entry for #992 + (DataDome IP exclusions and staging bypass, an operator-visible change). +- Environment files: document the two distinct configuration surfaces + separately. (a) Runtime variables the server reads (Appendix D). (b) The + `TRUSTED_SERVER__` typed overlay, which the runtime loader ignores but + `ts config validate/diff/push` still applies when building the config + blob (`crates/trusted-server-cli/tests/config_env_overlay.rs`). Repair + both `.env.example` and `.env.dev` (both still carry retired + `TRUSTED_SERVER__SYNTHETIC__*` keys), update + `docs/guide/getting-started.md:74-77` (which tells users to `cp .env.dev +.env` and source it), and smoke-test the Axum quick start as written. - `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section is `[consent]`. -- Remove the nonexistent `.with_asset(...)` builder method from - `docs/guide/creative-processing.md:808` and - `docs/guide/integration-guide.md:84,248`, replacing it with the real - registration builder API (`with_proxy`, `with_head_injector`, - `with_deferred_js`, ...). Closes #277. -Acceptance: grepping all tracked markdown and example config (the docs tree -plus root files and crate READMEs) for `first-party/ad`, `third-party/ad`, -`equativ`, `ad_servers`, `RequestWrapper`, `trackImpression`, `SEQUENCE.md`, -`synthetic_id`, `providers/your_provider`, `with_asset`, and `mock = true` -(APS context) -returns nothing; no sidebar entry points at a nonexistent integration. +Acceptance: every active-public page has a recorded disposition; grepping +the active public and active repo sets (Source sets above; historical set +exempt) for `first-party/ad`, `third-party/ad`, `equativ`, `ad_servers`, +`RequestWrapper`, `trackImpression`, `SEQUENCE.md`, `synthetic_id` (outside +shipped changelog entries), `providers/your_provider`, `with_asset`, +`type-check`, and `mock = true` (APS context) returns nothing; no sidebar +entry points at a nonexistent integration. ### WP3: Configuration reference completion Bring the two operator-facing config artifacts to parity with `Settings` (`core/src/settings.rs:1916`, `#[serde(deny_unknown_fields)]`). -- `trusted-server.example.toml`: add commented, documented example blocks for - the missing sections: `[tinybird]` (all 10 fields, with the note that +- `trusted-server.example.toml`: add commented, documented example blocks + for the sections it lacks: `[tinybird]` (all 10 fields, with the note that `access_enabled` must remain false), `[consent]` (mode, expiration, jurisdiction, conflict resolution, `consent_store`), `[rewrite]` (`exclude_domains`, already referenced by a CHANGELOG breaking entry), `[tester_cookie]`, `[image_optimizer]` (`profile_sets` with one worked - profile), `[[proxy.asset_routes]]` (one worked route with `path_pattern` - and optional S3 SigV4 auth block), `[integrations.osano]`, and the missing - `[auction]` keys (`mediator`, `creative_store`, - `allowed_context_keys`) plus `[debug].inject_adm_for_testing` with its - never-in-production warning. -- `docs/guide/configuration.md`: add the missing `### [consent]`, - `### [tinybird]`, and `### [debug]` sections; extend the Integration - Configurations section from 5 to all 14 IDs (add `aps`, `datadome`, - `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, + profile), `[[proxy.asset_routes]]` (a complete, valid route: `prefix`, + `origin_url`, paired `path_pattern`/`target_path`, optional S3 SigV4 auth + block), `[integrations.osano]`, the missing `[auction]` keys (`mediator`, + `creative_store`; `allowed_context_keys` is already present at line 145), + and `[debug].inject_adm_for_testing` with its never-in-production warning. +- `docs/guide/configuration.md`: add the missing `[consent]`, `[tinybird]`, + and `[debug]` sections (the `[tester_cookie]`, `[rewrite]`, and + image-optimizer sections already exist at lines 360, 702, and 946; + verify their field lists rather than re-adding them); extend the + Integration Configurations section from 5 to all 14 IDs (add `aps`, + `datadome`, `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, `google_tag_manager`, `adserver_mock`), each with its typed config keys from the integration source. -- Add a parity checklist to the PR description mapping each - of the 15 `Settings` fields to its example-toml block and configuration.md - heading (the table in Appendix B is the worklist). +- Every example block must actually parse: WP8 adds a test that feeds the + uncommented example config through `Settings::from_toml`, so examples are + finalized and validity-checked in CI rather than eyeballed. +- Add a parity checklist to the PR description mapping each of the 15 + `Settings` fields to its example-toml block and configuration.md heading + (the table in Appendix B is the worklist). Acceptance: every field of `Settings` appears in both `trusted-server.example.toml` and `docs/guide/configuration.md`; every integration ID accepted by deploy validation (`core/src/config.rs:29-44`) has -a config subsection. +a config subsection; the WP8 example-parse test passes. ### WP4: API reference rebuild -Rebuild `docs/guide/api-reference.md` from the route inventory (Appendix A). +Rebuild `docs/guide/api-reference.md` from the route inventory (Appendix A), +with per-endpoint contracts, not just paths. - Document every named route: health, discovery/signing endpoints, admin key rotation (and the deliberately 404-denied legacy `/admin/keys/*` aliases), EC partner API (`/_ts/api/v1/batch-sync`, `/_ts/api/v1/identify`), tester cookie endpoints, `POST /auction`, `GET /_ts/page-bids` plus the legacy - `/__ts/page-bids` alias, the four - `/first-party/*` proxy endpoints, `/_ts/debug/ja4`, and the tsjs bundle - endpoint (`/static/tsjs=...`, unified vs deferred vs standalone module - forms with ETag behavior). -- Add an adapter-availability matrix: several routes are Fastly-only - (EC partner API, tester cookies, JA4 debug, real key rotation, Tinybird - telemetry), `/health` is absent on Cloudflare, and Axum returns - `admin_key_management_not_supported`. This distinction exists nowhere in - the docs today. + `/__ts/page-bids` alias, the four `/first-party/*` proxy endpoints, + `/_ts/debug/ja4`, and the tsjs bundle endpoint (`/static/tsjs=...`, + unified vs deferred vs standalone module forms with ETag behavior). +- Each endpoint follows a contract checklist: methods (including guarded + ones, e.g. page-bids registers OPTIONS and denies it in-handler as a CORS + preflight guard), auth requirement, request parameters/body schema, + response codes and notable headers, cache/CORS behavior, the config gate + that enables it, rate limits where present, and one example. The + `/first-party/*` family gets explicit per-endpoint treatment: `/sign` + mints short-lived signed URLs while `/proxy`, `/click`, and + `/proxy-rebuild` validate different signed inputs; "tstoken signing" as a + group label is not sufficient for a security-sensitive surface. +- Add an adapter availability and capability matrix: route availability per + adapter (EC partner API, tester cookies, JA4 debug, and working key + rotation are Fastly-only; Spin registers the canonical admin routes but + returns unsupported responses; `/health` is absent on Cloudflare) plus the + platform capabilities that differ per adapter (stores, geo, TTL storage, + secrets, Tinybird sink construction, request filters), sourced from each + adapter's `app.rs`, `main.rs`/`lib.rs`, and `platform.rs`. - Document the fallback dispatch order (tsjs, integration proxy routes, - asset routes, publisher origin proxy) so route-shadowing questions are - answerable from docs. + asset routes, publisher origin proxy) and the fact that the publisher + fallback registers seven explicit methods (GET, POST, HEAD, OPTIONS, PUT, + PATCH, DELETE), so route-shadowing and method questions are answerable + from docs. - Add an Integration Endpoints section generated from each integration's `IntegrationProxy::routes()` registration (the integrations are enumerated in Appendix C) instead of today's three-entry list. -- State auth expectations per route group: Basic auth handlers covering - `Settings::ADMIN_ENDPOINTS`, Bearer auth on the partner API, `tstoken` - signing on first-party proxy URLs. Acceptance: the route list in the reference matches the union of the four -adapter route tables, with per-adapter availability flagged; every documented -route names its handler file. +adapter route tables with per-adapter availability flagged; every documented +route names its handler file and satisfies the contract checklist. ### WP5: New coverage pages and navigation repair -- New deployment guides parallel to `docs/guide/fastly.md`: - `docs/guide/cloudflare.md` (wrangler config, `TRUSTED_SERVER_KV` binding, - `TRUSTED_SERVER_CONFIG` var with blob envelope, missing `/health`), - `docs/guide/spin.md` (component variables encoding, `spin-full-url` - reconstruction, KV store), and `docs/guide/axum-dev.md` (env-var-backed - stores, `PORT`, unsupported admin ops). Group all four under a new - "Deployment" sidebar section and stop orphaning `fastly.md`. +- Deployment docs with honest maturity labels, grouped under a new + "Deployment" sidebar section alongside the existing (currently orphaned) + `docs/guide/fastly.md`: + - `docs/guide/cloudflare.md`: wrangler config, `TRUSTED_SERVER_KV` + binding, `TRUSTED_SERVER_CONFIG` var with blob envelope, missing + `/health`. + - `docs/guide/axum-dev.md`: explicitly a local-development guide + (env-var-backed stores, `PORT`, unsupported admin ops), not a + deployment target. + - `docs/guide/spin.md`: written only if the Spin runtime fix (follow-up + issue below) lands first and a `spin up` smoke test proves non-health + traffic works; otherwise the page is a short experimental-status notice + describing the current limitation. The docs never present Spin as + deployable while startup depends on the checked-in example config. +- A support matrix page (or architecture-page section) with owned columns: + build status, intended use, runtime capability, operational support, + known gaps, and release status per adapter. This matrix is the single + source for every "runs on X" claim elsewhere (WP2 aligns existing pages + to it). - New `docs/guide/edgezero.md`: the platform layer the app now sits on. The `edgezero.toml` manifest (app, logical stores, adapter blocks), the config flow (`trusted-server.toml` validated, pushed as a blob envelope via @@ -326,19 +460,22 @@ route names its handler file. (`build-all.mjs`, `build.rs` embedding, runtime concatenation and hashing), the bundle endpoint forms, the SPA page-bids flow, and the public `window.tsjs` surface from `crates/trusted-server-js/lib/src/core/types.ts`. -- Document `testlight` in its real context: it is the example/test - integration, so it belongs in the developer-facing - `docs/guide/integration-guide.md` (which already mirrors it) as a named - reference section, not as a partner integration page. Add - `docs/guide/integrations/adserver_mock.md` for the mock auction provider - used in local development, currently unmentioned in all docs. -- Extend `docs/guide/integration-guide.md` with the script-guard mechanism +- Integration guide integrity: make a compiling source the snippet origin. + Either extract snippets from `testlight` (which registration, proxy, and + TSJS tests already exercise) or add a doc-tested fixture integration; the + guide's current hand-written snippets omit `RuntimeServices` from + `IntegrationProxy::handle` and `proxy_request`, and import `fastly::http` + in platform-neutral core code. Document `testlight` itself as the example + integration in a named reference section of the guide. +- Extend the integration guide with the script-guard mechanism (`crates/trusted-server-js/lib/src/shared/script_guard.ts`, the per-integration guards such as `gpt/script_guard.ts` and `datadome/script_guard.ts`, and `shared/beacon_guard.ts`): what guards intercept, when an integration needs one, and how to add one. Today `script_guard` is mentioned only in `docs/guide/integrations/gpt.md`. Closes #341. +- Add `docs/guide/integrations/adserver_mock.md` for the mock auction + provider used in local development, currently unmentioned in all docs. - Extend `docs/guide/integrations/gpt.md` with the slot handoff mechanism (edge-injected `gpt_bootstrap.js`, the full shim takeover, targeting, APS renderer bridge, SPA hook); "handoff" currently appears nowhere in docs. @@ -347,41 +484,57 @@ route names its handler file. matrix (Appendix C). - Testing docs: rewrite root `TESTING.md` as the test-matrix index (the aliases from `.cargo/config.toml`, the seven `test.yml` jobs plus the four - integration-test workflow jobs, the parity - suite, `scripts/test-cli.sh`, integration/browser scripts, vitest), and - move its current content, an auction curl runbook, into - `docs/guide/auction-testing.md` cross-linked from the auction README. - Update `docs/guide/testing.md` to cover cloudflare/spin/parity/CLI/browser - suites and replace the fictional two-job CI YAML with the real seven-job - layout. + integration-test workflow jobs, the parity suite, `scripts/test-cli.sh`, + integration/browser scripts, vitest). Its current auction runbook is + verified against the running system and rewritten into + `docs/guide/auction-testing.md` (cross-linked from the auction README), + not mechanically moved: it contains fabricated log output and stale + behavior claims. Update `docs/guide/testing.md` to cover + cloudflare/spin/parity/CLI/browser suites and replace the fictional + two-job CI YAML with the real seven-job layout. - `docs/guide/cli.md`: full command reference from the clap tree (Appendix D), adding `ts config diff` and the `ts dev` subtree with its macOS-only gating, and linking to `ts-dev-proxy.md`. +- Site usability: enable VitePress `lastUpdated` (the deploy workflow + already fetches full history for it) and local search + (`themeConfig.search`), so the 1,600-line configuration reference is + navigable; give mermaid diagrams a one-paragraph prose equivalent nearby. - Navigation: add sidebar entries for the three orphaned real integrations - (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages; decide - placement for `onboarding.md` (internal; excluded by WP1 unless moved). + (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages. - `docs/guide/architecture.md`: describe all 10 workspace crates and the platform trait boundary; add the missing Cloudflare adapter section. Acceptance: every integration ID is documented and nav-reachable (testlight -via its reference section in the integration guide); every deployment target -has a guide; no real page is orphaned; `vitepress build` passes (dead links -fail the build). +via its reference section in the integration guide); every adapter has a +guide or an honest status notice consistent with the support matrix; no real +page is orphaned; integration-guide snippets compile; `vitepress build` +passes (dead links fail the build). ### WP6: Root markdown and crate READMEs +Audit every existing root and crate document, not only the missing ones. + - `README.md`: current quick start including the `ts` CLI path (`ts config init` / `ts serve --adapter ...`) alongside `fastly compute -serve`; link the four deployment guides; refresh the doc-site link table. +serve`; link the deployment guides; refresh the doc-site link table. - `CONTRIBUTING.md` (untouched since 2026-01): reference the per-target alias system and full CI gate list, point to `CLAUDE.md`/`AGENTS.md` for - agent workflows, fix the "could be dev/develop/master" boilerplate. + agent workflows, fix the "could be dev/develop/master" boilerplate, and + re-verify its error-handling guidance against current conventions. +- `CLAUDE.md` corrections beyond the CI gates section (WP8): it states the + workspace default target is wasm32-wasip1; `.cargo/config.toml` sets no + default target (per-target aliases and `Cargo.toml` `default-members` do + that work). Re-verify its other build-system claims while there. +- `crates/trusted-server-integration-tests/README.md`: fix the wrong CI job + name (line 231), the incomplete environments tree (lines 165-177), and + the missing browser spec (lines 141-145) flagged in Appendix E. - New crate READMEs (short, orientation-level: what it is, how it builds, where its docs live) for the seven crates lacking one: `trusted-server-adapter-fastly`, `-axum`, `-cloudflare`, `-spin`, `trusted-server-cli`, `trusted-server-js`, `trusted-server-openrtb-codegen`. Rewrite `crates/trusted-server-core/README.md` as an actual crate overview - (currently covers 2 of ~40 modules), linking to the deep-dive docs. + (currently covers 2 of ~40 modules), linking to the deep-dive docs. The + Spin README carries the same experimental-status note as WP5. - New `scripts/README.md` (one line per script). - `ProjectGovernance.md`: the two claims contradicted by repo state (meeting minutes "maintained within the repository" - none exist; @@ -394,11 +547,13 @@ serve`; link the four deployment guides; refresh the doc-site link table. exist. Acceptance: `find crates -maxdepth 2 -name README.md` returns one per crate; -README quick start commands all run against `main`. +every pre-existing root/crate document has a recorded +verified/rewritten/retired disposition; README quick start commands all run +against `main`. ### WP7: In-code documentation -Targeted, not exhaustive. Priorities in order: +Targeted, not exhaustive. The worklist below is the acceptance scope. 1. `core/src/lib.rs` module index: currently lists 12 of 40+ public modules and links a `test_support` module; make it complete and grouped @@ -410,11 +565,14 @@ Targeted, not exhaustive. Priorities in order: `adapter-fastly` (`main.rs`), `adapter-cloudflare`, `trusted-server-js`, and `trusted-server-cli` (whose `lib.rs` already contains the right prose as `//` comments; convert to `//!`). -4. Module docs for the undocumented operator/security-relevant core files: - `settings.rs`, `http_util.rs`, `proxy.rs`, `auth.rs`, `tsjs.rs`, - `html_processor.rs` (expand the 3-line header for a 1000-line streaming - rewriter), `integrations/registry.rs`, `integrations/prebid.rs`, and the - `nextjs/` and `datadome/` subtrees. +4. Module docs for the undocumented core files: `settings.rs`, + `settings_data.rs`, `http_util.rs`, `proxy.rs`, `auth.rs`, `tsjs.rs`, + `openrtb.rs`, `price_bucket.rs`, `rsc_flight.rs`, `host_rewrite.rs`, + `storage/mod.rs`, `html_processor.rs` (expand the 3-line header for a + 1000-line streaming rewriter), `integrations/registry.rs`, + `integrations/prebid.rs`, and the `nextjs/` and `datadome/` subtrees. + (`test_support.rs` and `migration_guards.rs` are deliberately out of + scope.) 5. `core/src/constants.rs`: document the 35 undocumented public constants (cookie and header names are de facto public API). 6. CLI module docs for `commands/audit/*`, `commands/config/*`, `run.rs`. @@ -429,22 +587,39 @@ added only where an example compiles as a doctest and earns its keep (`redacted.rs` is the model); this spec does not attempt examples on all ~589 public functions. -Acceptance: `cargo doc --no-deps` builds warning-free for core (native) and -each adapter (per target); every workspace crate and every `pub mod` in core -has a `//!` header. +Rustdoc verification commands (the exact matrix WP8 puts in CI): + +- `cargo doc --no-deps -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1` +- `cargo doc --no-deps -p trusted-server-adapter-fastly --target wasm32-wasip1` +- `cargo doc --no-deps -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare` +- `cargo doc --no-deps -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin` +- `cargo doc --no-deps -p trusted-server-adapter-axum` +- `cargo doc --no-deps -p trusted-server-cli -p trusted-server-openrtb-codegen --target ` + +Acceptance: every item on the worklist above is complete; the rustdoc +command matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the +listed TypeScript files each have a file-header JSDoc block and every +`core/types.ts` export is documented (checked by the WP8 jsdoc lint scoped +to those files, or a grep count recorded in the PR description). ### WP8: Enforcement -Prevent recurrence. All additions gate on existing tooling; no new services. +Prevent recurrence. Two layers: build gates (links, rustdoc, doctests) and +semantic parity checks for every inventory the docs maintain by hand. All +additions are tests, scripts, and workflow steps; no runtime code changes. + +Build gates: - Docs site: add `npm run build` to the `format-docs` job in `.github/workflows/format.yml` so dead links fail PRs instead of the post-merge deploy. Align the two workflows' npm cache keys (one keys on - `package.json`, the other on `package-lock.json`). -- Rustdoc: add a CI step running `cargo doc --no-deps` for - `trusted-server-core` plus the adapters on their matching targets with - `RUSTDOCFLAGS="-D warnings"` (this denies `rustdoc::broken_intra_doc_links` - by default). Do not enable `missing_docs`; the existing + `package.json`, the other on `package-lock.json`). Add `.tool-versions` + to `deploy-docs.yml` trigger paths (the site renders versions from it, so + version-only bumps must republish). +- Rustdoc: add a CI step running the WP7 command matrix with + `RUSTDOCFLAGS="-D warnings"` (this denies + `rustdoc::broken_intra_doc_links` by default). Do not enable + `missing_docs`; the existing `missing_errors_doc`/`missing_panics_doc`/`doc_markdown` clippy trio plus `-D warnings` stays the item-level gate. - Doctests: add a native-host `cargo test --doc -p trusted-server-core` step @@ -454,30 +629,50 @@ Prevent recurrence. All additions gate on existing tooling; no new services. one crate not inheriting the doc lints. - Dependabot: add the `github-actions` ecosystem and the Playwright `browser/package.json` npm root (both currently unmanaged). -- `CLAUDE.md`: update the CI Gates section to the real gate list (it omits + +Semantic parity checks (each catches a class of drift this audit found): + +- Example-config validity: a unit test feeding the uncommented + `trusted-server.example.toml` through `Settings::from_toml`, so every + example block parses and passes validation (guards WP3 forever; + `deny_unknown_fields` makes stale keys a hard failure). +- Route parity: a test per adapter asserting its registered route/method + set matches a checked-in snapshot that the API reference is written from + (guards Appendix A / WP4). +- CLI parity: a golden-file test of the rendered `ts` help tree (commands + and flags) that `docs/guide/cli.md` is written against (guards WP5's CLI + reference). +- Integration parity: a test asserting the registry's integration ID and + capability set matches the checked-in table used by + `integrations-overview.md` (guards Appendix C). +- Repo inventory: a CI script checking workspace members each have a + README, every active public page is reachable from the sidebar or an + explicit orphan allowlist, and the CI gate list in `CLAUDE.md` names the + jobs that actually exist in the workflows. +- `CLAUDE.md` CI gates section: update to the real gate list (it omits ESLint, the CLI/codegen clippy jobs, the bench compile check, the release WASM builds, and the entire integration-tests workflow) so agents and the - slash commands stay aligned with reality. Keep `MEMORY.md`-tracked crate - paths out of scope; this spec only touches repo files. + slash commands stay aligned with reality. - Optional, decide at review: enable a minimal `jsdoc/*` ESLint rule set - (e.g. `jsdoc/check-alignment`, `jsdoc/check-types`) now that the plugin is - installed; skipped by default to keep WP8 low-noise. + scoped to the WP7 TypeScript files; skipped by default to keep WP8 + low-noise. -Acceptance: a PR introducing a dead docs link, a broken intra-doc link, or a -failing doctest fails CI. +Acceptance: a PR introducing a dead docs link, a broken intra-doc link, a +failing doctest, an invalid example-config block, or a route/CLI/integration +inventory change without a matching docs snapshot update fails CI. ## Sequencing and estimate -| Order | Package | Size | Depends on | -| ----- | ------------------------ | ---- | ------------------------------- | -| 1 | WP1 hygiene | S | - | -| 2 | WP2 truth pass | M | - | -| 3 | WP3 config reference | M | - | -| 4 | WP4 API reference | M | WP2 | -| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | -| 6 | WP6 root + crate READMEs | M | - | -| 7 | WP7 in-code docs | M | - | -| 8 | WP8 enforcement | S | WP7 (doc build must pass first) | +| Order | Package | Size | Depends on | +| ----- | ------------------------ | ---- | --------------------------------- | +| 1 | WP1 hygiene | S | - | +| 2 | WP2 truth pass | M | - | +| 3 | WP3 config reference | M | - | +| 4 | WP4 API reference | M | WP2 | +| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | +| 6 | WP6 root + crate READMEs | M | - | +| 7 | WP7 in-code docs | M | - | +| 8 | WP8 enforcement | M | WP3, WP7 (gates must start green) | Commits land in this order within the single PR, after the spec commit; WP8 comes last so the new CI gates turn green on the same PR. @@ -487,66 +682,83 @@ comes last so the new CI gates turn green on the same PR. Before the PR is marked ready: `cd docs && npm run lint && npm run format && npm run build`; `cargo fmt --all -- --check`; the target-matched clippy/test aliases for any -crate whose source files changed (WP7); `cargo doc --no-deps` locally for -rustdoc changes. The acceptance greps listed in WP2-WP4 are run and their -output included in the PR description. For WP1, a local `vitepress build` -listing of `dist/` proves the exclusion set. +crate whose source files changed (WP7, WP8 tests); the WP7 rustdoc command +matrix locally. The acceptance greps listed in WP2-WP4 are run over the +defined source sets and their output included in the PR description, along +with the WP2/WP6 page-disposition inventories and the WP3 parity checklist. +For WP1, a local `vitepress build` listing of `dist/` proves the exclusion +set. The Axum quick start from the updated getting-started guide is +smoke-tested as written. ## Open questions 1. `fastly.toml` `service_id`: removal is policy-correct and the CHANGELOG - claims it already happened, but the deploy path may rely on it. Confirm - how `fastly compute publish` is invoked in practice before removing. + claims it already happened, but it changes deployment selection. Now an + operationally owned follow-up (see Non-goals): needs an owner, a + replacement mechanism, a non-production deployment test, and rollback + instructions. 2. `docs/public/CNAME`: delete (recommended, matches the `/trusted-server` base path) or configure a real custom domain? 3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages: this spec recommends - deletion; confirm no external links depend on them. -4. `docs/guide/onboarding.md`: exclude from the public site (WP1 default) or - keep it published? + deletion with an inbound-link inventory and redirect stubs where + referenced; confirm. +4. `docs/business-use-cases.md`: its quantitative claims need dated evidence + and assumptions, or the page leaves primary navigation until verified. + Which? 5. CHANGELOG: should a release be cut to drain the six breaking entries in `[Unreleased]`, or should the mechanical repairs land alone? (Mechanical repairs are in WP2 either way.) 6. Governance: who owns naming maintainers/CODEOWNERS and the meeting-minutes commitment? Out of scope here but flagged. +7. Delivery shape: the pre-implementation review recommends shipping WP1 + (publishing containment) as its own PR ahead of the rest; the current + single-PR plan is the owner's explicit instruction. Confirm or split. ## Follow-up issues to file (code, not docs) +- Spin adapter builds runtime settings from the checked-in + `trusted-server.example.toml` (`adapter-spin/src/app.rs:52`) and serves a + blanket 503 on startup failure while `/health` returns 200 + (`adapter-spin/src/app.rs:404`). Blocking for the WP5 Spin deployment + guide; until fixed, docs label Spin experimental. A `spin up` smoke test + proving non-health traffic belongs to the fix's acceptance criteria. - `ts --version` does not exist (no `#[command(version)]`). - Vendored `edgezero-cli` help text leaks internal spec references ("5.4", "spec 3.3 Model A") into `ts config push --help`; fix upstream at the `edgezero` repo and bump the pinned tag. - Tinybird access-log telemetry: config exists but is rejected at runtime; either wire it or remove the config surface. -- `docs/guide/proxy-signing.md` (oldest page, 2026-01-30) likely needs a - content review against `core/src/proxy.rs` signing; not audited deeply. ## Appendix A: HTTP route inventory (truth source for WP4) No single shared router exists; each adapter registers named routes plus a -publisher fallback. Fastly is the superset. Tables: `adapter-fastly/src/app.rs` -(`NAMED_ROUTES`, `routes_for_state()`), `adapter-axum/src/app.rs` -(`named_routes()`), `adapter-cloudflare/src/app.rs` (`build_router()`), -`adapter-spin/src/app.rs` (`named_fallback_paths()`). - -| Route | Methods | Availability | Handler | -| --------------------------------------------------------------------------------------------- | ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------- | -| `/health` | GET | all except Cloudflare | adapter entry points | -| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | -| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | -| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | -| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly real; Axum/Cloudflare return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | -| `/admin/keys/*` | all | all: deliberately 404-denied legacy aliases | adapter apps | -| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | -| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | -| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | -| `/auction` | POST | all | `core/src/auction/endpoints.rs` | -| `/_ts/page-bids` | GET | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | -| `/__ts/page-bids` | GET | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | -| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | -| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | -| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | -| asset route prefixes | GET, HEAD | operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request` | -| everything else | all | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | +publisher fallback. Fastly is the superset. Route tables: +`adapter-fastly/src/app.rs` (`NAMED_ROUTES`, `routes_for_state()`), +`adapter-axum/src/app.rs` (`named_routes()`), `adapter-cloudflare/src/app.rs` +(`build_router()`), `adapter-spin/src/app.rs` (`named_fallback_paths()`). +Adapter capability differences (stores, geo, TTL, secrets, Tinybird, +request filters) live in each adapter's `platform.rs` and entry point; WP4's +capability matrix is written from those files, not from this table alone. + +| Route | Methods | Availability | Handler | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| `/health` | GET | all except Cloudflare | adapter entry points | +| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | +| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | +| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | +| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | +| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | +| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | +| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | +| `/auction` | POST | all | `core/src/auction/endpoints.rs` | +| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | +| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | +| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | +| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | +| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | +| asset route prefixes | GET, HEAD | operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request` | +| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | Fallback dispatch order: GPT-diagnostics request prep, EC state build and integration request filters (DataDome may short-circuit), tsjs, integration @@ -554,26 +766,26 @@ proxy routes, asset routes, publisher proxy. ## Appendix B: Settings sections (truth source for WP3) -From `core/src/settings.rs` (`Settings`, line ~1916). Sections marked missing -have no block in `trusted-server.example.toml` today. - -| Section | Struct | Example toml today | -| -------------------------- | ----------------------------- | ------------------------------------------------------------------------ | -| `[publisher]` | `Publisher` | present | -| `[tester_cookie]` | `TesterCookieConfig` | missing | -| `[ec]` | `Ec` + `EcPartner` | present | -| `[integrations.*]` | per-integration typed configs | partial (osano missing; 9 IDs missing from configuration.md) | -| `[[handlers]]` | `Handler` | present | -| `response_headers` | map | present (commented) | -| `[request_signing]` | `RequestSigning` | present | -| `[rewrite]` | `Rewrite` | missing | -| `[auction]` | `AuctionConfig` | present but missing `mediator`, `creative_store`, `allowed_context_keys` | -| `[consent]` | `ConsentConfig` | missing | -| `[proxy]` | `Proxy` | partial; `asset_routes` missing | -| `[creative_opportunities]` | `CreativeOpportunitiesConfig` | present | -| `[image_optimizer]` | `ImageOptimizerSettings` | missing | -| `[tinybird]` | `TinybirdSettings` | missing | -| `[debug]` | `DebugConfig` | partial (`inject_adm_for_testing` missing) | +From `core/src/settings.rs` (`Settings`, line ~1916). Columns record what +each artifact carries today. + +| Section | Struct | `trusted-server.example.toml` | `configuration.md` | +| -------------------------- | ----------------------------- | ------------------------------------------------------------------------------- | -------------------------- | +| `[publisher]` | `Publisher` | present | present | +| `[tester_cookie]` | `TesterCookieConfig` | missing | present (line 360) | +| `[ec]` | `Ec` + `EcPartner` | present | present | +| `[integrations.*]` | per-integration typed configs | partial (osano missing) | 5 of 14 IDs | +| `[[handlers]]` | `Handler` | present | present | +| `response_headers` | map | present (commented) | present | +| `[request_signing]` | `RequestSigning` | present | present | +| `[rewrite]` | `Rewrite` | missing | present (line 702) | +| `[auction]` | `AuctionConfig` | missing `mediator`, `creative_store` (`allowed_context_keys` present, line 145) | present | +| `[consent]` | `ConsentConfig` | missing | missing | +| `[proxy]` | `Proxy` | partial; `asset_routes` missing | present incl. asset routes | +| `[creative_opportunities]` | `CreativeOpportunitiesConfig` | present | present | +| `[image_optimizer]` | `ImageOptimizerSettings` | missing | present (line 946 area) | +| `[tinybird]` | `TinybirdSettings` | missing | missing | +| `[debug]` | `DebugConfig` | partial (`inject_adm_for_testing` missing) | missing | ## Appendix C: Integration registry (truth source for WP5 overview table) @@ -609,49 +821,81 @@ DJS deferred JS, AP auction provider. lists no subcommands on other hosts). All commands and flags carry help text; `docs/guide/cli.md` must add `config diff` and the `dev` subtree. -Runtime environment variables to document (WP2 `.env.example`): +Runtime environment variables to document (WP2 `.env.example` / `.env.dev`): `FASTLY_SERVICE_VERSION`, `FASTLY_IS_STAGING`, `FASTLY_HOSTNAME`, `FASTLY_REGION`, `EDGEZERO_LOG_LEVEL`, `PORT` (Axum), `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` / `TRUSTED_SERVER_SECRET_{STORE}_{KEY}` (Axum stores), `TRUSTED_SERVER_CONFIG` (Cloudflare var), `EDGEZERO_*` store overrides, and the build-time `TSJS_SKIP_BUILD`/`TSJS_TEST`. +Documented separately from the runtime variables: the `TRUSTED_SERVER__` +typed overlay, ignored by the runtime loader but applied by +`ts config validate/diff/push` when building the config blob +(`crates/trusted-server-cli/tests/config_env_overlay.rs`). + ## Appendix E: Staleness finding index Compact index of audit findings driving WP1/WP2; each was verified against `main` at `2e85a1cdc`. -- Dead endpoints documented: `docs/guide/api-reference.md:85` (`/first-party/ad`), - `:190` (`/third-party/ad`); `docs/guide/integrations-overview.md:46-48`. +- Dead endpoints documented: `docs/guide/api-reference.md:85` + (`/first-party/ad`), `:190` (`/third-party/ad`); + `docs/guide/integrations-overview.md:46-48`; + `docs/guide/error-reference.md:658`; + `docs/guide/integrations/prebid.md:515-531`. +- Dead operator commands: `docs/guide/error-reference.md:597` + (`npm run type-check`), `:663` (`--validate-config`). +- Obsolete API examples: `docs/guide/key-rotation.md:301-310` + (`KeyRotationManager::new(...)?`; constructor returns `Self`), + unauthenticated admin curl examples. - Fabricated content: `docs/guide/ad-serving.md:11-18,43,48,77-83` (Equativ, `[ad_servers]`, `trackImpression`); `docs/guide/architecture.md:97-104` (`RequestWrapper`); `docs/guide/integration-guide.md:313` (equativ bidder). +- Integration-guide snippets that do not compile: + `integration-guide.md:96` (`handle` without `RuntimeServices`, vs + `registry.rs:282-288`), `:132` (`proxy_request` without `services`, vs + `proxy.rs:737-742`), `:134` (`use fastly::http` in core-neutral code). - Wrong config names: `docs/guide/getting-started.md:141` (`[gdpr]`). - Nonexistent builder method: `.with_asset(...)` in `docs/guide/creative-processing.md:808`, `docs/guide/integration-guide.md:84,248` (issue #277). - Script-guard mechanism absent from the integration guide (issue #341). - Old crate layout: `docs/roadmap.md:21-22` (the only surviving instance). -- Adapter maturity contradictions: `docs/index.md:27`, +- Adapter support contradictions: `docs/index.md:27`, `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38` vs - `docs/guide/architecture.md:154-159`. + `docs/guide/architecture.md:154-159`; Axum described as a deployment + target; Spin described as production-capable despite + `adapter-spin/src/app.rs:52` (settings from the checked-in example toml) + and `:404` (blanket 503 on startup failure). - Aspirational sidebar pages: `docs/guide/integrations/gam.md` (no such integration, "Q1 2026" passed), `kargo.md`. - Auction README: route table file/line rot, nonexistent `providers/` dir, removed APS `mock` key (`crates/trusted-server-core/src/auction/README.md:269-285,466-473,487-489,543-549,577`). - Dead links: `docs/guide/onboarding.md:51,107` (`SEQUENCE.md`); `CHANGELOG.md:51-52` (no `v1.2.0` tag). -- CHANGELOG: retired `synthetic_id` naming (`:24`), nonexistent - `.rust-analyzer.json` (`:51`), missing #992 entry, section order, typos. +- CHANGELOG: retired `synthetic_id` naming in `[Unreleased]` (`:24`), + nonexistent `.rust-analyzer.json` (`:51`), missing #992 entry, section + order, typos. +- Environment files: `.env.example` and `.env.dev` both carry retired + `TRUSTED_SERVER__SYNTHETIC__*` keys; `docs/guide/getting-started.md:74-77` + tells users to copy and source `.env.dev`. - Integration-tests README: wrong CI job name (`:231`), missing environment files (`:165-177`), missing browser spec (`:141-145`). -- fastly.toml: personal email (`:4`), service id (`:10`), orphaned script - reference (`:38`), unlabeled key fixtures (`:48-74`). +- fastly.toml: personal email (`:4`), service id (`:10`, ops-owned + follow-up), orphaned script reference (`:38`), unlabeled key fixtures + (`:48-74`). - Publishing: 75 `docs/superpowers/**` files built into the public site (no - `srcExclude`); `docs/public/CNAME` placeholder; empty `docs/guide/index.md`. + `srcExclude`); `docs/guide/onboarding.md` published with internal + contacts; `docs/public/CNAME` placeholder; empty `docs/guide/index.md`; + nav Guide link bypasses the landing page (`config.mts:61`); + `docs/package.json` not private, ISC license in an Apache-2.0 repo. +- Root-doc drift: `CLAUDE.md:102` (no workspace default target exists); + `CONTRIBUTING.md` stale since 2026-01. - Slash-command drift: `.claude/commands/{check-ci,verify,test-all}.md` omit Spin/cloudflare-wasm/parity gates; `test-crate.md` untargeted `cargo test`. - Tooling: no `cargo doc` in CI; doctests never run (cross-compile only); - `format-docs` never runs `vitepress build`; `eslint-plugin-jsdoc` inert; + `format-docs` never runs `vitepress build`; `deploy-docs.yml` not + triggered by `.tool-versions` changes; `eslint-plugin-jsdoc` inert; `openrtb-codegen` missing `[lints] workspace = true`; PR template says - `tracing`. + `tracing`; no semantic parity checks for routes, config, CLI, + integrations, crates, navigation, or CI gates. From 7d853a5d29312a5775506ff983c6de4f653329ac Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 10:42:46 -0700 Subject: [PATCH 07/33] Align documentation refresh spec with rc/202608 --- ...2026-08-19-documentation-refresh-design.md | 94 ++++++++++++------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index f600b2c0c..08836d402 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -3,7 +3,7 @@ **Date:** 2026-08-19 **Revised:** 2026-08-20 (addresses pre-implementation review) **Status:** Draft, pending review -**Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18). +**Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned 2026-08-20 to the release branch `rc/202608`, which this PR targets. Notable rc deltas folded in below: a 16th `[cache]` settings section, three admin EC diagnostic routes, a restructured CLI (`ts audit generate`, `ad-templates`, `active-version`, `healthcheck`, `rollback`, `config gc`), and rc's own updates to `cli.md`, `configuration.md`, and `api-reference.md`. Line citations are from the main baseline unless marked rc; spot-rechecked claims cite rc line numbers. ## Context @@ -17,7 +17,9 @@ drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference documents `GET /first-party/ad` and `POST /third-party/ad`; neither exists anywhere in `crates/` (the real client auction endpoint is `POST /auction`). - The same dead endpoints recur in `docs/guide/error-reference.md:658` and + On rc the dead sections sit at `api-reference.md:86,191` with two more + occurrences at `:707,711`, and the same dead endpoints recur in + `docs/guide/error-reference.md:658` and `docs/guide/integrations/prebid.md:515-531`. `docs/guide/ad-serving.md` documents an Equativ ad server and an `[ad_servers.equativ]` config key with zero code presence. @@ -34,13 +36,16 @@ drift in every surface. The failures fall into six categories: `KeyRotationManager::new(...)?` signature (the real constructor returns `Self`, not a `Result`). 2. **Incomplete references.** `docs/guide/configuration.md` has no - `[consent]`, `[tinybird]`, or `[debug]` sections, and its Integration + `[consent]`, `[tinybird]`, or `[debug]` sections (rc added a `[cache]` + section; those three remain missing), and its Integration Configurations section covers 5 of 14 IDs. `trusted-server.example.toml` has no `[tinybird]`, `[consent]`, `[rewrite]`, `[tester_cookie]`, or `[image_optimizer]` blocks. The API reference omits `POST /auction`, `/_ts/page-bids`, `/health`, `/_ts/debug/ja4`, the EC partner API, and all - integration endpoints except three. `docs/guide/cli.md` omits - `ts config diff` and the entire `ts dev` subtree. + integration endpoints except three. `docs/guide/cli.md` gained + `config diff`, `ts dev proxy`, and the ad-template workflows on rc, but + does not cover the new lifecycle commands (`active-version`, + `healthcheck`, `rollback`) or `config gc`. `docs/guide/integrations-overview.md` compares 7 of 14 integration IDs. `docs/guide/architecture.md` describes 4 of 10 workspace crates. The integration guide's code snippets do not compile against the current API: @@ -96,7 +101,8 @@ Treat documentation as a product surface with a defined source of truth per artifact, fix the audit findings in eight work packages ordered by risk, and add enforcement, including executable parity checks, so the same drift is caught by CI instead of by the next manual audit. Every claim in the -refreshed docs must be verifiable against code on `main`; anything +refreshed docs must be verifiable against code on the PR's target branch +(`rc/202608`, the August 2026 release); anything aspirational must be labeled as such or removed; adapter support claims must come from an honest, owned support matrix rather than marketing copy. @@ -137,7 +143,7 @@ under `docs/superpowers/`, legitimately contain every retired term): in active-set documentation exists in the code at `main`, with adapter-specific availability stated where behavior differs. - Every shipped, operator- or publisher-visible surface has documentation: - all 14 integration IDs, all 15 config sections, the deployment adapters + all 14 integration IDs, all 16 config sections, the deployment adapters (with honest maturity labels), all `ts` commands, the telemetry pipeline, and the tsjs module system. - The adapter support model is truthful: three deployment adapters (Fastly @@ -358,7 +364,11 @@ Bring the two operator-facing config artifacts to parity with `Settings` `origin_url`, paired `path_pattern`/`target_path`, optional S3 SigV4 auth block), `[integrations.osano]`, the missing `[auction]` keys (`mediator`, `creative_store`; `allowed_context_keys` is already present at line 145), - and `[debug].inject_adm_for_testing` with its never-in-production warning. + and `[debug].inject_adm_for_testing` with its never-in-production warning + (rc's `[debug]` block now carries `ja4_endpoint_enabled`, + `auction_html_comment`, and `auction_html_comment_options`). For the rc + `[cache]` section, promote the commented `[[cache.asset_rules]]` examples + to a complete worked block covered by the WP8 parse test. - `docs/guide/configuration.md`: add the missing `[consent]`, `[tinybird]`, and `[debug]` sections (the `[tester_cookie]`, `[rewrite]`, and image-optimizer sections already exist at lines 360, 702, and 946; @@ -370,7 +380,7 @@ Bring the two operator-facing config artifacts to parity with `Settings` - Every example block must actually parse: WP8 adds a test that feeds the uncommented example config through `Settings::from_toml`, so examples are finalized and validity-checked in CI rather than eyeballed. -- Add a parity checklist to the PR description mapping each of the 15 +- Add a parity checklist to the PR description mapping each of the 16 `Settings` fields to its example-toml block and configuration.md heading (the table in Appendix B is the worklist). @@ -386,6 +396,9 @@ with per-endpoint contracts, not just paths. - Document every named route: health, discovery/signing endpoints, admin key rotation (and the deliberately 404-denied legacy `/admin/keys/*` aliases), + the rc admin EC diagnostics (`GET /_ts/admin/ec`, `/_ts/admin/ec/{id}`, + `/_ts/admin/eids`; rc's api-reference already documents them, so the + rebuild folds them in under the same contract checklist), EC partner API (`/_ts/api/v1/batch-sync`, `/_ts/api/v1/identify`), tester cookie endpoints, `POST /auction`, `GET /_ts/page-bids` plus the legacy `/__ts/page-bids` alias, the four `/first-party/*` proxy endpoints, @@ -493,8 +506,10 @@ route names its handler file and satisfies the contract checklist. cloudflare/spin/parity/CLI/browser suites and replace the fictional two-job CI YAML with the real seven-job layout. - `docs/guide/cli.md`: full command reference from the clap tree (Appendix - D), adding `ts config diff` and the `ts dev` subtree with its macOS-only - gating, and linking to `ts-dev-proxy.md`. + D). rc already covers `config diff`, `ts dev proxy`, `audit generate`, + and the ad-template workflows; add the missing lifecycle commands + (`active-version`, `healthcheck`, `rollback`) and `config gc`, verify the + rc additions against the clap tree, and link to `ts-dev-proxy.md`. - Site usability: enable VitePress `lastUpdated` (the deploy workflow already fetches full history for it) and local search (`themeConfig.search`), so the 1,600-line configuration reference is @@ -747,6 +762,7 @@ capability matrix is written from those files, not from this table alone. | `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | | `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | | `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | +| `/_ts/admin/ec`, `/_ts/admin/ec/{id}`, `/_ts/admin/eids` | GET | Fastly only (rc); Basic-auth gated admin EC diagnostics | `core/src/ec/admin.rs` | | `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | | `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | | `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | @@ -766,26 +782,28 @@ proxy routes, asset routes, publisher proxy. ## Appendix B: Settings sections (truth source for WP3) -From `core/src/settings.rs` (`Settings`, line ~1916). Columns record what -each artifact carries today. - -| Section | Struct | `trusted-server.example.toml` | `configuration.md` | -| -------------------------- | ----------------------------- | ------------------------------------------------------------------------------- | -------------------------- | -| `[publisher]` | `Publisher` | present | present | -| `[tester_cookie]` | `TesterCookieConfig` | missing | present (line 360) | -| `[ec]` | `Ec` + `EcPartner` | present | present | -| `[integrations.*]` | per-integration typed configs | partial (osano missing) | 5 of 14 IDs | -| `[[handlers]]` | `Handler` | present | present | -| `response_headers` | map | present (commented) | present | -| `[request_signing]` | `RequestSigning` | present | present | -| `[rewrite]` | `Rewrite` | missing | present (line 702) | -| `[auction]` | `AuctionConfig` | missing `mediator`, `creative_store` (`allowed_context_keys` present, line 145) | present | -| `[consent]` | `ConsentConfig` | missing | missing | -| `[proxy]` | `Proxy` | partial; `asset_routes` missing | present incl. asset routes | -| `[creative_opportunities]` | `CreativeOpportunitiesConfig` | present | present | -| `[image_optimizer]` | `ImageOptimizerSettings` | missing | present (line 946 area) | -| `[tinybird]` | `TinybirdSettings` | missing | missing | -| `[debug]` | `DebugConfig` | partial (`inject_adm_for_testing` missing) | missing | +From `core/src/settings.rs` (`Settings`; 16 fields on rc/202608). Columns +record what each artifact carries today. On rc, `request_signing` and +`creative_opportunities` are `Option` fields. + +| Section | Struct | `trusted-server.example.toml` | `configuration.md` | +| -------------------------- | ----------------------------- | ------------------------------------------------------------------------------------ | -------------------------- | +| `[publisher]` | `Publisher` | present | present | +| `[tester_cookie]` | `TesterCookieConfig` | missing | present (line 360) | +| `[ec]` | `Ec` + `EcPartner` | present | present | +| `[integrations.*]` | per-integration typed configs | partial (osano missing) | 5 of 14 IDs | +| `[[handlers]]` | `Handler` | present | present | +| `response_headers` | map | present (commented) | present | +| `[request_signing]` | `RequestSigning` | present | present | +| `[rewrite]` | `Rewrite` | missing | present (line 702) | +| `[auction]` | `AuctionConfig` | missing `mediator`, `creative_store` (`allowed_context_keys` present, line 145) | present | +| `[consent]` | `ConsentConfig` | missing | missing | +| `[proxy]` | `Proxy` | partial; `asset_routes` missing | present incl. asset routes | +| `[creative_opportunities]` | `CreativeOpportunitiesConfig` | present | present | +| `[image_optimizer]` | `ImageOptimizerSettings` | missing | present (line 946 area) | +| `[tinybird]` | `TinybirdSettings` | missing | missing | +| `[debug]` | `DebugConfig` | present on rc incl. `auction_html_comment_options`; `inject_adm_for_testing` missing | missing | +| `[cache]` (rc) | `CacheSettings` | commented `[[cache.asset_rules]]` examples only | present (rc) | ## Appendix C: Integration registry (truth source for WP5 overview table) @@ -814,12 +832,16 @@ DJS deferred JS, AP auction provider. ## Appendix D: CLI tree and environment variables -`ts` commands (from `crates/trusted-server-cli/src/run.rs`): `audit`, -`auth login|logout|status`, `build`, `config init|diff|push|validate`, -`deploy`, `prebid bundle`, `provision`, `serve`, +`ts` commands (from `crates/trusted-server-cli/src/run.rs` on rc/202608): +`audit generate|ad-templates`, `active-version`, `auth login|logout|status`, +`build`, `config init|diff|push|validate|ad-templates|gc`, `deploy`, +`healthcheck`, `prebid bundle`, `provision`, `rollback`, `serve`, `dev proxy [ca path|install|uninstall|regenerate]` (macOS only; `ts dev` -lists no subcommands on other hosts). All commands and flags carry help text; -`docs/guide/cli.md` must add `config diff` and the `dev` subtree. +lists no subcommands on other hosts). Commands that detect drift +(`config diff`, `config ad-templates check`, audit verification) report a +distinct drift outcome with a stable exit code. All commands and flags carry +help text; `docs/guide/cli.md` must add `active-version`, `healthcheck`, +`rollback`, and `config gc`. Runtime environment variables to document (WP2 `.env.example` / `.env.dev`): `FASTLY_SERVICE_VERSION`, `FASTLY_IS_STAGING`, `FASTLY_HOSTNAME`, From 4056bf4411a468c75d1927f2159315f64083cbcc Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:03:38 -0700 Subject: [PATCH 08/33] Record accessibility scope decision in Non-goals --- .../specs/2026-08-19-documentation-refresh-design.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 08836d402..82897b857 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -180,6 +180,12 @@ under `docs/superpowers/`, legitimately contain every retired term): - No release-management policy changes. The CHANGELOG's 10-month untagged `[Unreleased]` backlog and the governance doc's unfulfilled commitments are flagged for maintainers, with only mechanical repairs in scope. +- No dedicated accessibility audit gate. The site uses the stock VitePress + theme with no custom interactive components; WP5 adds the pieces with + direct accessibility value (prose equivalents for mermaid diagrams, + local search, `lastUpdated` context), and anything beyond that + (keyboard/contrast/screen-reader smoke checks) is deliberately deferred + until the site carries custom components that need it. - Not chasing 100% rustdoc item coverage. In-code doc work targets module orientation (`//!`) and the highest-traffic public surfaces, not a `missing_docs` blanket. From 2c95da4213ee3d9309e2c52a01471acc8dd3dcab Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 20:45:46 -0700 Subject: [PATCH 09/33] Rework spec after second pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 743 +++++++++++------- 1 file changed, 468 insertions(+), 275 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 82897b857..6c1be010e 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,9 +1,18 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-20 (addresses pre-implementation review) +**Revised:** 2026-08-20 (round 2; addresses both pre-implementation reviews) **Status:** Draft, pending review -**Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned 2026-08-20 to the release branch `rc/202608`, which this PR targets. Notable rc deltas folded in below: a 16th `[cache]` settings section, three admin EC diagnostic routes, a restructured CLI (`ts audit generate`, `ad-templates`, `active-version`, `healthcheck`, `rollback`, `config gc`), and rc's own updates to `cli.md`, `configuration.md`, and `api-reference.md`. Line citations are from the main baseline unless marked rc; spot-rechecked claims cite rc line numbers. +**Scope:** Documentation and doc tooling. No runtime behavior changes. +Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and +rebased 2026-08-20 onto release branch `rc/202608` at `d4cd2cc82`, which is +the PR target and the truth source for every claim in this spec. After any +further rebase, the inventories in the appendices are re-verified against +the new merge base before implementation continues. The latest six rc +commits (APS native rendering, DataDome staging-requirement removal, APS +creative frame scrollbars) touch documented behavior and are explicitly +re-checked in WP2. Line citations are from the main baseline unless marked +rc; spot-rechecked claims cite rc line numbers. ## Context @@ -11,7 +20,7 @@ Trusted Server's documentation spans four surfaces: the VitePress site (`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and configuration templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track -audit of `main`, hardened by a pre-implementation review, found systemic +audit of `main`, hardened by two pre-implementation reviews, found systemic drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference @@ -31,14 +40,20 @@ drift in every surface. The failures fall into six categories: `providers/` directory that does not exist, and an APS `mock` config key that was removed. Operator instructions reference a nonexistent `npm run type-check` (`error-reference.md:597`) and a nonexistent - `--validate-config` flag (`error-reference.md:663`), and + `--validate-config` flag (`error-reference.md:663`); `docs/guide/key-rotation.md:301-310` shows an obsolete `KeyRotationManager::new(...)?` signature (the real constructor returns - `Self`, not a `Result`). + `Self`, not a `Result`); and `docs/guide/configuration.md:1954-1957` (rc) + shows a Rust example importing a nonexistent + `settings_data::get_settings` (the exported loader is + `get_settings_from_config_store`) and using `println!`, which the repo's + own conventions forbid. 2. **Incomplete references.** `docs/guide/configuration.md` has no `[consent]`, `[tinybird]`, or `[debug]` sections (rc added a `[cache]` section; those three remain missing), and its Integration - Configurations section covers 5 of 14 IDs. `trusted-server.example.toml` + Configurations section covers 5 of 14 IDs, with the existing five never + re-audited (the Prebid implementation exposes valid keys the reference + omits). `trusted-server.example.toml` has no `[tinybird]`, `[consent]`, `[rewrite]`, `[tester_cookie]`, or `[image_optimizer]` blocks. The API reference omits `POST /auction`, `/_ts/page-bids`, `/health`, `/_ts/debug/ja4`, the EC partner API, and all @@ -72,7 +87,11 @@ drift in every surface. The failures fall into six categories: that returns 503 for all traffic while `/health` still returns 200 (`adapter-spin/src/app.rs:404`). No smoke test proves non-health traffic works under `spin up`. CI compiles the Spin artifact; compilation is not - evidence of production maturity. + evidence of production maturity. Capability differences are also + documented nowhere: asset-route dispatch, integration request filters, + the image optimizer, and Tinybird auction telemetry exist only in the + Fastly adapter today; the other adapters construct no telemetry sink and + silently use the no-op default. 5. **Publishing and policy hygiene.** All 75 internal spec/plan files under `docs/superpowers/` are built and published to the public GitHub Pages site (no `srcExclude` in `docs/.vitepress/config.mts`), along with @@ -82,7 +101,10 @@ drift in every surface. The failures fall into six categories: personal email (`authors`, line 4) and a real Fastly service id (line 10) against the repo's own sensitive-data policy, and unlabeled base64 key fixtures that read as credentials. `docs/package.json` is not `private` - and declares an ISC license in an Apache-2.0 repository. + and declares an ISC license in an Apache-2.0 repository. Active examples + violate the fictional-data policy beyond that: `ec-setup-guide.md:15` + names a real deployment domain, and `.env.example` uses non-reserved + `publisher.com` values instead of `.example` domains. 6. **No enforcement.** `cargo doc` never runs in CI; the two existing doctests never execute (core is tested only cross-compiled, which skips doctests); no `missing_docs` or `rustdoc::*` lints are enabled; the docs @@ -99,25 +121,26 @@ Full finding indexes with `file:line` citations are in Appendix E. Treat documentation as a product surface with a defined source of truth per artifact, fix the audit findings in eight work packages ordered by risk, and -add enforcement, including executable parity checks, so the same drift is -caught by CI instead of by the next manual audit. Every claim in the -refreshed docs must be verifiable against code on the PR's target branch -(`rc/202608`, the August 2026 release); anything -aspirational must be labeled as such or removed; adapter support claims must -come from an honest, owned support matrix rather than marketing copy. +add enforcement, including executable parity checks that are bound to the +reader-facing markdown, so the same drift is caught by CI instead of by the +next manual audit. Every claim in the refreshed docs must be verifiable +against code at the PR HEAD's merge base with `rc/202608` (the August 2026 +release); anything aspirational must be labeled as such or removed; adapter +support claims must come from an honest, owned support matrix rather than +marketing copy. The source-of-truth map: -| Artifact | Truth source | Consumers | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | -| HTTP API reference | Adapter route tables and entry points (`adapter-*/src/app.rs`, `adapter-*/src/main.rs`, `adapter-*/src/platform.rs`) + core handlers | Publishers, partners | -| Config reference | `Settings` in `crates/trusted-server-core/src/settings.rs` (`deny_unknown_fields` makes parity checkable) | Operators | -| CLI reference | clap definitions in `crates/trusted-server-cli/src/run.rs` and command modules | Operators | -| Integration pages | `builders()` in `core/src/integrations/mod.rs` + registry capabilities | Publishers, integrators | -| Integration guide snippets | A compiling sample integration (`testlight` or a doc-tested fixture), never hand-written pseudo-code | Integrators | -| Deployment guides | `edgezero.toml` adapter blocks + per-adapter manifests + adapter support matrix | Operators | -| Architecture | `Cargo.toml` workspace members + `core/src/platform/` | Contributors | -| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | +| Artifact | Truth source | Consumers | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| HTTP API reference | Adapter route tables and entry points (`adapter-*/src/app.rs`, `adapter-*/src/main.rs`, `adapter-*/src/platform.rs`) + core handlers | Publishers, partners | +| Config reference | `Settings` in `crates/trusted-server-core/src/settings.rs` plus the typed per-integration config structs (the root uses `deny_unknown_fields`; `IntegrationSettings` is a flattened map, so integration blocks need their own direct deserialization) | Operators | +| CLI reference | The built `ts` binary's recursive `--help` tree (clap definitions in `crates/trusted-server-cli/src/run.rs` and command modules, plus flags owned by the lockfile-resolved `edgezero-cli`) | Operators | +| Integration pages | Three inventories, tested separately: registry `builders()` (13 registrations), the auction-provider inventory (`auction/mod.rs` `provider_builders()`, which adds `adserver_mock`), and the JS module registry (`JS_ALWAYS` adds `creative`) | Publishers, integrators | +| Integration guide snippets | A compiling sample integration (`testlight` or a doc-tested fixture), never hand-written pseudo-code | Integrators | +| Deployment guides | `edgezero.toml` adapter blocks + per-adapter manifests + adapter support matrix | Operators | +| Architecture | `Cargo.toml` workspace members + `core/src/platform/` | Contributors | +| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | ## Source sets @@ -132,6 +155,12 @@ under `docs/superpowers/`, legitimately contain every retired term): `CLAUDE.md`), crate READMEs, config templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`), and `.claude/commands/*.md`. +- **Active maintained internal set:** documents that are neither public-site + pages nor historical artifacts but are still maintained and must pass the + truth standard: `docs/README.md`, `docs/internal/**` (including the moved + onboarding page), `scripts/README.md` and `tinybird/README.md` once + created, and `.claude/skills/**` (operator-facing skills such as the + Fastly deployment skill). - **Historical set:** `docs/superpowers/**` (specs, plans, implementation notes, archive) and shipped `CHANGELOG.md` release entries. Exempt from retired-term greps; a changelog entry describing a rename may name the @@ -140,8 +169,9 @@ under `docs/superpowers/`, legitimately contain every retired term): ## Goals - Every endpoint, config key, command, flag, crate name, and code path named - in active-set documentation exists in the code at `main`, with - adapter-specific availability stated where behavior differs. + in active-set documentation exists in the code at the PR target + (`rc/202608`), with adapter-specific availability stated where behavior + differs. - Every shipped, operator- or publisher-visible surface has documentation: all 14 integration IDs, all 16 config sections, the deployment adapters (with honest maturity labels), all `ts` commands, the telemetry pipeline, @@ -149,15 +179,18 @@ under `docs/superpowers/`, legitimately contain every retired term): - The adapter support model is truthful: three deployment adapters (Fastly production; Cloudflare; Spin, currently experimental) plus the Axum local-development adapter, backed by a published support matrix. -- The public docs site publishes only intended pages: internal specs, plans, - epics, onboarding, and runbooks are excluded from the build, and - internal-only details are scrubbed from anything that stays in the public - repository regardless of whether VitePress builds it. -- Sensitive real-world values are removed from source-controlled config per - the repo policy in `CLAUDE.md`. -- CI gates catch documentation regressions: docs build (dead links) on PRs, - rustdoc build with broken-intra-doc-link denial, doctests actually - running, and executable parity checks for the hand-maintained inventories. +- The public docs site publishes only intended pages, and the containment + actually reaches the published site (see WP1: Pages deploys only from + `main`). Internal specs, plans, epics, onboarding, and runbooks are + excluded from the build, and internal-only details are scrubbed from + anything that stays in the public repository regardless of whether + VitePress builds it. +- Sensitive real-world values are removed from source-controlled config, and + examples use fictional data (reserved `.example` domains, clearly fake + credentials) per the repo policy in `CLAUDE.md`. +- CI gates catch documentation regressions: docs build (dead internal links) + on PRs, rustdoc build with broken-intra-doc-link denial, doctests actually + running, and executable parity checks bound to the reader-facing markdown. - Root markdown (`README`, `CONTRIBUTING`, `TESTING`, `CHANGELOG`) accurately describes the current workspace, build system, and test matrix. @@ -166,15 +199,17 @@ under `docs/superpowers/`, legitimately contain every retired term): - No changes to runtime behavior, routes, config schema, or code structure, with one boundary clarification: parity checks added by WP8 may add tests and scripts, but not alter runtime code. Code defects the audit exposed - (Spin's hardcoded example-config startup, `ts --version` missing, Tinybird + (Spin's hardcoded example-config startup, Tinybird access logging config present but not wired, internal spec references leaking into vendored `edgezero-cli` help text) are tracked as follow-up issues; the Spin one blocks publishing a Spin deployment guide (WP5). - No new documentation toolchains. VitePress, rustdoc, and clap help remain the three delivery mechanisms. No TypeDoc, no docs.rs publishing. -- No rewrite of `docs/business-use-cases.md` marketing copy. Its uncited - quantitative claims are flagged as an open question (evidence or removal - from primary navigation), not silently rewritten. `docs/roadmap.md` gets a +- No rewrite of `docs/business-use-cases.md` marketing copy. Default + handling changed after review: the page leaves primary navigation until + every quantitative claim carries dated evidence and unshipped features + are visibly labeled (open question 4 records the alternative of keeping + it with evidence added). `docs/roadmap.md` gets a factual status pass (shipped/active/deferred labels, correct crate names), not a strategy rewrite. - No release-management policy changes. The CHANGELOG's 10-month untagged @@ -194,20 +229,37 @@ under `docs/superpowers/`, legitimately contain every retired term): an operationally owned follow-up with its own replacement plan, staging test, and rollback instructions, not part of this refresh. +## Delivery shape + +The owner's standing instruction is one PR (#1049, branch +`spec-docs-refresh`, targeting `rc/202608`) carrying the spec plus all eight +work packages, one commit (or small series) per package, reviewable +commit-by-commit. The second review surfaced a mechanical constraint that +forces one exception: GitHub Pages deploys only on pushes to `main` +(`deploy-docs.yml`), so publishing containment merged to rc does not reach +the live site until rc merges to main. Therefore: + +- The WP1 publishing-containment subset (the `srcExclude` change, the + onboarding move/scrub, and the CNAME resolution) additionally ships as a + minimal separate PR straight to `main` so the exposure closes immediately. + The rc PR carries the same changes; the rc→main merge reconciles to an + identical state. +- Everything else lands only in the single rc PR. +- If release-branch PRs are expected to get CodeQL analysis, `rc/*` must be + added to `codeql.yml`'s branch triggers (WP8 records this alongside the + other workflow changes). + +Open question 7 asks the owner to confirm this shape. + ## Work packages -All eight packages ship in the same single PR as this spec (#1049, branch -`spec-docs-refresh`): the spec commit lands first, then each package as one -commit (or a small commit series) in the order below, so the PR is -reviewable commit-by-commit. WP1 and WP2 are corrective, WP3-WP6 are -completion work, WP7-WP8 are quality and enforcement. (The reviewer -recommended splitting at least WP1 into its own PR for urgent publishing -containment; the single-PR delivery is a deliberate owner decision, recorded -in open question 7.) +WP1 and WP2 are corrective, WP3-WP6 are completion work, WP7-WP8 are quality +and enforcement. Commits land in the order below. ### WP1: Publishing and policy hygiene -Smallest package, highest urgency. +Smallest package, highest urgency. The containment subset also ships to +`main` directly (see Delivery shape). - Add `srcExclude` to `docs/.vitepress/config.mts` covering `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, and @@ -244,19 +296,24 @@ real key` labels to the `[local_server]` secret/JWKS entries; add one-line missing Spin/cloudflare-wasm gates to `AGENTS.md`'s fallback list. Acceptance: `vitepress build` output contains no `superpowers/`, `internal/`, -`epics/`, or onboarding pages; no real personal emails in tracked config; no -internal contacts or access instructions anywhere in the repo; every command -file lists the same gates as `CLAUDE.md`. +`epics/`, or onboarding pages; the containment PR to `main` is merged and +the live site no longer serves those URLs; no real personal emails in +tracked config; no internal contacts or access instructions anywhere in the +repo; every command file lists the same gates as `CLAUDE.md`. ### WP2: Truth pass over existing content Nothing new is written here beyond minimal replacement prose; the goal is that nothing in the active sets is false. The pass starts from a complete -page inventory: every page in the active public set gets an explicit -disposition, verified, rewrite, or retire, recorded in the PR description. -Token greps establish that retired names are gone; they cannot validate -commands, APIs, auth, or behavior, so each "verified" disposition means the -page's commands and examples were actually checked against code. +page inventory: every page in the active public and active maintained +internal sets gets an explicit disposition, verified, rewrite, or retire. +The inventory is checked into the repository (under +`docs/superpowers/implementation-notes/`) with per-page source anchors, not +left in a PR description. Token greps establish that retired names are +gone; they cannot validate commands, APIs, auth, or behavior, so each +"verified" disposition means the page's commands and examples were actually +checked against code, and marked Rust/shell/TOML/JSON snippets are compiled +or parsed wherever feasible (the WP8 harness runs them). - `docs/guide/api-reference.md`: delete `GET /first-party/ad` and `POST /third-party/ad` sections (endpoints do not exist). The full @@ -268,6 +325,10 @@ page's commands and examples were actually checked against code. - `docs/guide/error-reference.md`: remove or replace the nonexistent `npm run type-check` (line 597) and `--validate-config` (line 663) instructions with commands that exist. +- `docs/guide/configuration.md:1954-1957` (rc): fix the loader example + (`settings_data::get_settings` does not exist; the exported function is + `get_settings_from_config_store`) and remove the `println!` usage the + repo's conventions forbid. - `docs/guide/key-rotation.md`: rewrite the Rust API examples against `core/src/request_signing/rotation.rs` (`KeyRotationManager::new` returns `Self`, not a `Result`) and add the Basic-auth requirement to the curl @@ -275,6 +336,20 @@ page's commands and examples were actually checked against code. - `docs/guide/proxy-signing.md`: full content review against `core/src/proxy.rs` signing (promoted from a follow-up; a security-relevant page cannot sit outside a documentation audit). +- Fictional-data policy audit over the active public, active repo, and + active maintained internal sets: replace the real deployment domain in + `ec-setup-guide.md:15` and the non-reserved `publisher.com` values in + `.env.example` with reserved `.example` domains and clearly fictional + values; sweep both sets for other real domains, customer names, or + credential-shaped strings. Reviewed canonical vendor endpoints (e.g. real + GPT/DataDome CDN hosts an integration genuinely proxies) stay, everything + else becomes fictional. +- Re-verify the pages touched by the final six rc commits: + `docs/guide/integrations/datadome.md` (the staging requirement was + removed from protection behavior in the same commit that rewrote the + page; confirm prose and code now agree) and + `docs/guide/integrations/aps.md` (native rendering mode landed; confirm + the page describes the current render modes and the conditional proxy). - `docs/guide/ad-serving.md`: remove the Equativ section, the `[ad_servers.equativ]` block, the top-level `[prebid]` block (real section is `[integrations.prebid]`), and the placeholder `trackImpression` API; @@ -343,22 +418,29 @@ page's commands and examples were actually checked against code. both `.env.example` and `.env.dev` (both still carry retired `TRUSTED_SERVER__SYNTHETIC__*` keys), update `docs/guide/getting-started.md:74-77` (which tells users to `cp .env.dev -.env` and source it), and smoke-test the Axum quick start as written. +.env` and source it), and smoke-test the Axum quick start against the + contract defined in Verification. - `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section is `[consent]`. -Acceptance: every active-public page has a recorded disposition; grepping -the active public and active repo sets (Source sets above; historical set -exempt) for `first-party/ad`, `third-party/ad`, `equativ`, `ad_servers`, -`RequestWrapper`, `trackImpression`, `SEQUENCE.md`, `synthetic_id` (outside -shipped changelog entries), `providers/your_provider`, `with_asset`, -`type-check`, and `mock = true` (APS context) returns nothing; no sidebar -entry points at a nonexistent integration. +Acceptance: the checked-in inventory covers every page in the active public +and active maintained internal sets with a disposition and source anchors; +grepping the active sets (historical set exempt) for `first-party/ad`, +`third-party/ad`, `equativ`, `ad_servers`, `RequestWrapper`, +`trackImpression`, `SEQUENCE.md`, `synthetic_id` (outside shipped changelog +entries), `providers/your_provider`, `with_asset`, `type-check`, +`settings_data::get_settings`, and `mock = true` (APS context) returns +nothing; the fictional-data sweep finds no unreviewed real domains or +credential-shaped values; no sidebar entry points at a nonexistent +integration. ### WP3: Configuration reference completion Bring the two operator-facing config artifacts to parity with `Settings` -(`core/src/settings.rs:1916`, `#[serde(deny_unknown_fields)]`). +(`core/src/settings.rs`, 16 fields on rc, `#[serde(deny_unknown_fields)]` at +the root) and with the typed per-integration configs, which the root parse +does NOT validate: `IntegrationSettings` is a flattened map, and disabled +integrations can skip typed deserialization entirely. - `trusted-server.example.toml`: add commented, documented example blocks for the sections it lacks: `[tinybird]` (all 10 fields, with the note that @@ -374,37 +456,44 @@ Bring the two operator-facing config artifacts to parity with `Settings` (rc's `[debug]` block now carries `ja4_endpoint_enabled`, `auction_html_comment`, and `auction_html_comment_options`). For the rc `[cache]` section, promote the commented `[[cache.asset_rules]]` examples - to a complete worked block covered by the WP8 parse test. + to a complete worked block covered by the WP8 example harness. +- Field-path inventories: for every nested `Settings` type and all 14 typed + integration config structs, enumerate the full field paths from the + source, then reconcile `docs/guide/configuration.md`'s field tables + against that inventory. This audits the five existing integration + sections (Prebid's reference is already missing valid keys) as well as + adding the nine absent ones (`aps`, `datadome`, `didomi`, `sourcepoint`, + `lockr`, `gpt`, `gpt_diagnostics`, `google_tag_manager`, `adserver_mock`). - `docs/guide/configuration.md`: add the missing `[consent]`, `[tinybird]`, and `[debug]` sections (the `[tester_cookie]`, `[rewrite]`, and image-optimizer sections already exist at lines 360, 702, and 946; - verify their field lists rather than re-adding them); extend the - Integration Configurations section from 5 to all 14 IDs (add `aps`, - `datadome`, `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, - `google_tag_manager`, `adserver_mock`), each with its typed config keys - from the integration source. -- Every example block must actually parse: WP8 adds a test that feeds the - uncommented example config through `Settings::from_toml`, so examples are - finalized and validity-checked in CI rather than eyeballed. + reconcile their field lists against the inventory rather than re-adding + them). +- Every example block must actually validate. WP8 builds the harness this + package relies on: placeholder-substituting parse of the full template, + plus marker-extracted parses of each commented example block and direct + typed deserialization of each integration example (bypassing the + disabled-integration short-circuit). - Add a parity checklist to the PR description mapping each of the 16 `Settings` fields to its example-toml block and configuration.md heading (the table in Appendix B is the worklist). -Acceptance: every field of `Settings` appears in both -`trusted-server.example.toml` and `docs/guide/configuration.md`; every -integration ID accepted by deploy validation (`core/src/config.rs:29-44`) has -a config subsection; the WP8 example-parse test passes. +Acceptance: every field path in the inventory appears in both +`trusted-server.example.toml` (as a real or commented example) and +`docs/guide/configuration.md`; every integration ID accepted by deploy +validation has a config subsection whose field table matches its struct; +the WP8 example harness passes. ### WP4: API reference rebuild Rebuild `docs/guide/api-reference.md` from the route inventory (Appendix A), -with per-endpoint contracts, not just paths. +with per-endpoint contracts, not just paths, and with per-adapter accuracy. - Document every named route: health, discovery/signing endpoints, admin key rotation (and the deliberately 404-denied legacy `/admin/keys/*` aliases), - the rc admin EC diagnostics (`GET /_ts/admin/ec`, `/_ts/admin/ec/{id}`, - `/_ts/admin/eids`; rc's api-reference already documents them, so the - rebuild folds them in under the same contract checklist), + the rc admin diagnostics (`GET /_ts/admin/ec` and `/_ts/admin/ec/{id}`, + Fastly-only because they need the EC KV store; `GET /_ts/admin/eids`, + which is a request-inspection handler registered on all four adapters), EC partner API (`/_ts/api/v1/batch-sync`, `/_ts/api/v1/identify`), tester cookie endpoints, `POST /auction`, `GET /_ts/page-bids` plus the legacy `/__ts/page-bids` alias, the four `/first-party/*` proxy endpoints, @@ -419,25 +508,33 @@ with per-endpoint contracts, not just paths. mints short-lived signed URLs while `/proxy`, `/click`, and `/proxy-rebuild` validate different signed inputs; "tstoken signing" as a group label is not sufficient for a security-sensitive surface. +- Document per-adapter request pipelines rather than one generalized + pipeline: integration request filters (DataDome) run pre-route and exist + only in the Fastly adapter today; asset-route dispatch and the image + optimizer are Fastly-only; Tinybird auction emission is Fastly-only (the + other adapters construct no sink and use the no-op default). The Fastly + fallback order is tsjs, integration proxy routes, asset routes, publisher + origin proxy; the other adapters dispatch tsjs, integration proxy routes, + publisher proxy. - Add an adapter availability and capability matrix: route availability per - adapter (EC partner API, tester cookies, JA4 debug, and working key - rotation are Fastly-only; Spin registers the canonical admin routes but - returns unsupported responses; `/health` is absent on Cloudflare) plus the - platform capabilities that differ per adapter (stores, geo, TTL storage, - secrets, Tinybird sink construction, request filters), sourced from each - adapter's `app.rs`, `main.rs`/`lib.rs`, and `platform.rs`. -- Document the fallback dispatch order (tsjs, integration proxy routes, - asset routes, publisher origin proxy) and the fact that the publisher - fallback registers seven explicit methods (GET, POST, HEAD, OPTIONS, PUT, - PATCH, DELETE), so route-shadowing and method questions are answerable - from docs. + adapter (EC partner API, tester cookies, JA4 debug, admin EC KV lookups, + and working key rotation are Fastly-only; Spin registers the canonical + admin key routes but returns unsupported responses; `/health` is absent on + Cloudflare) plus the platform capabilities that differ per adapter + (stores, geo, TTL storage, secrets, Tinybird sink construction, request + filters, asset routes, image optimizer), sourced from each adapter's + `app.rs`, `main.rs`/`lib.rs`, and `platform.rs`. +- The publisher fallback registers seven explicit methods (GET, POST, HEAD, + OPTIONS, PUT, PATCH, DELETE); document that set rather than "all methods". - Add an Integration Endpoints section generated from each integration's `IntegrationProxy::routes()` registration (the integrations are enumerated in Appendix C) instead of today's three-entry list. Acceptance: the route list in the reference matches the union of the four -adapter route tables with per-adapter availability flagged; every documented -route names its handler file and satisfies the contract checklist. +adapter route tables, with per-adapter availability flagged; every documented +route names its handler file and satisfies the contract checklist; the WP8 +route snapshots (which include response semantics, not just method and +path) agree with the published tables. ### WP5: New coverage pages and navigation repair @@ -446,7 +543,8 @@ route names its handler file and satisfies the contract checklist. `docs/guide/fastly.md`: - `docs/guide/cloudflare.md`: wrangler config, `TRUSTED_SERVER_KV` binding, `TRUSTED_SERVER_CONFIG` var with blob envelope, missing - `/health`. + `/health`, no asset routes/filters/telemetry (per the capability + matrix). - `docs/guide/axum-dev.md`: explicitly a local-development guide (env-var-backed stores, `PORT`, unsupported admin ops), not a deployment target. @@ -465,15 +563,17 @@ route names its handler file and satisfies the contract checklist. flow (`trusted-server.toml` validated, pushed as a blob envelope via `ts config push`, resolved at runtime through `settings_data.rs` including Fastly chunked storage), and the `ts` lifecycle commands - (auth/build/serve/deploy/provision). Fold the still-relevant parts of - `docs/internal/EDGEZERO_MIGRATION.md` in; the internal runbook itself stays - excluded from the site. + (auth/build/serve/deploy/provision, plus the rc additions + `active-version`, `healthcheck`, `rollback`). Fold the still-relevant + parts of `docs/internal/EDGEZERO_MIGRATION.md` in; the internal runbook + itself stays excluded from the site. - New `docs/guide/telemetry.md`: auction telemetry from `[tinybird]` config through `auction_sink_from_settings` to the `tinybird/` datasources, pipes, and rollups; the operator setup path - (Tinybird tokens in `ts_secrets`); explicit note that access-log telemetry - is not yet wired and `access_enabled` must remain false. New - `tinybird/README.md` covering the `tb` workflow and file layout. + (Tinybird tokens in `ts_secrets`); explicit notes that emission is + Fastly-only today and that access-log telemetry is not yet wired + (`access_enabled` must remain false). New `tinybird/README.md` covering + the `tb` workflow and file layout. - New `docs/guide/tsjs.md`: the module system (core + immediate vs deferred integration modules, `JS_ALWAYS` creative module), the build pipeline (`build-all.mjs`, `build.rs` embedding, runtime concatenation and @@ -499,8 +599,9 @@ route names its handler file and satisfies the contract checklist. (edge-injected `gpt_bootstrap.js`, the full shim takeover, targeting, APS renderer bridge, SPA hook); "handoff" currently appears nowhere in docs. - `docs/guide/integrations-overview.md`: extend the comparison and - performance tables from 7 to all 14 IDs using the registry capability - matrix (Appendix C). + performance tables from 7 to all 14 IDs using the three-inventory + capability data (Appendix C), including APS's actual shape (head injector + always, auction provider, proxy conditional on rendering mode). - Testing docs: rewrite root `TESTING.md` as the test-matrix index (the aliases from `.cargo/config.toml`, the seven `test.yml` jobs plus the four integration-test workflow jobs, the parity suite, `scripts/test-cli.sh`, @@ -511,17 +612,20 @@ route names its handler file and satisfies the contract checklist. behavior claims. Update `docs/guide/testing.md` to cover cloudflare/spin/parity/CLI/browser suites and replace the fictional two-job CI YAML with the real seven-job layout. -- `docs/guide/cli.md`: full command reference from the clap tree (Appendix - D). rc already covers `config diff`, `ts dev proxy`, `audit generate`, - and the ad-template workflows; add the missing lifecycle commands - (`active-version`, `healthcheck`, `rollback`) and `config gc`, verify the - rc additions against the clap tree, and link to `ts-dev-proxy.md`. +- `docs/guide/cli.md`: full command reference generated from the built + binary's recursive help tree (Appendix D). rc already covers + `config diff`, `ts dev proxy`, `audit generate`, and the ad-template + workflows; add the missing lifecycle commands (`active-version`, + `healthcheck`, `rollback`) and `config gc`, verify the rc additions + against the help tree, and link to `ts-dev-proxy.md`. - Site usability: enable VitePress `lastUpdated` (the deploy workflow already fetches full history for it) and local search (`themeConfig.search`), so the 1,600-line configuration reference is navigable; give mermaid diagrams a one-paragraph prose equivalent nearby. - Navigation: add sidebar entries for the three orphaned real integrations - (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages. + (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages; remove + `business-use-cases` from primary navigation per the Non-goals default + (open question 4). - `docs/guide/architecture.md`: describe all 10 workspace crates and the platform trait boundary; add the missing Cloudflare adapter section. @@ -529,7 +633,7 @@ Acceptance: every integration ID is documented and nav-reachable (testlight via its reference section in the integration guide); every adapter has a guide or an honest status notice consistent with the support matrix; no real page is orphaned; integration-guide snippets compile; `vitepress build` -passes (dead links fail the build). +passes (dead internal links fail the build). ### WP6: Root markdown and crate READMEs @@ -556,7 +660,12 @@ serve`; link the deployment guides; refresh the doc-site link table. Rewrite `crates/trusted-server-core/README.md` as an actual crate overview (currently covers 2 of ~40 modules), linking to the deep-dive docs. The Spin README carries the same experimental-status note as WP5. -- New `scripts/README.md` (one line per script). +- New `scripts/README.md` (one line per script) and `tinybird/README.md` + (WP5); both join the active maintained internal set and the WP2 + disposition inventory. +- `.claude/skills/**`: audit the operator-facing skills (including the + Fastly deployment skill) against current commands and config, same truth + standard as the command files. - `ProjectGovernance.md`: the two claims contradicted by repo state (meeting minutes "maintained within the repository" - none exist; "continuous releases" - none tagged since v1.1.0) become accurate @@ -568,9 +677,9 @@ serve`; link the deployment guides; refresh the doc-site link table. exist. Acceptance: `find crates -maxdepth 2 -name README.md` returns one per crate; -every pre-existing root/crate document has a recorded +every pre-existing root/crate/skill document has a recorded verified/rewritten/retired disposition; README quick start commands all run -against `main`. +against the PR HEAD. ### WP7: In-code documentation @@ -608,7 +717,9 @@ added only where an example compiles as a doctest and earns its keep (`redacted.rs` is the model); this spec does not attempt examples on all ~589 public functions. -Rustdoc verification commands (the exact matrix WP8 puts in CI): +Rustdoc verification commands (the exact matrix WP8 puts in CI; the CI job +needs pinned Node/npm setup because documenting `trusted-server-js` runs its +npm-based build script): - `cargo doc --no-deps -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1` - `cargo doc --no-deps -p trusted-server-adapter-fastly --target wasm32-wasip1` @@ -626,21 +737,30 @@ to those files, or a grep count recorded in the PR description). ### WP8: Enforcement Prevent recurrence. Two layers: build gates (links, rustdoc, doctests) and -semantic parity checks for every inventory the docs maintain by hand. All +semantic parity checks. Crucially, the parity checks are bound to the +reader-facing markdown, not only to snapshots a contributor could update +while leaving the prose stale: the reference tables in +`api-reference.md`, `cli.md`, `configuration.md`, and +`integrations-overview.md` live inside delimited generated regions +(``) produced from the machine-readable +inventories, and CI fails when regenerating them produces a diff. All additions are tests, scripts, and workflow steps; no runtime code changes. Build gates: - Docs site: add `npm run build` to the `format-docs` job in - `.github/workflows/format.yml` so dead links fail PRs instead of the - post-merge deploy. Align the two workflows' npm cache keys (one keys on + `.github/workflows/format.yml` so dead internal links fail PRs instead of + the post-merge deploy. External links are out of the PR gate; add an + allowlisted scheduled link check (or a documented manual audit cadence) + instead. Align the two workflows' npm cache keys (one keys on `package.json`, the other on `package-lock.json`). Add `.tool-versions` to `deploy-docs.yml` trigger paths (the site renders versions from it, so - version-only bumps must republish). + version-only bumps must republish). If release-branch PRs are expected to + get CodeQL analysis, add `rc/*` to `codeql.yml` branch triggers. - Rustdoc: add a CI step running the WP7 command matrix with `RUSTDOCFLAGS="-D warnings"` (this denies - `rustdoc::broken_intra_doc_links` by default). Do not enable - `missing_docs`; the existing + `rustdoc::broken_intra_doc_links` by default), with pinned Node per the + WP7 note. Do not enable `missing_docs`; the existing `missing_errors_doc`/`missing_panics_doc`/`doc_markdown` clippy trio plus `-D warnings` stays the item-level gate. - Doctests: add a native-host `cargo test --doc -p trusted-server-core` step @@ -648,24 +768,42 @@ Build gates: cross-compiled). - Add `[lints] workspace = true` to `trusted-server-openrtb-codegen`, the one crate not inheriting the doc lints. -- Dependabot: add the `github-actions` ecosystem and the Playwright - `browser/package.json` npm root (both currently unmanaged). +- Dependency governance: Dependabot gains the `github-actions` ecosystem, + the Playwright `browser/package.json` npm root, and the Next.js fixture + npm root (all currently unmanaged). Pin the Wrangler version used in + CI/docs instead of installing latest; state the tested Spin and Tinybird + CLI versions (or compatibility ranges) in the deployment/telemetry + guides. Semantic parity checks (each catches a class of drift this audit found): -- Example-config validity: a unit test feeding the uncommented - `trusted-server.example.toml` through `Settings::from_toml`, so every - example block parses and passes validation (guards WP3 forever; - `deny_unknown_fields` makes stale keys a hard failure). -- Route parity: a test per adapter asserting its registered route/method - set matches a checked-in snapshot that the API reference is written from - (guards Appendix A / WP4). -- CLI parity: a golden-file test of the rendered `ts` help tree (commands - and flags) that `docs/guide/cli.md` is written against (guards WP5's CLI - reference). -- Integration parity: a test asserting the registry's integration ID and - capability set matches the checked-in table used by - `integrations-overview.md` (guards Appendix C). +- Example-config harness (replaces the naive parse test, which cannot pass: + `Settings` finalization deliberately rejects the template's placeholder + admin password, and TOML parsing ignores commented blocks). The harness + (a) applies a deterministic substitution of the known placeholders with + synthetic valid values and asserts the substituted template fully parses + and finalizes; (b) extracts every commented example block via explicit + begin/end markers and parses each one (typed integration blocks are + deserialized directly into their config structs, bypassing the + disabled-integration short-circuit); and (c) separately asserts the + distributed template still contains the placeholder markers, so a + template that would deploy without customization fails CI. +- Route parity: a test per adapter asserting its registered route set, + methods, and response semantics/status for guarded routes match the + machine-readable inventory that feeds the api-reference generated + regions. +- CLI parity: a golden file of the built `ts` binary's recursive `--help` + tree (commands and flags, including the dependency-owned `edgezero-cli` + lifecycle flags at the locked version) that feeds the cli.md generated + region. +- Integration parity: tests over the three inventories (registry + `builders()`, auction `provider_builders()`, JS module registry including + `JS_ALWAYS`) that together feed the integrations-overview generated + region; capabilities not exposed by registry metadata (e.g. APS's + conditional proxy) are asserted by the per-integration tests that own + them. +- Config parity: the field-path inventories from WP3 feed the + configuration.md field tables' generated regions. - Repo inventory: a CI script checking workspace members each have a README, every active public page is reachable from the sidebar or an explicit orphan allowlist, and the CI gate list in `CLAUDE.md` names the @@ -678,62 +816,84 @@ Semantic parity checks (each catches a class of drift this audit found): scoped to the WP7 TypeScript files; skipped by default to keep WP8 low-noise. -Acceptance: a PR introducing a dead docs link, a broken intra-doc link, a -failing doctest, an invalid example-config block, or a route/CLI/integration -inventory change without a matching docs snapshot update fails CI. +Acceptance: a PR introducing a dead internal docs link, a broken intra-doc +link, a failing doctest, an invalid example-config block, or a +route/CLI/config/integration inventory change without the matching +regenerated markdown region fails CI; regenerating all generated regions at +the final PR HEAD produces no diff. ## Sequencing and estimate -| Order | Package | Size | Depends on | -| ----- | ------------------------ | ---- | --------------------------------- | -| 1 | WP1 hygiene | S | - | -| 2 | WP2 truth pass | M | - | -| 3 | WP3 config reference | M | - | -| 4 | WP4 API reference | M | WP2 | -| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | -| 6 | WP6 root + crate READMEs | M | - | -| 7 | WP7 in-code docs | M | - | -| 8 | WP8 enforcement | M | WP3, WP7 (gates must start green) | - -Commits land in this order within the single PR, after the spec commit; WP8 -comes last so the new CI gates turn green on the same PR. +| Order | Package | Size | Depends on | +| ----- | ------------------------------------------- | ---- | ------------------------------------------------------------ | +| 0 | WP1 containment subset → separate `main` PR | XS | - | +| 1 | WP1 hygiene (full, in rc PR) | S | - | +| 2 | WP2 truth pass | M | - | +| 3 | WP3 config reference | M | - | +| 4 | WP4 API reference | M | WP2 | +| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | +| 6 | WP6 root + crate READMEs | M | - | +| 7 | WP7 in-code docs | M | - | +| 8 | WP8 enforcement | L | WP3, WP4, WP7 (generated regions and gates must start green) | + +Commits land in this order within the single rc PR, after the spec commit; +WP8 comes last so the new CI gates turn green on the same PR. ## Verification -Before the PR is marked ready: -`cd docs && npm run lint && npm run format && npm run build`; -`cargo fmt --all -- --check`; the target-matched clippy/test aliases for any -crate whose source files changed (WP7, WP8 tests); the WP7 rustdoc command -matrix locally. The acceptance greps listed in WP2-WP4 are run over the -defined source sets and their output included in the PR description, along -with the WP2/WP6 page-disposition inventories and the WP3 parity checklist. -For WP1, a local `vitepress build` listing of `dist/` proves the exclusion -set. The Axum quick start from the updated getting-started guide is -smoke-tested as written. +Before the rc PR is marked ready, at its final HEAD: + +- All applicable GitHub checks green, explicitly including: format + (fmt/clippy matrix, ESLint, Prettier for js and docs), the seven `test.yml` + jobs (rust/axum/cloudflare/spin/parity/cli/typescript), the four + integration-test workflow jobs (including browser), the release WASM + builds, and the JS build (`node build-all.mjs`) and test + (`npx vitest run`) suites for the TypeScript/MJS files WP7 touches. +- The new WP8 parity tests and scripts run green, and regenerating every + generated markdown region produces no diff. +- `cd docs && npm run lint && npm run format && npm run build`. +- The WP7 rustdoc command matrix locally with `RUSTDOCFLAGS="-D warnings"`. +- The acceptance greps from WP2-WP4 over the defined source sets, output + recorded in the PR description alongside the WP3 parity checklist; the + page-disposition inventory is checked in (WP2). +- For WP1, a local `vitepress build` listing of `dist/` proves the exclusion + set, and the separate `main` containment PR is merged (live-site URLs + return 404). +- The Axum quick start smoke test with a defined first-success contract: + starting from the updated getting-started instructions with a canonical + config, the server starts, `GET /health` returns 200 `ok`, and one + representative publisher-proxy request against a local stub origin + returns the expected rewritten HTML; the run and cleanup steps are + recorded in the PR description. ## Open questions -1. `fastly.toml` `service_id`: removal is policy-correct and the CHANGELOG - claims it already happened, but it changes deployment selection. Now an - operationally owned follow-up (see Non-goals): needs an owner, a - replacement mechanism, a non-production deployment test, and rollback - instructions. -2. `docs/public/CNAME`: delete (recommended, matches the `/trusted-server` - base path) or configure a real custom domain? -3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages: this spec recommends - deletion with an inbound-link inventory and redirect stubs where - referenced; confirm. -4. `docs/business-use-cases.md`: its quantitative claims need dated evidence - and assumptions, or the page leaves primary navigation until verified. - Which? -5. CHANGELOG: should a release be cut to drain the six breaking entries in - `[Unreleased]`, or should the mechanical repairs land alone? (Mechanical - repairs are in WP2 either way.) -6. Governance: who owns naming maintainers/CODEOWNERS and the meeting-minutes - commitment? Out of scope here but flagged. -7. Delivery shape: the pre-implementation review recommends shipping WP1 - (publishing containment) as its own PR ahead of the rest; the current - single-PR plan is the owner's explicit instruction. Confirm or split. +Owner for all: the repo maintainer driving this refresh. Each question +blocks the named package; none blocks starting WP2-WP7 content work except +where stated. + +1. `fastly.toml` `service_id` (ops-owned follow-up; blocks nothing here): + needs an owner, a replacement mechanism, a non-production deployment + test, and rollback instructions. +2. `docs/public/CNAME` (blocks WP1 containment PR): delete (recommended, + matches the `/trusted-server` base path) or configure a real custom + domain? +3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages (blocks their WP2 + deletions): this spec recommends deletion with an inbound-link inventory + and redirect stubs where referenced; confirm. +4. `docs/business-use-cases.md` (blocks the WP5 nav change): default is now + removal from primary navigation until quantitative claims carry dated + evidence; the alternative is keeping it with evidence added in this + pass. Confirm the default. +5. CHANGELOG release cut (blocks nothing; mechanical repairs are in WP2 + either way): should a release be cut to drain the six breaking entries + in `[Unreleased]`? +6. Governance ownership (blocks the WP6 governance edit only): who owns + naming maintainers/CODEOWNERS and the meeting-minutes commitment? +7. Delivery shape (blocks starting implementation): confirm the shape in + "Delivery shape": one rc PR for everything, plus the minimal + publishing-containment PR to `main` that the Pages deploy trigger makes + necessary. ## Follow-up issues to file (code, not docs) @@ -743,12 +903,13 @@ smoke-tested as written. (`adapter-spin/src/app.rs:404`). Blocking for the WP5 Spin deployment guide; until fixed, docs label Spin experimental. A `spin up` smoke test proving non-health traffic belongs to the fix's acceptance criteria. -- `ts --version` does not exist (no `#[command(version)]`). - Vendored `edgezero-cli` help text leaks internal spec references ("5.4", "spec 3.3 Model A") into `ts config push --help`; fix upstream at the `edgezero` repo and bump the pinned tag. - Tinybird access-log telemetry: config exists but is rejected at runtime; - either wire it or remove the config surface. + either wire it or remove the config surface. Auction emission is also + Fastly-only; wiring the sink in other adapters is a code decision to + file, not a docs gap. ## Appendix A: HTTP route inventory (truth source for WP4) @@ -758,96 +919,118 @@ publisher fallback. Fastly is the superset. Route tables: `adapter-axum/src/app.rs` (`named_routes()`), `adapter-cloudflare/src/app.rs` (`build_router()`), `adapter-spin/src/app.rs` (`named_fallback_paths()`). Adapter capability differences (stores, geo, TTL, secrets, Tinybird, -request filters) live in each adapter's `platform.rs` and entry point; WP4's -capability matrix is written from those files, not from this table alone. - -| Route | Methods | Availability | Handler | -| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| `/health` | GET | all except Cloudflare | adapter entry points | -| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | -| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | -| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | -| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | -| `/_ts/admin/ec`, `/_ts/admin/ec/{id}`, `/_ts/admin/eids` | GET | Fastly only (rc); Basic-auth gated admin EC diagnostics | `core/src/ec/admin.rs` | -| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | -| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | -| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | -| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | -| `/auction` | POST | all | `core/src/auction/endpoints.rs` | -| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | -| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | -| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | -| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | -| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | -| asset route prefixes | GET, HEAD | operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request` | -| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | - -Fallback dispatch order: GPT-diagnostics request prep, EC state build and -integration request filters (DataDome may short-circuit), tsjs, integration -proxy routes, asset routes, publisher proxy. +request filters, asset routes, image optimizer) live in each adapter's +`platform.rs` and entry point; WP4's capability matrix is written from those +files, not from this table alone. WP8's route snapshots also record response +semantics/status for guarded and unsupported routes, not only method and +path. + +| Route | Methods | Availability | Handler | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `/health` | GET | all except Cloudflare | adapter entry points | +| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | +| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | +| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | +| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | +| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | Fastly only (requires the EC identity KV store); Basic-auth gated | `core/src/ec/admin.rs` | +| `/_ts/admin/eids` | GET | all four adapters (request-inspection handler); Basic-auth gated | `core/src/ec/admin.rs`; registrations in `adapter-axum/src/app.rs:342`, `adapter-cloudflare/src/app.rs:506`, `adapter-spin/src/app.rs:533` (rc) | +| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | +| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | +| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | +| `/auction` | POST | all | `core/src/auction/endpoints.rs` | +| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | +| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | +| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | +| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | +| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | +| asset route prefixes | GET, HEAD | Fastly only today; operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request`, dispatched from `adapter-fastly/src/app.rs` | +| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | + +Per-adapter pipelines: Fastly runs pre-route integration request filters +(DataDome), then dispatches tsjs, integration proxy routes, asset routes, +publisher proxy. Axum, Cloudflare, and Spin have no request filters or +asset-route dispatch today: tsjs, integration proxy routes, publisher +proxy. ## Appendix B: Settings sections (truth source for WP3) From `core/src/settings.rs` (`Settings`; 16 fields on rc/202608). Columns record what each artifact carries today. On rc, `request_signing` and -`creative_opportunities` are `Option` fields. - -| Section | Struct | `trusted-server.example.toml` | `configuration.md` | -| -------------------------- | ----------------------------- | ------------------------------------------------------------------------------------ | -------------------------- | -| `[publisher]` | `Publisher` | present | present | -| `[tester_cookie]` | `TesterCookieConfig` | missing | present (line 360) | -| `[ec]` | `Ec` + `EcPartner` | present | present | -| `[integrations.*]` | per-integration typed configs | partial (osano missing) | 5 of 14 IDs | -| `[[handlers]]` | `Handler` | present | present | -| `response_headers` | map | present (commented) | present | -| `[request_signing]` | `RequestSigning` | present | present | -| `[rewrite]` | `Rewrite` | missing | present (line 702) | -| `[auction]` | `AuctionConfig` | missing `mediator`, `creative_store` (`allowed_context_keys` present, line 145) | present | -| `[consent]` | `ConsentConfig` | missing | missing | -| `[proxy]` | `Proxy` | partial; `asset_routes` missing | present incl. asset routes | -| `[creative_opportunities]` | `CreativeOpportunitiesConfig` | present | present | -| `[image_optimizer]` | `ImageOptimizerSettings` | missing | present (line 946 area) | -| `[tinybird]` | `TinybirdSettings` | missing | missing | -| `[debug]` | `DebugConfig` | present on rc incl. `auction_html_comment_options`; `inject_adm_for_testing` missing | missing | -| `[cache]` (rc) | `CacheSettings` | commented `[[cache.asset_rules]]` examples only | present (rc) | - -## Appendix C: Integration registry (truth source for WP5 overview table) - -From `core/src/integrations/mod.rs` `builders()` and per-integration -registrations. Capabilities: P proxy, AR attribute rewriter, SR script -rewriter, HI head injector, PP html post-processor, RF request filter, -DJS deferred JS, AP auction provider. - -| ID | Capabilities | JS module | Docs page today | -| -------------------- | ---------------------------------- | ------------------ | ------------------------------- | -| `prebid` | P, AR, HI, DJS, AP | yes | in sidebar | -| `aps` | P (renderer), AP, no JS bundle | render helper only | in sidebar | -| `datadome` | P, AR, HI, RF (when protection on) | yes | in sidebar | -| `gpt` | P, AR, HI | yes | orphaned | -| `gpt_diagnostics` | standalone JS on demand | yes | in sidebar | -| `google_tag_manager` | P, AR, SR | yes | orphaned | -| `didomi` | P, HI | yes | in sidebar | -| `sourcepoint` | P, AR, HI | yes | orphaned | -| `osano` | bare registration | yes | in sidebar | -| `permutive` | P, AR | yes | in sidebar (thin) | -| `lockr` | P, AR | yes | in sidebar | -| `nextjs` | SR x2, PP, no JS | no | in sidebar | -| `testlight` | P, AR | yes | none | -| `adserver_mock` | AP only (no registration) | no | none | -| `creative` (JS-only) | always injected (`JS_ALWAYS`) | yes | covered via creative-processing | +`creative_opportunities` are `Option` fields. Root parsing does not +validate integration blocks (flattened map) or commented examples; the WP8 +harness covers both. + +| Section | Struct | `trusted-server.example.toml` | `configuration.md` | +| -------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------ | +| `[publisher]` | `Publisher` | present | present | +| `[tester_cookie]` | `TesterCookieConfig` | missing | present (line 360) | +| `[ec]` | `Ec` + `EcPartner` | present | present | +| `[integrations.*]` | per-integration typed configs | partial (osano missing) | 5 of 14 IDs; existing five unaudited | +| `[[handlers]]` | `Handler` | present | present | +| `response_headers` | map | present (commented) | present | +| `[request_signing]` | `RequestSigning` (Option) | present | present | +| `[rewrite]` | `Rewrite` | missing | present (line 702) | +| `[auction]` | `AuctionConfig` | missing `mediator`, `creative_store` (`allowed_context_keys` present, line 145) | present | +| `[consent]` | `ConsentConfig` | missing | missing | +| `[cache]` (rc) | `CacheSettings` | commented `[[cache.asset_rules]]` examples only | present (rc) | +| `[proxy]` | `Proxy` | partial; `asset_routes` missing | present incl. asset routes | +| `[creative_opportunities]` | `CreativeOpportunitiesConfig` (Option) | present | present | +| `[image_optimizer]` | `ImageOptimizerSettings` | missing | present (line 946 area) | +| `[tinybird]` | `TinybirdSettings` | missing | missing | +| `[debug]` | `DebugConfig` | present on rc incl. `auction_html_comment_options`; `inject_adm_for_testing` missing | missing | + +## Appendix C: Integration inventories (truth source for WP5 overview table) + +Three inventories together describe the integration surface; no single +registry API exposes all of it, so WP8 tests them separately: + +1. Registry registrations: `core/src/integrations/mod.rs` `builders()` + (13 entries; capabilities below). +2. Auction providers: `core/src/auction/mod.rs` `provider_builders()` + (adds `adserver_mock`; also registers prebid/APS providers). +3. JS modules: `registry.rs` module-id functions plus `JS_ALWAYS` + (adds the always-injected `creative` module). + +Capabilities: P proxy, AR attribute rewriter, SR script rewriter, HI head +injector, PP html post-processor, RF request filter, DJS deferred JS, AP +auction provider. Conditional capabilities are stated as such; registry +metadata alone does not expose them. + +| ID | Capabilities | JS module | Docs page today | +| -------------------- | ------------------------------------------------------------------------------- | ------------------ | ------------------------------- | +| `prebid` | P, AR, HI, DJS, AP | yes | in sidebar | +| `aps` | HI always, AP; P conditional on the trusted-server rendering mode; no JS bundle | render helper only | in sidebar | +| `datadome` | P, AR, HI, RF (when protection on) | yes | in sidebar | +| `gpt` | P, AR, HI | yes | orphaned | +| `gpt_diagnostics` | standalone JS on demand | yes | in sidebar | +| `google_tag_manager` | P, AR, SR | yes | orphaned | +| `didomi` | P, HI | yes | in sidebar | +| `sourcepoint` | P, AR, HI | yes | orphaned | +| `osano` | bare registration | yes | in sidebar | +| `permutive` | P, AR | yes | in sidebar (thin) | +| `lockr` | P, AR | yes | in sidebar | +| `nextjs` | SR x2, PP, no JS | no | in sidebar | +| `testlight` | P, AR | yes | none | +| `adserver_mock` | AP only (auction-provider inventory, no registry registration) | no | none | +| `creative` (JS-only) | always injected (`JS_ALWAYS`) | yes | covered via creative-processing | ## Appendix D: CLI tree and environment variables -`ts` commands (from `crates/trusted-server-cli/src/run.rs` on rc/202608): -`audit generate|ad-templates`, `active-version`, `auth login|logout|status`, -`build`, `config init|diff|push|validate|ad-templates|gc`, `deploy`, -`healthcheck`, `prebid bundle`, `provision`, `rollback`, `serve`, +`ts` commands (from `crates/trusted-server-cli/src/run.rs` on rc/202608; +the canonical reference is the built binary's recursive `--help` tree, +which the WP8 golden file captures, including flags owned by the +lockfile-resolved `edgezero-cli`): +`audit page|generate|ad-templates generate|verify`, `active-version`, +`auth login|logout|status`, `build`, +`config init|diff|push|validate|gc|ad-templates lint|match|check|explain`, +`deploy`, `healthcheck`, `prebid bundle`, `provision`, `rollback`, `serve`, `dev proxy [ca path|install|uninstall|regenerate]` (macOS only; `ts dev` -lists no subcommands on other hosts). Commands that detect drift -(`config diff`, `config ad-templates check`, audit verification) report a -distinct drift outcome with a stable exit code. All commands and flags carry -help text; `docs/guide/cli.md` must add `active-version`, `healthcheck`, -`rollback`, and `config gc`. +lists no subcommands on other hosts). `ts --version` is available +(`#[command(version)]`). Commands that detect drift (`config diff`, +`config ad-templates check`, audit verification) report a distinct drift +outcome with a stable exit code. `docs/guide/cli.md` must add +`active-version`, `healthcheck`, `rollback`, and `config gc`. Runtime environment variables to document (WP2 `.env.example` / `.env.dev`): `FASTLY_SERVICE_VERSION`, `FASTLY_IS_STAGING`, `FASTLY_HOSTNAME`, @@ -863,11 +1046,11 @@ typed overlay, ignored by the runtime loader but applied by ## Appendix E: Staleness finding index -Compact index of audit findings driving WP1/WP2; each was verified against -`main` at `2e85a1cdc`. +Compact index of audit findings driving WP1/WP2; verified against the main +baseline and re-verified on rc/202608 where marked. -- Dead endpoints documented: `docs/guide/api-reference.md:85` - (`/first-party/ad`), `:190` (`/third-party/ad`); +- Dead endpoints documented: `docs/guide/api-reference.md:86,191,707,711` + (rc; `/first-party/ad`, `/third-party/ad`); `docs/guide/integrations-overview.md:46-48`; `docs/guide/error-reference.md:658`; `docs/guide/integrations/prebid.md:515-531`. @@ -875,7 +1058,9 @@ Compact index of audit findings driving WP1/WP2; each was verified against (`npm run type-check`), `:663` (`--validate-config`). - Obsolete API examples: `docs/guide/key-rotation.md:301-310` (`KeyRotationManager::new(...)?`; constructor returns `Self`), - unauthenticated admin curl examples. + unauthenticated admin curl examples; + `docs/guide/configuration.md:1954-1957` (rc; nonexistent + `settings_data::get_settings`, `println!` against repo conventions). - Fabricated content: `docs/guide/ad-serving.md:11-18,43,48,77-83` (Equativ, `[ad_servers]`, `trackImpression`); `docs/guide/architecture.md:97-104` (`RequestWrapper`); `docs/guide/integration-guide.md:313` (equativ bidder). @@ -883,6 +1068,8 @@ Compact index of audit findings driving WP1/WP2; each was verified against `integration-guide.md:96` (`handle` without `RuntimeServices`, vs `registry.rs:282-288`), `:132` (`proxy_request` without `services`, vs `proxy.rs:737-742`), `:134` (`use fastly::http` in core-neutral code). +- Fictional-data policy violations: `docs/guide/ec-setup-guide.md:15` (real + deployment domain); `.env.example:8-10` (non-reserved `publisher.com`). - Wrong config names: `docs/guide/getting-started.md:141` (`[gdpr]`). - Nonexistent builder method: `.with_asset(...)` in `docs/guide/creative-processing.md:808`, @@ -894,7 +1081,9 @@ Compact index of audit findings driving WP1/WP2; each was verified against `docs/guide/architecture.md:154-159`; Axum described as a deployment target; Spin described as production-capable despite `adapter-spin/src/app.rs:52` (settings from the checked-in example toml) - and `:404` (blanket 503 on startup failure). + and `:404` (blanket 503 on startup failure); asset routes, request + filters, image optimizer, and Tinybird emission are Fastly-only but + documented as generic. - Aspirational sidebar pages: `docs/guide/integrations/gam.md` (no such integration, "Q1 2026" passed), `kargo.md`. - Auction README: route table file/line rot, nonexistent `providers/` dir, @@ -916,14 +1105,18 @@ Compact index of audit findings driving WP1/WP2; each was verified against `srcExclude`); `docs/guide/onboarding.md` published with internal contacts; `docs/public/CNAME` placeholder; empty `docs/guide/index.md`; nav Guide link bypasses the landing page (`config.mts:61`); - `docs/package.json` not private, ISC license in an Apache-2.0 repo. + `docs/package.json` not private, ISC license in an Apache-2.0 repo; + Pages deploys only from `main`, so rc-merged containment does not reach + the live site (`deploy-docs.yml:3`). - Root-doc drift: `CLAUDE.md:102` (no workspace default target exists); `CONTRIBUTING.md` stale since 2026-01. - Slash-command drift: `.claude/commands/{check-ci,verify,test-all}.md` omit Spin/cloudflare-wasm/parity gates; `test-crate.md` untargeted `cargo test`. - Tooling: no `cargo doc` in CI; doctests never run (cross-compile only); `format-docs` never runs `vitepress build`; `deploy-docs.yml` not - triggered by `.tool-versions` changes; `eslint-plugin-jsdoc` inert; + triggered by `.tool-versions` changes; CodeQL PR analysis limited to + `main` branches; `eslint-plugin-jsdoc` inert; `openrtb-codegen` missing `[lints] workspace = true`; PR template says `tracing`; no semantic parity checks for routes, config, CLI, - integrations, crates, navigation, or CI gates. + integrations, crates, navigation, or CI gates, and no binding between + inventories and the reader-facing markdown. From a7cdc8c2147967ec3c01d7cf975b070ff095b53b Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 21:55:14 -0700 Subject: [PATCH 10/33] Rework spec after third pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 280 +++++++++++------- 1 file changed, 181 insertions(+), 99 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 6c1be010e..8103f2b24 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-20 (round 2; addresses both pre-implementation reviews) +**Revised:** 2026-08-20 (round 3; addresses all three pre-implementation reviews) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and @@ -92,7 +92,7 @@ drift in every surface. The failures fall into six categories: the image optimizer, and Tinybird auction telemetry exist only in the Fastly adapter today; the other adapters construct no telemetry sink and silently use the no-op default. -5. **Publishing and policy hygiene.** All 75 internal spec/plan files under +5. **Publishing and policy hygiene.** All 120 internal spec/plan markdown files under `docs/superpowers/` are built and published to the public GitHub Pages site (no `srcExclude` in `docs/.vitepress/config.mts`), along with `docs/guide/onboarding.md` (internal contacts, meetings, access guidance), @@ -101,7 +101,11 @@ drift in every surface. The failures fall into six categories: personal email (`authors`, line 4) and a real Fastly service id (line 10) against the repo's own sensitive-data policy, and unlabeled base64 key fixtures that read as credentials. `docs/package.json` is not `private` - and declares an ISC license in an Apache-2.0 repository. Active examples + and declares an ISC license in an Apache-2.0 repository. The maintained + agent instructions under `.claude/agents/` are badly stale: they still + describe a three-crate Fastly-only workspace, cite the nonexistent + `RequestWrapper` trait, list outdated verification gates, and assume all + PRs target `main`. Active examples violate the fictional-data policy beyond that: `ec-setup-guide.md:15` names a real deployment domain, and `.env.example` uses non-reserved `publisher.com` values instead of `.example` domains. @@ -159,8 +163,9 @@ under `docs/superpowers/`, legitimately contain every retired term): pages nor historical artifacts but are still maintained and must pass the truth standard: `docs/README.md`, `docs/internal/**` (including the moved onboarding page), `scripts/README.md` and `tinybird/README.md` once - created, and `.claude/skills/**` (operator-facing skills such as the - Fastly deployment skill). + created, `.claude/skills/**` (operator-facing skills such as the + Fastly deployment skill), `.claude/agents/**` (maintained agent + instructions), and `.github/pull_request_template.md`. - **Historical set:** `docs/superpowers/**` (specs, plans, implementation notes, archive) and shipped `CHANGELOG.md` release entries. Exempt from retired-term greps; a changelog entry describing a rename may name the @@ -206,10 +211,14 @@ under `docs/superpowers/`, legitimately contain every retired term): - No new documentation toolchains. VitePress, rustdoc, and clap help remain the three delivery mechanisms. No TypeDoc, no docs.rs publishing. - No rewrite of `docs/business-use-cases.md` marketing copy. Default - handling changed after review: the page leaves primary navigation until - every quantitative claim carries dated evidence and unshipped features - are visibly labeled (open question 4 records the alternative of keeping - it with evidence added). `docs/roadmap.md` gets a + handling hardened after review: the page is excluded from the built site + (`srcExclude`) until every quantitative claim carries dated evidence and + unshipped features are visibly labeled - nav-only removal would leave a + known-false page published and locally searchable, and would violate + WP5's no-orphan acceptance (the page presents planned headless-browser + malvertising detection as shipped while the roadmap calls it planned). + Open question 4 records the alternative of an evidence-based rewrite in + this pass. `docs/roadmap.md` gets a factual status pass (shipped/active/deferred labels, correct crate names), not a strategy rewrite. - No release-management policy changes. The CHANGELOG's 10-month untagged @@ -245,9 +254,10 @@ the live site until rc merges to main. Therefore: The rc PR carries the same changes; the rc→main merge reconciles to an identical state. - Everything else lands only in the single rc PR. -- If release-branch PRs are expected to get CodeQL analysis, `rc/*` must be - added to `codeql.yml`'s branch triggers (WP8 records this alongside the - other workflow changes). +- CodeQL today analyzes only PRs targeting `main`, so the rc PR carrying + new workflows, generators, and scripts would go unanalyzed. Decision: + WP8 adds `rc/*` to `codeql.yml`'s PR branch triggers, and CodeQL joins + the final gate list in Verification. Open question 7 asks the owner to confirm this shape. @@ -269,6 +279,9 @@ Smallest package, highest urgency. The containment subset also ships to the repository is public, so source-sensitive details are scrubbed even from excluded files. Verify with a local `vitepress build` that the dist no longer contains those paths. +- The containment pieces are independent of the CNAME decision and must + not wait for it: `srcExclude` and the onboarding move/scrub ship + immediately; the CNAME change follows its own resolution (open question 2) in a separate commit. - Resolve `docs/public/CNAME`: it currently ships the placeholder `your-custom-domain.com` into every Pages deploy while `base` is set to `/trusted-server` (the two are mutually inconsistent). Default action: @@ -295,11 +308,17 @@ real key` labels to the `[local_server]` secret/JWKS entries; add one-line `cargo test -p`, the exact pattern `AGENTS.md` warns will fail). Add the missing Spin/cloudflare-wasm gates to `AGENTS.md`'s fallback list. -Acceptance: `vitepress build` output contains no `superpowers/`, `internal/`, -`epics/`, or onboarding pages; the containment PR to `main` is merged and -the live site no longer serves those URLs; no real personal emails in -tracked config; no internal contacts or access instructions anywhere in the -repo; every command file lists the same gates as `CLAUDE.md`. +Acceptance: `vitepress build` output contains no `superpowers/`, +`internal/`, `epics/`, onboarding, or business-use-cases pages; the +containment PR to `main` is merged, the live site no longer serves those +URLs, and a positive post-deploy smoke passes (site root, the Guide +landing page, and one reference page return 200 with expected content); +the rollback procedure is documented in the containment PR (owner: the +maintainer driving this refresh; procedure: revert the containment commit +on `main` and re-run the Pages deploy via `workflow_dispatch`); no real +personal emails in tracked config; no internal contacts or access +instructions anywhere in the repo; every command file lists the same gates +as `CLAUDE.md`. ### WP2: Truth pass over existing content @@ -307,13 +326,19 @@ Nothing new is written here beyond minimal replacement prose; the goal is that nothing in the active sets is false. The pass starts from a complete page inventory: every page in the active public and active maintained internal sets gets an explicit disposition, verified, rewrite, or retire. -The inventory is checked into the repository (under -`docs/superpowers/implementation-notes/`) with per-page source anchors, not -left in a PR description. Token greps establish that retired names are +The inventory is checked into the repository under +`docs/internal/audits/` (inside the active maintained internal set, not +the exempt historical tree), stamped with the audited merge-base SHA, with +per-page source anchors, not left in a PR description. The inventory is an +audit record of this pass; the WP8 parity gates, not the inventory, are +the continuing control. Token greps establish that retired names are gone; they cannot validate commands, APIs, auth, or behavior, so each "verified" disposition means the page's commands and examples were actually -checked against code, and marked Rust/shell/TOML/JSON snippets are compiled -or parsed wherever feasible (the WP8 harness runs them). +checked against code, and executable fences are governed by the WP8 +snippet manifest: every Rust/shell/TOML/JSON fence in the active public +set gets a checked-in disposition (rust-compile, toml/json-parse, bash -n, +safe smoke test, or manual with a reason and source anchor), and CI fails +on new executable fences with no classification. - `docs/guide/api-reference.md`: delete `GET /first-party/ad` and `POST /third-party/ad` sections (endpoints do not exist). The full @@ -343,7 +368,10 @@ or parsed wherever feasible (the WP8 harness runs them). values; sweep both sets for other real domains, customer names, or credential-shaped strings. Reviewed canonical vendor endpoints (e.g. real GPT/DataDome CDN hosts an integration genuinely proxies) stay, everything - else becomes fictional. + else becomes fictional. Legitimate vendor endpoints are recorded in a + checked-in allowlist (category, rationale, owner) that the WP8 scanner + reads; `CLAUDE.md`'s example-domains-only policy gains a sentence + describing this vendor-endpoint exception (WP6 makes that edit). - Re-verify the pages touched by the final six rc commits: `docs/guide/integrations/datadome.md` (the staging requirement was removed from protection behavior in the same commit that rewrote the @@ -458,9 +486,14 @@ integrations can skip typed deserialization entirely. `[cache]` section, promote the commented `[[cache.asset_rules]]` examples to a complete worked block covered by the WP8 example harness. - Field-path inventories: for every nested `Settings` type and all 14 typed - integration config structs, enumerate the full field paths from the - source, then reconcile `docs/guide/configuration.md`'s field tables - against that inventory. This audits the five existing integration + integration config structs, the inventory is derived from the serde + surface itself (a schema-derive or serializer-walk in a test, with an + explicit grammar for dynamic map keys, enum variants, aliases, flattened + fields, and skipped fields), so a newly added field breaks CI until the + inventory, template, and reference are updated - the chain is + Rust serde surface to machine inventory to template to generated + markdown, checked in both directions. Reconcile + `docs/guide/configuration.md`'s field tables against that inventory. This audits the five existing integration sections (Prebid's reference is already missing valid keys) as well as adding the nine absent ones (`aps`, `datadome`, `didomi`, `sourcepoint`, `lockr`, `gpt`, `gpt_diagnostics`, `google_tag_manager`, `adserver_mock`). @@ -492,8 +525,10 @@ with per-endpoint contracts, not just paths, and with per-adapter accuracy. - Document every named route: health, discovery/signing endpoints, admin key rotation (and the deliberately 404-denied legacy `/admin/keys/*` aliases), the rc admin diagnostics (`GET /_ts/admin/ec` and `/_ts/admin/ec/{id}`, - Fastly-only because they need the EC KV store; `GET /_ts/admin/eids`, - which is a request-inspection handler registered on all four adapters), + registered on all four adapters but functional only on Fastly, which has + the EC identity KV store - the others return not-supported, matching the + key-rotation pattern; `GET /_ts/admin/eids`, a request-inspection + handler that works on all four adapters), EC partner API (`/_ts/api/v1/batch-sync`, `/_ts/api/v1/identify`), tester cookie endpoints, `POST /auction`, `GET /_ts/page-bids` plus the legacy `/__ts/page-bids` alias, the four `/first-party/*` proxy endpoints, @@ -621,11 +656,20 @@ path) agree with the published tables. - Site usability: enable VitePress `lastUpdated` (the deploy workflow already fetches full history for it) and local search (`themeConfig.search`), so the 1,600-line configuration reference is - navigable; give mermaid diagrams a one-paragraph prose equivalent nearby. + navigable. +- Release identity: add a global banner stating the site documents the + unreleased `main` line, plus a short compatibility statement + (server/config/CLI move together via the blob-envelope contract), and + the rule that versioned documentation is published when a release is + actually tagged. +- Diagram accessibility: inventory the active public mermaid diagrams and + give each a nearby one-paragraph prose equivalent; the inventory with a + per-diagram checkbox is part of WP5's recorded acceptance. - Navigation: add sidebar entries for the three orphaned real integrations - (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages; remove - `business-use-cases` from primary navigation per the Non-goals default - (open question 4). + (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages; + `business-use-cases` is excluded from the build per the Non-goals + default (open question 4), so it neither sits in navigation nor counts + against the no-orphan acceptance. - `docs/guide/architecture.md`: describe all 10 workspace crates and the platform trait boundary; add the missing Cloudflare adapter section. @@ -666,6 +710,13 @@ serve`; link the deployment guides; refresh the doc-site link table. - `.claude/skills/**`: audit the operator-facing skills (including the Fastly deployment skill) against current commands and config, same truth standard as the command files. +- `.claude/agents/**`: audit every agent instruction file; they currently + describe a three-crate Fastly-only workspace, cite the nonexistent + `RequestWrapper` trait (`code-architect.md:11`, `repo-explorer.md:12`), + omit Cloudflare/Spin/parity gates (`verify-app.md:19`), and assume PRs + target `main` (`pr-creator.md:177`). +- `CLAUDE.md` policy edits owned here: the vendor-endpoint exception + sentence (WP2) and the `# Examples` standard reconciliation (WP7). - `ProjectGovernance.md`: the two claims contradicted by repo state (meeting minutes "maintained within the repository" - none exist; "continuous releases" - none tagged since v1.1.0) become accurate @@ -712,10 +763,13 @@ Targeted, not exhaustive. The worklist below is the acceptance scope. documented), which is the public tsjs type surface. Add a header block to `build-prebid-external.mjs` (401 lines, no header). -Style follows `CLAUDE.md` documentation standards. `# Examples` sections are -added only where an example compiles as a doctest and earns its keep -(`redacted.rs` is the model); this spec does not attempt examples on all ~589 -public functions. +Style follows `CLAUDE.md` documentation standards with one deliberate +divergence that WP6 reconciles: `CLAUDE.md` currently mandates +`# Examples` on every public API function, which no part of the codebase +satisfies; the standard is updated to require examples where they compile +as doctests and earn their keep (`redacted.rs` is the model), so the two +documents state the same rule. This spec does not attempt examples on all +~589 public functions. Rustdoc verification commands (the exact matrix WP8 puts in CI; the CI job needs pinned Node/npm setup because documenting `trusted-server-js` runs its @@ -731,8 +785,8 @@ npm-based build script): Acceptance: every item on the worklist above is complete; the rustdoc command matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the listed TypeScript files each have a file-header JSDoc block and every -`core/types.ts` export is documented (checked by the WP8 jsdoc lint scoped -to those files, or a grep count recorded in the PR description). +`core/types.ts` export is documented, enforced by the mandatory WP8 jsdoc +lint scoped to those files. ### WP8: Enforcement @@ -750,10 +804,11 @@ Build gates: - Docs site: add `npm run build` to the `format-docs` job in `.github/workflows/format.yml` so dead internal links fail PRs instead of - the post-merge deploy. External links are out of the PR gate; add an - allowlisted scheduled link check (or a documented manual audit cadence) - instead. Align the two workflows' npm cache keys (one keys on - `package.json`, the other on `package-lock.json`). Add `.tool-versions` + the post-merge deploy. External links are out of the PR gate; the decided policy is an + allowlisted scheduled link-check workflow (weekly), failing into an + issue rather than blocking PRs. Normalize every `setup-node` cache key across all workflows to the + relevant `package-lock.json` (today one keys on `package.json`, another + on the lockfile). Add `.tool-versions` to `deploy-docs.yml` trigger paths (the site renders versions from it, so version-only bumps must republish). If release-branch PRs are expected to get CodeQL analysis, add `rc/*` to `codeql.yml` branch triggers. @@ -765,7 +820,9 @@ Build gates: `-D warnings` stays the item-level gate. - Doctests: add a native-host `cargo test --doc -p trusted-server-core` step (doctests are silently skipped today because core is only tested - cross-compiled). + cross-compiled). This job needs the same pinned Node/npm setup as the + rustdoc job: core depends on `trusted-server-js`, whose build script + invokes npm. - Add `[lints] workspace = true` to `trusted-server-openrtb-codegen`, the one crate not inheriting the doc lints. - Dependency governance: Dependabot gains the `github-actions` ecosystem, @@ -780,22 +837,36 @@ Semantic parity checks (each catches a class of drift this audit found): - Example-config harness (replaces the naive parse test, which cannot pass: `Settings` finalization deliberately rejects the template's placeholder admin password, and TOML parsing ignores commented blocks). The harness - (a) applies a deterministic substitution of the known placeholders with - synthetic valid values and asserts the substituted template fully parses - and finalizes; (b) extracts every commented example block via explicit - begin/end markers and parses each one (typed integration blocks are - deserialized directly into their config structs, bypassing the - disabled-integration short-circuit); and (c) separately asserts the - distributed template still contains the placeholder markers, so a - template that would deploy without customization fails CI. + (a) applies a deterministic substitution of the known placeholders and + deliberately invalid disabled-block values (e.g. empty IDs) with + synthetic valid values, and asserts the substituted template fully + parses and finalizes; (b) extracts every commented example block via + explicit begin/end markers and, for typed integration blocks, + deserializes each directly into its config struct with ignored-key + detection (several structs, including Permutive's, do not reject + unknown fields), runs its `Validate::validate`, and exercises the + integration-specific deploy/startup checks from `core/src/config.rs`; + and (c) separately asserts the distributed template still contains the + placeholder markers, so a template that would deploy without + customization fails CI. - Route parity: a test per adapter asserting its registered route set, methods, and response semantics/status for guarded routes match the machine-readable inventory that feeds the api-reference generated - regions. -- CLI parity: a golden file of the built `ts` binary's recursive `--help` - tree (commands and flags, including the dependency-owned `edgezero-cli` - lifecycle flags at the locked version) that feeds the cli.md generated - region. + regions. Route definitions expose only path, methods, and handler, so + the generated regions cover the route/availability tables; the + per-endpoint contract prose (auth, schemas, headers, cache/CORS, config + gates, rate limits) is explicitly manually owned, marked as such in the + page, and backed by targeted tests where they exist + (`Settings::ADMIN_ENDPOINTS` coverage, config-gate behavior tests) + rather than falsely claimed as generated. The adapter capability matrix + rows are likewise either tied to a per-adapter test or marked manually + owned. +- CLI parity: golden files of the built `ts` binary's recursive `--help` + tree on both Linux and macOS (the `ts dev` subtree is compile-time + gated to macOS, and CI already runs the CLI suite on both hosts), + merged into a platform-annotated union (including the dependency-owned + `edgezero-cli` lifecycle flags at the locked version) that feeds the + cli.md generated region. - Integration parity: tests over the three inventories (registry `builders()`, auction `provider_builders()`, JS module registry including `JS_ALWAYS`) that together feed the integrations-overview generated @@ -804,6 +875,13 @@ Semantic parity checks (each catches a class of drift this audit found): them. - Config parity: the field-path inventories from WP3 feed the configuration.md field tables' generated regions. +- Snippet manifest: a checked-in manifest classifying every executable + fence in the active public set (rust-compile, toml/json-parse, + `bash -n`, safe smoke test, or manual with reason and source anchor); + CI runs the classified checks and fails on unclassified new fences. +- Domain scanner: a deterministic scan of all active sets for + non-`.example` domains and credential-shaped strings, allowlist-aware + (the WP2 vendor allowlist). - Repo inventory: a CI script checking workspace members each have a README, every active public page is reachable from the sidebar or an explicit orphan allowlist, and the CI gate list in `CLAUDE.md` names the @@ -812,9 +890,9 @@ Semantic parity checks (each catches a class of drift this audit found): ESLint, the CLI/codegen clippy jobs, the bench compile check, the release WASM builds, and the entire integration-tests workflow) so agents and the slash commands stay aligned with reality. -- Optional, decide at review: enable a minimal `jsdoc/*` ESLint rule set - scoped to the WP7 TypeScript files; skipped by default to keep WP8 - low-noise. +- A scoped `jsdoc/*` ESLint rule set over the WP7 TypeScript files is + mandatory (a PR-description grep count provides no recurrence + protection); the plugin is already installed with zero rules enabled. Acceptance: a PR introducing a dead internal docs link, a broken intra-doc link, a failing doctest, an invalid example-config block, or a @@ -824,17 +902,17 @@ the final PR HEAD produces no diff. ## Sequencing and estimate -| Order | Package | Size | Depends on | -| ----- | ------------------------------------------- | ---- | ------------------------------------------------------------ | -| 0 | WP1 containment subset → separate `main` PR | XS | - | -| 1 | WP1 hygiene (full, in rc PR) | S | - | -| 2 | WP2 truth pass | M | - | -| 3 | WP3 config reference | M | - | -| 4 | WP4 API reference | M | WP2 | -| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | -| 6 | WP6 root + crate READMEs | M | - | -| 7 | WP7 in-code docs | M | - | -| 8 | WP8 enforcement | L | WP3, WP4, WP7 (generated regions and gates must start green) | +| Order | Package | Size | Depends on | +| ----- | ------------------------------------------- | ---- | ---------------------------------------------------------------- | +| 0 | WP1 containment subset → separate `main` PR | XS | - | +| 1 | WP1 hygiene (full, in rc PR) | S | - | +| 2 | WP2 truth pass | M | - | +| 3 | WP3 config reference | M | - | +| 4 | WP4 API reference | M | WP2 | +| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | +| 6 | WP6 root + crate READMEs | M | - | +| 7 | WP7 in-code docs | M | - | +| 8 | WP8 enforcement | L | WP3-WP7 (generated regions, goldens, and gates must start green) | Commits land in this order within the single rc PR, after the spec commit; WP8 comes last so the new CI gates turn green on the same PR. @@ -843,7 +921,8 @@ WP8 comes last so the new CI gates turn green on the same PR. Before the rc PR is marked ready, at its final HEAD: -- All applicable GitHub checks green, explicitly including: format +- All applicable GitHub checks green, explicitly including: CodeQL (with + `rc/*` added to its PR triggers), format (fmt/clippy matrix, ESLint, Prettier for js and docs), the seven `test.yml` jobs (rust/axum/cloudflare/spin/parity/cli/typescript), the four integration-test workflow jobs (including browser), the release WASM @@ -881,13 +960,16 @@ where stated. 3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages (blocks their WP2 deletions): this spec recommends deletion with an inbound-link inventory and redirect stubs where referenced; confirm. -4. `docs/business-use-cases.md` (blocks the WP5 nav change): default is now - removal from primary navigation until quantitative claims carry dated - evidence; the alternative is keeping it with evidence added in this - pass. Confirm the default. +4. `docs/business-use-cases.md` (blocks the WP1/WP5 exclusion): default is + exclusion from the built site until quantitative claims carry dated + evidence and unshipped features are labeled; the alternative is an + evidence-based rewrite in this pass. Confirm the default. 5. CHANGELOG release cut (blocks nothing; mechanical repairs are in WP2 - either way): should a release be cut to drain the six breaking entries - in `[Unreleased]`? + either way): should a release be cut to drain the seven breaking + entries in `[Unreleased]`? If no release is cut, the deterministic WP2 + edit is: keep the `[1.2.0]` section with an explicit "(tag v1.2.0 was + never published)" annotation, repoint the link references to + resolvable compares, and leave entries untouched. 6. Governance ownership (blocks the WP6 governance edit only): who owns naming maintainers/CODEOWNERS and the meeting-minutes commitment? 7. Delivery shape (blocks starting implementation): confirm the shape in @@ -925,27 +1007,27 @@ files, not from this table alone. WP8's route snapshots also record response semantics/status for guarded and unsupported routes, not only method and path. -| Route | Methods | Availability | Handler | -| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `/health` | GET | all except Cloudflare | adapter entry points | -| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | -| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | -| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | -| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | -| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | Fastly only (requires the EC identity KV store); Basic-auth gated | `core/src/ec/admin.rs` | -| `/_ts/admin/eids` | GET | all four adapters (request-inspection handler); Basic-auth gated | `core/src/ec/admin.rs`; registrations in `adapter-axum/src/app.rs:342`, `adapter-cloudflare/src/app.rs:506`, `adapter-spin/src/app.rs:533` (rc) | -| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | -| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | -| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | -| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | -| `/auction` | POST | all | `core/src/auction/endpoints.rs` | -| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | -| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | -| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | -| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | -| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | -| asset route prefixes | GET, HEAD | Fastly only today; operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request`, dispatched from `adapter-fastly/src/app.rs` | -| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | +| Route | Methods | Availability | Handler | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `/health` | GET | all except Cloudflare | adapter entry points | +| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | +| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | +| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | +| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | +| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | registered on all four adapters; functional only on Fastly (EC identity KV store), others return not-supported (`adapter-axum/src/app.rs:330`, `adapter-cloudflare/src/app.rs:500`, `adapter-spin/src/app.rs:800`, rc) | `core/src/ec/admin.rs` | +| `/_ts/admin/eids` | GET | all four adapters (request-inspection handler); Basic-auth gated | `core/src/ec/admin.rs`; registrations in `adapter-axum/src/app.rs:342`, `adapter-cloudflare/src/app.rs:506`, `adapter-spin/src/app.rs:533` (rc) | +| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | +| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | +| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | +| `/auction` | POST | all | `core/src/auction/endpoints.rs` | +| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | +| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | +| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | +| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | +| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | +| asset route prefixes | GET, HEAD | Fastly only today; operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request`, dispatched from `adapter-fastly/src/app.rs` | +| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | Per-adapter pipelines: Fastly runs pre-route integration request filters (DataDome), then dispatches tsjs, integration proxy routes, asset routes, @@ -1101,7 +1183,7 @@ baseline and re-verified on rc/202608 where marked. - fastly.toml: personal email (`:4`), service id (`:10`, ops-owned follow-up), orphaned script reference (`:38`), unlabeled key fixtures (`:48-74`). -- Publishing: 75 `docs/superpowers/**` files built into the public site (no +- Publishing: 120 `docs/superpowers/**` markdown files built into the public site (no `srcExclude`); `docs/guide/onboarding.md` published with internal contacts; `docs/public/CNAME` placeholder; empty `docs/guide/index.md`; nav Guide link bypasses the landing page (`config.mts:61`); From 1adcb25e5350a2a557cccefcd086d0a4a9a810f7 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:40:24 -0700 Subject: [PATCH 11/33] Rework spec after fourth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 300 +++++++++++------- 1 file changed, 192 insertions(+), 108 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 8103f2b24..198ba5ad0 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-20 (round 3; addresses all three pre-implementation reviews) +**Revised:** 2026-08-20 (round 4; addresses all four pre-implementation reviews) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and @@ -20,7 +20,7 @@ Trusted Server's documentation spans four surfaces: the VitePress site (`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and configuration templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track -audit of `main`, hardened by two pre-implementation reviews, found systemic +audit of `main`, hardened by four pre-implementation reviews, found systemic drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference @@ -83,9 +83,11 @@ drift in every surface. The failures fall into six categories: when it is a local-development adapter with no deploy command, and say nothing about the Spin adapter's actual runtime state: it builds its settings from the checked-in `trusted-server.example.toml` - (`adapter-spin/src/app.rs:52`), and a startup failure installs a router - that returns 503 for all traffic while `/health` still returns 200 - (`adapter-spin/src/app.rs:404`). No smoke test proves non-health traffic + (`build_state()` in `adapter-spin/src/app.rs`, line 58 on rc), and a + startup failure installs a router that returns 503 for all traffic + while `/health` still returns 200 (`startup_error_router()`, line 420 + on rc). Unstable Rust files are cited by symbol; line numbers are + hints. No smoke test proves non-health traffic works under `spin up`. CI compiles the Spin artifact; compilation is not evidence of production maturity. Capability differences are also documented nowhere: asset-route dispatch, integration request filters, @@ -192,7 +194,11 @@ under `docs/superpowers/`, legitimately contain every retired term): VitePress builds it. - Sensitive real-world values are removed from source-controlled config, and examples use fictional data (reserved `.example` domains, clearly fake - credentials) per the repo policy in `CLAUDE.md`. + credentials) per the repo policy in `CLAUDE.md`. One narrowly scoped + exception is recognized: the `fastly.toml` `service_id`, whose removal + is an operational migration (open question 1). It is recorded in the + scanner allowlist as owner-approved and time-bounded, with rationale + and a review date, so the goal and the scanner acceptance agree. - CI gates catch documentation regressions: docs build (dead internal links) on PRs, rustdoc build with broken-intra-doc-link denial, doctests actually running, and executable parity checks bound to the reader-facing markdown. @@ -248,11 +254,14 @@ forces one exception: GitHub Pages deploys only on pushes to `main` (`deploy-docs.yml`), so publishing containment merged to rc does not reach the live site until rc merges to main. Therefore: -- The WP1 publishing-containment subset (the `srcExclude` change, the - onboarding move/scrub, and the CNAME resolution) additionally ships as a - minimal separate PR straight to `main` so the exposure closes immediately. - The rc PR carries the same changes; the rc→main merge reconciles to an - identical state. +- The WP1 publishing-containment subset ships as a minimal separate PR + straight to `main` so the exposure closes immediately. Its contents are + exactly: the `srcExclude` change (covering `superpowers/**`, + `internal/**`, `epics/**`, `guide/onboarding.md`, `README.md`, and + `business-use-cases.md`) and the onboarding move/scrub. Nothing else - + neither the CNAME resolution nor the marketing-page disposition blocks + or rides in it. The rc PR carries the same changes; the rc→main merge + reconciles to an identical state. - Everything else lands only in the single rc PR. - CodeQL today analyzes only PRs targeting `main`, so the rc PR carrying new workflows, generators, and scripts would go unanalyzed. Decision: @@ -272,8 +281,9 @@ Smallest package, highest urgency. The containment subset also ships to `main` directly (see Delivery shape). - Add `srcExclude` to `docs/.vitepress/config.mts` covering - `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, and - `README.md`, and move `docs/guide/onboarding.md` to + `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, + `README.md`, and `business-use-cases.md` (per the Non-goals default), + and move `docs/guide/onboarding.md` to `docs/internal/onboarding.md` after scrubbing internal contacts, meeting, and access details. Exclusion from the build is not sufficient on its own: the repository is public, so source-sensitive details are scrubbed even @@ -314,8 +324,12 @@ containment PR to `main` is merged, the live site no longer serves those URLs, and a positive post-deploy smoke passes (site root, the Guide landing page, and one reference page return 200 with expected content); the rollback procedure is documented in the containment PR (owner: the -maintainer driving this refresh; procedure: revert the containment commit -on `main` and re-run the Pages deploy via `workflow_dispatch`); no real +maintainer driving this refresh) and treats the exclusions and scrubbing +as non-rollbackable security invariants: failure-prone cosmetic changes +(CNAME, navigation) live in separate commits, and recovery means +reverting only the causal non-security commit or redeploying a known-good +artifact that retains the exclusion and scrub - never republishing the +excluded material; no real personal emails in tracked config; no internal contacts or access instructions anywhere in the repo; every command file lists the same gates as `CLAUDE.md`. @@ -335,10 +349,10 @@ the continuing control. Token greps establish that retired names are gone; they cannot validate commands, APIs, auth, or behavior, so each "verified" disposition means the page's commands and examples were actually checked against code, and executable fences are governed by the WP8 -snippet manifest: every Rust/shell/TOML/JSON fence in the active public -set gets a checked-in disposition (rust-compile, toml/json-parse, bash -n, -safe smoke test, or manual with a reason and source anchor), and CI fails -on new executable fences with no classification. +snippet manifest: every Rust/shell/TOML/JSON fence in every non-historical +set gets a checked-in disposition (graded modes per WP8, from compile and +typed-validation down to expiring manual waivers), and CI fails on new +executable fences with no classification. - `docs/guide/api-reference.md`: delete `GET /first-party/ad` and `POST /third-party/ad` sections (endpoints do not exist). The full @@ -401,10 +415,11 @@ on new executable fences with no classification. - Retire `docs/guide/integrations/gam.md` and `kargo.md` (delete pages, remove sidebar entries). Neither integration exists; GAM ad serving is already covered factually via GPT/creative-opportunities docs. Before - deletion, inventory inbound links (site-internal grep plus a GitHub search - for the public URLs) and leave a client-side redirect stub for any - previously published URL with known inbound references; `vitepress build` - catches surviving internal links but not bookmarks or external links. + deletion, leave a static tombstone at EVERY previously published route + (successor link, canonical metadata, optional meta refresh) - inbound + discovery cannot find bookmarks or unindexed links, so tombstones are + unconditional, and old-route smoke tests assert they serve; a + client-side stub is not an HTTP redirect and is not claimed to be one. - Retire `FAQ_POC.md`: its headline answer ("NOT ready for use", two-partner Fastly+Equativ POC) is false on every axis. Delete it (git history preserves it); fold any still-true answers into @@ -485,14 +500,26 @@ integrations can skip typed deserialization entirely. `auction_html_comment`, and `auction_html_comment_options`). For the rc `[cache]` section, promote the commented `[[cache.asset_rules]]` examples to a complete worked block covered by the WP8 example harness. -- Field-path inventories: for every nested `Settings` type and all 14 typed - integration config structs, the inventory is derived from the serde - surface itself (a schema-derive or serializer-walk in a test, with an - explicit grammar for dynamic map keys, enum variants, aliases, flattened - fields, and skipped fields), so a newly added field breaks CI until the - inventory, template, and reference are updated - the chain is +- Field-path inventories, exact implementation (decided): a Serde-aware + AST extractor - a dev-only tool (`syn`-based, living with the WP8 + checks, touching no production structs or dependencies) that parses the + config struct definitions and their serde attributes (`rename`, + `alias`, `default`, `flatten`, `skip`, `deserialize_with`, + `deny_unknown_fields`) into the machine inventory. A serializer walk is + explicitly rejected: it cannot see deserialization-only aliases + (`pub_id` in `aps.rs`, `s3_sig_v4` in `settings.rs`), custom + `deserialize_with` shapes, defaulted/`Option` fields, the flattened + `IntegrationSettings` map, or `serde(skip)` implementation fields. What + the AST cannot decide (the accepted shapes of each `deserialize_with`, + dynamic map-key grammars, tagged-enum representations) lives in a + checked companion manifest the extractor requires an entry for, so an + unannotated custom deserializer fails CI. The inventory distinguishes + canonical keys (documented) from accepted-but-deprecated aliases + (listed as aliases, never as primary documentation), and records how + flattened and dynamic-key forms render in the reference. The chain is Rust serde surface to machine inventory to template to generated - markdown, checked in both directions. Reconcile + markdown, checked in both directions: a newly added field breaks CI + until inventory, template, and reference are updated. Reconcile `docs/guide/configuration.md`'s field tables against that inventory. This audits the five existing integration sections (Prebid's reference is already missing valid keys) as well as adding the nine absent ones (`aps`, `datadome`, `didomi`, `sourcepoint`, @@ -655,13 +682,22 @@ path) agree with the published tables. against the help tree, and link to `ts-dev-proxy.md`. - Site usability: enable VitePress `lastUpdated` (the deploy workflow already fetches full history for it) and local search - (`themeConfig.search`), so the 1,600-line configuration reference is - navigable. -- Release identity: add a global banner stating the site documents the - unreleased `main` line, plus a short compatibility statement - (server/config/CLI move together via the blob-envelope contract), and - the rule that versioned documentation is published when a release is - actually tagged. + (`themeConfig.search`). +- Information architecture: today Configuration and CLI sit under a + "Development" sidebar group; restructure navigation into Operator, + Deployment, and Reference groups alongside the developer material, add + a generated section index at the top of the ~2,000-line configuration + reference (preserving URLs and anchors), and include the four reader + journeys (evaluator, local developer, operator, integration author) as + explicit acceptance walks. +- Release identity: add a global banner stating the site is rolling + documentation of the unreleased `main` line, audited against a recorded + SHA. No versioned-docs machinery exists (Pages publishes one `main` + build; the blob envelope is an integrity check, not a schema/version + handshake), so the banner promises none: per-release documentation, if + ever wanted, is a separately designed follow-up. Compatibility guidance + stays factual: upgrade the binary before pushing configs that carry new + fields, per the CHANGELOG rollback notes. - Diagram accessibility: inventory the active public mermaid diagrams and give each a nearby one-paragraph prose equivalent; the inventory with a per-diagram checkbox is part of WP5's recorded acceptance. @@ -677,7 +713,10 @@ Acceptance: every integration ID is documented and nav-reachable (testlight via its reference section in the integration guide); every adapter has a guide or an honest status notice consistent with the support matrix; no real page is orphaned; integration-guide snippets compile; `vitepress build` -passes (dead internal links fail the build). +passes (dead internal links fail the build); local search returns results +for a sampled query set; the rolling-main banner renders on every page; +the mermaid inventory shows a checked prose equivalent for every diagram; +the four reader-journey walks are recorded. ### WP6: Root markdown and crate READMEs @@ -780,7 +819,7 @@ npm-based build script): - `cargo doc --no-deps -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare` - `cargo doc --no-deps -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin` - `cargo doc --no-deps -p trusted-server-adapter-axum` -- `cargo doc --no-deps -p trusted-server-cli -p trusted-server-openrtb-codegen --target ` +- `cargo doc --no-deps -p trusted-server-cli -p trusted-server-openrtb-codegen --target x86_64-unknown-linux-gnu` (CI; locally substitute the host triple, e.g. `aarch64-apple-darwin`) Acceptance: every item on the worklist above is complete; the rustdoc command matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the @@ -790,8 +829,16 @@ lint scoped to those files. ### WP8: Enforcement -Prevent recurrence. Two layers: build gates (links, rustdoc, doctests) and -semantic parity checks. Crucially, the parity checks are bound to the +Prevent recurrence. Delivered in two stages so content work can depend on +it: WP8a (scaffolding) lands FIRST, immediately after WP1 - the AST +extractor and config inventory, the example-config harness, the snippet +manifest tooling, the capability record, the route/CLI golden tooling, and +the generated-region generator - so WP3-WP5 write against working +generators instead of retrofitting them at the end; WP8b (gate +activation: wiring everything into CI as blocking checks, workflow edits, +Dependabot, CodeQL, link checks) lands last so gates turn green on the +same PR. Two layers: build gates (links, rustdoc, doctests) and semantic +parity checks. Crucially, the parity checks are bound to the reader-facing markdown, not only to snapshots a contributor could update while leaving the prose stale: the reference tables in `api-reference.md`, `cli.md`, `configuration.md`, and @@ -804,14 +851,19 @@ Build gates: - Docs site: add `npm run build` to the `format-docs` job in `.github/workflows/format.yml` so dead internal links fail PRs instead of - the post-merge deploy. External links are out of the PR gate; the decided policy is an - allowlisted scheduled link-check workflow (weekly), failing into an - issue rather than blocking PRs. Normalize every `setup-node` cache key across all workflows to the + the post-merge deploy. External links are out of the PR gate; the decided policy is a weekly + scheduled link-check workflow with concrete mechanics: a pinned checker + version, `issues: write` scoped to that job only, deduplication against + the existing open issue, auto-close on recovery, a named owner (the + maintainer driving this refresh) with a best-effort SLA, and a + fixture-based test of the reporter before it merges. Normalize every `setup-node` cache key across all workflows to the relevant `package-lock.json` (today one keys on `package.json`, another on the lockfile). Add `.tool-versions` to `deploy-docs.yml` trigger paths (the site renders versions from it, so - version-only bumps must republish). If release-branch PRs are expected to - get CodeQL analysis, add `rc/*` to `codeql.yml` branch triggers. + version-only bumps must republish). Add `rc/*` to `codeql.yml` PR branch + triggers (decided in Delivery shape; not conditional). Whether `push` + events on release branches should also be analyzed is a separate + maintainer choice recorded in open question 8. - Rustdoc: add a CI step running the WP7 command matrix with `RUSTDOCFLAGS="-D warnings"` (this denies `rustdoc::broken_intra_doc_links` by default), with pinned Node per the @@ -840,12 +892,16 @@ Semantic parity checks (each catches a class of drift this audit found): (a) applies a deterministic substitution of the known placeholders and deliberately invalid disabled-block values (e.g. empty IDs) with synthetic valid values, and asserts the substituted template fully - parses and finalizes; (b) extracts every commented example block via - explicit begin/end markers and, for typed integration blocks, - deserializes each directly into its config struct with ignored-key - detection (several structs, including Permutive's, do not reject - unknown fields), runs its `Validate::validate`, and exercises the - integration-specific deploy/startup checks from `core/src/config.rs`; + parses and finalizes; (b) enumerates every `[integrations.*]` table in + the template - commented or uncommented, enabled or disabled (runtime + typed parsing deliberately skips explicitly disabled integrations, so + full-template finalization alone would false-green on them) - plus + every marker-delimited commented example block, and deserializes each + directly into its config struct with ignored-key detection (several + structs, including Permutive's, do not reject unknown fields), runs its + `Validate::validate`, and exercises the integration-specific + deploy/startup checks from `core/src/config.rs`, all independent of + `enabled`; and (c) separately asserts the distributed template still contains the placeholder markers, so a template that would deploy without customization fails CI. @@ -867,21 +923,39 @@ Semantic parity checks (each catches a class of drift this audit found): merged into a platform-annotated union (including the dependency-owned `edgezero-cli` lifecycle flags at the locked version) that feeds the cli.md generated region. -- Integration parity: tests over the three inventories (registry - `builders()`, auction `provider_builders()`, JS module registry including - `JS_ALWAYS`) that together feed the integrations-overview generated - region; capabilities not exposed by registry metadata (e.g. APS's - conditional proxy) are asserted by the per-integration tests that own - them. +- Integration parity: a checked capability record, keyed by stable + integration/provider ID and including conditional expressions (e.g. + APS's proxy conditional on rendering mode), is the single source that + both the parity tests assert against the three inventories (registry + `builders()`, auction `provider_builders()`, JS module registry + including `JS_ALWAYS`) and the integrations-overview generated region + renders from. This record exists because no single registry API is + sufficient: `IntegrationMetadata` omits HTML post-processors and JS + loading modes, and `provider_builders()` is a private list of bare + function pointers without stable IDs. - Config parity: the field-path inventories from WP3 feed the configuration.md field tables' generated regions. - Snippet manifest: a checked-in manifest classifying every executable - fence in the active public set (rust-compile, toml/json-parse, - `bash -n`, safe smoke test, or manual with reason and source anchor); - CI runs the classified checks and fails on unclassified new fences. -- Domain scanner: a deterministic scan of all active sets for - non-`.example` domains and credential-shaped strings, allowlist-aware - (the WP2 vendor allowlist). + fence in every non-historical set (active public, active repo, and + active maintained internal - root READMEs, TESTING.md, agent and + command files included). Modes are graded to actually catch the + failures this audit found: shell fences distinguish syntax-only + (`bash -n`) from command/flag-existence and help/dry-run checks, which + are required for operator instructions; config fences use typed/schema + validation via the WP3 inventory, not bare TOML/JSON parsing; Rust + fences compile. Manual waivers are not an open escape hatch: each + carries owner, reason, expiry/review date, and source anchor, and CI + fails on expired waivers and on unclassified new fences. +- Domain/credential scanner: a deterministic scan for secrets, PII, + customer domains, and customer identifiers over ALL tracked source + files - the historical set is exempt from retired-term greps, not from + privacy scanning (archived docs already carry publisher-specific + identifiers, e.g. the 2026-03-24 publisher-ID audit). Allowlist-aware + (the WP2 vendor allowlist, plus reviewed historical exceptions), with + negative fixtures proving it fails on planted values. Anything the scan + finds is scrubbed from the current tree; whether a finding warrants + credential rotation or history rewriting is escalated to the + maintainer as a per-finding decision, recorded in the audit inventory. - Repo inventory: a CI script checking workspace members each have a README, every active public page is reachable from the sidebar or an explicit orphan allowlist, and the CI gate list in `CLAUDE.md` names the @@ -894,25 +968,32 @@ Semantic parity checks (each catches a class of drift this audit found): mandatory (a PR-description grep count provides no recurrence protection); the plugin is already installed with zero rules enabled. -Acceptance: a PR introducing a dead internal docs link, a broken intra-doc -link, a failing doctest, an invalid example-config block, or a -route/CLI/config/integration inventory change without the matching -regenerated markdown region fails CI; regenerating all generated regions at -the final PR HEAD produces no diff. +Acceptance: every gate above is exercised by at least one negative +fixture proving it fails on the regression it exists to catch: a dead +internal docs link, a broken intra-doc link, a failing doctest, an invalid +or unknown-keyed example-config block (including a disabled integration +table), a planted non-allowlisted domain or credential-shaped string, an +unclassified or expired-waiver snippet fence, a missing JSDoc block in a +WP7-scoped file, a route/CLI/config/integration inventory change without +the matching regenerated markdown region (including a macOS-only CLI +divergence), and a removed manual-ownership marker. Regenerating all +generated regions and both CLI goldens at the final PR HEAD produces no +diff; the scheduled link reporter's fixture test passes. ## Sequencing and estimate -| Order | Package | Size | Depends on | -| ----- | ------------------------------------------- | ---- | ---------------------------------------------------------------- | -| 0 | WP1 containment subset → separate `main` PR | XS | - | -| 1 | WP1 hygiene (full, in rc PR) | S | - | -| 2 | WP2 truth pass | M | - | -| 3 | WP3 config reference | M | - | -| 4 | WP4 API reference | M | WP2 | -| 5 | WP5 new pages + nav | L | WP2 (nav), WP3 (links) | -| 6 | WP6 root + crate READMEs | M | - | -| 7 | WP7 in-code docs | M | - | -| 8 | WP8 enforcement | L | WP3-WP7 (generated regions, goldens, and gates must start green) | +| Order | Package | Size | Depends on | +| ----- | ------------------------------------------- | ---- | ----------------------------------------------------------- | +| 0 | WP1 containment subset → separate `main` PR | XS | - | +| 1 | WP1 hygiene (full, in rc PR) | S | - | +| 2 | WP8a enforcement scaffolding | M | - (extractor, harness, manifests, goldens, generators) | +| 3 | WP2 truth pass | M | WP8a (snippet manifest) | +| 4 | WP3 config reference | M | WP8a (inventory + harness) | +| 5 | WP4 API reference | M | WP2, WP8a (route inventory + regions) | +| 6 | WP5 new pages + nav | L | WP2 (nav), WP3 (links), WP8a (CLI union, capability record) | +| 7 | WP6 root + crate READMEs | M | - | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 (all gates must start green) | Commits land in this order within the single rc PR, after the spec commit; WP8 comes last so the new CI gates turn green on the same PR. @@ -954,16 +1035,16 @@ where stated. 1. `fastly.toml` `service_id` (ops-owned follow-up; blocks nothing here): needs an owner, a replacement mechanism, a non-production deployment test, and rollback instructions. -2. `docs/public/CNAME` (blocks WP1 containment PR): delete (recommended, - matches the `/trusted-server` base path) or configure a real custom - domain? +2. `docs/public/CNAME` (blocks only its own follow-up commit, never the + containment PR): delete (recommended, matches the `/trusted-server` + base path) or configure a real custom domain? 3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages (blocks their WP2 deletions): this spec recommends deletion with an inbound-link inventory and redirect stubs where referenced; confirm. -4. `docs/business-use-cases.md` (blocks the WP1/WP5 exclusion): default is - exclusion from the built site until quantitative claims carry dated - evidence and unshipped features are labeled; the alternative is an - evidence-based rewrite in this pass. Confirm the default. +4. `docs/business-use-cases.md` (does not block containment - the default + exclusion ships in it): default is exclusion from the built site until + quantitative claims carry dated evidence and unshipped features are + labeled; the alternative is an evidence-based rewrite in this pass. 5. CHANGELOG release cut (blocks nothing; mechanical repairs are in WP2 either way): should a release be cut to drain the seven breaking entries in `[Unreleased]`? If no release is cut, the deterministic WP2 @@ -976,6 +1057,9 @@ where stated. "Delivery shape": one rc PR for everything, plus the minimal publishing-containment PR to `main` that the Pages deploy trigger makes necessary. +8. CodeQL `push` coverage for release branches (blocks nothing; PR-trigger + coverage is already decided): should `push` events on `rc/*` also be + analyzed? ## Follow-up issues to file (code, not docs) @@ -1007,27 +1091,27 @@ files, not from this table alone. WP8's route snapshots also record response semantics/status for guarded and unsupported routes, not only method and path. -| Route | Methods | Availability | Handler | -| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `/health` | GET | all except Cloudflare | adapter entry points | -| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | -| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | -| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | -| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | -| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | registered on all four adapters; functional only on Fastly (EC identity KV store), others return not-supported (`adapter-axum/src/app.rs:330`, `adapter-cloudflare/src/app.rs:500`, `adapter-spin/src/app.rs:800`, rc) | `core/src/ec/admin.rs` | -| `/_ts/admin/eids` | GET | all four adapters (request-inspection handler); Basic-auth gated | `core/src/ec/admin.rs`; registrations in `adapter-axum/src/app.rs:342`, `adapter-cloudflare/src/app.rs:506`, `adapter-spin/src/app.rs:533` (rc) | -| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | -| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | -| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | -| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | -| `/auction` | POST | all | `core/src/auction/endpoints.rs` | -| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | -| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | -| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | -| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | -| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | -| asset route prefixes | GET, HEAD | Fastly only today; operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request`, dispatched from `adapter-fastly/src/app.rs` | -| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | +| Route | Methods | Availability | Handler | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `/health` | GET | all except Cloudflare | adapter entry points | +| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | +| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | +| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | +| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | +| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | registered on all four adapters; functional only on Fastly (EC identity KV store), others return not-supported (`adapter-axum/src/app.rs:330`, `adapter-cloudflare/src/app.rs:500`, `adapter-spin/src/app.rs:800`, rc) | `core/src/ec/admin.rs` | +| `/_ts/admin/eids` | GET | all four adapters (request-inspection handler); Basic-auth gated | `core/src/ec/admin.rs`; registrations in `adapter-axum/src/app.rs:342`, `adapter-cloudflare/src/app.rs:506`, `adapter-spin/src/app.rs:802` (rc; lines are hints, cite `admin_eids_handler` registrations) | +| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | +| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | +| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | +| `/auction` | POST | all | `core/src/auction/endpoints.rs` | +| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | +| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | +| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | +| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | +| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | +| asset route prefixes | GET, HEAD | Fastly only today; operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request`, dispatched from `adapter-fastly/src/app.rs` | +| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | Per-adapter pipelines: Fastly runs pre-route integration request filters (DataDome), then dispatches tsjs, integration proxy routes, asset routes, From aa5327da1d9221b0938ac98759c65c64a28674b5 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 21 Aug 2026 00:23:33 -0700 Subject: [PATCH 12/33] Rework spec after fifth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 248 ++++++++++++------ 1 file changed, 163 insertions(+), 85 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 198ba5ad0..a7c735892 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-20 (round 4; addresses all four pre-implementation reviews) +**Revised:** 2026-08-21 (round 5; addresses all five pre-implementation reviews) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and @@ -20,7 +20,7 @@ Trusted Server's documentation spans four surfaces: the VitePress site (`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and configuration templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track -audit of `main`, hardened by four pre-implementation reviews, found systemic +audit of `main`, hardened by five pre-implementation reviews, found systemic drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference @@ -157,7 +157,8 @@ under `docs/superpowers/`, legitimately contain every retired term): - **Active public set:** everything VitePress builds, i.e. `docs/**` excluding the WP1 `srcExclude` list. This is what site visitors see. - **Active repo set:** root markdown (`README.md`, `CONTRIBUTING.md`, - `TESTING.md`, `CHANGELOG.md`, `ProjectGovernance.md`, `AGENTS.md`, + `TESTING.md`, `CHANGELOG.md`, `FAQ_POC.md` until it is actually + retired, `ProjectGovernance.md`, `AGENTS.md`, `CLAUDE.md`), crate READMEs, config templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`), and `.claude/commands/*.md`. @@ -224,7 +225,9 @@ under `docs/superpowers/`, legitimately contain every retired term): WP5's no-orphan acceptance (the page presents planned headless-browser malvertising detection as shipped while the roadmap calls it planned). Open question 4 records the alternative of an evidence-based rewrite in - this pass. `docs/roadmap.md` gets a + this pass. While it remains in the repository unpublished, the source + file carries a prominent top banner stating it is unverified and + excluded from the site. `docs/roadmap.md` gets a factual status pass (shipped/active/deferred labels, correct crate names), not a strategy rewrite. - No release-management policy changes. The CHANGELOG's 10-month untagged @@ -258,10 +261,15 @@ the live site until rc merges to main. Therefore: straight to `main` so the exposure closes immediately. Its contents are exactly: the `srcExclude` change (covering `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, `README.md`, and - `business-use-cases.md`) and the onboarding move/scrub. Nothing else - - neither the CNAME resolution nor the marketing-page disposition blocks - or rides in it. The rc PR carries the same changes; the rc→main merge - reconciles to an identical state. + `business-use-cases.md`), the onboarding move/scrub, the filled Guide + landing page (`docs/guide/index.md` is empty today, and the post-deploy + smoke asserts it), and the navigation edits that removing pages forces: + retarget the top-nav Guide link at the landing page and drop the + Business Value nav item. Removing every link to an excluded source is a + containment invariant - a build that navigates to excluded pages fails. + Nothing else - neither the CNAME resolution nor the marketing-page + content disposition blocks or rides in it. The rc PR carries the same + changes; the rc→main merge reconciles to an identical state. - Everything else lands only in the single rc PR. - CodeQL today analyzes only PRs targeting `main`, so the rc PR carrying new workflows, generators, and scripts would go unanalyzed. Decision: @@ -349,10 +357,10 @@ the continuing control. Token greps establish that retired names are gone; they cannot validate commands, APIs, auth, or behavior, so each "verified" disposition means the page's commands and examples were actually checked against code, and executable fences are governed by the WP8 -snippet manifest: every Rust/shell/TOML/JSON fence in every non-historical -set gets a checked-in disposition (graded modes per WP8, from compile and +snippet manifest: every nonempty fence in every non-historical set gets a +checked-in disposition (graded modes per WP8, from compile and typed-validation down to expiring manual waivers), and CI fails on new -executable fences with no classification. +fences with no classification. - `docs/guide/api-reference.md`: delete `GET /first-party/ad` and `POST /third-party/ad` sections (endpoints do not exist). The full @@ -382,10 +390,14 @@ executable fences with no classification. values; sweep both sets for other real domains, customer names, or credential-shaped strings. Reviewed canonical vendor endpoints (e.g. real GPT/DataDome CDN hosts an integration genuinely proxies) stay, everything - else becomes fictional. Legitimate vendor endpoints are recorded in a - checked-in allowlist (category, rationale, owner) that the WP8 scanner - reads; `CLAUDE.md`'s example-domains-only policy gains a sentence - describing this vendor-endpoint exception (WP6 makes that edit). + else becomes fictional. All exceptions live in ONE typed allowlist + schema - entry categories: vendor URL, exact-path/hash-pinned fake + credential fixture (e.g. the `fastly.toml` local JWKS material), + historical example, service ID - each entry carrying owner, rationale, + and expiry/review date; the scanner rejects expired or orphaned + entries. The scanner and allowlist scaffolding land in WP8a so this + pass can use them. `CLAUDE.md`'s example-domains-only policy gains a + sentence describing the exception model (WP6 makes that edit). - Re-verify the pages touched by the final six rc commits: `docs/guide/integrations/datadome.md` (the staging requirement was removed from protection behavior in the same commit that rewrote the @@ -412,14 +424,15 @@ executable fences with no classification. development only. Fix `docs/roadmap.md:21-22` old crate names (`/common/`, `/cloudflare/`) and give roadmap line items shipped/active/deferred status labels. -- Retire `docs/guide/integrations/gam.md` and `kargo.md` (delete pages, - remove sidebar entries). Neither integration exists; GAM ad serving is - already covered factually via GPT/creative-opportunities docs. Before - deletion, leave a static tombstone at EVERY previously published route - (successor link, canonical metadata, optional meta refresh) - inbound - discovery cannot find bookmarks or unindexed links, so tombstones are - unconditional, and old-route smoke tests assert they serve; a - client-side stub is not an HTTP redirect and is not claimed to be one. +- Retire `docs/guide/integrations/gam.md` and `kargo.md`: the markdown + files are REPLACED with tombstone content (successor link, canonical + metadata, optional meta refresh) so every previously published route + keeps serving, unconditionally - inbound discovery cannot find + bookmarks or unindexed links; a client-side stub is not an HTTP + redirect and is not claimed to be one. Sidebar entries are removed; + old-route smoke tests assert the tombstones serve. Neither integration + exists; GAM ad serving is already covered factually via + GPT/creative-opportunities docs. - Retire `FAQ_POC.md`: its headline answer ("NOT ready for use", two-partner Fastly+Equativ POC) is false on every axis. Delete it (git history preserves it); fold any still-true answers into @@ -501,11 +514,18 @@ integrations can skip typed deserialization entirely. `[cache]` section, promote the commented `[[cache.asset_rules]]` examples to a complete worked block covered by the WP8 example harness. - Field-path inventories, exact implementation (decided): a Serde-aware - AST extractor - a dev-only tool (`syn`-based, living with the WP8 - checks, touching no production structs or dependencies) that parses the - config struct definitions and their serde attributes (`rename`, - `alias`, `default`, `flatten`, `skip`, `deserialize_with`, - `deny_unknown_fields`) into the machine inventory. A serializer walk is + AST extractor - a new dev-only tool crate, `tools/docs-parity` + (host-target, outside workspace default-members, the only place the new + `syn` dependency lives, so production crates and their dependency + closure are untouched), run as + `cargo run -p docs-parity -- check|generate` with a JSON output schema + checked into the tool. It parses the config struct definitions and + their serde attributes - field-level (`rename`, `alias`, `default`, + `flatten`, `skip`, `skip_deserializing`, `deserialize_with`) AND + container/variant-level (`rename_all`, `deny_unknown_fields`, `tag`, + `content`, `untagged`), since renamed/tagged enums change accepted + values - into the machine inventory, and FAILS CLOSED on any + shape-changing serde attribute it does not recognize. A serializer walk is explicitly rejected: it cannot see deserialization-only aliases (`pub_id` in `aps.rs`, `s3_sig_v4` in `settings.rs`), custom `deserialize_with` shapes, defaulted/`Option` fields, the flattened @@ -691,8 +711,12 @@ path) agree with the published tables. journeys (evaluator, local developer, operator, integration author) as explicit acceptance walks. - Release identity: add a global banner stating the site is rolling - documentation of the unreleased `main` line, audited against a recorded - SHA. No versioned-docs machinery exists (Pages publishes one `main` + documentation of the unreleased `main` line. Provenance is mechanical: + the Pages build injects `GITHUB_SHA` (local builds use + `git rev-parse HEAD`), the banner links the exact build SHA, and the + deploy smoke asserts the built HTML contains it; the content-audit + baseline SHA (recorded in the audit inventory) is a distinct value and + labeled as such. No versioned-docs machinery exists (Pages publishes one `main` build; the blob envelope is an integrity check, not a schema/version handshake), so the banner promises none: per-release documentation, if ever wanted, is a separately designed follow-up. Compatibility guidance @@ -749,6 +773,13 @@ serve`; link the deployment guides; refresh the doc-site link table. - `.claude/skills/**`: audit the operator-facing skills (including the Fastly deployment skill) against current commands and config, same truth standard as the command files. +- Human-facing workflow comments join the maintained truth set. Two + known-false comments are repaired: `.github/workflows/test.yml` (Spin + release-build comment claims environment overrides make the artifact + boot with usable settings, but the Spin adapter loads the checked-in + example TOML directly; and the `test-cli` comment claims a workspace + default target that `.cargo/config.toml` does not set - the mechanism + is `Cargo.toml` `default-members`). - `.claude/agents/**`: audit every agent instruction file; they currently describe a three-crate Fastly-only workspace, cite the nonexistent `RequestWrapper` trait (`code-architect.md:11`, `repo-explorer.md:12`), @@ -766,7 +797,8 @@ serve`; link the deployment guides; refresh the doc-site link table. - Add `readme = "README.md"` to each crate's `Cargo.toml` once the READMEs exist. -Acceptance: `find crates -maxdepth 2 -name README.md` returns one per crate; +Acceptance: every workspace package reported by `cargo metadata` has a +README (the metadata-based WP8 check is authoritative); every pre-existing root/crate/skill document has a recorded verified/rewritten/retired disposition; README quick start commands all run against the PR HEAD. @@ -775,12 +807,14 @@ against the PR HEAD. Targeted, not exhaustive. The worklist below is the acceptance scope. -1. `core/src/lib.rs` module index: currently lists 12 of 40+ public modules +1. `core/src/lib.rs` module index: currently lists 12 of 37 public modules and links a `test_support` module; make it complete and grouped (identity, consent, auction, HTML pipeline, proxy, platform, config). -2. `core/src/platform/` (2/8 files documented): module docs for `traits.rs`, - `types.rs`, `kv.rs`, `http.rs`, `error.rs`. This is the cross-adapter - contract and the highest-value rustdoc gap in the repo. +2. `core/src/platform/` (on rc: 4 of 10 files carry `//!` - `mod.rs`, + `image_optimizer.rs`, `template_assembly.rs`, `template_cache.rs`): + module docs for `traits.rs`, `types.rs`, `kv.rs`, `http.rs`, + `error.rs`; the test-only module stays excluded. This is the + cross-adapter contract and the highest-value rustdoc gap in the repo. 3. Crate-level `//!` headers for the crates missing them: `adapter-fastly` (`main.rs`), `adapter-cloudflare`, `trusted-server-js`, and `trusted-server-cli` (whose `lib.rs` already contains the right prose @@ -892,16 +926,20 @@ Semantic parity checks (each catches a class of drift this audit found): (a) applies a deterministic substitution of the known placeholders and deliberately invalid disabled-block values (e.g. empty IDs) with synthetic valid values, and asserts the substituted template fully - parses and finalizes; (b) enumerates every `[integrations.*]` table in - the template - commented or uncommented, enabled or disabled (runtime - typed parsing deliberately skips explicitly disabled integrations, so - full-template finalization alone would false-green on them) - plus - every marker-delimited commented example block, and deserializes each - directly into its config struct with ignored-key detection (several - structs, including Permutive's, do not reject unknown fields), runs its - `Validate::validate`, and exercises the integration-specific - deploy/startup checks from `core/src/config.rs`, all independent of - `enabled`; + parses and finalizes; (b) groups the template's `[integrations.*]` + tables by their first-segment integration ID (nested tables such as + `[integrations.prebid.bundle]` are part of their parent's subtree, not + standalone configs), and for each of the 14 IDs deserializes the + complete subtree into its typed config struct with ignored-key + detection (several structs, including Permutive's, do not reject + unknown fields) and runs `Validate::validate`; because the runtime + path (`Settings::get_typed` and the deploy checks that delegate + through it) deliberately returns `None` for disabled integrations, the + harness then constructs an isolated `Settings` fixture per integration + with that integration forced enabled (14 named fixtures, with any + inter-integration dependencies stated explicitly) and runs the real + deploy/startup validation from `core/src/config.rs` against it; the + same treatment applies to marker-delimited commented example blocks; and (c) separately asserts the distributed template still contains the placeholder markers, so a template that would deploy without customization fails CI. @@ -922,28 +960,42 @@ Semantic parity checks (each catches a class of drift this audit found): gated to macOS, and CI already runs the CLI suite on both hosts), merged into a platform-annotated union (including the dependency-owned `edgezero-cli` lifecycle flags at the locked version) that feeds the - cli.md generated region. + cli.md generated region. The generated projection is defined: command + and flag names, argument shapes, and defaults are generated verbatim; + description text passes through the same retired/internal-term gate as + prose, and descriptions that fail it (the vendored help currently + leaks internal spec references like "5.4" and "spec 3.3 Model A") are + replaced from a checked description-override table until the upstream + fix lands, so known-internal strings are never published. - Integration parity: a checked capability record, keyed by stable - integration/provider ID and including conditional expressions (e.g. - APS's proxy conditional on rendering mode), is the single source that - both the parity tests assert against the three inventories (registry - `builders()`, auction `provider_builders()`, JS module registry - including `JS_ALWAYS`) and the integrations-overview generated region - renders from. This record exists because no single registry API is + integration/provider ID, is the single source that both the parity + tests assert against the three inventories (registry `builders()`, + auction `provider_builders()`, JS module registry including + `JS_ALWAYS`) and the integrations-overview generated region renders + from. Conditions (APS's proxy conditional on rendering mode, DataDome's + request filter conditional on protection) use a small typed grammar - + capability, config predicate - not prose, and a fixture matrix + evaluates every condition in both states; the parity test requires set + equality against each inventory, not subset containment. This record exists because no single registry API is sufficient: `IntegrationMetadata` omits HTML post-processors and JS loading modes, and `provider_builders()` is a private list of bare function pointers without stable IDs. - Config parity: the field-path inventories from WP3 feed the configuration.md field tables' generated regions. -- Snippet manifest: a checked-in manifest classifying every executable +- Snippet manifest: a checked-in manifest classifying every nonempty fence in every non-historical set (active public, active repo, and active maintained internal - root READMEs, TESTING.md, agent and - command files included). Modes are graded to actually catch the - failures this audit found: shell fences distinguish syntax-only + command files included), all languages, not only the four the audit + started from (the active public pages alone carry ~30 HTML, ~13 HTTP, + ~8 JS/TS, ~5 CSS, and 1 YAML fence). Modes are graded to actually catch + the failures this audit found: shell fences distinguish syntax-only (`bash -n`) from command/flag-existence and help/dry-run checks, which are required for operator instructions; config fences use typed/schema validation via the WP3 inventory, not bare TOML/JSON parsing; Rust - fences compile. Manual waivers are not an open escape hatch: each + fences compile; JS/TS and YAML fences parse (typecheck where cheap); + HTTP fences are checked against the route inventory (method and path + must exist); HTML/CSS fences get structural checks or explicit manual + waivers. Manual waivers are not an open escape hatch: each carries owner, reason, expiry/review date, and source anchor, and CI fails on expired waivers and on unclassified new fences. - Domain/credential scanner: a deterministic scan for secrets, PII, @@ -957,18 +1009,26 @@ Semantic parity checks (each catches a class of drift this audit found): credential rotation or history rewriting is escalated to the maintainer as a per-finding decision, recorded in the audit inventory. - Repo inventory: a CI script checking workspace members each have a - README, every active public page is reachable from the sidebar or an - explicit orphan allowlist, and the CI gate list in `CLAUDE.md` names the - jobs that actually exist in the workflows. + README (via `cargo metadata`, the authoritative package list, not a + `find` over directories) and every active public page is reachable from + the sidebar or an explicit orphan allowlist. +- Gate manifest: one checked manifest of the canonical CI gates, compared + against the workflows AND against every human-facing copy - `CLAUDE.md`, + `AGENTS.md`, `.claude/commands/*.md`, and the PR template - so WP8b's + own gate additions cannot silently invalidate WP1's alignment of those + same files at the final commit. - `CLAUDE.md` CI gates section: update to the real gate list (it omits ESLint, the CLI/codegen clippy jobs, the bench compile check, the release - WASM builds, and the entire integration-tests workflow) so agents and the - slash commands stay aligned with reality. + WASM builds, and the entire integration-tests workflow), regenerated + from the gate manifest above together with `AGENTS.md`, the command + files, and the PR template, so all four surfaces change in the same + commit. - A scoped `jsdoc/*` ESLint rule set over the WP7 TypeScript files is mandatory (a PR-description grep count provides no recurrence protection); the plugin is already installed with zero rules enabled. -Acceptance: every gate above is exercised by at least one negative +Acceptance, in two explicitly separated classes. Executable regression +fixtures - every runtime gate is exercised by at least one negative fixture proving it fails on the regression it exists to catch: a dead internal docs link, a broken intra-doc link, a failing doctest, an invalid or unknown-keyed example-config block (including a disabled integration @@ -976,9 +1036,14 @@ table), a planted non-allowlisted domain or credential-shaped string, an unclassified or expired-waiver snippet fence, a missing JSDoc block in a WP7-scoped file, a route/CLI/config/integration inventory change without the matching regenerated markdown region (including a macOS-only CLI -divergence), and a removed manual-ownership marker. Regenerating all -generated regions and both CLI goldens at the final PR HEAD produces no -diff; the scheduled link reporter's fixture test passes. +divergence), a missing crate README or unlisted orphan page, a gate-list +mismatch in any of the four human-facing surfaces, and a removed +manual-ownership marker. Static configuration assertions - checked once +in review with the evidence linked in the PR description, not fixtures: +CodeQL branch triggers, normalized cache keys, Dependabot roots, pinned +Wrangler/checker versions. Regenerating all generated regions and both +CLI goldens at the final PR HEAD produces no diff; the scheduled link +reporter's fixture test passes. ## Sequencing and estimate @@ -996,7 +1061,8 @@ diff; the scheduled link reporter's fixture test passes. | 9 | WP8b gate activation | M | WP2-WP7 (all gates must start green) | Commits land in this order within the single rc PR, after the spec commit; -WP8 comes last so the new CI gates turn green on the same PR. +WP8b comes last so the new CI gates turn green on the same PR (WP8a is +deliberately early). ## Verification @@ -1032,25 +1098,35 @@ Owner for all: the repo maintainer driving this refresh. Each question blocks the named package; none blocks starting WP2-WP7 content work except where stated. -1. `fastly.toml` `service_id` (ops-owned follow-up; blocks nothing here): - needs an owner, a replacement mechanism, a non-production deployment - test, and rollback instructions. +1. `fastly.toml` `service_id` (ops-owned follow-up): the allowlist entry + that lets the scanner pass requires an owner and review date up front, + so naming that owner blocks WP8a's scanner activation (not content + work); the migration itself (replacement mechanism, non-production + deployment test, rollback instructions) blocks nothing here. 2. `docs/public/CNAME` (blocks only its own follow-up commit, never the - containment PR): delete (recommended, matches the `/trusted-server` - base path) or configure a real custom domain? + containment PR). Both branches are specified: delete (recommended, + matches the `/trusted-server` base path; smoke re-runs against project + URLs), or configure a real custom domain, which requires `base: '/'`, + Pages custom-domain + DNS + TLS configuration, and canonical-URL and + asset-URL smoke tests before it ships. 3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages (blocks their WP2 - deletions): this spec recommends deletion with an inbound-link inventory - and redirect stubs where referenced; confirm. + retirements): this spec recommends retiring them, with the gam/kargo + routes unconditionally preserved as tombstones. If deletion of + `FAQ_POC.md` is rejected, the defined fallback is archival under the + historical tree or a factual rewrite - it does not silently stay; it + remains in the active repo set until one of those happens. 4. `docs/business-use-cases.md` (does not block containment - the default exclusion ships in it): default is exclusion from the built site until quantitative claims carry dated evidence and unshipped features are labeled; the alternative is an evidence-based rewrite in this pass. -5. CHANGELOG release cut (blocks nothing; mechanical repairs are in WP2 - either way): should a release be cut to drain the seven breaking - entries in `[Unreleased]`? If no release is cut, the deterministic WP2 - edit is: keep the `[1.2.0]` section with an explicit "(tag v1.2.0 was - never published)" annotation, repoint the link references to - resolvable compares, and leave entries untouched. +5. CHANGELOG (blocks nothing): release management stays out of scope + entirely - cutting a release to drain the seven breaking `[Unreleased]` + entries is a maintainer decision outside this project. The + deterministic WP2 edit assumes no release: keep the `[1.2.0]` section + with an explicit "(tag v1.2.0 was never published)" annotation, repoint + the link references to resolvable compares, and leave entries + untouched. If a release lands externally before this PR merges, the + branch rebases and re-audits rather than absorbing release work. 6. Governance ownership (blocks the WP6 governance edit only): who owns naming maintainers/CODEOWNERS and the meeting-minutes commitment? 7. Delivery shape (blocks starting implementation): confirm the shape in @@ -1064,9 +1140,10 @@ where stated. ## Follow-up issues to file (code, not docs) - Spin adapter builds runtime settings from the checked-in - `trusted-server.example.toml` (`adapter-spin/src/app.rs:52`) and serves a - blanket 503 on startup failure while `/health` returns 200 - (`adapter-spin/src/app.rs:404`). Blocking for the WP5 Spin deployment + `trusted-server.example.toml` (`build_state()` in + `adapter-spin/src/app.rs`) and serves a blanket 503 on startup failure + while `/health` returns 200 (`startup_error_router()`). Blocking for + the WP5 Spin deployment guide; until fixed, docs label Spin experimental. A `spin up` smoke test proving non-health traffic belongs to the fix's acceptance criteria. - Vendored `edgezero-cli` help text leaks internal spec references @@ -1245,9 +1322,10 @@ baseline and re-verified on rc/202608 where marked. - Adapter support contradictions: `docs/index.md:27`, `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38` vs `docs/guide/architecture.md:154-159`; Axum described as a deployment - target; Spin described as production-capable despite - `adapter-spin/src/app.rs:52` (settings from the checked-in example toml) - and `:404` (blanket 503 on startup failure); asset routes, request + target; Spin described as production-capable despite `build_state()` + loading the checked-in example toml and `startup_error_router()` + serving a blanket 503 on startup failure (`adapter-spin/src/app.rs`); + asset routes, request filters, image optimizer, and Tinybird emission are Fastly-only but documented as generic. - Aspirational sidebar pages: `docs/guide/integrations/gam.md` (no such From 5a324d275b38b2ad4828c69231667a6cca12a538 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 21 Aug 2026 00:58:02 -0700 Subject: [PATCH 13/33] Rework spec after sixth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 175 ++++++++++++++---- 1 file changed, 135 insertions(+), 40 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index a7c735892..09f132834 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-21 (round 5; addresses all five pre-implementation reviews) +**Revised:** 2026-08-21 (round 6; addresses all six pre-implementation reviews) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and @@ -20,7 +20,7 @@ Trusted Server's documentation spans four surfaces: the VitePress site (`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and configuration templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track -audit of `main`, hardened by five pre-implementation reviews, found systemic +audit of `main`, hardened by six pre-implementation reviews, found systemic drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference @@ -165,10 +165,21 @@ under `docs/superpowers/`, legitimately contain every retired term): - **Active maintained internal set:** documents that are neither public-site pages nor historical artifacts but are still maintained and must pass the truth standard: `docs/README.md`, `docs/internal/**` (including the moved - onboarding page), `scripts/README.md` and `tinybird/README.md` once - created, `.claude/skills/**` (operator-facing skills such as the + onboarding page), `docs/epics/**` (classified as maintained internal + records - WP2 edits one, so they are not historical), + `docs/business-use-cases.md` while it remains excluded-but-tracked, + `scripts/README.md` and `tinybird/README.md` once + created, the human-facing comments of `.github/workflows/**` and + `.github/actions/**` (including the integration-test setup action) and + of `scripts/*.sh` usage headers, the comment surfaces of the adapter + manifests (`fastly.toml`, `wrangler.toml`, `spin.toml`, `axum.toml`, + already named as deployment truth sources), `.claude/skills/**` + (operator-facing skills such as the Fastly deployment skill), `.claude/agents/**` (maintained agent - instructions), and `.github/pull_request_template.md`. + instructions), and `.github/pull_request_template.md`. A checked + maintained-source manifest enumerates these files, and the WP8b + inventory gate asserts final set equality against it - a maintained + surface outside the manifest, or a manifest entry with no file, fails. - **Historical set:** `docs/superpowers/**` (specs, plans, implementation notes, archive) and shipped `CHANGELOG.md` release entries. Exempt from retired-term greps; a changelog entry describing a rename may name the @@ -227,7 +238,8 @@ under `docs/superpowers/`, legitimately contain every retired term): Open question 4 records the alternative of an evidence-based rewrite in this pass. While it remains in the repository unpublished, the source file carries a prominent top banner stating it is unverified and - excluded from the site. `docs/roadmap.md` gets a + excluded from the site; adding that banner is a WP1 work item and its + presence is asserted by WP1 acceptance. `docs/roadmap.md` gets a factual status pass (shipped/active/deferred labels, correct crate names), not a strategy rewrite. - No release-management policy changes. The CHANGELOG's 10-month untagged @@ -276,7 +288,14 @@ the live site until rc merges to main. Therefore: WP8 adds `rc/*` to `codeql.yml`'s PR branch triggers, and CodeQL joins the final gate list in Verification. -Open question 7 asks the owner to confirm this shape. +The single rc PR is unusually large (eight packages, a new parity tool, +generated artifacts, CI enforcement, broad content changes), so if the +shape is retained it carries structural mitigations: package-level review +checkpoints (each package commit is reviewable and carries its acceptance +evidence in the PR description before the next lands), generated-output +changes isolated in their own commits, and no squash on merge so the +package boundaries survive. Open question 7 asks the owner to confirm +this shape. ## Work packages @@ -339,8 +358,9 @@ reverting only the causal non-security commit or redeploying a known-good artifact that retains the exclusion and scrub - never republishing the excluded material; no real personal emails in tracked config; no internal contacts or access -instructions anywhere in the repo; every command file lists the same gates -as `CLAUDE.md`. +instructions anywhere in the repo; the unpublished +`business-use-cases.md` source carries its unverified banner; every +command file lists the same gates as `CLAUDE.md`. ### WP2: Truth pass over existing content @@ -472,10 +492,22 @@ fences with no classification. `ts config validate/diff/push` still applies when building the config blob (`crates/trusted-server-cli/tests/config_env_overlay.rs`). Repair both `.env.example` and `.env.dev` (both still carry retired - `TRUSTED_SERVER__SYNTHETIC__*` keys), update + `TRUSTED_SERVER__SYNTHETIC__*` keys), and update `docs/guide/getting-started.md:74-77` (which tells users to `cp .env.dev -.env` and source it), and smoke-test the Axum quick start against the - contract defined in Verification. +.env` and source it). The Axum quick start additionally needs a working + configuration handoff, which does not exist today: + `ts config push --adapter axum --local` writes + `.edgezero/local-config-trusted_server_config.json`, but the Axum + server reads config only from `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` + environment variables (`AxumPlatformConfigStore`), so the + init/push/serve journey cannot serve traffic as documented. Decided + fix, docs-only: the quick start documents the exact bridge - extract + the pushed blob envelope from the local store file and export it as + the runtime variable (the integration suite already starts Axum this + way); the Verification smoke proves the exact commands as written. A + named follow-up (below) makes `ts serve --adapter axum` perform this + handoff natively; until it lands, README and EdgeZero-lifecycle prose + describe the bridge, not a seamless serve. - `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section is `[consent]`. @@ -514,12 +546,14 @@ integrations can skip typed deserialization entirely. `[cache]` section, promote the commented `[[cache.asset_rules]]` examples to a complete worked block covered by the WP8 example harness. - Field-path inventories, exact implementation (decided): a Serde-aware - AST extractor - a new dev-only tool crate, `tools/docs-parity` - (host-target, outside workspace default-members, the only place the new - `syn` dependency lives, so production crates and their dependency - closure are untouched), run as - `cargo run -p docs-parity -- check|generate` with a JSON output schema - checked into the tool. It parses the config struct definitions and + AST extractor - a new dev-only tool crate, `tools/docs-parity`, kept + OUTSIDE the root workspace entirely (its own `[workspace]` table), so + the 10-package workspace universe, the README worklist, and the + `cargo metadata` acceptance are all unchanged and the new `syn` + dependency never enters the workspace lockfile. It is run as + `cargo run --manifest-path tools/docs-parity/Cargo.toml -- check|generate` + with a JSON output schema checked into the tool, ships its own README, + and gets explicit host `fmt`/`clippy`/`test` steps in WP8b CI. It parses the config struct definitions and their serde attributes - field-level (`rename`, `alias`, `default`, `flatten`, `skip`, `skip_deserializing`, `deserialize_with`) AND container/variant-level (`rename_all`, `deny_unknown_fields`, `tag`, @@ -539,7 +573,17 @@ integrations can skip typed deserialization entirely. flattened and dynamic-key forms render in the reference. The chain is Rust serde surface to machine inventory to template to generated markdown, checked in both directions: a newly added field breaks CI - until inventory, template, and reference are updated. Reconcile + until inventory, template, and reference are updated. The inventory + carries semantics, not just names and shapes: resolved default (from + `default =` functions), requiredness, accepted grammar, units, ranges + (the extractor also parses `#[validate(...)]` attributes; manual + normalization and cross-field rules live in the companion manifest), + sensitivity (Redacted-typed fields), deprecation/alias status, and a + source anchor - so a published default, range, or conditional + requirement that contradicts the code fails parity rather than passing + as a matching field name. `serde(skip)` implementation fields (e.g. + `Handler`'s compiled regex) are never documented config paths, and the + extractor asserts that. Reconcile `docs/guide/configuration.md`'s field tables against that inventory. This audits the five existing integration sections (Prebid's reference is already missing valid keys) as well as adding the nine absent ones (`aps`, `datadome`, `didomi`, `sourcepoint`, @@ -609,8 +653,14 @@ with per-endpoint contracts, not just paths, and with per-adapter accuracy. - The publisher fallback registers seven explicit methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE); document that set rather than "all methods". - Add an Integration Endpoints section generated from each integration's - `IntegrationProxy::routes()` registration (the integrations are enumerated - in Appendix C) instead of today's three-entry list. + `IntegrationProxy::routes()` registration (the integrations are + enumerated in Appendix C) instead of today's three-entry list. Route + records carry a family: literal, template, config-derived (e.g. + Prebid's operator-configured `script_patterns`, overridable proxy + prefixes), or conditional (e.g. APS's renderer route existing only in + Trusted Server rendering mode), each with its config source or + predicate - so the generated table is neither falsely exhaustive nor + publishing fixture-specific paths. Acceptance: the route list in the reference matches the union of the four adapter route tables, with per-adapter availability flagged; every documented @@ -637,9 +687,14 @@ path) agree with the published tables. deployable while startup depends on the checked-in example config. - A support matrix page (or architecture-page section) with owned columns: build status, intended use, runtime capability, operational support, - known gaps, and release status per adapter. This matrix is the single - source for every "runs on X" claim elsewhere (WP2 aligns existing pages - to it). + known gaps, and release status per adapter, rendered from a checked + adapter-support record. The record is mechanically canonical, not just + editorially: the repeated one-line status summaries in `README.md`, + `docs/index.md`, `docs/roadmap.md`, `architecture.md`, and the + deployment guides are generated regions from the same record, and + duplicate hand-written maturity prose outside those regions is + prohibited (WP2 aligns existing pages by converting their claims to + the generated form or links). - New `docs/guide/edgezero.md`: the platform layer the app now sits on. The `edgezero.toml` manifest (app, logical stores, adapter blocks), the config flow (`trusted-server.toml` validated, pushed as a blob envelope via @@ -914,9 +969,11 @@ Build gates: - Dependency governance: Dependabot gains the `github-actions` ecosystem, the Playwright `browser/package.json` npm root, and the Next.js fixture npm root (all currently unmanaged). Pin the Wrangler version used in - CI/docs instead of installing latest; state the tested Spin and Tinybird - CLI versions (or compatibility ranges) in the deployment/telemetry - guides. + CI/docs instead of installing latest. Spin and Tinybird CLI versions in + the deployment/telemetry guides are stated as "known-compatible" + versions with the evidence recorded (what was exercised, when, at + which SHA) - not "tested", since final verification runs neither; if a + real smoke is added later it upgrades the wording. Semantic parity checks (each catches a class of drift this audit found): @@ -966,7 +1023,11 @@ Semantic parity checks (each catches a class of drift this audit found): prose, and descriptions that fail it (the vendored help currently leaks internal spec references like "5.4" and "spec 3.3 Model A") are replaced from a checked description-override table until the upstream - fix lands, so known-internal strings are never published. + fix lands, so known-internal strings are never published. Each + override entry carries owner, rationale, review/expiry date, and the + exact rejected source text; CI fails when the source text no longer + matches (the override is stale) or the description now passes the gate + (the override is unnecessary). - Integration parity: a checked capability record, keyed by stable integration/provider ID, is the single source that both the parity tests assert against the three inventories (registry `builders()`, @@ -976,7 +1037,14 @@ Semantic parity checks (each catches a class of drift this audit found): request filter conditional on protection) use a small typed grammar - capability, config predicate - not prose, and a fixture matrix evaluates every condition in both states; the parity test requires set - equality against each inventory, not subset containment. This record exists because no single registry API is + equality against each inventory, not subset containment. Ownership is + explicit: the registries are `pub(crate)`/private, so the equality + assertions live as module-local `#[cfg(test)]` tests inside + `trusted-server-core`, reading the checked capability record from the + repo; the external `docs-parity` tool only renders from that same + record. The tool's contract: deterministic ordering, a `check` mode + that writes nothing, atomic generated-region updates, and its own host + fmt/clippy/test in CI. This record exists because no single registry API is sufficient: `IntegrationMetadata` omits HTML post-processors and JS loading modes, and `provider_builders()` is a private list of bare function pointers without stable IDs. @@ -1008,15 +1076,26 @@ Semantic parity checks (each catches a class of drift this audit found): finds is scrubbed from the current tree; whether a finding warrants credential rotation or history rewriting is escalated to the maintainer as a per-finding decision, recorded in the audit inventory. +- Disposition inventory closure: pages and READMEs created by later + packages (WP5, WP6) enter the inventory with a `created` disposition, + and the WP8b inventory gate requires exact equality between the + inventory and the final active-document set at the final PR HEAD - a + document without a disposition, or a disposition without a document, + fails. - Repo inventory: a CI script checking workspace members each have a README (via `cargo metadata`, the authoritative package list, not a `find` over directories) and every active public page is reachable from the sidebar or an explicit orphan allowlist. - Gate manifest: one checked manifest of the canonical CI gates, compared - against the workflows AND against every human-facing copy - `CLAUDE.md`, - `AGENTS.md`, `.claude/commands/*.md`, and the PR template - so WP8b's - own gate additions cannot silently invalidate WP1's alignment of those - same files at the final commit. + against the workflows, with an enumerated surface list and a mode per + surface: generated regions in `CLAUDE.md`, `TESTING.md`, and + `docs/guide/testing.md` (the documents that reproduce the full list); + link-only for `AGENTS.md`, `.claude/commands/*.md`, + `.claude/agents/**`, `CONTRIBUTING.md`, and the PR template, which + point at the canonical region instead of copying it. The checker fails + on any gate-list-shaped reproduction outside a managed region, so + WP8b's own gate additions cannot silently invalidate WP1's alignment + of those same files at the final commit. - `CLAUDE.md` CI gates section: update to the real gate list (it omits ESLint, the CLI/codegen clippy jobs, the bench compile check, the release WASM builds, and the entire integration-tests workflow), regenerated @@ -1087,10 +1166,11 @@ Before the rc PR is marked ready, at its final HEAD: return 404). - The Axum quick start smoke test with a defined first-success contract: starting from the updated getting-started instructions with a canonical - config, the server starts, `GET /health` returns 200 `ok`, and one - representative publisher-proxy request against a local stub origin - returns the expected rewritten HTML; the run and cleanup steps are - recorded in the PR description. + config and the documented blob-envelope export bridge (WP2), the server + starts, `GET /health` returns 200 `ok`, and one representative + publisher-proxy request against a local stub origin returns the + expected rewritten HTML; the run and cleanup steps are recorded in the + PR description. ## Open questions @@ -1123,9 +1203,10 @@ where stated. entirely - cutting a release to drain the seven breaking `[Unreleased]` entries is a maintainer decision outside this project. The deterministic WP2 edit assumes no release: keep the `[1.2.0]` section - with an explicit "(tag v1.2.0 was never published)" annotation, repoint - the link references to resolvable compares, and leave entries - untouched. If a release lands externally before this PR merges, the + with an explicit "(tag v1.2.0 was never published)" annotation, remove + the `[1.2.0]` link reference entirely (no tag exists to anchor a + compare, and no commit boundary is recorded), repoint `[Unreleased]` + to `v1.1.0...HEAD`, and leave entries untouched. If a release lands externally before this PR merges, the branch rebases and re-audits rather than absorbing release work. 6. Governance ownership (blocks the WP6 governance edit only): who owns naming maintainers/CODEOWNERS and the meeting-minutes commitment? @@ -1146,6 +1227,11 @@ where stated. the WP5 Spin deployment guide; until fixed, docs label Spin experimental. A `spin up` smoke test proving non-health traffic belongs to the fix's acceptance criteria. +- `ts serve --adapter axum` does not consume the local config store that + `ts config push --adapter axum --local` writes; the server reads only + `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` environment variables. Wire the + handoff natively so the documented bridge (WP2) becomes unnecessary. + Until then, the bridge is the documented path. - Vendored `edgezero-cli` help text leaks internal spec references ("5.4", "spec 3.3 Model A") into `ts config push --help`; fix upstream at the `edgezero` repo and bump the pinned tag. @@ -1235,6 +1321,15 @@ registry API exposes all of it, so WP8 tests them separately: 3. JS modules: `registry.rs` module-id functions plus `JS_ALWAYS` (adds the always-injected `creative` module). +Named sets and expected counts, so the set-equality assertions are +unambiguous: deploy/config-validated integration IDs (14), Rust registry +registrations (13; no `adserver_mock`), auction-provider IDs (3: prebid, +aps, adserver_mock), JS bundle modules (core, per-integration bundles, +plus the always-injected `creative`). The overview table renders the 14 +configurable IDs plus one separate row for JS-only `creative` (15 rows +total). "All 14 integration IDs" elsewhere in this spec means the +deploy/config-validated set. + Capabilities: P proxy, AR attribute rewriter, SR script rewriter, HI head injector, PP html post-processor, RF request filter, DJS deferred JS, AP auction provider. Conditional capabilities are stated as such; registry From f5a838555974f2fee4565852cead9fea09bc0a5d Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 21 Aug 2026 11:12:24 -0700 Subject: [PATCH 14/33] Rework spec after seventh pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 152 ++++++++++++++---- 1 file changed, 122 insertions(+), 30 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 09f132834..824d9c37f 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-21 (round 6; addresses all six pre-implementation reviews) +**Revised:** 2026-08-21 (round 7; addresses all seven pre-implementation reviews) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and @@ -20,7 +20,7 @@ Trusted Server's documentation spans four surfaces: the VitePress site (`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and configuration templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track -audit of `main`, hardened by six pre-implementation reviews, found systemic +audit of `main`, hardened by seven pre-implementation reviews, found systemic drift in every surface. The failures fall into six categories: 1. **Fabricated or dead content presented as real.** The API reference @@ -415,8 +415,15 @@ fences with no classification. credential fixture (e.g. the `fastly.toml` local JWKS material), historical example, service ID - each entry carrying owner, rationale, and expiry/review date; the scanner rejects expired or orphaned - entries. The scanner and allowlist scaffolding land in WP8a so this - pass can use them. `CLAUDE.md`'s example-domains-only policy gains a + entries, plus a hash-pinned test-fixture category for fixtures that + are semantically necessary as-is. Remediation of non-document tracked + files the all-tracked scan surfaces (e.g. the scraped + `html_processor.test.html` fixture carrying real-looking domains, + author names, and social identities) is owned by this WP2 pass: + fixtures are re-cut with reserved `.example` data and their regression + tests re-run; binaries and generated outputs are excluded by + manifest-listed path. The scanner and allowlist scaffolding land in + WP8a so this pass can use them. `CLAUDE.md`'s example-domains-only policy gains a sentence describing the exception model (WP6 makes that edit). - Re-verify the pages touched by the final six rc commits: `docs/guide/integrations/datadome.md` (the staging requirement was @@ -552,8 +559,11 @@ integrations can skip typed deserialization entirely. `cargo metadata` acceptance are all unchanged and the new `syn` dependency never enters the workspace lockfile. It is run as `cargo run --manifest-path tools/docs-parity/Cargo.toml -- check|generate` - with a JSON output schema checked into the tool, ships its own README, - and gets explicit host `fmt`/`clippy`/`test` steps in WP8b CI. It parses the config struct definitions and + with a JSON output schema checked into the tool. It ships its own + README and a committed `Cargo.lock`, gets a Dependabot `cargo` entry + for `/tools/docs-parity` and a CI cache key tied to that nested + lockfile (WP8b), and gets explicit host `fmt`/`clippy`/`test` steps in + WP8b CI. It parses the config struct definitions and their serde attributes - field-level (`rename`, `alias`, `default`, `flatten`, `skip`, `skip_deserializing`, `deserialize_with`) AND container/variant-level (`rename_all`, `deny_unknown_fields`, `tag`, @@ -574,14 +584,23 @@ integrations can skip typed deserialization entirely. Rust serde surface to machine inventory to template to generated markdown, checked in both directions: a newly added field breaks CI until inventory, template, and reference are updated. The inventory - carries semantics, not just names and shapes: resolved default (from - `default =` functions), requiredness, accepted grammar, units, ranges + carries semantics, not just names and shapes: resolved defaults, + requiredness, accepted grammar, units, ranges (the extractor also parses `#[validate(...)]` attributes; manual normalization and cross-field rules live in the companion manifest), sensitivity (Redacted-typed fields), deprecation/alias status, and a source anchor - so a published default, range, or conditional requirement that contradicts the code fails parity rather than passing - as a matching field name. `serde(skip)` implementation fields (e.g. + as a matching field name. Defaults use a realizable mechanism, not + AST evaluation: `syn` cannot execute the ~87 `default_*` functions + (some build vectors, nested structs, or call generated helpers), so + the extractor records only literal defaults directly; every + nonliteral or type-level (`#[serde(default)]`) default requires a + companion-manifest value, fail-closed, and a compiled probe test in + core's suite deserializes a minimal document per struct and asserts + each documented default equals the actually resolved value - the + manifest is tested against real deserialization, never trusted. + `serde(skip)` implementation fields (e.g. `Handler`'s compiled regex) are never documented config paths, and the extractor asserts that. Reconcile `docs/guide/configuration.md`'s field tables against that inventory. This audits the five existing integration @@ -676,7 +695,19 @@ path) agree with the published tables. - `docs/guide/cloudflare.md`: wrangler config, `TRUSTED_SERVER_KV` binding, `TRUSTED_SERVER_CONFIG` var with blob envelope, missing `/health`, no asset routes/filters/telemetry (per the capability - matrix). + matrix). The lifecycle has the same disconnect as Axum, documented + honestly: `ts provision`/`ts config push` write the blob into the + EdgeZero config store (Wrangler KV), but Worker startup reads only + the `TRUSTED_SERVER_CONFIG` `[vars]` JSON - so the generic + push-then-deploy journey can exit 0 while producing a Worker that + fails startup. Decided fix, docs-only: the guide documents the + bridge - transfer the generated blob envelope into the Wrangler + variable (the integration suite already starts the Worker this way) + - with an end-to-end Cloudflare first-success smoke, and warns that + a green push does not configure the Worker. A named follow-up + (below) makes the Worker read the EdgeZero config store natively; + the support matrix row says "deployable with a documented config + bridge" until it lands. - `docs/guide/axum-dev.md`: explicitly a local-development guide (env-var-backed stores, `PORT`, unsupported admin ops), not a deployment target. @@ -749,6 +780,13 @@ path) agree with the published tables. behavior claims. Update `docs/guide/testing.md` to cover cloudflare/spin/parity/CLI/browser suites and replace the fictional two-job CI YAML with the real seven-job layout. +- Secret-exposure warning, owned here and in WP3's config docs: + `TrustedServerAppConfig::secret_fields()` is intentionally empty, so + configuration secrets travel inline in the blob - `ts config diff`, + `--dry-run`, and push output can therefore print secret values, and + the CLI and configuration pages must warn operators not to paste that + output into issues or retain it in public CI logs. Redaction or + secret-store migration is a separately tracked code follow-up. - `docs/guide/cli.md`: full command reference generated from the built binary's recursive help tree (Appendix D). rc already covers `config diff`, `ts dev proxy`, `audit generate`, and the ad-template @@ -828,6 +866,14 @@ serve`; link the deployment guides; refresh the doc-site link table. - `.claude/skills/**`: audit the operator-facing skills (including the Fastly deployment skill) against current commands and config, same truth standard as the command files. +- Retired-token cleanup for the active maintained internal set happens + HERE, not in WP6, because WP2's own checkpoint acceptance greps these + files: `.claude/agents/code-architect.md:16` (`RequestWrapper`), + `.claude/agents/issue-creator.md:85` (Equativ affected-area entry), and + any sibling occurrences. WP6 keeps the deeper agent-instruction audit; + WP2 only removes falsehoods. Checkpoint acceptance is scoped to the + surfaces the package touches; the full-set greps run again at final + HEAD. - Human-facing workflow comments join the maintained truth set. Two known-false comments are repaired: `.github/workflows/test.yml` (Spin release-build comment claims environment overrides make the artifact @@ -855,8 +901,9 @@ serve`; link the deployment guides; refresh the doc-site link table. Acceptance: every workspace package reported by `cargo metadata` has a README (the metadata-based WP8 check is authoritative); every pre-existing root/crate/skill document has a recorded -verified/rewritten/retired disposition; README quick start commands all run -against the PR HEAD. +verified/rewritten/retired disposition; README quick start journeys satisfy +their first-success contracts (Axum and Fastly smokes in Verification), +not merely "commands run". ### WP7: In-code documentation @@ -1003,7 +1050,13 @@ Semantic parity checks (each catches a class of drift this audit found): - Route parity: a test per adapter asserting its registered route set, methods, and response semantics/status for guarded routes match the machine-readable inventory that feeds the api-reference generated - regions. Route definitions expose only path, methods, and handler, so + regions. Fastly, Axum, and Spin expose named route collections; + Cloudflare builds routes inline in `build_router` with catch-all + fallbacks, and black-box tests cannot prove no undocumented route + exists - so `docs-parity` additionally source-parses the Cloudflare + builder chain's registration calls (string-literal paths and methods) + and asserts equality with the checked inventory, with catch-alls + represented as family entries. Route definitions expose only path, methods, and handler, so the generated regions cover the route/availability tables; the per-endpoint contract prose (auth, schemas, headers, cache/CORS, config gates, rate limits) is explicitly manually owned, marked as such in the @@ -1063,7 +1116,14 @@ Semantic parity checks (each catches a class of drift this audit found): fences compile; JS/TS and YAML fences parse (typecheck where cheap); HTTP fences are checked against the route inventory (method and path must exist); HTML/CSS fences get structural checks or explicit manual - waivers. Manual waivers are not an open escape hatch: each + waivers. Deliberately wrong examples are first-class, not perpetual + waivers: modes `compile_with_harness`, `expected_compile_failure`, and + `expected_validation_failure` (each with the expected error + code/pattern) plus `illustrative_fragment` (with source anchor) cover + the error-reference style of intentionally malformed TOML and invalid + values, and a negative example FAILS CI when it unexpectedly becomes + valid or stops producing its documented error. Manual waivers remain + for the rest and are not an open escape hatch: each carries owner, reason, expiry/review date, and source anchor, and CI fails on expired waivers and on unclassified new fences. - Domain/credential scanner: a deterministic scan for secrets, PII, @@ -1076,6 +1136,11 @@ Semantic parity checks (each catches a class of drift this audit found): finds is scrubbed from the current tree; whether a finding warrants credential rotation or history rewriting is escalated to the maintainer as a per-finding decision, recorded in the audit inventory. +- The disposition schema covers non-page surfaces too: workflow/action + files, script usage headers, adapter-manifest comment surfaces, agent + and skill files get region-level dispositions with source anchors + (membership in the maintained-source manifest proves enumeration, not + review; the disposition proves review). - Disposition inventory closure: pages and READMEs created by later packages (WP5, WP6) enter the inventory with a `created` disposition, and the WP8b inventory gate requires exact equality between the @@ -1085,23 +1150,28 @@ Semantic parity checks (each catches a class of drift this audit found): - Repo inventory: a CI script checking workspace members each have a README (via `cargo metadata`, the authoritative package list, not a `find` over directories) and every active public page is reachable from - the sidebar or an explicit orphan allowlist. + the sidebar or an explicit orphan allowlist; the allowlist has a typed + `tombstone` entry kind, valid only for pages carrying canonical + successor metadata and covered by an old-route smoke, so the gam/kargo + tombstones pass the no-orphan gate by declaration, not exception. - Gate manifest: one checked manifest of the canonical CI gates, compared against the workflows, with an enumerated surface list and a mode per - surface: generated regions in `CLAUDE.md`, `TESTING.md`, and - `docs/guide/testing.md` (the documents that reproduce the full list); - link-only for `AGENTS.md`, `.claude/commands/*.md`, - `.claude/agents/**`, `CONTRIBUTING.md`, and the PR template, which - point at the canonical region instead of copying it. The checker fails - on any gate-list-shaped reproduction outside a managed region, so - WP8b's own gate additions cannot silently invalidate WP1's alignment - of those same files at the final commit. + surface: generated regions in `CLAUDE.md`, `AGENTS.md` (it is the + fallback for agents that cannot read `CLAUDE.md`, so it must carry the + list, not a link), `TESTING.md`, and `docs/guide/testing.md`; + link-only for `.claude/commands/*.md`, `.claude/agents/**`, + `CONTRIBUTING.md`, and the PR template, which point at the canonical + region instead of copying it. Every package's acceptance is + mode-aware (WP1's alignment of the command files means converting + them to links), and the checker fails on any gate-list-shaped + reproduction outside a managed region, so WP8b's own gate additions + cannot silently invalidate WP1's alignment of those same files at the + final commit. - `CLAUDE.md` CI gates section: update to the real gate list (it omits ESLint, the CLI/codegen clippy jobs, the bench compile check, the release WASM builds, and the entire integration-tests workflow), regenerated - from the gate manifest above together with `AGENTS.md`, the command - files, and the PR template, so all four surfaces change in the same - commit. + from the gate manifest above together with every other manifest + surface in its declared mode, all in the same commit. - A scoped `jsdoc/*` ESLint rule set over the WP7 TypeScript files is mandatory (a PR-description grep count provides no recurrence protection); the plugin is already installed with zero rules enabled. @@ -1116,8 +1186,9 @@ unclassified or expired-waiver snippet fence, a missing JSDoc block in a WP7-scoped file, a route/CLI/config/integration inventory change without the matching regenerated markdown region (including a macOS-only CLI divergence), a missing crate README or unlisted orphan page, a gate-list -mismatch in any of the four human-facing surfaces, and a removed -manual-ownership marker. Static configuration assertions - checked once +mismatch in any gate-manifest surface (the fixture iterates every +manifest entry in its declared mode, not a hard-coded list), and a +removed manual-ownership marker. Static configuration assertions - checked once in review with the evidence linked in the PR description, not fixtures: CodeQL branch triggers, normalized cache keys, Dependabot roots, pinned Wrangler/checker versions. Regenerating all generated regions and both @@ -1171,6 +1242,17 @@ Before the rc PR is marked ready, at its final HEAD: publisher-proxy request against a local stub origin returns the expected rewritten HTML; the run and cleanup steps are recorded in the PR description. +- A Fastly quick start smoke with the same first-success contract: the + checked-in Viceroy config store is empty and Fastly startup swaps in a + startup-error router on load failure, so "the process starts" is false + confidence. The contract: initialize and validate a config, + `ts config push --adapter fastly --local`, start + `fastly compute serve`, assert `/health`, exercise one publisher + request against a stub origin, then clean up (the local push mutates + `fastly.toml`, so the smoke restores it). +- The Cloudflare first-success smoke per the WP5 bridge: push, transfer + the blob into the Wrangler variable, `wrangler dev`, assert a + non-health route serves. ## Open questions @@ -1227,6 +1309,12 @@ where stated. the WP5 Spin deployment guide; until fixed, docs label Spin experimental. A `spin up` smoke test proving non-health traffic belongs to the fix's acceptance criteria. +- The Cloudflare Worker does not read the EdgeZero config store that + `ts provision`/`ts config push --adapter cloudflare` write (Wrangler + KV); startup consumes only the `TRUSTED_SERVER_CONFIG` `[vars]` JSON. + Wire the store read natively so the documented bridge (WP5) becomes + unnecessary; until then the bridge is the documented path and the + support matrix says so. - `ts serve --adapter axum` does not consume the local config store that `ts config push --adapter axum --local` writes; the server reads only `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` environment variables. Wire the @@ -1324,8 +1412,12 @@ registry API exposes all of it, so WP8 tests them separately: Named sets and expected counts, so the set-equality assertions are unambiguous: deploy/config-validated integration IDs (14), Rust registry registrations (13; no `adserver_mock`), auction-provider IDs (3: prebid, -aps, adserver_mock), JS bundle modules (core, per-integration bundles, -plus the always-injected `creative`). The overview table renders the 14 +aps, adserver_mock), JS integration module IDs (12 `index.ts` modules - +13 integration directories, but `aps` ships only a render helper with no +bundle - including the always-injected `creative`), and emitted bundle +outputs (13: the 12 modules plus the core bundle; core is a build +artifact, not an integration, and the capability record never represents +it). The overview table renders the 14 configurable IDs plus one separate row for JS-only `creative` (15 rows total). "All 14 integration IDs" elsewhere in this spec means the deploy/config-validated set. From df1bd9640cffc2b604d1162e83e044eb14163087 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:53:02 -0700 Subject: [PATCH 15/33] Regenerate spec against rc/202608 at a163367b3 --- ...2026-08-19-documentation-refresh-design.md | 2261 ++++++----------- 1 file changed, 767 insertions(+), 1494 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 824d9c37f..b80332c38 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,1553 +1,826 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-21 (round 7; addresses all seven pre-implementation reviews) +**Revised:** 2026-08-27 (round 8; re-audited and regenerated against the moved release branch) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. -Baseline audited at `main` commit `2e85a1cdc` (2026-08-18); realigned and -rebased 2026-08-20 onto release branch `rc/202608` at `d4cd2cc82`, which is -the PR target and the truth source for every claim in this spec. After any -further rebase, the inventories in the appendices are re-verified against -the new merge base before implementation continues. The latest six rc -commits (APS native rendering, DataDome staging-requirement removal, APS -creative frame scrollbars) touch documented behavior and are explicitly -re-checked in WP2. Line citations are from the main baseline unless marked -rc; spot-rechecked claims cite rc line numbers. +**Baseline:** `rc/202608` at merge base `a163367b3` (2026-08-27, the main→rc +merge that landed #870). Every inventory in this spec was regenerated from +that commit by a fresh four-track delta audit. Baseline contract: before +implementation starts, and after any rebase, assert that the branch's merge +base with `origin/rc/202608` equals the SHA recorded here; if it does not, +re-run the delta audit and update this spec first. Rounds 1-7 of review +history live in git; this revision supersedes their inventories. ## Context -Trusted Server's documentation spans four surfaces: the VitePress site -(`docs/`), root and per-crate markdown, in-code documentation (rustdoc, clap -help, JSDoc), and configuration templates (`trusted-server.example.toml`, -`fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`). A four-track -audit of `main`, hardened by seven pre-implementation reviews, found systemic -drift in every surface. The failures fall into six categories: - -1. **Fabricated or dead content presented as real.** The API reference - documents `GET /first-party/ad` and `POST /third-party/ad`; neither exists - anywhere in `crates/` (the real client auction endpoint is `POST /auction`). - On rc the dead sections sit at `api-reference.md:86,191` with two more - occurrences at `:707,711`, and the same dead endpoints recur in - `docs/guide/error-reference.md:658` and - `docs/guide/integrations/prebid.md:515-531`. - `docs/guide/ad-serving.md` documents an Equativ ad server and an - `[ad_servers.equativ]` config key with zero code presence. - `docs/guide/architecture.md:97-104` shows a `RequestWrapper` trait that does - not exist. Sidebar-linked pages exist for `gam` ("Target Release: Q1 2026", - now past) and `kargo` integrations that have no implementation. - `crates/trusted-server-core/src/auction/README.md` cites a route table in - `main.rs` with invented line numbers (routes moved to `app.rs`), a - `providers/` directory that does not exist, and an APS `mock` config key - that was removed. Operator instructions reference a nonexistent - `npm run type-check` (`error-reference.md:597`) and a nonexistent - `--validate-config` flag (`error-reference.md:663`); - `docs/guide/key-rotation.md:301-310` shows an obsolete - `KeyRotationManager::new(...)?` signature (the real constructor returns - `Self`, not a `Result`); and `docs/guide/configuration.md:1954-1957` (rc) - shows a Rust example importing a nonexistent - `settings_data::get_settings` (the exported loader is - `get_settings_from_config_store`) and using `println!`, which the repo's - own conventions forbid. -2. **Incomplete references.** `docs/guide/configuration.md` has no - `[consent]`, `[tinybird]`, or `[debug]` sections (rc added a `[cache]` - section; those three remain missing), and its Integration - Configurations section covers 5 of 14 IDs, with the existing five never - re-audited (the Prebid implementation exposes valid keys the reference - omits). `trusted-server.example.toml` - has no `[tinybird]`, `[consent]`, `[rewrite]`, `[tester_cookie]`, or - `[image_optimizer]` blocks. The API reference omits `POST /auction`, - `/_ts/page-bids`, `/health`, `/_ts/debug/ja4`, the EC partner API, and all - integration endpoints except three. `docs/guide/cli.md` gained - `config diff`, `ts dev proxy`, and the ad-template workflows on rc, but - does not cover the new lifecycle commands (`active-version`, - `healthcheck`, `rollback`) or `config gc`. - `docs/guide/integrations-overview.md` compares 7 of 14 integration IDs. - `docs/guide/architecture.md` describes 4 of 10 workspace crates. The - integration guide's code snippets do not compile against the current API: - `IntegrationProxy::handle` is shown without its `RuntimeServices` argument - (`integration-guide.md:96` vs `registry.rs:282-288`), `proxy_request` is - shown without its `services` argument (`integration-guide.md:132` vs - `proxy.rs:737-742`), and a platform-neutral core example imports - `fastly::http` (`integration-guide.md:134`). -3. **Missing coverage.** No pages exist for: Cloudflare or Spin deployment - (only Fastly has a setup guide, and it is orphaned from the nav), the - EdgeZero platform layer, auction telemetry/Tinybird (a 17-file `tinybird/` - directory with no operator path to a working config), the tsjs module - system, GPT slot handoff, script guards, cross-adapter parity testing, - `testlight` (the example/test integration the integration guide mirrors), - and `adserver_mock`. Seven of ten crates have no README, including all - adapters and the CLI. -4. **A misleading adapter support model.** The docs describe Cloudflare and - Spin inconsistently (in-development on the homepage and roadmap, - production on the architecture page), count Axum as a deployment target - when it is a local-development adapter with no deploy command, and say - nothing about the Spin adapter's actual runtime state: it builds its - settings from the checked-in `trusted-server.example.toml` - (`build_state()` in `adapter-spin/src/app.rs`, line 58 on rc), and a - startup failure installs a router that returns 503 for all traffic - while `/health` still returns 200 (`startup_error_router()`, line 420 - on rc). Unstable Rust files are cited by symbol; line numbers are - hints. No smoke test proves non-health traffic - works under `spin up`. CI compiles the Spin artifact; compilation is not - evidence of production maturity. Capability differences are also - documented nowhere: asset-route dispatch, integration request filters, - the image optimizer, and Tinybird auction telemetry exist only in the - Fastly adapter today; the other adapters construct no telemetry sink and - silently use the no-op default. -5. **Publishing and policy hygiene.** All 120 internal spec/plan markdown files under - `docs/superpowers/` are built and published to the public GitHub Pages site - (no `srcExclude` in `docs/.vitepress/config.mts`), along with - `docs/guide/onboarding.md` (internal contacts, meetings, access guidance), - an internal epic, and an ops runbook. `docs/public/CNAME` contains the - literal placeholder `your-custom-domain.com`. `fastly.toml` carries a real - personal email (`authors`, line 4) and a real Fastly service id (line 10) - against the repo's own sensitive-data policy, and unlabeled base64 key - fixtures that read as credentials. `docs/package.json` is not `private` - and declares an ISC license in an Apache-2.0 repository. The maintained - agent instructions under `.claude/agents/` are badly stale: they still - describe a three-crate Fastly-only workspace, cite the nonexistent - `RequestWrapper` trait, list outdated verification gates, and assume all - PRs target `main`. Active examples - violate the fictional-data policy beyond that: `ec-setup-guide.md:15` - names a real deployment domain, and `.env.example` uses non-reserved - `publisher.com` values instead of `.example` domains. -6. **No enforcement.** `cargo doc` never runs in CI; the two existing - doctests never execute (core is tested only cross-compiled, which skips - doctests); no `missing_docs` or `rustdoc::*` lints are enabled; the docs - PR workflow runs lint and Prettier but never `vitepress build`, so dead - links are only caught after merge when the deploy breaks the live site; - `eslint-plugin-jsdoc` is installed but has zero rules enabled. Nothing - checks that the docs' hand-maintained copies of routes, config fields, - CLI commands, integration IDs, crate lists, or CI gates match the code, - which is exactly how the drift above accumulated. - -Full finding indexes with `file:line` citations are in Appendix E. +Trusted Server's documentation spans the VitePress site (`docs/`), root and +per-crate markdown, in-code documentation (rustdoc, clap help, JSDoc), and +configuration templates. The original audit found systemic drift; the +release branch has since fixed a meaningful subset itself and changed the +runtime model underneath the rest. Current state at the baseline: + +**Already fixed on rc (removed from this spec's worklist):** + +- The fabricated `GET /first-party/ad` / `POST /third-party/ad` endpoints + are gone from all four pages that carried them; `api-reference.md` now + documents `POST /auction` and the `/_ts/admin/ec`, `/_ts/admin/ec/{id}`, + and `/_ts/admin/eids` diagnostics with an auth-coverage contract. +- `format.yml` now runs `vitepress build` on PRs (dead internal links fail + CI). Note the interaction: with no `srcExclude`, CI now builds all 127 + internal `docs/superpowers/**` files as site pages. +- `cli.md` covers `active-version`, `healthcheck`, `rollback`, and + `config gc`; `configuration.md` documents the secret-store migration + (all 11 secret paths), `[trusted_client_ip]`, and the config-first + `[auction.providers.]` model; `getting-started.md` was rewritten + around the blob + secret-reference flow. +- Spin no longer hardcodes the example config: startup reads the blob from + Spin's `default` KV store and resolves secret references through Spin + variables. The old blocking follow-up is closed. + +**Still open (verified at the baseline):** + +1. **Publishing and policy hygiene.** No `srcExclude` in + `docs/.vitepress/config.mts` (127 internal spec/plan files build into + the public site); `docs/guide/index.md` is 0 bytes; the nav Guide link + targets `/guide/getting-started` and a Business Value nav item points at + `business-use-cases.md` (uncited quantitative claims; presents planned + headless-browser malvertising detection as shipped while `roadmap.md` + calls it planned); `docs/public/CNAME` is the literal + `your-custom-domain.com`; `fastly.toml` carries a real personal email + (line 4), the real service id (line 10), the orphaned + `test-prebid-eids.sh` comment (line 38), and inconsistently labeled key + fixtures; `docs/package.json` is ISC and not `private`; + `docs/guide/onboarding.md` publishes internal contacts and access + guidance. +2. **Fabricated or dead content that survives.** + `architecture.md:93-104` still shows the nonexistent `RequestWrapper` + trait (also in `.claude/agents/code-architect.md:16`); `ad-serving.md:11` + still documents Equativ (also `.claude/agents/issue-creator.md:85` and + `FAQ_POC.md`; it is gone from `integration-guide.md`); + `.with_asset(...)` remains in `creative-processing.md:808` and + `integration-guide.md:84,248`; `error-reference.md:614` still says + `npm run type-check`; `configuration.md:2287` still imports the + nonexistent `settings_data::get_settings`; the auction README's rotted + route table, `providers/` directory, and APS `mock` sections; + `onboarding.md`'s dead `SEQUENCE.md` links; `TESTING.md` is still the + auction curl runbook; `FAQ_POC.md` is still false on every axis. +3. **References behind the new runtime model.** `Settings` now has 17 root + fields (new `Option`; `request_signing` and + `creative_opportunities` are also `Option`), but `configuration.md` + still has no `[consent]` or `[debug]` reference sections, documents + `[tinybird]` only inside Quick Start, and its "Key Sections" table + lists 10 of 17 roots. Docs claim reserved-field protection for both + `request_ext` and `imp_ext` while `reject_reserved_fields` guards only + `request_ext`. `adserver_mock` is doubly stranded: rc deleted its old + config subsection without a replacement page. The CHANGELOG carries 8 + breaking `[Unreleased]` entries with inconsistent `**Breaking**` + formatting and two dead `v1.2.0` compare links (no tag exists). +4. **Adapter truth gaps.** `/health` is not registered on Cloudflare; + `/_ts/admin/eids` is a real handler on all four adapters while + `/_ts/admin/ec{,/{id}}` are registered everywhere but functional only + on Fastly (KV-backed) and key rotation returns 501 off Fastly; + Cloudflare and Spin reject multi-provider auction plans at startup + (capability `concurrent_provider_fanout = false`; dormant configs are + accepted); startup-failure behavior differs (Spin: hardened 503 router + that keeps `/health` alive; Cloudflare/Fastly: 500; Cloudflare/Axum + degraded routers answer errors on every path); `Hooks::stores()` is + implemented only by Fastly, so on Cloudflare and Spin the request-time + config/KV registries are empty - the declared `TRUSTED_SERVER_KV` + binding is never opened, Spin's `v_current_x2dkid`/`v_active_x2dkids` + variables are unreachable, the Cloudflare `platform.rs:579-592` rustdoc + describing injected handles is false, and `cloudflare.toml` is dead + config referenced by nothing live. A bare `fastly compute serve` from + the checked-in `fastly.toml` cannot start the app: the + `trusted_server_config` store is empty and `ts_secrets` lacks the three + required keys, so every non-health path returns 500. +5. **Missing coverage.** No pages exist for Cloudflare, Spin, or Axum + deployment, EdgeZero, telemetry/Tinybird, tsjs, GPT slot handoff, + script guards, parity testing, or `adserver_mock`; seven of ten crates + have no README; the integration guide's snippets still do not compile + (`RuntimeServices` omissions, `fastly::http` import in core-neutral + code); `integrations-overview.md` covers 7 of 14 IDs. +6. **No enforcement beyond the new docs build.** No `cargo doc` in CI, + doctests never run (cross-compile only), no parity between code and the + hand-maintained inventories, `eslint-plugin-jsdoc` inert, + `openrtb-codegen` missing `[lints] workspace = true`, the PR template + still says `tracing`, and the slash-command files omit Spin/parity + gates. + +Appendices A-E carry the regenerated inventories with citations. ## Decision Treat documentation as a product surface with a defined source of truth per -artifact, fix the audit findings in eight work packages ordered by risk, and -add enforcement, including executable parity checks that are bound to the -reader-facing markdown, so the same drift is caught by CI instead of by the -next manual audit. Every claim in the refreshed docs must be verifiable -against code at the PR HEAD's merge base with `rc/202608` (the August 2026 -release); anything aspirational must be labeled as such or removed; adapter -support claims must come from an honest, owned support matrix rather than -marketing copy. +artifact, fix the verified-open findings in eight work packages, and add +enforcement, including executable parity checks bound to the reader-facing +markdown, so drift is caught by CI instead of by the next manual audit. +Every claim in the refreshed docs must be verifiable against code at the PR +HEAD's merge base with `rc/202608`; anything aspirational is labeled or +removed; adapter support claims come from an owned support matrix grounded +in current operational evidence. The source-of-truth map: -| Artifact | Truth source | Consumers | -| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | -| HTTP API reference | Adapter route tables and entry points (`adapter-*/src/app.rs`, `adapter-*/src/main.rs`, `adapter-*/src/platform.rs`) + core handlers | Publishers, partners | -| Config reference | `Settings` in `crates/trusted-server-core/src/settings.rs` plus the typed per-integration config structs (the root uses `deny_unknown_fields`; `IntegrationSettings` is a flattened map, so integration blocks need their own direct deserialization) | Operators | -| CLI reference | The built `ts` binary's recursive `--help` tree (clap definitions in `crates/trusted-server-cli/src/run.rs` and command modules, plus flags owned by the lockfile-resolved `edgezero-cli`) | Operators | -| Integration pages | Three inventories, tested separately: registry `builders()` (13 registrations), the auction-provider inventory (`auction/mod.rs` `provider_builders()`, which adds `adserver_mock`), and the JS module registry (`JS_ALWAYS` adds `creative`) | Publishers, integrators | -| Integration guide snippets | A compiling sample integration (`testlight` or a doc-tested fixture), never hand-written pseudo-code | Integrators | -| Deployment guides | `edgezero.toml` adapter blocks + per-adapter manifests + adapter support matrix | Operators | -| Architecture | `Cargo.toml` workspace members + `core/src/platform/` | Contributors | -| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | +| Artifact | Truth source | Consumers | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | +| HTTP API reference | Adapter route tables and entry points (`adapter-*/src/{app,main,lib,platform}.rs`) + core handlers | Publishers, partners | +| Config reference | `Settings` (17 roots, `deny_unknown_fields`) + the typed per-integration configs + the provider profile schemas (`PROFILE_REGISTRATIONS`) + `secret_fields()` | Operators | +| CLI reference | The built `ts` binary's recursive `--help` tree (Linux + macOS) | Operators | +| Integration pages | The named inventories in Appendix C: deploy IDs (14), registry `builders()` (11), plan registrations (prebid, aps), profile registry (3), mediator (`adserver_mock`), JS modules (12) and bundles (13) | Publishers, integrators | +| Integration guide snippets | A compiling sample integration (`testlight` or a doc-tested fixture) | Integrators | +| Deployment guides | Adapter manifests + per-adapter startup paths (Appendix D) + support matrix | Operators | +| Architecture | `Cargo.toml` workspace members + `core/src/platform/` + `AuctionPlan` | Contributors | +| Test/CI docs | `.cargo/config.toml` aliases + `.github/workflows/*` | Contributors | ## Source sets -Truth-pass acceptance criteria and parity checks operate on defined source -sets, not "all tracked files" (the refresh spec itself, and archived specs -under `docs/superpowers/`, legitimately contain every retired term): +Truth-pass acceptance and parity checks operate on defined source sets: -- **Active public set:** everything VitePress builds, i.e. `docs/**` - excluding the WP1 `srcExclude` list. This is what site visitors see. +- **Active public set:** everything VitePress builds (`docs/**` minus the + WP1 `srcExclude` list). - **Active repo set:** root markdown (`README.md`, `CONTRIBUTING.md`, - `TESTING.md`, `CHANGELOG.md`, `FAQ_POC.md` until it is actually - retired, `ProjectGovernance.md`, `AGENTS.md`, - `CLAUDE.md`), crate READMEs, config templates - (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, + `TESTING.md`, `CHANGELOG.md`, `FAQ_POC.md` until actually retired, + `ProjectGovernance.md`, `AGENTS.md`, `CLAUDE.md`), crate READMEs, config + templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`), and `.claude/commands/*.md`. -- **Active maintained internal set:** documents that are neither public-site - pages nor historical artifacts but are still maintained and must pass the - truth standard: `docs/README.md`, `docs/internal/**` (including the moved - onboarding page), `docs/epics/**` (classified as maintained internal - records - WP2 edits one, so they are not historical), - `docs/business-use-cases.md` while it remains excluded-but-tracked, - `scripts/README.md` and `tinybird/README.md` once - created, the human-facing comments of `.github/workflows/**` and - `.github/actions/**` (including the integration-test setup action) and - of `scripts/*.sh` usage headers, the comment surfaces of the adapter - manifests (`fastly.toml`, `wrangler.toml`, `spin.toml`, `axum.toml`, - already named as deployment truth sources), `.claude/skills/**` - (operator-facing skills such as the - Fastly deployment skill), `.claude/agents/**` (maintained agent - instructions), and `.github/pull_request_template.md`. A checked - maintained-source manifest enumerates these files, and the WP8b - inventory gate asserts final set equality against it - a maintained - surface outside the manifest, or a manifest entry with no file, fails. -- **Historical set:** `docs/superpowers/**` (specs, plans, implementation - notes, archive) and shipped `CHANGELOG.md` release entries. Exempt from - retired-term greps; a changelog entry describing a rename may name the - old identifier. +- **Active maintained internal set:** `docs/README.md`, `docs/internal/**` + (including the moved onboarding page), `docs/epics/**` (maintained + internal records), `docs/business-use-cases.md` while excluded-but- + tracked, `scripts/README.md`, `tinybird/README.md`, and + `tools/docs-parity/README.md` once created, the human-facing comments of + `.github/workflows/**`, `.github/actions/**`, and `scripts/*.sh` usage + headers, the comment surfaces of the adapter manifests (`fastly.toml`, + `wrangler.toml`, `wrangler.ci.toml`, `spin.toml`, `axum.toml`, and + `cloudflare.toml` until retired), `.claude/skills/**`, + `.claude/agents/**`, and `.github/pull_request_template.md`. A checked + maintained-source manifest enumerates entries as `{path, mode, +selector}` (whole-file vs comment-region), and the WP8b inventory gate + asserts final set equality against it. +- **Historical set:** `docs/superpowers/**` and shipped `CHANGELOG.md` + release entries. Exempt from retired-term greps only; privacy/secret + scanning covers ALL tracked files (see WP2). ## Goals -- Every endpoint, config key, command, flag, crate name, and code path named - in active-set documentation exists in the code at the PR target - (`rc/202608`), with adapter-specific availability stated where behavior - differs. -- Every shipped, operator- or publisher-visible surface has documentation: - all 14 integration IDs, all 16 config sections, the deployment adapters - (with honest maturity labels), all `ts` commands, the telemetry pipeline, - and the tsjs module system. -- The adapter support model is truthful: three deployment adapters (Fastly - production; Cloudflare; Spin, currently experimental) plus the Axum - local-development adapter, backed by a published support matrix. -- The public docs site publishes only intended pages, and the containment - actually reaches the published site (see WP1: Pages deploys only from - `main`). Internal specs, plans, epics, onboarding, and runbooks are - excluded from the build, and internal-only details are scrubbed from - anything that stays in the public repository regardless of whether - VitePress builds it. -- Sensitive real-world values are removed from source-controlled config, and - examples use fictional data (reserved `.example` domains, clearly fake - credentials) per the repo policy in `CLAUDE.md`. One narrowly scoped - exception is recognized: the `fastly.toml` `service_id`, whose removal - is an operational migration (open question 1). It is recorded in the - scanner allowlist as owner-approved and time-bounded, with rationale - and a review date, so the goal and the scanner acceptance agree. -- CI gates catch documentation regressions: docs build (dead internal links) - on PRs, rustdoc build with broken-intra-doc-link denial, doctests actually - running, and executable parity checks bound to the reader-facing markdown. -- Root markdown (`README`, `CONTRIBUTING`, `TESTING`, `CHANGELOG`) accurately - describes the current workspace, build system, and test matrix. +- Every endpoint, config key, command, flag, crate name, and code path + named in active-set documentation exists at the baseline, with + per-adapter availability stated where behavior differs. +- Every shipped operator- or publisher-visible surface is documented: all + 14 deploy-validated integration IDs, all 17 config roots, the provider + profile model, the adapters (with evidence-based maturity labels), all + `ts` commands, telemetry, and tsjs. +- The adapter support model is truthful and mechanically canonical: a + checked adapter-support record renders the matrix and every repeated + status summary. +- The public site publishes only intended pages, containment reaches the + live site (Pages deploys only from `main`), and internal details are + scrubbed from the public repository regardless of build exclusion. +- Sensitive real-world values are removed or covered by the typed, + expiring allowlist (the `fastly.toml` `service_id` is its one + owner-approved, time-bounded entry pending the ops-owned migration). +- CI catches regressions: docs build (already live on rc), rustdoc with + broken-intra-doc-link denial, doctests, and semantic parity bound to the + reader-facing markdown. ## Non-goals -- No changes to runtime behavior, routes, config schema, or code structure, - with one boundary clarification: parity checks added by WP8 may add tests - and scripts, but not alter runtime code. Code defects the audit exposed - (Spin's hardcoded example-config startup, Tinybird - access logging config present but not wired, internal spec references - leaking into vendored `edgezero-cli` help text) are tracked as follow-up - issues; the Spin one blocks publishing a Spin deployment guide (WP5). -- No new documentation toolchains. VitePress, rustdoc, and clap help remain - the three delivery mechanisms. No TypeDoc, no docs.rs publishing. -- No rewrite of `docs/business-use-cases.md` marketing copy. Default - handling hardened after review: the page is excluded from the built site - (`srcExclude`) until every quantitative claim carries dated evidence and - unshipped features are visibly labeled - nav-only removal would leave a - known-false page published and locally searchable, and would violate - WP5's no-orphan acceptance (the page presents planned headless-browser - malvertising detection as shipped while the roadmap calls it planned). - Open question 4 records the alternative of an evidence-based rewrite in - this pass. While it remains in the repository unpublished, the source - file carries a prominent top banner stating it is unverified and - excluded from the site; adding that banner is a WP1 work item and its - presence is asserted by WP1 acceptance. `docs/roadmap.md` gets a - factual status pass (shipped/active/deferred labels, correct crate names), - not a strategy rewrite. -- No release-management policy changes. The CHANGELOG's 10-month untagged - `[Unreleased]` backlog and the governance doc's unfulfilled commitments are - flagged for maintainers, with only mechanical repairs in scope. -- No dedicated accessibility audit gate. The site uses the stock VitePress - theme with no custom interactive components; WP5 adds the pieces with - direct accessibility value (prose equivalents for mermaid diagrams, - local search, `lastUpdated` context), and anything beyond that - (keyboard/contrast/screen-reader smoke checks) is deliberately deferred - until the site carries custom components that need it. -- Not chasing 100% rustdoc item coverage. In-code doc work targets module - orientation (`//!`) and the highest-traffic public surfaces, not a - `missing_docs` blanket. -- Operational changes to deployment selection. Removing or externalizing the - `fastly.toml` `service_id` changes which service a deploy targets; it is - an operationally owned follow-up with its own replacement plan, staging - test, and rollback instructions, not part of this refresh. +- No runtime changes. Code defects found by the audits are follow-ups + (list below), not in-scope work; parity checks add tests/tools only. +- No new documentation toolchains beyond the `tools/docs-parity` dev tool. +- No rewrite of `business-use-cases.md` marketing copy: it is excluded + from the build via `srcExclude` and carries a source-level unverified + banner (WP1) until an evidence-based rewrite happens (open question 4). + `roadmap.md` gets a factual status pass only. +- No release management. The 8 breaking `[Unreleased]` entries are a + maintainer decision; the deterministic no-release CHANGELOG edit is: + normalize the `**Breaking**` marker formatting, keep `[1.2.0]` with a + "(tag v1.2.0 was never published)" annotation, remove its dead link + reference, repoint `[Unreleased]` to `v1.1.0...HEAD`. If a release lands + before merge, rebase and re-audit. +- No accessibility audit gate (stock VitePress theme); WP5 ships diagram + prose equivalents, local search, and `lastUpdated`. +- Not chasing 100% rustdoc coverage. "Full surface" for in-code docs means + the WP7 worklist plus the known-false rustdoc repairs (e.g. the + Cloudflare `platform.rs` stores claim), not every item. ## Delivery shape -The owner's standing instruction is one PR (#1049, branch -`spec-docs-refresh`, targeting `rc/202608`) carrying the spec plus all eight -work packages, one commit (or small series) per package, reviewable -commit-by-commit. The second review surfaced a mechanical constraint that -forces one exception: GitHub Pages deploys only on pushes to `main` -(`deploy-docs.yml`), so publishing containment merged to rc does not reach -the live site until rc merges to main. Therefore: - -- The WP1 publishing-containment subset ships as a minimal separate PR - straight to `main` so the exposure closes immediately. Its contents are - exactly: the `srcExclude` change (covering `superpowers/**`, - `internal/**`, `epics/**`, `guide/onboarding.md`, `README.md`, and - `business-use-cases.md`), the onboarding move/scrub, the filled Guide - landing page (`docs/guide/index.md` is empty today, and the post-deploy - smoke asserts it), and the navigation edits that removing pages forces: - retarget the top-nav Guide link at the landing page and drop the - Business Value nav item. Removing every link to an excluded source is a - containment invariant - a build that navigates to excluded pages fails. - Nothing else - neither the CNAME resolution nor the marketing-page - content disposition blocks or rides in it. The rc PR carries the same - changes; the rc→main merge reconciles to an identical state. -- Everything else lands only in the single rc PR. -- CodeQL today analyzes only PRs targeting `main`, so the rc PR carrying - new workflows, generators, and scripts would go unanalyzed. Decision: - WP8 adds `rc/*` to `codeql.yml`'s PR branch triggers, and CodeQL joins - the final gate list in Verification. - -The single rc PR is unusually large (eight packages, a new parity tool, -generated artifacts, CI enforcement, broad content changes), so if the -shape is retained it carries structural mitigations: package-level review -checkpoints (each package commit is reviewable and carries its acceptance -evidence in the PR description before the next lands), generated-output -changes isolated in their own commits, and no squash on merge so the -package boundaries survive. Open question 7 asks the owner to confirm -this shape. +Owner-directed single rc PR (#1049) carrying the spec plus all packages, +one reviewable commit (or small series) per package, with package-level +review checkpoints (acceptance evidence recorded before the next package +lands), generated-output changes in their own commits, and no squash on +merge. One exception forced by mechanics: GitHub Pages deploys only on +pushes to `main`, so the containment subset also ships as a minimal +separate PR straight to `main`: exactly the `srcExclude` change (covering +`superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, +`README.md`, `business-use-cases.md`), the onboarding move/scrub, the +filled Guide landing page, and the nav edits that removing pages forces +(Guide link to the landing page; Business Value item dropped). Removing +every link to an excluded source is a containment invariant. Neither the +CNAME resolution nor the marketing-page disposition blocks or rides in it. +CodeQL gets `rc/*` PR triggers (WP8b) and joins the final gate list. +Rollback treats exclusions and scrubbing as non-rollbackable security +invariants: recovery reverts only the causal non-security commit or +redeploys a known-good artifact that retains them. Open question 7 asks +the owner to confirm this shape. ## Work packages -WP1 and WP2 are corrective, WP3-WP6 are completion work, WP7-WP8 are quality -and enforcement. Commits land in the order below. - ### WP1: Publishing and policy hygiene -Smallest package, highest urgency. The containment subset also ships to -`main` directly (see Delivery shape). - -- Add `srcExclude` to `docs/.vitepress/config.mts` covering - `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, - `README.md`, and `business-use-cases.md` (per the Non-goals default), - and move `docs/guide/onboarding.md` to - `docs/internal/onboarding.md` after scrubbing internal contacts, meeting, - and access details. Exclusion from the build is not sufficient on its own: - the repository is public, so source-sensitive details are scrubbed even - from excluded files. Verify with a local `vitepress build` that the dist - no longer contains those paths. -- The containment pieces are independent of the CNAME decision and must - not wait for it: `srcExclude` and the onboarding move/scrub ship - immediately; the CNAME change follows its own resolution (open question 2) in a separate commit. -- Resolve `docs/public/CNAME`: it currently ships the placeholder - `your-custom-domain.com` into every Pages deploy while `base` is set to - `/trusted-server` (the two are mutually inconsistent). Default action: - delete the CNAME and keep the project-path deploy; revisit if a custom - domain is actually provisioned. Update `docs/README.md:138` and `:172` - accordingly. -- `fastly.toml`: replace the personal email in `authors` with an empty list - (matching the workspace `Cargo.toml`); add `# local test fixture, not a -real key` labels to the `[local_server]` secret/JWKS entries; add one-line - comments to the four KV store declarations; remove the orphaned reference - to the deleted `scripts/test-prebid-eids.sh` (line 38). The `service_id` - removal is out of scope here (see Non-goals and open question 1). -- `docs/package.json`: set `"private": true` and align the license with the - repository (Apache-2.0, currently ISC). -- `.github/pull_request_template.md`: fix `tracing` to `log` (line 40); add - Cloudflare, Spin, and parity gates to the test-plan checkboxes. -- Fill `docs/guide/index.md` (currently 0 bytes, renders a blank page) with a - guide landing page organized by reader journey (evaluator, operator, - integrator, contributor), and retarget the top-nav Guide link - (`config.mts:61`, currently `/guide/getting-started`) at it. -- Align `.claude/commands/{check-ci,verify,test-all,test-crate}.md` with the - canonical gate list in `CLAUDE.md` (all currently omit Spin and - `clippy-cloudflare-wasm`; `test-crate.md` uses an untargeted - `cargo test -p`, the exact pattern `AGENTS.md` warns will fail). Add the - missing Spin/cloudflare-wasm gates to `AGENTS.md`'s fallback list. - -Acceptance: `vitepress build` output contains no `superpowers/`, -`internal/`, `epics/`, onboarding, or business-use-cases pages; the -containment PR to `main` is merged, the live site no longer serves those -URLs, and a positive post-deploy smoke passes (site root, the Guide -landing page, and one reference page return 200 with expected content); -the rollback procedure is documented in the containment PR (owner: the -maintainer driving this refresh) and treats the exclusions and scrubbing -as non-rollbackable security invariants: failure-prone cosmetic changes -(CNAME, navigation) live in separate commits, and recovery means -reverting only the causal non-security commit or redeploying a known-good -artifact that retains the exclusion and scrub - never republishing the -excluded material; no real -personal emails in tracked config; no internal contacts or access -instructions anywhere in the repo; the unpublished -`business-use-cases.md` source carries its unverified banner; every -command file lists the same gates as `CLAUDE.md`. +- `srcExclude` + onboarding move/scrub + Guide landing page + nav edits + (contents fixed in Delivery shape); the containment PR to `main` ships + first and its post-deploy smoke asserts: excluded URLs 404; site root, + the Guide landing page, and one reference page return 200 with expected + content. +- `business-use-cases.md` gains its source-level unverified banner + (asserted by WP1 acceptance). +- Resolve `docs/public/CNAME` in its own follow-up commit (open question + 2; both branches specified: delete and re-smoke project URLs, or custom + domain with `base: '/'`, Pages/DNS/TLS setup, canonical+asset smokes, + and a project-owned-public-domain allowlist classification). +- `fastly.toml`: empty `authors` list; label the key fixtures + consistently as local test fixtures; comment the four KV stores; remove + the `test-prebid-eids.sh` comment. `service_id` stays under its + allowlist entry pending the ops migration (open question 1). +- `docs/package.json`: `"private": true`, license Apache-2.0. +- `.github/pull_request_template.md`: `tracing` → `log`; test-plan gates + become a link to the canonical gate region (link-only mode). +- `.claude/commands/*.md` converted to link-only gate references; + `AGENTS.md` gets a generated gate region (it is the fallback for agents + that cannot read `CLAUDE.md`, so it carries the list). + +Acceptance: `vitepress build` output contains none of the excluded pages; +the `main` containment PR is merged and its smoke passes; the banner is +present; no internal contacts or access instructions anywhere in the repo; +every command file links to (not copies) the canonical gates. ### WP2: Truth pass over existing content -Nothing new is written here beyond minimal replacement prose; the goal is -that nothing in the active sets is false. The pass starts from a complete -page inventory: every page in the active public and active maintained -internal sets gets an explicit disposition, verified, rewrite, or retire. -The inventory is checked into the repository under -`docs/internal/audits/` (inside the active maintained internal set, not -the exempt historical tree), stamped with the audited merge-base SHA, with -per-page source anchors, not left in a PR description. The inventory is an -audit record of this pass; the WP8 parity gates, not the inventory, are -the continuing control. Token greps establish that retired names are -gone; they cannot validate commands, APIs, auth, or behavior, so each -"verified" disposition means the page's commands and examples were actually -checked against code, and executable fences are governed by the WP8 -snippet manifest: every nonempty fence in every non-historical set gets a -checked-in disposition (graded modes per WP8, from compile and -typed-validation down to expiring manual waivers), and CI fails on new -fences with no classification. - -- `docs/guide/api-reference.md`: delete `GET /first-party/ad` and - `POST /third-party/ad` sections (endpoints do not exist). The full - replacement reference is WP4; in this package, add a stub for - `POST /auction` so the primary endpoint is not undocumented in the interim. -- Remove the same dead endpoints from `docs/guide/integrations-overview.md:46-48`, - `docs/guide/error-reference.md:658`, and - `docs/guide/integrations/prebid.md:515-531`. -- `docs/guide/error-reference.md`: remove or replace the nonexistent - `npm run type-check` (line 597) and `--validate-config` (line 663) - instructions with commands that exist. -- `docs/guide/configuration.md:1954-1957` (rc): fix the loader example - (`settings_data::get_settings` does not exist; the exported function is - `get_settings_from_config_store`) and remove the `println!` usage the - repo's conventions forbid. -- `docs/guide/key-rotation.md`: rewrite the Rust API examples against - `core/src/request_signing/rotation.rs` (`KeyRotationManager::new` returns - `Self`, not a `Result`) and add the Basic-auth requirement to the curl - examples for admin endpoints. -- `docs/guide/proxy-signing.md`: full content review against - `core/src/proxy.rs` signing (promoted from a follow-up; a - security-relevant page cannot sit outside a documentation audit). -- Fictional-data policy audit over the active public, active repo, and - active maintained internal sets: replace the real deployment domain in - `ec-setup-guide.md:15` and the non-reserved `publisher.com` values in - `.env.example` with reserved `.example` domains and clearly fictional - values; sweep both sets for other real domains, customer names, or - credential-shaped strings. Reviewed canonical vendor endpoints (e.g. real - GPT/DataDome CDN hosts an integration genuinely proxies) stay, everything - else becomes fictional. All exceptions live in ONE typed allowlist - schema - entry categories: vendor URL, exact-path/hash-pinned fake - credential fixture (e.g. the `fastly.toml` local JWKS material), - historical example, service ID - each entry carrying owner, rationale, - and expiry/review date; the scanner rejects expired or orphaned - entries, plus a hash-pinned test-fixture category for fixtures that - are semantically necessary as-is. Remediation of non-document tracked - files the all-tracked scan surfaces (e.g. the scraped - `html_processor.test.html` fixture carrying real-looking domains, - author names, and social identities) is owned by this WP2 pass: - fixtures are re-cut with reserved `.example` data and their regression - tests re-run; binaries and generated outputs are excluded by - manifest-listed path. The scanner and allowlist scaffolding land in - WP8a so this pass can use them. `CLAUDE.md`'s example-domains-only policy gains a - sentence describing the exception model (WP6 makes that edit). -- Re-verify the pages touched by the final six rc commits: - `docs/guide/integrations/datadome.md` (the staging requirement was - removed from protection behavior in the same commit that rewrote the - page; confirm prose and code now agree) and - `docs/guide/integrations/aps.md` (native rendering mode landed; confirm - the page describes the current render modes and the conditional proxy). -- `docs/guide/ad-serving.md`: remove the Equativ section, the - `[ad_servers.equativ]` block, the top-level `[prebid]` block (real section - is `[integrations.prebid]`), and the placeholder `trackImpression` API; - also remove the `equativ` bidder from the example in - `docs/guide/integration-guide.md:313`. - Rewrite the page as a short, accurate description of the real flow: - creative opportunities matched during HTML processing, server-side auction, - creative rewriting to the first-party proxy, GPT handoff. -- `docs/guide/architecture.md`: remove the nonexistent `RequestWrapper` trait - example; replace with the real platform traits from - `core/src/platform/traits.rs` (`PlatformKvStore`, `PlatformConfigStore`, - `PlatformHttpClient`, ...). -- Adapter support consistency: `docs/index.md:27`, - `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38`, and - `docs/guide/architecture.md:154-159` currently give four different - answers. Align all of them with the WP5 support matrix: Fastly production, - Cloudflare deployable, Spin experimental (see Context item 4), Axum local - development only. Fix `docs/roadmap.md:21-22` old crate names (`/common/`, - `/cloudflare/`) and give roadmap line items shipped/active/deferred - status labels. -- Retire `docs/guide/integrations/gam.md` and `kargo.md`: the markdown - files are REPLACED with tombstone content (successor link, canonical - metadata, optional meta refresh) so every previously published route - keeps serving, unconditionally - inbound discovery cannot find - bookmarks or unindexed links; a client-side stub is not an HTTP - redirect and is not claimed to be one. Sidebar entries are removed; - old-route smoke tests assert the tombstones serve. Neither integration - exists; GAM ad serving is already covered factually via - GPT/creative-opportunities docs. -- Retire `FAQ_POC.md`: its headline answer ("NOT ready for use", two-partner - Fastly+Equativ POC) is false on every axis. Delete it (git history - preserves it); fold any still-true answers into - `docs/guide/what-is-trusted-server.md`. Same inbound-link inventory as - above before deletion. -- Remove the nonexistent `.with_asset(...)` builder method from - `docs/guide/creative-processing.md:808` and - `docs/guide/integration-guide.md:84,248`, replacing it with the real - registration builder API (`with_proxy`, `with_head_injector`, - `with_deferred_js`, ...). Closes #277. (The deeper integration-guide - signature drift is fixed in WP5 by switching snippets to a compiling - source.) -- `crates/trusted-server-core/src/auction/README.md`: point the route table - at `crates/trusted-server-adapter-fastly/src/app.rs` and drop the invented - line numbers (name the tables, `NAMED_ROUTES` / `routes_for_state()`, - instead of line numbers so this cannot rot the same way); remove the - `providers/your_provider.rs` instructions in favor of the real layout - (`auction/provider.rs`, provider registration via - `register_providers` in each integration); delete the APS `mock = true` - sections (field no longer exists). -- `docs/guide/onboarding.md:51,107`: remove or retarget the two links to the - nonexistent root `SEQUENCE.md` (as part of the WP1 move). -- `docs/epics/revenue-operations-dashboard.md`: correct its telemetry - sections to reflect the shipped Tinybird pipeline (the epic proposes - BigQuery/Grafana/Prometheus and predates it). It is excluded from the site - by WP1 either way. -- `CHANGELOG.md` mechanical repairs: fix the two dead compare links (no - `v1.2.0` tag exists), the `.rust-analyzer.json` reference (file does not - exist), the retired `synthetic_id`/`x-synthetic-id` naming in the - `[Unreleased]` entry (subsystem is now EC; shipped historical entries keep - their original wording), section ordering per Keep-a-Changelog, the - "fastly.tom" and "gogernance" typos, and add the missing entry for #992 - (DataDome IP exclusions and staging bypass, an operator-visible change). -- Environment files: document the two distinct configuration surfaces - separately. (a) Runtime variables the server reads (Appendix D). (b) The - `TRUSTED_SERVER__` typed overlay, which the runtime loader ignores but - `ts config validate/diff/push` still applies when building the config - blob (`crates/trusted-server-cli/tests/config_env_overlay.rs`). Repair - both `.env.example` and `.env.dev` (both still carry retired - `TRUSTED_SERVER__SYNTHETIC__*` keys), and update - `docs/guide/getting-started.md:74-77` (which tells users to `cp .env.dev -.env` and source it). The Axum quick start additionally needs a working - configuration handoff, which does not exist today: - `ts config push --adapter axum --local` writes - `.edgezero/local-config-trusted_server_config.json`, but the Axum - server reads config only from `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` - environment variables (`AxumPlatformConfigStore`), so the - init/push/serve journey cannot serve traffic as documented. Decided - fix, docs-only: the quick start documents the exact bridge - extract - the pushed blob envelope from the local store file and export it as - the runtime variable (the integration suite already starts Axum this - way); the Verification smoke proves the exact commands as written. A - named follow-up (below) makes `ts serve --adapter axum` perform this - handoff natively; until it lands, README and EdgeZero-lifecycle prose - describe the bridge, not a seamless serve. -- `docs/guide/getting-started.md:141`: `[gdpr]` does not exist; the section - is `[consent]`. - -Acceptance: the checked-in inventory covers every page in the active public -and active maintained internal sets with a disposition and source anchors; -grepping the active sets (historical set exempt) for `first-party/ad`, -`third-party/ad`, `equativ`, `ad_servers`, `RequestWrapper`, -`trackImpression`, `SEQUENCE.md`, `synthetic_id` (outside shipped changelog -entries), `providers/your_provider`, `with_asset`, `type-check`, -`settings_data::get_settings`, and `mock = true` (APS context) returns -nothing; the fictional-data sweep finds no unreviewed real domains or -credential-shaped values; no sidebar entry points at a nonexistent -integration. +Disposition-based: every document in the active public and maintained +internal sets gets verified / rewrite / retire / created recorded in a +checked inventory under `docs/internal/audits/`, stamped with the audited +merge-base SHA, with source anchors; non-page surfaces get region-level +dispositions. The inventory is an audit record; the WP8 gates are the +continuing control. Executable fences are governed by the WP8a snippet +manifest (all languages, graded modes, expiring waivers). + +Content items (all verified open at the baseline): + +- Remove `RequestWrapper` from `architecture.md` (replace with the real + platform traits) and from `.claude/agents/code-architect.md`; remove + Equativ from `ad-serving.md` (rewrite the page around the real flow), + `.claude/agents/issue-creator.md`, and via the `FAQ_POC.md` handling + below; remove `.with_asset(...)` (closes #277); fix `npm run +type-check` and the `settings_data::get_settings` example (the exported + loader path); retired-token cleanup for maintained agent files happens + here so WP2's own checkpoint grep can pass. +- Reserved-field truth fix: docs claim `request_ext` and `imp_ext` + protection; code guards only `request_ext`. Fix the docs to match code + and file the follow-up asking whether `imp_ext` should be guarded. +- Auction README repairs (route table by symbol name, real provider + layout: `AuctionPlan`, `PROFILE_REGISTRATIONS`, `GenericOpenRtbProvider`, + mediator; remove the removed-`mock` sections). +- Retire `FAQ_POC.md` (fallback if rejected: archive or factual rewrite); + replace `gam.md`/`kargo.md` with tombstone content (routes preserved + unconditionally, `tombstone` orphan-allowlist kind, old-route smokes). +- `TESTING.md` rewritten as the test-matrix index; the auction runbook + verified-then-rewritten into `docs/guide/auction-testing.md`. +- CHANGELOG: the deterministic no-release edit (Non-goals) plus the + missing operator-visible entries check. +- Environment files: `.env.example`/`.env.dev` retired-key cleanup and the + two-surface model (runtime variables vs `ts config` overlay). +- Fictional-data pass with the typed allowlist (vendor URL / hash-pinned + fixture / historical / service ID / project-owned public domain), owner + - rationale + expiry per entry; scanning covers ALL tracked files + (negative fixtures are synthesized at test time, never checked in); + remediation of non-document fixtures (e.g. the scraped + `html_processor.test.html`) is owned here with regression tests re-run; + rotation/history-rewrite decisions escalate per finding. +- Human-facing workflow comment repairs: the Spin release-build comment + (claims env overrides fix embedded settings; startup now reads the KV + store) and the `test-cli` comment (claims a workspace default target + that `.cargo/config.toml` does not set). +- Roadmap status pass (shipped/active/deferred; also reconcile the + malvertising-detection claim with `business-use-cases.md`). + +Acceptance: checked-in inventory complete; greps over the active sets +(historical exempt) for `RequestWrapper`, `equativ`, `with_asset`, +`type-check`, `settings_data::get_settings`, `SEQUENCE.md`, +`synthetic_id` (outside shipped changelog entries), and `mock = true` (APS +context) return nothing; the all-tracked privacy scan is clean modulo the +allowlist; checkpoint scope = surfaces this package touches, full-set +greps re-run at final HEAD. ### WP3: Configuration reference completion -Bring the two operator-facing config artifacts to parity with `Settings` -(`core/src/settings.rs`, 16 fields on rc, `#[serde(deny_unknown_fields)]` at -the root) and with the typed per-integration configs, which the root parse -does NOT validate: `IntegrationSettings` is a flattened map, and disabled -integrations can skip typed deserialization entirely. - -- `trusted-server.example.toml`: add commented, documented example blocks - for the sections it lacks: `[tinybird]` (all 10 fields, with the note that - `access_enabled` must remain false), `[consent]` (mode, expiration, - jurisdiction, conflict resolution, `consent_store`), `[rewrite]` - (`exclude_domains`, already referenced by a CHANGELOG breaking entry), - `[tester_cookie]`, `[image_optimizer]` (`profile_sets` with one worked - profile), `[[proxy.asset_routes]]` (a complete, valid route: `prefix`, - `origin_url`, paired `path_pattern`/`target_path`, optional S3 SigV4 auth - block), `[integrations.osano]`, the missing `[auction]` keys (`mediator`, - `creative_store`; `allowed_context_keys` is already present at line 145), - and `[debug].inject_adm_for_testing` with its never-in-production warning - (rc's `[debug]` block now carries `ja4_endpoint_enabled`, - `auction_html_comment`, and `auction_html_comment_options`). For the rc - `[cache]` section, promote the commented `[[cache.asset_rules]]` examples - to a complete worked block covered by the WP8 example harness. -- Field-path inventories, exact implementation (decided): a Serde-aware - AST extractor - a new dev-only tool crate, `tools/docs-parity`, kept - OUTSIDE the root workspace entirely (its own `[workspace]` table), so - the 10-package workspace universe, the README worklist, and the - `cargo metadata` acceptance are all unchanged and the new `syn` - dependency never enters the workspace lockfile. It is run as - `cargo run --manifest-path tools/docs-parity/Cargo.toml -- check|generate` - with a JSON output schema checked into the tool. It ships its own - README and a committed `Cargo.lock`, gets a Dependabot `cargo` entry - for `/tools/docs-parity` and a CI cache key tied to that nested - lockfile (WP8b), and gets explicit host `fmt`/`clippy`/`test` steps in - WP8b CI. It parses the config struct definitions and - their serde attributes - field-level (`rename`, `alias`, `default`, - `flatten`, `skip`, `skip_deserializing`, `deserialize_with`) AND - container/variant-level (`rename_all`, `deny_unknown_fields`, `tag`, - `content`, `untagged`), since renamed/tagged enums change accepted - values - into the machine inventory, and FAILS CLOSED on any - shape-changing serde attribute it does not recognize. A serializer walk is - explicitly rejected: it cannot see deserialization-only aliases - (`pub_id` in `aps.rs`, `s3_sig_v4` in `settings.rs`), custom - `deserialize_with` shapes, defaulted/`Option` fields, the flattened - `IntegrationSettings` map, or `serde(skip)` implementation fields. What - the AST cannot decide (the accepted shapes of each `deserialize_with`, - dynamic map-key grammars, tagged-enum representations) lives in a - checked companion manifest the extractor requires an entry for, so an - unannotated custom deserializer fails CI. The inventory distinguishes - canonical keys (documented) from accepted-but-deprecated aliases - (listed as aliases, never as primary documentation), and records how - flattened and dynamic-key forms render in the reference. The chain is - Rust serde surface to machine inventory to template to generated - markdown, checked in both directions: a newly added field breaks CI - until inventory, template, and reference are updated. The inventory - carries semantics, not just names and shapes: resolved defaults, - requiredness, accepted grammar, units, ranges - (the extractor also parses `#[validate(...)]` attributes; manual - normalization and cross-field rules live in the companion manifest), - sensitivity (Redacted-typed fields), deprecation/alias status, and a - source anchor - so a published default, range, or conditional - requirement that contradicts the code fails parity rather than passing - as a matching field name. Defaults use a realizable mechanism, not - AST evaluation: `syn` cannot execute the ~87 `default_*` functions - (some build vectors, nested structs, or call generated helpers), so - the extractor records only literal defaults directly; every - nonliteral or type-level (`#[serde(default)]`) default requires a - companion-manifest value, fail-closed, and a compiled probe test in - core's suite deserializes a minimal document per struct and asserts - each documented default equals the actually resolved value - the - manifest is tested against real deserialization, never trusted. - `serde(skip)` implementation fields (e.g. - `Handler`'s compiled regex) are never documented config paths, and the - extractor asserts that. Reconcile - `docs/guide/configuration.md`'s field tables against that inventory. This audits the five existing integration - sections (Prebid's reference is already missing valid keys) as well as - adding the nine absent ones (`aps`, `datadome`, `didomi`, `sourcepoint`, - `lockr`, `gpt`, `gpt_diagnostics`, `google_tag_manager`, `adserver_mock`). -- `docs/guide/configuration.md`: add the missing `[consent]`, `[tinybird]`, - and `[debug]` sections (the `[tester_cookie]`, `[rewrite]`, and - image-optimizer sections already exist at lines 360, 702, and 946; - reconcile their field lists against the inventory rather than re-adding - them). -- Every example block must actually validate. WP8 builds the harness this - package relies on: placeholder-substituting parse of the full template, - plus marker-extracted parses of each commented example block and direct - typed deserialization of each integration example (bypassing the - disabled-integration short-circuit). -- Add a parity checklist to the PR description mapping each of the 16 - `Settings` fields to its example-toml block and configuration.md heading - (the table in Appendix B is the worklist). - -Acceptance: every field path in the inventory appears in both -`trusted-server.example.toml` (as a real or commented example) and -`docs/guide/configuration.md`; every integration ID accepted by deploy -validation has a config subsection whose field table matches its struct; -the WP8 example harness passes. - -### WP4: API reference rebuild - -Rebuild `docs/guide/api-reference.md` from the route inventory (Appendix A), -with per-endpoint contracts, not just paths, and with per-adapter accuracy. - -- Document every named route: health, discovery/signing endpoints, admin key - rotation (and the deliberately 404-denied legacy `/admin/keys/*` aliases), - the rc admin diagnostics (`GET /_ts/admin/ec` and `/_ts/admin/ec/{id}`, - registered on all four adapters but functional only on Fastly, which has - the EC identity KV store - the others return not-supported, matching the - key-rotation pattern; `GET /_ts/admin/eids`, a request-inspection - handler that works on all four adapters), - EC partner API (`/_ts/api/v1/batch-sync`, `/_ts/api/v1/identify`), tester - cookie endpoints, `POST /auction`, `GET /_ts/page-bids` plus the legacy - `/__ts/page-bids` alias, the four `/first-party/*` proxy endpoints, - `/_ts/debug/ja4`, and the tsjs bundle endpoint (`/static/tsjs=...`, - unified vs deferred vs standalone module forms with ETag behavior). -- Each endpoint follows a contract checklist: methods (including guarded - ones, e.g. page-bids registers OPTIONS and denies it in-handler as a CORS - preflight guard), auth requirement, request parameters/body schema, - response codes and notable headers, cache/CORS behavior, the config gate - that enables it, rate limits where present, and one example. The - `/first-party/*` family gets explicit per-endpoint treatment: `/sign` - mints short-lived signed URLs while `/proxy`, `/click`, and - `/proxy-rebuild` validate different signed inputs; "tstoken signing" as a - group label is not sufficient for a security-sensitive surface. -- Document per-adapter request pipelines rather than one generalized - pipeline: integration request filters (DataDome) run pre-route and exist - only in the Fastly adapter today; asset-route dispatch and the image - optimizer are Fastly-only; Tinybird auction emission is Fastly-only (the - other adapters construct no sink and use the no-op default). The Fastly - fallback order is tsjs, integration proxy routes, asset routes, publisher - origin proxy; the other adapters dispatch tsjs, integration proxy routes, - publisher proxy. -- Add an adapter availability and capability matrix: route availability per - adapter (EC partner API, tester cookies, JA4 debug, admin EC KV lookups, - and working key rotation are Fastly-only; Spin registers the canonical - admin key routes but returns unsupported responses; `/health` is absent on - Cloudflare) plus the platform capabilities that differ per adapter - (stores, geo, TTL storage, secrets, Tinybird sink construction, request - filters, asset routes, image optimizer), sourced from each adapter's - `app.rs`, `main.rs`/`lib.rs`, and `platform.rs`. -- The publisher fallback registers seven explicit methods (GET, POST, HEAD, - OPTIONS, PUT, PATCH, DELETE); document that set rather than "all methods". -- Add an Integration Endpoints section generated from each integration's - `IntegrationProxy::routes()` registration (the integrations are - enumerated in Appendix C) instead of today's three-entry list. Route - records carry a family: literal, template, config-derived (e.g. - Prebid's operator-configured `script_patterns`, overridable proxy - prefixes), or conditional (e.g. APS's renderer route existing only in - Trusted Server rendering mode), each with its config source or - predicate - so the generated table is neither falsely exhaustive nor - publishing fixture-specific paths. - -Acceptance: the route list in the reference matches the union of the four -adapter route tables, with per-adapter availability flagged; every documented -route names its handler file and satisfies the contract checklist; the WP8 -route snapshots (which include response semantics, not just method and -path) agree with the published tables. - -### WP5: New coverage pages and navigation repair - -- Deployment docs with honest maturity labels, grouped under a new - "Deployment" sidebar section alongside the existing (currently orphaned) - `docs/guide/fastly.md`: - - `docs/guide/cloudflare.md`: wrangler config, `TRUSTED_SERVER_KV` - binding, `TRUSTED_SERVER_CONFIG` var with blob envelope, missing - `/health`, no asset routes/filters/telemetry (per the capability - matrix). The lifecycle has the same disconnect as Axum, documented - honestly: `ts provision`/`ts config push` write the blob into the - EdgeZero config store (Wrangler KV), but Worker startup reads only - the `TRUSTED_SERVER_CONFIG` `[vars]` JSON - so the generic - push-then-deploy journey can exit 0 while producing a Worker that - fails startup. Decided fix, docs-only: the guide documents the - bridge - transfer the generated blob envelope into the Wrangler - variable (the integration suite already starts the Worker this way) - - with an end-to-end Cloudflare first-success smoke, and warns that - a green push does not configure the Worker. A named follow-up - (below) makes the Worker read the EdgeZero config store natively; - the support matrix row says "deployable with a documented config - bridge" until it lands. - - `docs/guide/axum-dev.md`: explicitly a local-development guide - (env-var-backed stores, `PORT`, unsupported admin ops), not a - deployment target. - - `docs/guide/spin.md`: written only if the Spin runtime fix (follow-up - issue below) lands first and a `spin up` smoke test proves non-health - traffic works; otherwise the page is a short experimental-status notice - describing the current limitation. The docs never present Spin as - deployable while startup depends on the checked-in example config. -- A support matrix page (or architecture-page section) with owned columns: - build status, intended use, runtime capability, operational support, - known gaps, and release status per adapter, rendered from a checked - adapter-support record. The record is mechanically canonical, not just - editorially: the repeated one-line status summaries in `README.md`, - `docs/index.md`, `docs/roadmap.md`, `architecture.md`, and the - deployment guides are generated regions from the same record, and - duplicate hand-written maturity prose outside those regions is - prohibited (WP2 aligns existing pages by converting their claims to - the generated form or links). -- New `docs/guide/edgezero.md`: the platform layer the app now sits on. The - `edgezero.toml` manifest (app, logical stores, adapter blocks), the config - flow (`trusted-server.toml` validated, pushed as a blob envelope via - `ts config push`, resolved at runtime through `settings_data.rs` including - Fastly chunked storage), and the `ts` lifecycle commands - (auth/build/serve/deploy/provision, plus the rc additions - `active-version`, `healthcheck`, `rollback`). Fold the still-relevant - parts of `docs/internal/EDGEZERO_MIGRATION.md` in; the internal runbook - itself stays excluded from the site. -- New `docs/guide/telemetry.md`: auction telemetry from - `[tinybird]` config through `auction_sink_from_settings` to the - `tinybird/` datasources, pipes, and rollups; the operator setup path - (Tinybird tokens in `ts_secrets`); explicit notes that emission is - Fastly-only today and that access-log telemetry is not yet wired - (`access_enabled` must remain false). New `tinybird/README.md` covering - the `tb` workflow and file layout. -- New `docs/guide/tsjs.md`: the module system (core + immediate vs deferred - integration modules, `JS_ALWAYS` creative module), the build pipeline - (`build-all.mjs`, `build.rs` embedding, runtime concatenation and - hashing), the bundle endpoint forms, the SPA page-bids flow, and the - public `window.tsjs` surface from `crates/trusted-server-js/lib/src/core/types.ts`. -- Integration guide integrity: make a compiling source the snippet origin. - Either extract snippets from `testlight` (which registration, proxy, and - TSJS tests already exercise) or add a doc-tested fixture integration; the - guide's current hand-written snippets omit `RuntimeServices` from - `IntegrationProxy::handle` and `proxy_request`, and import `fastly::http` - in platform-neutral core code. Document `testlight` itself as the example - integration in a named reference section of the guide. -- Extend the integration guide with the script-guard mechanism - (`crates/trusted-server-js/lib/src/shared/script_guard.ts`, the - per-integration guards such as `gpt/script_guard.ts` and - `datadome/script_guard.ts`, and `shared/beacon_guard.ts`): what guards - intercept, when an integration needs one, and how to add one. Today - `script_guard` is mentioned only in `docs/guide/integrations/gpt.md`. - Closes #341. -- Add `docs/guide/integrations/adserver_mock.md` for the mock auction - provider used in local development, currently unmentioned in all docs. -- Extend `docs/guide/integrations/gpt.md` with the slot handoff mechanism - (edge-injected `gpt_bootstrap.js`, the full shim takeover, targeting, APS - renderer bridge, SPA hook); "handoff" currently appears nowhere in docs. -- `docs/guide/integrations-overview.md`: extend the comparison and - performance tables from 7 to all 14 IDs using the three-inventory - capability data (Appendix C), including APS's actual shape (head injector - always, auction provider, proxy conditional on rendering mode). -- Testing docs: rewrite root `TESTING.md` as the test-matrix index (the - aliases from `.cargo/config.toml`, the seven `test.yml` jobs plus the four - integration-test workflow jobs, the parity suite, `scripts/test-cli.sh`, - integration/browser scripts, vitest). Its current auction runbook is - verified against the running system and rewritten into - `docs/guide/auction-testing.md` (cross-linked from the auction README), - not mechanically moved: it contains fabricated log output and stale - behavior claims. Update `docs/guide/testing.md` to cover - cloudflare/spin/parity/CLI/browser suites and replace the fictional - two-job CI YAML with the real seven-job layout. -- Secret-exposure warning, owned here and in WP3's config docs: - `TrustedServerAppConfig::secret_fields()` is intentionally empty, so - configuration secrets travel inline in the blob - `ts config diff`, - `--dry-run`, and push output can therefore print secret values, and - the CLI and configuration pages must warn operators not to paste that - output into issues or retain it in public CI logs. Redaction or - secret-store migration is a separately tracked code follow-up. -- `docs/guide/cli.md`: full command reference generated from the built - binary's recursive help tree (Appendix D). rc already covers - `config diff`, `ts dev proxy`, `audit generate`, and the ad-template - workflows; add the missing lifecycle commands (`active-version`, - `healthcheck`, `rollback`) and `config gc`, verify the rc additions - against the help tree, and link to `ts-dev-proxy.md`. -- Site usability: enable VitePress `lastUpdated` (the deploy workflow - already fetches full history for it) and local search - (`themeConfig.search`). -- Information architecture: today Configuration and CLI sit under a - "Development" sidebar group; restructure navigation into Operator, - Deployment, and Reference groups alongside the developer material, add - a generated section index at the top of the ~2,000-line configuration - reference (preserving URLs and anchors), and include the four reader - journeys (evaluator, local developer, operator, integration author) as - explicit acceptance walks. -- Release identity: add a global banner stating the site is rolling - documentation of the unreleased `main` line. Provenance is mechanical: - the Pages build injects `GITHUB_SHA` (local builds use - `git rev-parse HEAD`), the banner links the exact build SHA, and the - deploy smoke asserts the built HTML contains it; the content-audit - baseline SHA (recorded in the audit inventory) is a distinct value and - labeled as such. No versioned-docs machinery exists (Pages publishes one `main` - build; the blob envelope is an integrity check, not a schema/version - handshake), so the banner promises none: per-release documentation, if - ever wanted, is a separately designed follow-up. Compatibility guidance - stays factual: upgrade the binary before pushing configs that carry new - fields, per the CHANGELOG rollback notes. -- Diagram accessibility: inventory the active public mermaid diagrams and - give each a nearby one-paragraph prose equivalent; the inventory with a - per-diagram checkbox is part of WP5's recorded acceptance. -- Navigation: add sidebar entries for the three orphaned real integrations - (`gpt`, `google_tag_manager`, `sourcepoint`) and the new pages; - `business-use-cases` is excluded from the build per the Non-goals - default (open question 4), so it neither sits in navigation nor counts - against the no-orphan acceptance. -- `docs/guide/architecture.md`: describe all 10 workspace crates and the - platform trait boundary; add the missing Cloudflare adapter section. - -Acceptance: every integration ID is documented and nav-reachable (testlight -via its reference section in the integration guide); every adapter has a -guide or an honest status notice consistent with the support matrix; no real -page is orphaned; integration-guide snippets compile; `vitepress build` -passes (dead internal links fail the build); local search returns results -for a sampled query set; the rolling-main banner renders on every page; -the mermaid inventory shows a checked prose equivalent for every diagram; -the four reader-journey walks are recorded. +Parity with the 17-field `Settings`, the typed integration configs, the +provider profile schemas, and the secret model. + +- `configuration.md`: add the missing `[consent]` and `[debug]` reference + sections; promote `[tinybird]` from Quick Start prose to a reference + section; complete the "Key Sections" table to all 17 roots; extend + Integration Configurations from 5 to all 14 IDs (audit the existing + five against the field inventory); generate the + `[auction.providers..profile_config]` reference from the three + typed profile schemas (each profile's fields, defaults, timeout + defaults, byte/depth/key limits, endpoint canonicalization, reserved + fields). +- `trusted-server.example.toml`: add the missing commented blocks + (`[consent]`, `[rewrite]`, `[tester_cookie]`, `[image_optimizer]`, + `[[proxy.asset_routes]]`, osano; extend `[tinybird]` if incomplete), + preserving the template's contract: placeholder strings must stay in + the rejection constants' exact forms (three test call sites splice on + the literal strings and the `# [header]` comment style), and + secret-reference fields carry key names, never values. +- Secret-model documentation: classify every `Redacted` path as + store-resolved (the 11 `secret_fields()` entries) or deliberately + inline (`trusted_client_ip.shared_secret`, + `tinybird.access_token_secret`) with explicit exposure guidance; the + migration section already on rc gets a verified disposition. CLI/config + pages warn that `ts config diff`/`--dry-run`/push output can print + inline-secret values. +- The extractor-based field inventory (WP8a) carries semantics: resolved + defaults via the literal-AST + companion-manifest + compiled-probe + chain; requiredness; grammars; ranges from `#[validate]`; every custom + validator in Appendix B's inventory gets a companion entry with + positive/negative probes, fail-closed on unclassified validator + functions; `serde(skip)` fields never become documented paths; + canonical keys vs deprecated aliases (`pub_id`, `s3_sig_v4`). + +Acceptance: every inventory field path appears in template and reference; +every deploy-validated ID has a config subsection matching its struct; +the WP8a harness passes; the parity checklist (Appendix B) is in the PR +description. + +### WP4: API reference completion + +rc already rebuilt much of the reference; this package brings it to the +contract standard and binds it to generated regions. + +- Route/availability tables become generated regions from the checked + route inventory (Appendix A), which records per-adapter availability + (Cloudflare lacks `/health`; `/_ts/admin/eids` real on all four; + `/_ts/admin/ec{,/{id}}` registered everywhere, functional only on + Fastly; key rotation 501 off Fastly; EC partner API, tester cookies, + and JA4 debug Fastly-only), method shapes (page-bids GET plus + denied-OPTIONS; sign/proxy-rebuild GET+POST; identify GET+OPTIONS), + the seven-method publisher fallback, and route families (literal / + template / config-derived / conditional with config source or + predicate - Prebid `script_patterns`, prefix overrides, APS renderer + route only in `trusted_server` rendering mode). +- Per-endpoint contract checklist for manually owned prose (auth, + schemas, response codes, cache/CORS, config gates, rate limits, + examples), with explicit ownership markers; `/first-party/sign` (mints) + vs `/proxy`/`/click`/`/proxy-rebuild` (validate) stay distinguished. +- Startup-failure behavior documented per adapter (Spin hardened 503 with + live `/health`; Cloudflare/Fastly 500; degraded-router differences). +- `trusted_client_ip` documented as middleware (sanitization on all + adapters, IP resolution only on Fastly), not a route. +- Cloudflare route parity source: `docs-parity` parses the `build_router` + chain with a fail-closed grammar that expands the known constants and + loops (path arrays, `publisher_fallback_methods()`); an unrecognized + construct fails the check rather than undercounting. + +Acceptance: generated regions match the checked inventory per adapter; +contract checklist satisfied; manual-ownership markers present. + +### WP5: New coverage pages and navigation + +- Deployment guides grounded in the audited startup paths, each with a + first-success smoke that provisions BOTH halves (config store and + secret store) from clean state: + - `fastly.md` additions + quick start: init/validate → + `ts config push --adapter fastly --local` → seed the three required + `ts_secrets` keys → `fastly compute serve` → `/health` → publisher + request against a stub origin → restore the mutated `fastly.toml`. + - `cloudflare.md`: the nested `{"app_config": ""}` wrapper, + the documented envelope-transfer bridge (push writes KV that the + Worker never reads - warn that a green push does not configure the + Worker), `wrangler secret put` for the required key names, the + `wrangler.ci.toml`/generated-manifest pattern, no `/health` + (readiness via another route), the single-provider restriction, and + the unwired stores (`TRUSTED_SERVER_KV` never opened) per the + follow-up. + - `spin.md` (now writable - the runtime fix landed): `ts config push +--adapter spin --local` with the required + `EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME=default` + mapping (push writes SQLite under `.spin/`; runtime reads store label + `default` - the mismatch footgun is documented), Spin variable names + generated from the operator's key names via the encoder (empty + defaults fail closed), `spin up`, a non-health request, cleanup. + Maturity label from current evidence: experimental - no + integration-test environment, single-provider only, request-time + config/KV stores unwired. + - `axum-dev.md`: local development only; the env-var config/secret + bridge with exact variable names; read-only admin EIDs available, + key rotation and EC KV lookups not. +- Support matrix from a checked adapter-support record (generated regions + everywhere adapter status is stated), including provider fan-out + capability and startup-failure behavior columns. +- `edgezero.md` (manifest, stores, blob flow, lifecycle commands), + `telemetry.md` (+ `tinybird/README.md`; emission Fastly-only), + `tsjs.md` (module system including the third loading mode: + `gpt_diagnostics` standalone tag; 12 modules / 13 bundles), + `integrations/adserver_mock.md` (as the mediator, with + `auction.mediator` context - its old config subsection was deleted on + rc), GPT slot handoff in `gpt.md`, script guards in the integration + guide (closes #341), `testlight` reference section, compiling snippet + source for the integration guide. +- `integrations-overview.md` extended to 14 IDs + the creative row from + the capability record. +- Navigation restructure (Operator/Deployment/Reference groups), local + search, `lastUpdated`, rolling-main banner with build-SHA provenance + (`GITHUB_SHA` injected; smoke asserts it), diagram prose equivalents + with a checked inventory, journey walks in acceptance. +- CLI reference from the two-platform help union; description text passes + the internal-term gate with the expiring override table. + +Acceptance: every ID documented and nav-reachable; every adapter guide +consistent with the support record; snippets compile; `vitepress build` +green; search/banner/diagram/journey assertions recorded; each adapter +smoke's exact commands and cleanup recorded in the PR description. ### WP6: Root markdown and crate READMEs -Audit every existing root and crate document, not only the missing ones. - -- `README.md`: current quick start including the `ts` CLI path - (`ts config init` / `ts serve --adapter ...`) alongside `fastly compute -serve`; link the deployment guides; refresh the doc-site link table. -- `CONTRIBUTING.md` (untouched since 2026-01): reference the per-target - alias system and full CI gate list, point to `CLAUDE.md`/`AGENTS.md` for - agent workflows, fix the "could be dev/develop/master" boilerplate, and - re-verify its error-handling guidance against current conventions. -- `CLAUDE.md` corrections beyond the CI gates section (WP8): it states the - workspace default target is wasm32-wasip1; `.cargo/config.toml` sets no - default target (per-target aliases and `Cargo.toml` `default-members` do - that work). Re-verify its other build-system claims while there. -- `crates/trusted-server-integration-tests/README.md`: fix the wrong CI job - name (line 231), the incomplete environments tree (lines 165-177), and - the missing browser spec (lines 141-145) flagged in Appendix E. -- New crate READMEs (short, orientation-level: what it is, how it builds, - where its docs live) for the seven crates lacking one: - `trusted-server-adapter-fastly`, `-axum`, `-cloudflare`, `-spin`, - `trusted-server-cli`, `trusted-server-js`, `trusted-server-openrtb-codegen`. - Rewrite `crates/trusted-server-core/README.md` as an actual crate overview - (currently covers 2 of ~40 modules), linking to the deep-dive docs. The - Spin README carries the same experimental-status note as WP5. -- New `scripts/README.md` (one line per script) and `tinybird/README.md` - (WP5); both join the active maintained internal set and the WP2 - disposition inventory. -- `.claude/skills/**`: audit the operator-facing skills (including the - Fastly deployment skill) against current commands and config, same truth - standard as the command files. -- Retired-token cleanup for the active maintained internal set happens - HERE, not in WP6, because WP2's own checkpoint acceptance greps these - files: `.claude/agents/code-architect.md:16` (`RequestWrapper`), - `.claude/agents/issue-creator.md:85` (Equativ affected-area entry), and - any sibling occurrences. WP6 keeps the deeper agent-instruction audit; - WP2 only removes falsehoods. Checkpoint acceptance is scoped to the - surfaces the package touches; the full-set greps run again at final - HEAD. -- Human-facing workflow comments join the maintained truth set. Two - known-false comments are repaired: `.github/workflows/test.yml` (Spin - release-build comment claims environment overrides make the artifact - boot with usable settings, but the Spin adapter loads the checked-in - example TOML directly; and the `test-cli` comment claims a workspace - default target that `.cargo/config.toml` does not set - the mechanism - is `Cargo.toml` `default-members`). -- `.claude/agents/**`: audit every agent instruction file; they currently - describe a three-crate Fastly-only workspace, cite the nonexistent - `RequestWrapper` trait (`code-architect.md:11`, `repo-explorer.md:12`), - omit Cloudflare/Spin/parity gates (`verify-app.md:19`), and assume PRs - target `main` (`pr-creator.md:177`). -- `CLAUDE.md` policy edits owned here: the vendor-endpoint exception - sentence (WP2) and the `# Examples` standard reconciliation (WP7). -- `ProjectGovernance.md`: the two claims contradicted by repo state - (meeting minutes "maintained within the repository" - none exist; - "continuous releases" - none tagged since v1.1.0) become accurate - statements of intent, unless open question 6 resolves them differently; - link it from `CONTRIBUTING.md` so the governance model is - visible at the contribution point. Naming maintainers/CODEOWNERS is a - maintainer decision, flagged as an open question. -- Add `readme = "README.md"` to each crate's `Cargo.toml` once the READMEs - exist. - -Acceptance: every workspace package reported by `cargo metadata` has a -README (the metadata-based WP8 check is authoritative); -every pre-existing root/crate/skill document has a recorded -verified/rewritten/retired disposition; README quick start journeys satisfy -their first-success contracts (Axum and Fastly smokes in Verification), -not merely "commands run". +- Audit every existing root/crate/skill/agent document (deep audit here; + WP2 already removed falsehoods): `README.md` quick starts must satisfy + the first-success contracts; `CONTRIBUTING.md` refresh (link-only gate + reference); `CLAUDE.md` corrections (no workspace default target; the + integration-system section predates the plan model; `# Examples` + standard reconciled to the earn-their-keep rule; vendor-endpoint + exception sentence); integration-tests README fixes; governance doc + intent statements. +- New READMEs for the seven crates lacking one, plus `scripts/README.md`; + core README rewritten as a real overview; `readme =` keys in Cargo.toml. + +Acceptance: every `cargo metadata` package has a README; dispositions +recorded; quick-start journeys proven. ### WP7: In-code documentation -Targeted, not exhaustive. The worklist below is the acceptance scope. - -1. `core/src/lib.rs` module index: currently lists 12 of 37 public modules - and links a `test_support` module; make it complete and grouped - (identity, consent, auction, HTML pipeline, proxy, platform, config). -2. `core/src/platform/` (on rc: 4 of 10 files carry `//!` - `mod.rs`, - `image_optimizer.rs`, `template_assembly.rs`, `template_cache.rs`): - module docs for `traits.rs`, `types.rs`, `kv.rs`, `http.rs`, - `error.rs`; the test-only module stays excluded. This is the - cross-adapter contract and the highest-value rustdoc gap in the repo. -3. Crate-level `//!` headers for the crates missing them: - `adapter-fastly` (`main.rs`), `adapter-cloudflare`, `trusted-server-js`, - and `trusted-server-cli` (whose `lib.rs` already contains the right prose - as `//` comments; convert to `//!`). -4. Module docs for the undocumented core files: `settings.rs`, - `settings_data.rs`, `http_util.rs`, `proxy.rs`, `auth.rs`, `tsjs.rs`, - `openrtb.rs`, `price_bucket.rs`, `rsc_flight.rs`, `host_rewrite.rs`, - `storage/mod.rs`, `html_processor.rs` (expand the 3-line header for a - 1000-line streaming rewriter), `integrations/registry.rs`, - `integrations/prebid.rs`, and the `nextjs/` and `datadome/` subtrees. - (`test_support.rs` and `migration_guards.rs` are deliberately out of - scope.) -5. `core/src/constants.rs`: document the 35 undocumented public constants - (cookie and header names are de facto public API). -6. CLI module docs for `commands/audit/*`, `commands/config/*`, `run.rs`. -7. TypeScript: file-header JSDoc for the zero-doc multi-export files - (`core/render.ts`, `shared/globals.ts`, `core/registry.ts`, - `integrations/creative/*`), and complete `core/types.ts` (17/35 exports - documented), which is the public tsjs type surface. Add a header block to - `build-prebid-external.mjs` (401 lines, no header). - -Style follows `CLAUDE.md` documentation standards with one deliberate -divergence that WP6 reconciles: `CLAUDE.md` currently mandates -`# Examples` on every public API function, which no part of the codebase -satisfies; the standard is updated to require examples where they compile -as doctests and earn their keep (`redacted.rs` is the model), so the two -documents state the same rule. This spec does not attempt examples on all -~589 public functions. - -Rustdoc verification commands (the exact matrix WP8 puts in CI; the CI job -needs pinned Node/npm setup because documenting `trusted-server-js` runs its -npm-based build script): - -- `cargo doc --no-deps -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1` -- `cargo doc --no-deps -p trusted-server-adapter-fastly --target wasm32-wasip1` -- `cargo doc --no-deps -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare` -- `cargo doc --no-deps -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin` -- `cargo doc --no-deps -p trusted-server-adapter-axum` -- `cargo doc --no-deps -p trusted-server-cli -p trusted-server-openrtb-codegen --target x86_64-unknown-linux-gnu` (CI; locally substitute the host triple, e.g. `aarch64-apple-darwin`) - -Acceptance: every item on the worklist above is complete; the rustdoc -command matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the -listed TypeScript files each have a file-header JSDoc block and every -`core/types.ts` export is documented, enforced by the mandatory WP8 jsdoc -lint scoped to those files. - -### WP8: Enforcement - -Prevent recurrence. Delivered in two stages so content work can depend on -it: WP8a (scaffolding) lands FIRST, immediately after WP1 - the AST -extractor and config inventory, the example-config harness, the snippet -manifest tooling, the capability record, the route/CLI golden tooling, and -the generated-region generator - so WP3-WP5 write against working -generators instead of retrofitting them at the end; WP8b (gate -activation: wiring everything into CI as blocking checks, workflow edits, -Dependabot, CodeQL, link checks) lands last so gates turn green on the -same PR. Two layers: build gates (links, rustdoc, doctests) and semantic -parity checks. Crucially, the parity checks are bound to the -reader-facing markdown, not only to snapshots a contributor could update -while leaving the prose stale: the reference tables in -`api-reference.md`, `cli.md`, `configuration.md`, and -`integrations-overview.md` live inside delimited generated regions -(``) produced from the machine-readable -inventories, and CI fails when regenerating them produces a diff. All -additions are tests, scripts, and workflow steps; no runtime code changes. - -Build gates: - -- Docs site: add `npm run build` to the `format-docs` job in - `.github/workflows/format.yml` so dead internal links fail PRs instead of - the post-merge deploy. External links are out of the PR gate; the decided policy is a weekly - scheduled link-check workflow with concrete mechanics: a pinned checker - version, `issues: write` scoped to that job only, deduplication against - the existing open issue, auto-close on recovery, a named owner (the - maintainer driving this refresh) with a best-effort SLA, and a - fixture-based test of the reporter before it merges. Normalize every `setup-node` cache key across all workflows to the - relevant `package-lock.json` (today one keys on `package.json`, another - on the lockfile). Add `.tool-versions` - to `deploy-docs.yml` trigger paths (the site renders versions from it, so - version-only bumps must republish). Add `rc/*` to `codeql.yml` PR branch - triggers (decided in Delivery shape; not conditional). Whether `push` - events on release branches should also be analyzed is a separate - maintainer choice recorded in open question 8. -- Rustdoc: add a CI step running the WP7 command matrix with - `RUSTDOCFLAGS="-D warnings"` (this denies - `rustdoc::broken_intra_doc_links` by default), with pinned Node per the - WP7 note. Do not enable `missing_docs`; the existing - `missing_errors_doc`/`missing_panics_doc`/`doc_markdown` clippy trio plus - `-D warnings` stays the item-level gate. -- Doctests: add a native-host `cargo test --doc -p trusted-server-core` step - (doctests are silently skipped today because core is only tested - cross-compiled). This job needs the same pinned Node/npm setup as the - rustdoc job: core depends on `trusted-server-js`, whose build script - invokes npm. -- Add `[lints] workspace = true` to `trusted-server-openrtb-codegen`, the - one crate not inheriting the doc lints. -- Dependency governance: Dependabot gains the `github-actions` ecosystem, - the Playwright `browser/package.json` npm root, and the Next.js fixture - npm root (all currently unmanaged). Pin the Wrangler version used in - CI/docs instead of installing latest. Spin and Tinybird CLI versions in - the deployment/telemetry guides are stated as "known-compatible" - versions with the evidence recorded (what was exercised, when, at - which SHA) - not "tested", since final verification runs neither; if a - real smoke is added later it upgrades the wording. - -Semantic parity checks (each catches a class of drift this audit found): - -- Example-config harness (replaces the naive parse test, which cannot pass: - `Settings` finalization deliberately rejects the template's placeholder - admin password, and TOML parsing ignores commented blocks). The harness - (a) applies a deterministic substitution of the known placeholders and - deliberately invalid disabled-block values (e.g. empty IDs) with - synthetic valid values, and asserts the substituted template fully - parses and finalizes; (b) groups the template's `[integrations.*]` - tables by their first-segment integration ID (nested tables such as - `[integrations.prebid.bundle]` are part of their parent's subtree, not - standalone configs), and for each of the 14 IDs deserializes the - complete subtree into its typed config struct with ignored-key - detection (several structs, including Permutive's, do not reject - unknown fields) and runs `Validate::validate`; because the runtime - path (`Settings::get_typed` and the deploy checks that delegate - through it) deliberately returns `None` for disabled integrations, the - harness then constructs an isolated `Settings` fixture per integration - with that integration forced enabled (14 named fixtures, with any - inter-integration dependencies stated explicitly) and runs the real - deploy/startup validation from `core/src/config.rs` against it; the - same treatment applies to marker-delimited commented example blocks; - and (c) separately asserts the distributed template still contains the - placeholder markers, so a template that would deploy without - customization fails CI. -- Route parity: a test per adapter asserting its registered route set, - methods, and response semantics/status for guarded routes match the - machine-readable inventory that feeds the api-reference generated - regions. Fastly, Axum, and Spin expose named route collections; - Cloudflare builds routes inline in `build_router` with catch-all - fallbacks, and black-box tests cannot prove no undocumented route - exists - so `docs-parity` additionally source-parses the Cloudflare - builder chain's registration calls (string-literal paths and methods) - and asserts equality with the checked inventory, with catch-alls - represented as family entries. Route definitions expose only path, methods, and handler, so - the generated regions cover the route/availability tables; the - per-endpoint contract prose (auth, schemas, headers, cache/CORS, config - gates, rate limits) is explicitly manually owned, marked as such in the - page, and backed by targeted tests where they exist - (`Settings::ADMIN_ENDPOINTS` coverage, config-gate behavior tests) - rather than falsely claimed as generated. The adapter capability matrix - rows are likewise either tied to a per-adapter test or marked manually - owned. -- CLI parity: golden files of the built `ts` binary's recursive `--help` - tree on both Linux and macOS (the `ts dev` subtree is compile-time - gated to macOS, and CI already runs the CLI suite on both hosts), - merged into a platform-annotated union (including the dependency-owned - `edgezero-cli` lifecycle flags at the locked version) that feeds the - cli.md generated region. The generated projection is defined: command - and flag names, argument shapes, and defaults are generated verbatim; - description text passes through the same retired/internal-term gate as - prose, and descriptions that fail it (the vendored help currently - leaks internal spec references like "5.4" and "spec 3.3 Model A") are - replaced from a checked description-override table until the upstream - fix lands, so known-internal strings are never published. Each - override entry carries owner, rationale, review/expiry date, and the - exact rejected source text; CI fails when the source text no longer - matches (the override is stale) or the description now passes the gate - (the override is unnecessary). -- Integration parity: a checked capability record, keyed by stable - integration/provider ID, is the single source that both the parity - tests assert against the three inventories (registry `builders()`, - auction `provider_builders()`, JS module registry including - `JS_ALWAYS`) and the integrations-overview generated region renders - from. Conditions (APS's proxy conditional on rendering mode, DataDome's - request filter conditional on protection) use a small typed grammar - - capability, config predicate - not prose, and a fixture matrix - evaluates every condition in both states; the parity test requires set - equality against each inventory, not subset containment. Ownership is - explicit: the registries are `pub(crate)`/private, so the equality - assertions live as module-local `#[cfg(test)]` tests inside - `trusted-server-core`, reading the checked capability record from the - repo; the external `docs-parity` tool only renders from that same - record. The tool's contract: deterministic ordering, a `check` mode - that writes nothing, atomic generated-region updates, and its own host - fmt/clippy/test in CI. This record exists because no single registry API is - sufficient: `IntegrationMetadata` omits HTML post-processors and JS - loading modes, and `provider_builders()` is a private list of bare - function pointers without stable IDs. -- Config parity: the field-path inventories from WP3 feed the - configuration.md field tables' generated regions. -- Snippet manifest: a checked-in manifest classifying every nonempty - fence in every non-historical set (active public, active repo, and - active maintained internal - root READMEs, TESTING.md, agent and - command files included), all languages, not only the four the audit - started from (the active public pages alone carry ~30 HTML, ~13 HTTP, - ~8 JS/TS, ~5 CSS, and 1 YAML fence). Modes are graded to actually catch - the failures this audit found: shell fences distinguish syntax-only - (`bash -n`) from command/flag-existence and help/dry-run checks, which - are required for operator instructions; config fences use typed/schema - validation via the WP3 inventory, not bare TOML/JSON parsing; Rust - fences compile; JS/TS and YAML fences parse (typecheck where cheap); - HTTP fences are checked against the route inventory (method and path - must exist); HTML/CSS fences get structural checks or explicit manual - waivers. Deliberately wrong examples are first-class, not perpetual - waivers: modes `compile_with_harness`, `expected_compile_failure`, and - `expected_validation_failure` (each with the expected error - code/pattern) plus `illustrative_fragment` (with source anchor) cover - the error-reference style of intentionally malformed TOML and invalid - values, and a negative example FAILS CI when it unexpectedly becomes - valid or stops producing its documented error. Manual waivers remain - for the rest and are not an open escape hatch: each - carries owner, reason, expiry/review date, and source anchor, and CI - fails on expired waivers and on unclassified new fences. -- Domain/credential scanner: a deterministic scan for secrets, PII, - customer domains, and customer identifiers over ALL tracked source - files - the historical set is exempt from retired-term greps, not from - privacy scanning (archived docs already carry publisher-specific - identifiers, e.g. the 2026-03-24 publisher-ID audit). Allowlist-aware - (the WP2 vendor allowlist, plus reviewed historical exceptions), with - negative fixtures proving it fails on planted values. Anything the scan - finds is scrubbed from the current tree; whether a finding warrants - credential rotation or history rewriting is escalated to the - maintainer as a per-finding decision, recorded in the audit inventory. -- The disposition schema covers non-page surfaces too: workflow/action - files, script usage headers, adapter-manifest comment surfaces, agent - and skill files get region-level dispositions with source anchors - (membership in the maintained-source manifest proves enumeration, not - review; the disposition proves review). -- Disposition inventory closure: pages and READMEs created by later - packages (WP5, WP6) enter the inventory with a `created` disposition, - and the WP8b inventory gate requires exact equality between the - inventory and the final active-document set at the final PR HEAD - a - document without a disposition, or a disposition without a document, - fails. -- Repo inventory: a CI script checking workspace members each have a - README (via `cargo metadata`, the authoritative package list, not a - `find` over directories) and every active public page is reachable from - the sidebar or an explicit orphan allowlist; the allowlist has a typed - `tombstone` entry kind, valid only for pages carrying canonical - successor metadata and covered by an old-route smoke, so the gam/kargo - tombstones pass the no-orphan gate by declaration, not exception. -- Gate manifest: one checked manifest of the canonical CI gates, compared - against the workflows, with an enumerated surface list and a mode per - surface: generated regions in `CLAUDE.md`, `AGENTS.md` (it is the - fallback for agents that cannot read `CLAUDE.md`, so it must carry the - list, not a link), `TESTING.md`, and `docs/guide/testing.md`; - link-only for `.claude/commands/*.md`, `.claude/agents/**`, - `CONTRIBUTING.md`, and the PR template, which point at the canonical - region instead of copying it. Every package's acceptance is - mode-aware (WP1's alignment of the command files means converting - them to links), and the checker fails on any gate-list-shaped - reproduction outside a managed region, so WP8b's own gate additions - cannot silently invalidate WP1's alignment of those same files at the - final commit. -- `CLAUDE.md` CI gates section: update to the real gate list (it omits - ESLint, the CLI/codegen clippy jobs, the bench compile check, the release - WASM builds, and the entire integration-tests workflow), regenerated - from the gate manifest above together with every other manifest - surface in its declared mode, all in the same commit. -- A scoped `jsdoc/*` ESLint rule set over the WP7 TypeScript files is - mandatory (a PR-description grep count provides no recurrence - protection); the plugin is already installed with zero rules enabled. - -Acceptance, in two explicitly separated classes. Executable regression -fixtures - every runtime gate is exercised by at least one negative -fixture proving it fails on the regression it exists to catch: a dead -internal docs link, a broken intra-doc link, a failing doctest, an invalid -or unknown-keyed example-config block (including a disabled integration -table), a planted non-allowlisted domain or credential-shaped string, an -unclassified or expired-waiver snippet fence, a missing JSDoc block in a -WP7-scoped file, a route/CLI/config/integration inventory change without -the matching regenerated markdown region (including a macOS-only CLI -divergence), a missing crate README or unlisted orphan page, a gate-list -mismatch in any gate-manifest surface (the fixture iterates every -manifest entry in its declared mode, not a hard-coded list), and a -removed manual-ownership marker. Static configuration assertions - checked once -in review with the evidence linked in the PR description, not fixtures: -CodeQL branch triggers, normalized cache keys, Dependabot roots, pinned -Wrangler/checker versions. Regenerating all generated regions and both -CLI goldens at the final PR HEAD produces no diff; the scheduled link -reporter's fixture test passes. - -## Sequencing and estimate - -| Order | Package | Size | Depends on | -| ----- | ------------------------------------------- | ---- | ----------------------------------------------------------- | -| 0 | WP1 containment subset → separate `main` PR | XS | - | -| 1 | WP1 hygiene (full, in rc PR) | S | - | -| 2 | WP8a enforcement scaffolding | M | - (extractor, harness, manifests, goldens, generators) | -| 3 | WP2 truth pass | M | WP8a (snippet manifest) | -| 4 | WP3 config reference | M | WP8a (inventory + harness) | -| 5 | WP4 API reference | M | WP2, WP8a (route inventory + regions) | -| 6 | WP5 new pages + nav | L | WP2 (nav), WP3 (links), WP8a (CLI union, capability record) | -| 7 | WP6 root + crate READMEs | M | - | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 (all gates must start green) | - -Commits land in this order within the single rc PR, after the spec commit; -WP8b comes last so the new CI gates turn green on the same PR (WP8a is -deliberately early). +Worklist (acceptance scope): core `lib.rs` module index (12 of 37 listed +today); `platform/` docs (4 of 10 files documented on rc; test-only +excluded); crate-level headers for fastly/cloudflare/js/cli; module docs +for the undocumented core files (settings, settings_data, http_util, +proxy, auth, tsjs, openrtb, price_bucket, rsc_flight, host_rewrite, +storage, html_processor, registry, prebid, nextjs/, datadome/); +`constants.rs` items; CLI module docs; the TypeScript files (headers + +complete `core/types.ts`), `build-prebid-external.mjs` header. Plus the +known-false rustdoc repairs: the Cloudflare `platform.rs:579-592` stores +claim (and its Spin sibling) rewritten to match the unwired reality, +citing the follow-up. Rustdoc command matrix as before (CI triple +`x86_64-unknown-linux-gnu`; pinned Node for the js build script; doctest +job gets the same Node setup). + +Acceptance: worklist complete; matrix builds warning-free with +`RUSTDOCFLAGS="-D warnings"`; the WP8 jsdoc lint (mandatory, scoped) +green. + +### WP8: Enforcement (WP8a scaffolding early, WP8b activation last) + +WP8a (lands right after WP1): + +- `tools/docs-parity` (outside the workspace, own committed lockfile, + Dependabot cargo entry, nested-lockfile cache key, own README, host + fmt/clippy/test): the Serde-aware AST extractor (field and + container/variant attributes including `rename_all`/`tag`/`content`/ + `untagged`, fail-closed on unknown shape-changing attributes, companion + manifest for custom deserializers, validators, and nonliteral + defaults), generated-region generator (deterministic ordering, no-write + check mode, atomic updates), the Cloudflare builder parser (fail-closed + grammar above), CLI help goldens (both platforms, merged + platform-annotated union, description override table with owner/ + rationale/expiry/source-text staleness check), snippet-manifest tooling + (all languages; graded modes incl. `expected_compile_failure` / + `expected_validation_failure` / `illustrative_fragment`; expiring + waivers), the domain/credential scanner + typed allowlist, the + maintained-source manifest checker, and the gate manifest. +- The example harness, redesigned around the secret model: validate the + template as a deploy-time, key-name-bearing `TrustedServerAppConfig` + (placeholder strings intact - the template must keep failing deploy + until customized); serialize the blob envelope; resolve through a fake + secret store; run runtime validation post-resolution; never substitute + plaintext into the template itself. Enumerate every `[integrations.*]` + subtree (grouped by first-segment ID, nested tables in their parent) + and every `[auction.providers.]` entry - commented or active, + enabled or disabled - deserializing into the typed structs with + ignored-key detection, running `Validate::validate`, the profile + compilers, and the deploy/startup checks via per-ID isolated `Settings` + fixtures with the integration/provider forced enabled (the runtime path + skips disabled blocks); valid and invalid compiled probes per profile. +- Inventory equality tests live where visibility allows: module-local + `#[cfg(test)]` tests inside core assert set equality for the private + registries (builders, plan registrations, profile registry, mediator, + JS module sets - replacing the deploy-ID constant's one-directional + subset check) against the same checked records the tool renders from. + +WP8b (lands last): + +- Wire everything as blocking CI: rustdoc matrix, native doctests (with + pinned Node), generated-region clean-diff, snippet manifest, scanner, + jsdoc lint, gate-manifest check across every surface in its mode, + repo/orphan/tombstone inventory, disposition-set equality, + maintained-source manifest equality. +- Workflow edits: CodeQL `rc/*` PR triggers; `.tool-versions` in + deploy-docs paths; normalized setup-node cache keys (all workflows, to + lockfiles); Dependabot roots (github-actions, browser and Next.js + fixture npm, docs-parity cargo); pinned Wrangler; the scheduled + external link check (pinned checker, job-scoped `issues: write`, + dedup, auto-close, named owner, fixture-tested); + `[lints] workspace = true` for openrtb-codegen. Where a governance + value can be asserted deterministically (a YAML-parsing static test + over workflow triggers, cache keys, Dependabot roots), it is; the + remainder is review-time evidence explicitly listed in the PR + description. +- `CLAUDE.md`/`AGENTS.md`/`TESTING.md`/`docs/guide/testing.md` gate + regions regenerated from the manifest in the same commit. + +Acceptance: every runtime gate has a synthesized negative fixture (dead +link, broken intra-doc link, failing doctest, invalid or unknown-keyed +example block including a disabled integration table and a bad +`profile_config`, planted non-allowlisted domain, unclassified or +expired-waiver snippet fence, missing scoped JSDoc, inventory change +without regenerated region including a macOS-only CLI divergence, missing +README or unlisted orphan, gate mismatch in any manifest surface, removed +ownership marker); the static workflow assertions pass; regenerating all +regions and goldens at final HEAD produces no diff. + +## Sequencing + +| Order | Package | Size | Depends on | +| ----- | ---------------------------------- | ---- | ------------------------- | +| 0 | WP1 containment subset → `main` PR | XS | - | +| 1 | WP1 hygiene (full, rc PR) | S | - | +| 2 | WP8a scaffolding | L | - | +| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | +| 4 | WP3 config reference | L | WP8a (extractor, harness) | +| 5 | WP4 API reference | M | WP2, WP8a | +| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | +| 7 | WP6 root + READMEs | M | WP2 | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 | ## Verification -Before the rc PR is marked ready, at its final HEAD: - -- All applicable GitHub checks green, explicitly including: CodeQL (with - `rc/*` added to its PR triggers), format - (fmt/clippy matrix, ESLint, Prettier for js and docs), the seven `test.yml` - jobs (rust/axum/cloudflare/spin/parity/cli/typescript), the four - integration-test workflow jobs (including browser), the release WASM - builds, and the JS build (`node build-all.mjs`) and test - (`npx vitest run`) suites for the TypeScript/MJS files WP7 touches. -- The new WP8 parity tests and scripts run green, and regenerating every - generated markdown region produces no diff. -- `cd docs && npm run lint && npm run format && npm run build`. -- The WP7 rustdoc command matrix locally with `RUSTDOCFLAGS="-D warnings"`. -- The acceptance greps from WP2-WP4 over the defined source sets, output - recorded in the PR description alongside the WP3 parity checklist; the - page-disposition inventory is checked in (WP2). -- For WP1, a local `vitepress build` listing of `dist/` proves the exclusion - set, and the separate `main` containment PR is merged (live-site URLs - return 404). -- The Axum quick start smoke test with a defined first-success contract: - starting from the updated getting-started instructions with a canonical - config and the documented blob-envelope export bridge (WP2), the server - starts, `GET /health` returns 200 `ok`, and one representative - publisher-proxy request against a local stub origin returns the - expected rewritten HTML; the run and cleanup steps are recorded in the - PR description. -- A Fastly quick start smoke with the same first-success contract: the - checked-in Viceroy config store is empty and Fastly startup swaps in a - startup-error router on load failure, so "the process starts" is false - confidence. The contract: initialize and validate a config, - `ts config push --adapter fastly --local`, start - `fastly compute serve`, assert `/health`, exercise one publisher - request against a stub origin, then clean up (the local push mutates - `fastly.toml`, so the smoke restores it). -- The Cloudflare first-success smoke per the WP5 bridge: push, transfer - the blob into the Wrangler variable, `wrangler dev`, assert a - non-health route serves. +At the final rc-PR HEAD: all GitHub checks green (CodeQL with rc +triggers, format including the docs build, the seven test.yml jobs, the +four integration-test jobs, release builds, JS build/test); the WP8 +parity suite and negative fixtures green; regeneration produces no diff; +`cd docs && npm run lint && npm run format && npm run build`; the rustdoc +matrix locally; acceptance greps over the defined sets with output in the +PR description; the four adapter first-success smokes (Axum env bridge, +Fastly local push + secrets, Cloudflare envelope transfer, Spin local +push + variables) executed as documented with commands and cleanup +recorded; the `main` containment PR merged with its positive smoke; the +baseline assertion (merge base equals the recorded SHA) passing. ## Open questions -Owner for all: the repo maintainer driving this refresh. Each question -blocks the named package; none blocks starting WP2-WP7 content work except -where stated. - -1. `fastly.toml` `service_id` (ops-owned follow-up): the allowlist entry - that lets the scanner pass requires an owner and review date up front, - so naming that owner blocks WP8a's scanner activation (not content - work); the migration itself (replacement mechanism, non-production - deployment test, rollback instructions) blocks nothing here. -2. `docs/public/CNAME` (blocks only its own follow-up commit, never the - containment PR). Both branches are specified: delete (recommended, - matches the `/trusted-server` base path; smoke re-runs against project - URLs), or configure a real custom domain, which requires `base: '/'`, - Pages custom-domain + DNS + TLS configuration, and canonical-URL and - asset-URL smoke tests before it ships. -3. `FAQ_POC.md` and the `gam.md`/`kargo.md` pages (blocks their WP2 - retirements): this spec recommends retiring them, with the gam/kargo - routes unconditionally preserved as tombstones. If deletion of - `FAQ_POC.md` is rejected, the defined fallback is archival under the - historical tree or a factual rewrite - it does not silently stay; it - remains in the active repo set until one of those happens. -4. `docs/business-use-cases.md` (does not block containment - the default - exclusion ships in it): default is exclusion from the built site until - quantitative claims carry dated evidence and unshipped features are - labeled; the alternative is an evidence-based rewrite in this pass. -5. CHANGELOG (blocks nothing): release management stays out of scope - entirely - cutting a release to drain the seven breaking `[Unreleased]` - entries is a maintainer decision outside this project. The - deterministic WP2 edit assumes no release: keep the `[1.2.0]` section - with an explicit "(tag v1.2.0 was never published)" annotation, remove - the `[1.2.0]` link reference entirely (no tag exists to anchor a - compare, and no commit boundary is recorded), repoint `[Unreleased]` - to `v1.1.0...HEAD`, and leave entries untouched. If a release lands externally before this PR merges, the - branch rebases and re-audits rather than absorbing release work. -6. Governance ownership (blocks the WP6 governance edit only): who owns - naming maintainers/CODEOWNERS and the meeting-minutes commitment? -7. Delivery shape (blocks starting implementation): confirm the shape in - "Delivery shape": one rc PR for everything, plus the minimal - publishing-containment PR to `main` that the Pages deploy trigger makes - necessary. -8. CodeQL `push` coverage for release branches (blocks nothing; PR-trigger - coverage is already decided): should `push` events on `rc/*` also be - analyzed? +1. `fastly.toml` `service_id` allowlist owner and review date (blocks + WP8a scanner activation); the ops migration itself blocks nothing. +2. CNAME: delete (recommended) or custom domain (fully specified branch). +3. `FAQ_POC.md` retirement; gam/kargo tombstones (routes preserved either + way). +4. `business-use-cases.md`: excluded-with-banner default vs an + evidence-based rewrite in this pass. +5. CHANGELOG release cut (out of scope; deterministic no-release edit + defined in Non-goals). +6. Governance ownership (CODEOWNERS/minutes) - blocks the WP6 governance + edit only. +7. Delivery shape confirmation (blocks starting implementation). +8. CodeQL `push` coverage for `rc/*` (non-blocking). ## Follow-up issues to file (code, not docs) -- Spin adapter builds runtime settings from the checked-in - `trusted-server.example.toml` (`build_state()` in - `adapter-spin/src/app.rs`) and serves a blanket 503 on startup failure - while `/health` returns 200 (`startup_error_router()`). Blocking for - the WP5 Spin deployment - guide; until fixed, docs label Spin experimental. A `spin up` smoke test - proving non-health traffic belongs to the fix's acceptance criteria. -- The Cloudflare Worker does not read the EdgeZero config store that - `ts provision`/`ts config push --adapter cloudflare` write (Wrangler - KV); startup consumes only the `TRUSTED_SERVER_CONFIG` `[vars]` JSON. - Wire the store read natively so the documented bridge (WP5) becomes - unnecessary; until then the bridge is the documented path and the - support matrix says so. -- `ts serve --adapter axum` does not consume the local config store that - `ts config push --adapter axum --local` writes; the server reads only - `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` environment variables. Wire the - handoff natively so the documented bridge (WP2) becomes unnecessary. - Until then, the bridge is the documented path. -- Vendored `edgezero-cli` help text leaks internal spec references - ("5.4", "spec 3.3 Model A") into `ts config push --help`; fix upstream at - the `edgezero` repo and bump the pinned tag. -- Tinybird access-log telemetry: config exists but is rejected at runtime; - either wire it or remove the config surface. Auction emission is also - Fastly-only; wiring the sink in other adapters is a code decision to - file, not a docs gap. - -## Appendix A: HTTP route inventory (truth source for WP4) - -No single shared router exists; each adapter registers named routes plus a -publisher fallback. Fastly is the superset. Route tables: -`adapter-fastly/src/app.rs` (`NAMED_ROUTES`, `routes_for_state()`), -`adapter-axum/src/app.rs` (`named_routes()`), `adapter-cloudflare/src/app.rs` -(`build_router()`), `adapter-spin/src/app.rs` (`named_fallback_paths()`). -Adapter capability differences (stores, geo, TTL, secrets, Tinybird, -request filters, asset routes, image optimizer) live in each adapter's -`platform.rs` and entry point; WP4's capability matrix is written from those -files, not from this table alone. WP8's route snapshots also record response -semantics/status for guarded and unsupported routes, not only method and -path. - -| Route | Methods | Availability | Handler | -| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `/health` | GET | all except Cloudflare | adapter entry points | -| `/_ts/debug/ja4` | GET | Fastly only, gated by `debug.ja4_endpoint_enabled` | `adapter-fastly/src/main.rs` | -| `/.well-known/trusted-server.json` | GET | all | `core/src/request_signing/endpoints.rs` | -| `/verify-signature` | POST | all | `core/src/request_signing/endpoints.rs` | -| `/_ts/admin/keys/rotate`, `/_ts/admin/keys/deactivate` | POST | Fastly working; Axum, Cloudflare, and Spin register the routes and return not-supported | `core/src/request_signing/endpoints.rs`, `adapter-fastly/src/management_api.rs` | -| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | registered on all four adapters; functional only on Fastly (EC identity KV store), others return not-supported (`adapter-axum/src/app.rs:330`, `adapter-cloudflare/src/app.rs:500`, `adapter-spin/src/app.rs:800`, rc) | `core/src/ec/admin.rs` | -| `/_ts/admin/eids` | GET | all four adapters (request-inspection handler); Basic-auth gated | `core/src/ec/admin.rs`; registrations in `adapter-axum/src/app.rs:342`, `adapter-cloudflare/src/app.rs:506`, `adapter-spin/src/app.rs:802` (rc; lines are hints, cite `admin_eids_handler` registrations) | -| `/admin/keys/*` | the seven fallback methods | all: deliberately 404-denied legacy aliases | adapter apps | -| `/_ts/api/v1/batch-sync` | POST | Fastly only; Bearer auth + rate limit | `core/src/ec/batch_sync.rs` | -| `/_ts/api/v1/identify` | GET, OPTIONS | Fastly only | `core/src/ec/identify.rs` | -| `/_ts/set-tester`, `/_ts/clear-tester` | GET | Fastly only, gated by `tester_cookie.enabled` | `core/src/tester_cookie.rs` | -| `/auction` | POST | all | `core/src/auction/endpoints.rs` | -| `/_ts/page-bids` | GET; OPTIONS registered and denied in-handler (CORS preflight guard) | all; gated by `X-TSJS-Page-Bids` header | `core/src/publisher.rs` | -| `/__ts/page-bids` | GET; OPTIONS registered and denied | legacy alias of `/_ts/page-bids` | `core/src/publisher.rs` | -| `/first-party/proxy`, `/first-party/click`, `/first-party/sign`, `/first-party/proxy-rebuild` | GET (sign/rebuild also POST) | all | `core/src/proxy.rs` | -| `/static/tsjs=` | GET | all (fallback chain) | `core/src/publisher.rs` `handle_tsjs_dynamic` | -| `/integrations//...` | varies | per enabled integration (Appendix C) | integration proxies | -| asset route prefixes | GET, HEAD | Fastly only today; operator-configured `[[proxy.asset_routes]]` | `core/src/proxy.rs` `handle_asset_proxy_request`, dispatched from `adapter-fastly/src/app.rs` | -| everything else | the seven registered fallback methods (GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE) | publisher origin proxy + HTML rewriting | `core/src/publisher.rs` `handle_publisher_request` | - -Per-adapter pipelines: Fastly runs pre-route integration request filters -(DataDome), then dispatches tsjs, integration proxy routes, asset routes, -publisher proxy. Axum, Cloudflare, and Spin have no request filters or -asset-route dispatch today: tsjs, integration proxy routes, publisher -proxy. - -## Appendix B: Settings sections (truth source for WP3) - -From `core/src/settings.rs` (`Settings`; 16 fields on rc/202608). Columns -record what each artifact carries today. On rc, `request_signing` and -`creative_opportunities` are `Option` fields. Root parsing does not -validate integration blocks (flattened map) or commented examples; the WP8 -harness covers both. - -| Section | Struct | `trusted-server.example.toml` | `configuration.md` | -| -------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------ | -| `[publisher]` | `Publisher` | present | present | -| `[tester_cookie]` | `TesterCookieConfig` | missing | present (line 360) | -| `[ec]` | `Ec` + `EcPartner` | present | present | -| `[integrations.*]` | per-integration typed configs | partial (osano missing) | 5 of 14 IDs; existing five unaudited | -| `[[handlers]]` | `Handler` | present | present | -| `response_headers` | map | present (commented) | present | -| `[request_signing]` | `RequestSigning` (Option) | present | present | -| `[rewrite]` | `Rewrite` | missing | present (line 702) | -| `[auction]` | `AuctionConfig` | missing `mediator`, `creative_store` (`allowed_context_keys` present, line 145) | present | -| `[consent]` | `ConsentConfig` | missing | missing | -| `[cache]` (rc) | `CacheSettings` | commented `[[cache.asset_rules]]` examples only | present (rc) | -| `[proxy]` | `Proxy` | partial; `asset_routes` missing | present incl. asset routes | -| `[creative_opportunities]` | `CreativeOpportunitiesConfig` (Option) | present | present | -| `[image_optimizer]` | `ImageOptimizerSettings` | missing | present (line 946 area) | -| `[tinybird]` | `TinybirdSettings` | missing | missing | -| `[debug]` | `DebugConfig` | present on rc incl. `auction_html_comment_options`; `inject_adm_for_testing` missing | missing | - -## Appendix C: Integration inventories (truth source for WP5 overview table) - -Three inventories together describe the integration surface; no single -registry API exposes all of it, so WP8 tests them separately: - -1. Registry registrations: `core/src/integrations/mod.rs` `builders()` - (13 entries; capabilities below). -2. Auction providers: `core/src/auction/mod.rs` `provider_builders()` - (adds `adserver_mock`; also registers prebid/APS providers). -3. JS modules: `registry.rs` module-id functions plus `JS_ALWAYS` - (adds the always-injected `creative` module). - -Named sets and expected counts, so the set-equality assertions are -unambiguous: deploy/config-validated integration IDs (14), Rust registry -registrations (13; no `adserver_mock`), auction-provider IDs (3: prebid, -aps, adserver_mock), JS integration module IDs (12 `index.ts` modules - -13 integration directories, but `aps` ships only a render helper with no -bundle - including the always-injected `creative`), and emitted bundle -outputs (13: the 12 modules plus the core bundle; core is a build -artifact, not an integration, and the capability record never represents -it). The overview table renders the 14 -configurable IDs plus one separate row for JS-only `creative` (15 rows -total). "All 14 integration IDs" elsewhere in this spec means the -deploy/config-validated set. - -Capabilities: P proxy, AR attribute rewriter, SR script rewriter, HI head -injector, PP html post-processor, RF request filter, DJS deferred JS, AP -auction provider. Conditional capabilities are stated as such; registry -metadata alone does not expose them. - -| ID | Capabilities | JS module | Docs page today | -| -------------------- | ------------------------------------------------------------------------------- | ------------------ | ------------------------------- | -| `prebid` | P, AR, HI, DJS, AP | yes | in sidebar | -| `aps` | HI always, AP; P conditional on the trusted-server rendering mode; no JS bundle | render helper only | in sidebar | -| `datadome` | P, AR, HI, RF (when protection on) | yes | in sidebar | -| `gpt` | P, AR, HI | yes | orphaned | -| `gpt_diagnostics` | standalone JS on demand | yes | in sidebar | -| `google_tag_manager` | P, AR, SR | yes | orphaned | -| `didomi` | P, HI | yes | in sidebar | -| `sourcepoint` | P, AR, HI | yes | orphaned | -| `osano` | bare registration | yes | in sidebar | -| `permutive` | P, AR | yes | in sidebar (thin) | -| `lockr` | P, AR | yes | in sidebar | -| `nextjs` | SR x2, PP, no JS | no | in sidebar | -| `testlight` | P, AR | yes | none | -| `adserver_mock` | AP only (auction-provider inventory, no registry registration) | no | none | -| `creative` (JS-only) | always injected (`JS_ALWAYS`) | yes | covered via creative-processing | - -## Appendix D: CLI tree and environment variables - -`ts` commands (from `crates/trusted-server-cli/src/run.rs` on rc/202608; -the canonical reference is the built binary's recursive `--help` tree, -which the WP8 golden file captures, including flags owned by the -lockfile-resolved `edgezero-cli`): +- `Hooks::stores()` unimplemented on Cloudflare, Spin, and Axum: request- + time config/KV registries are empty, the declared `TRUSTED_SERVER_KV` + binding is never opened, Spin's request-signing kid variables are + unreachable, and `cloudflare.toml` is dead config - wire `stores()` or + retire the manifests and the stale rustdoc claims (docs fix the rustdoc + in WP7 either way). +- The Cloudflare Worker does not read the config store `ts config push` + writes (nested-var startup only), and the CLI has no envelope + export/output flag - wire the store read or add the export so the + documented bridge becomes unnecessary. +- `ts serve --adapter axum` does not consume the local config store the + push writes; the env-var bridge is the documented path. +- Cloudflare registers no `/health`; startup-failure status/health + behavior differs per adapter (Spin 503 + live health, others 500) - + decide a uniform contract. +- `imp_ext` reserved-field protection: docs claimed it, code guards only + `request_ext` - decide whether to guard `imp_ext`. +- `ec.partners[*].ts_pull_token` template placeholder + (`replace-with-partner-ts-pull-token`) is in no rejection constant + list. +- `trusted_client_ip.shared_secret` and `tinybird.access_token_secret` + are `Redacted` but inline in the blob (not in `secret_fields()`) - + confirm intended or migrate to store references. +- The deploy-ID constant is `#[cfg(test)]` with a one-directional subset + check (a stale extra entry passes) - superseded by the WP8a + set-equality test, but the constant itself should be fixed or removed. +- Vendored `edgezero-cli` help text leaks internal spec references into + `ts config push --help`; fix upstream and bump the pin. +- Tinybird access-log telemetry config present but rejected at runtime; + auction emission Fastly-only. +- `.env.dev` references an `opid_store` that `fastly.toml` does not + declare. + +## Appendix A: Route inventory (regenerated at `a163367b3`) + +Sources: `adapter-fastly/src/{main,app}.rs`, `adapter-axum/src/app.rs`, +`adapter-cloudflare/src/app.rs`, `adapter-spin/src/app.rs`. Symbols cited; +line numbers are hints. WP8 snapshots record response semantics for +guarded/unsupported routes. `trusted_client_ip` is middleware (sanitize +outermost on Axum/Cloudflare/Spin; Fastly sanitizes in `main.rs`; only +Fastly resolves the client IP from it). Spin adds an innermost +`NormalizeMiddleware` (spin-header derivation). + +| Route | Methods | Fastly | Axum | Cloudflare | Spin | +| -------------------------------------- | ------------------------------- | ------------------------------------ | ------------- | ------------------------------- | ---------------------------------- | +| `/health` | GET | pre-router, survives startup failure | real | **absent** (falls to publisher) | real; also alive in the 503 router | +| `/_ts/debug/ja4` | GET | pre-router, config-gated | - | - | - | +| `/.well-known/trusted-server.json` | GET | real | real | real | real | +| `/verify-signature` | POST | real | real | real | real | +| `/_ts/admin/keys/{rotate,deactivate}` | POST | real | 501 | 501 | 501 | +| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | real (EC KV) | not-supported | not-supported | not-supported | +| `/_ts/admin/eids` | GET | real | real | real | real | +| `/admin/keys/*` (legacy) | 7 methods | 404 deny | 404 deny | 404 deny | 404 deny | +| `/_ts/api/v1/batch-sync` | POST | real (Bearer + rate limit) | - | - | - | +| `/_ts/api/v1/identify` | GET, OPTIONS | real | - | - | - | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | real (gated) | - | - | - | +| `/auction` | POST | real | real | real | real | +| `/_ts/page-bids`, `/__ts/page-bids` | GET; OPTIONS denied in-handler | real | real | real | real | +| `/first-party/{proxy,click}` | GET | real | real | real | real | +| `/first-party/{sign,proxy-rebuild}` | GET, POST | real | real | real | real | +| `/static/tsjs=` | GET (fallback chain) | real | real | real | real | +| `/integrations//...` | varies; families per Appendix C | real | real | real | real | +| asset route prefixes | GET, HEAD | Fastly only | - | - | - | +| publisher fallback | 7 explicit methods | real | real | real | real | + +Startup failure: Spin installs a hardened 503 router (generic body, +`/health` 200, all fallback methods); Cloudflare and Fastly serve 500 +from the error router; the Cloudflare and Axum degraded routers answer on +every path. EC partner API, tester cookies, and JA4 remain Fastly-only +(they need platform KV and entry-point wiring). Fastly-only capabilities: +asset routes, image optimizer, request filters (DataDome pre-route), +Tinybird emission. Provider fan-out: Fastly and Axum allow multiple +enabled providers; Cloudflare and Spin reject them at startup (dormant +multi-provider configs are accepted when `auction.enabled = false`). + +## Appendix B: Settings inventory (17 roots at `a163367b3`) + +`Settings` is `deny_unknown_fields`; `Option` roots: `trusted_client_ip`, +`request_signing`, `creative_opportunities`. + +| # | Root | example.toml | configuration.md | +| --- | -------------------------------- | ------------------------------- | -------------------- | +| 1 | `publisher` | active | yes | +| 2 | `tester_cookie` | commented | yes | +| 3 | `trusted_client_ip` (Opt) | commented | yes | +| 4 | `ec` | active (partners commented) | yes | +| 5 | `integrations` | mixed (5 active stubs) | 5 of 14 subsections | +| 6 | `handlers` | active | yes | +| 7 | `response_headers` | commented | yes | +| 8 | `request_signing` (Opt) | commented | yes | +| 9 | `rewrite` | commented | yes | +| 10 | `auction` (+ providers, bidders) | active incl. `pbs-main` | yes (config-first) | +| 11 | `consent` | commented | **missing section** | +| 12 | `cache` | commented rules | yes | +| 13 | `proxy` | header active, leaves commented | yes | +| 14 | `creative_opportunities` (Opt) | active | yes | +| 15 | `image_optimizer` | commented | nested under Proxy | +| 16 | `tinybird` | commented | **Quick Start only** | +| 17 | `debug` | commented (+ comment options) | **missing section** | + +The docs "Key Sections" table lists 10 of 17. Secret model: 11 +`secret_fields()` paths, all `KeyInDefault` (3 required: +`publisher.proxy_secret`, `ec.passphrase`, `handlers[*].password`); +resolution flow is verify → `remove_inactive_secret_references` → +`resolve_secret_references` → `from_json_value` → +`validate_settings_for_runtime`; deploy validation excludes secret-leaf +attributes and requires key names conditionally. Redacted-but-inline: +`trusted_client_ip.shared_secret`, `tinybird.access_token_secret`. +Template placeholder constants and their three literal-string test call +sites (the config.rs template tests, the CLI ad_templates substitutions, +the `config init` byte source) are load-bearing. Validator inventory for +companion entries: 2 struct-level schema validators +(`validate_trusted_client_ip`, APS inventory-identity override), 23 +field-level custom-validator sites, the imperative +`finalize_deserialized` pipeline (normalize → prepare_runtime → derive +validate → admin coverage → placeholder rejection), the plan-compiler +family (`ProviderId`/`BidderId` grammars, `canonicalize_endpoint`, +notification caps, mediator match, signing gate, +`validate_for_target`), the profile compilers (standard: 16KiB / +depth-8 / 256-key extension limits, `request_ext`-only reserved fields; +prebid-server: override-rule engine; aps: account and inventory +validators), and the integration deploy/startup pairs (DataDome split, +prebid browser-ownership cross-check against the plan, PartnerRegistry +deploy/runtime). + +## Appendix C: Integration and provider inventories (at `a163367b3`) + +Named sets and counts (set-equality tested in WP8a): + +- Deploy-validated IDs: **14** (`validate_enabled_integrations`; the + `#[cfg(test)]` constant currently checks subset one-directionally). +- Registry `builders()`: **11** - testlight, nextjs, permutive, lockr, + didomi, sourcepoint, osano, google_tag_manager, datadome, gpt, + gpt_diagnostics. +- Plan registrations: **2** - `prebid::register_for_plan` and + `aps::register_for_plan`, prepended by `IntegrationRegistry::with_plan` + (builders take only `&Settings`; these two need the compiled + `AuctionPlan`, the single config→runtime boundary shared with the + orchestrator via one `Arc`); APS registers iff + `plan.has_profile("aps")`. +- Profile registry: **3** - `standard` (auction-inherited timeout), + `prebid-server` (1000ms), `aps` (800ms), in `PROFILE_REGISTRATIONS`; + provider instances are operator-defined `[auction.providers.]` + (`ProviderId` grammar `^[a-z][a-z0-9-]{0,62}$`; `profile_config` is a + raw JSON object discriminated by the sibling `profile` field, compiled + into the profile's typed `deny_unknown_fields` struct) flowing through + `GenericOpenRtbProvider`. +- Mediator: `adserver_mock` via `register_providers`, matched exactly to + `auction.mediator`; it never enters the integration registry. +- JS: **12** integration `index.ts` modules (13 dirs; `aps` ships only a + render helper imported by core/prebid/gpt), **13** emitted bundles + (+core). `JS_ALWAYS = ["creative"]`. Three loading modes: bundled, + deferred (prebid only), standalone tag (`gpt_diagnostics`, which is + `.without_js()` in the registry but served via its own decision path). +- `IntegrationMetadata` omits post-processors, JS modes, and plan info - + the checked capability record (typed capability + config-predicate + conditions, fixture matrix evaluating both states) is the rendering + source; equality tests live module-local where the private registries + are visible. + +Capabilities (P proxy, AR attribute rewriter, SR script rewriter, HI head +injector, PP post-processor, RF request filter): prebid P/AR/HI + +deferred JS; aps HI always, P conditional on `trusted_server` rendering +mode, no JS bundle; testlight P/AR; nextjs SRx2/PP, no JS; permutive +P/AR; lockr P/AR; didomi P/HI; sourcepoint P/AR/HI; osano bare; +google_tag_manager P/AR/SR; datadome P/AR/HI + RF conditional on +`enable_protection`; gpt P/AR/HI; gpt_diagnostics bare + standalone JS; +creative JS-only, always injected. + +## Appendix D: CLI and per-adapter configuration handoff + +`ts` (the two-platform help union is canonical; macOS adds `dev proxy`): `audit page|generate|ad-templates generate|verify`, `active-version`, `auth login|logout|status`, `build`, `config init|diff|push|validate|gc|ad-templates lint|match|check|explain`, -`deploy`, `healthcheck`, `prebid bundle`, `provision`, `rollback`, `serve`, -`dev proxy [ca path|install|uninstall|regenerate]` (macOS only; `ts dev` -lists no subcommands on other hosts). `ts --version` is available -(`#[command(version)]`). Commands that detect drift (`config diff`, -`config ad-templates check`, audit verification) report a distinct drift -outcome with a stable exit code. `docs/guide/cli.md` must add -`active-version`, `healthcheck`, `rollback`, and `config gc`. - -Runtime environment variables to document (WP2 `.env.example` / `.env.dev`): -`FASTLY_SERVICE_VERSION`, `FASTLY_IS_STAGING`, `FASTLY_HOSTNAME`, -`FASTLY_REGION`, `EDGEZERO_LOG_LEVEL`, `PORT` (Axum), -`TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` / `TRUSTED_SERVER_SECRET_{STORE}_{KEY}` -(Axum stores), `TRUSTED_SERVER_CONFIG` (Cloudflare var), `EDGEZERO_*` store -overrides, and the build-time `TSJS_SKIP_BUILD`/`TSJS_TEST`. - -Documented separately from the runtime variables: the `TRUSTED_SERVER__` -typed overlay, ignored by the runtime loader but applied by -`ts config validate/diff/push` when building the config blob -(`crates/trusted-server-cli/tests/config_env_overlay.rs`). - -## Appendix E: Staleness finding index - -Compact index of audit findings driving WP1/WP2; verified against the main -baseline and re-verified on rc/202608 where marked. - -- Dead endpoints documented: `docs/guide/api-reference.md:86,191,707,711` - (rc; `/first-party/ad`, `/third-party/ad`); - `docs/guide/integrations-overview.md:46-48`; - `docs/guide/error-reference.md:658`; - `docs/guide/integrations/prebid.md:515-531`. -- Dead operator commands: `docs/guide/error-reference.md:597` - (`npm run type-check`), `:663` (`--validate-config`). -- Obsolete API examples: `docs/guide/key-rotation.md:301-310` - (`KeyRotationManager::new(...)?`; constructor returns `Self`), - unauthenticated admin curl examples; - `docs/guide/configuration.md:1954-1957` (rc; nonexistent - `settings_data::get_settings`, `println!` against repo conventions). -- Fabricated content: `docs/guide/ad-serving.md:11-18,43,48,77-83` (Equativ, - `[ad_servers]`, `trackImpression`); `docs/guide/architecture.md:97-104` - (`RequestWrapper`); `docs/guide/integration-guide.md:313` (equativ bidder). -- Integration-guide snippets that do not compile: - `integration-guide.md:96` (`handle` without `RuntimeServices`, vs - `registry.rs:282-288`), `:132` (`proxy_request` without `services`, vs - `proxy.rs:737-742`), `:134` (`use fastly::http` in core-neutral code). -- Fictional-data policy violations: `docs/guide/ec-setup-guide.md:15` (real - deployment domain); `.env.example:8-10` (non-reserved `publisher.com`). -- Wrong config names: `docs/guide/getting-started.md:141` (`[gdpr]`). -- Nonexistent builder method: `.with_asset(...)` in - `docs/guide/creative-processing.md:808`, - `docs/guide/integration-guide.md:84,248` (issue #277). -- Script-guard mechanism absent from the integration guide (issue #341). -- Old crate layout: `docs/roadmap.md:21-22` (the only surviving instance). -- Adapter support contradictions: `docs/index.md:27`, - `docs/guide/what-is-trusted-server.md:32`, `docs/roadmap.md:19,34-38` vs - `docs/guide/architecture.md:154-159`; Axum described as a deployment - target; Spin described as production-capable despite `build_state()` - loading the checked-in example toml and `startup_error_router()` - serving a blanket 503 on startup failure (`adapter-spin/src/app.rs`); - asset routes, request - filters, image optimizer, and Tinybird emission are Fastly-only but - documented as generic. -- Aspirational sidebar pages: `docs/guide/integrations/gam.md` (no such - integration, "Q1 2026" passed), `kargo.md`. -- Auction README: route table file/line rot, nonexistent `providers/` dir, - removed APS `mock` key (`crates/trusted-server-core/src/auction/README.md:269-285,466-473,487-489,543-549,577`). -- Dead links: `docs/guide/onboarding.md:51,107` (`SEQUENCE.md`); - `CHANGELOG.md:51-52` (no `v1.2.0` tag). -- CHANGELOG: retired `synthetic_id` naming in `[Unreleased]` (`:24`), - nonexistent `.rust-analyzer.json` (`:51`), missing #992 entry, section - order, typos. -- Environment files: `.env.example` and `.env.dev` both carry retired - `TRUSTED_SERVER__SYNTHETIC__*` keys; `docs/guide/getting-started.md:74-77` - tells users to copy and source `.env.dev`. -- Integration-tests README: wrong CI job name (`:231`), missing environment - files (`:165-177`), missing browser spec (`:141-145`). -- fastly.toml: personal email (`:4`), service id (`:10`, ops-owned - follow-up), orphaned script reference (`:38`), unlabeled key fixtures - (`:48-74`). -- Publishing: 120 `docs/superpowers/**` markdown files built into the public site (no - `srcExclude`); `docs/guide/onboarding.md` published with internal - contacts; `docs/public/CNAME` placeholder; empty `docs/guide/index.md`; - nav Guide link bypasses the landing page (`config.mts:61`); - `docs/package.json` not private, ISC license in an Apache-2.0 repo; - Pages deploys only from `main`, so rc-merged containment does not reach - the live site (`deploy-docs.yml:3`). -- Root-doc drift: `CLAUDE.md:102` (no workspace default target exists); - `CONTRIBUTING.md` stale since 2026-01. -- Slash-command drift: `.claude/commands/{check-ci,verify,test-all}.md` omit - Spin/cloudflare-wasm/parity gates; `test-crate.md` untargeted `cargo test`. -- Tooling: no `cargo doc` in CI; doctests never run (cross-compile only); - `format-docs` never runs `vitepress build`; `deploy-docs.yml` not - triggered by `.tool-versions` changes; CodeQL PR analysis limited to - `main` branches; `eslint-plugin-jsdoc` inert; - `openrtb-codegen` missing `[lints] workspace = true`; PR template says - `tracing`; no semantic parity checks for routes, config, CLI, - integrations, crates, navigation, or CI gates, and no binding between - inventories and the reader-facing markdown. +`deploy`, `healthcheck`, `prebid bundle`, `provision`, `rollback`, +`serve`, `dev proxy [ca ...]`. `--version` is available. Drift-detecting +commands use a stable drift exit code. rc's cli.md already covers the +lifecycle commands; the remaining work is the generated-region conversion +and description gating. + +Per-adapter configuration handoff (deployment-guide truth): + +- Fastly: config store `trusted_server_config` + secret store + `ts_secrets` (logical mapping via the `edgezero_runtime_env` config + store); `ts config push --local` mutates `fastly.toml`; the checked-in + store ships empty, so a bare `fastly compute serve` serves 500 on + non-health paths. +- Axum: config via + `TRUSTED_SERVER_CONFIG_TRUSTED_SERVER_CONFIG_TRUSTED_SERVER_CONFIG`, + secrets via `TRUSTED_SERVER_SECRET_{STORE}_{KEY}`. +- Cloudflare: `TRUSTED_SERVER_CONFIG` var = `{"app_config": ""}` (nested wrapper) + `wrangler secret put `; + push-written KV is unread by the Worker. +- Spin: blob in KV store label `default` (push requires + `EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME=default` or it + lands in the wrong store); secrets via encoded Spin variables + (`v__v_` encoding; empty defaults fail closed). + +Runtime environment variables and the `TRUSTED_SERVER__` CLI overlay are +documented as separate surfaces (WP2). + +## Appendix E: Still-open finding index (verified 2026-08-27) + +- `srcExclude` absent; 127 superpowers files in the CI-built site; empty + `guide/index.md`; nav Guide/Business Value links; CNAME placeholder. +- `fastly.toml:4,10,38` + inconsistent fixture labels; + `docs/package.json` ISC/not-private; onboarding published. +- `RequestWrapper` (`architecture.md:93-104`, + `.claude/agents/code-architect.md:16`); Equativ (`ad-serving.md:11`, + `.claude/agents/issue-creator.md:85`, `FAQ_POC.md`); `.with_asset` + (`creative-processing.md:808`, `integration-guide.md:84,248`); + `npm run type-check` (`error-reference.md:614`); + `settings_data::get_settings` (`configuration.md:2287`); auction README + rot; `SEQUENCE.md` links; `TESTING.md` runbook. +- `[consent]`/`[debug]` sections missing; `[tinybird]` Quick-Start-only; + Key Sections 10/17; reserved-field `imp_ext` docs/code mismatch; + integration subsections 5/14; `adserver_mock` stranded. +- CHANGELOG: 8 breaking entries, inconsistent markers, dead v1.2.0 links. +- Stale rustdoc: the Cloudflare `platform.rs:579-592` stores claim (and + its Spin sibling); `cloudflare.toml` dead; false workflow comments + (Spin release build, test-cli default target). +- Env files carry retired `TRUSTED_SERVER__SYNTHETIC__*` keys; + `opid_store` mismatch. +- Missing pages: cloudflare/spin/axum-dev/edgezero/telemetry/tsjs/ + adserver_mock guides; 7 crate READMEs; integration-guide snippets do + not compile. +- Enforcement gaps: no rustdoc/doctests in CI, no parity checks, jsdoc + inert, openrtb-codegen lints, PR template `tracing`, slash-command gate + drift. From 0e9441fedd785dca74fc7e4f10add9e3a46a2634 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 27 Aug 2026 22:37:05 -0700 Subject: [PATCH 16/33] Rework spec after ninth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 305 +++++++++++++----- 1 file changed, 220 insertions(+), 85 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index b80332c38..6c251024e 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,16 +1,22 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-27 (round 8; re-audited and regenerated against the moved release branch) +**Revised:** 2026-08-27 (round 9) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. -**Baseline:** `rc/202608` at merge base `a163367b3` (2026-08-27, the main→rc -merge that landed #870). Every inventory in this spec was regenerated from -that commit by a fresh four-track delta audit. Baseline contract: before -implementation starts, and after any rebase, assert that the branch's merge -base with `origin/rc/202608` equals the SHA recorded here; if it does not, -re-run the delta audit and update this spec first. Rounds 1-7 of review -history live in git; this revision supersedes their inventories. +**Baseline:** audited_target_tip `985ff2298` ("Restore secret-store key +references in the config template", 2026-08-27). The bulk inventories were +regenerated by a four-track delta audit at `a163367b3` (the main→rc merge +that landed #870); the one commit between them was reviewed individually +(it restored secret-store key references in the template and widened the +literal-placeholder consumer surface; folded into WP3/WP8a below). +Baseline contract - a merge-base check alone false-greens when the target +advances without a rebase, so the guard is exact-tip: after fetching, +`origin/rc/202608` must equal the recorded audited_target_tip AND the +implementation branch must contain that commit; the same assertion repeats +at the final PR HEAD. If either fails, rebase, re-audit the delta, and +update this spec first. Rounds 1-8 of review history live in git; this +revision supersedes their inventories. ## Context @@ -187,7 +193,8 @@ selector}` (whole-file vs comment-region), and the WP8b inventory gate - No runtime changes. Code defects found by the audits are follow-ups (list below), not in-scope work; parity checks add tests/tools only. -- No new documentation toolchains beyond the `tools/docs-parity` dev tool. +- No new documentation toolchains beyond the `tools/docs-parity` dev + tool and the SHA-pinned external link-checker action. - No rewrite of `business-use-cases.md` marketing copy: it is excluded from the build via `srcExclude` and carries a source-level unverified banner (WP1) until an evidence-based rewrite happens (open question 4). @@ -206,7 +213,12 @@ selector}` (whole-file vs comment-region), and the WP8b inventory gate ## Delivery shape -Owner-directed single rc PR (#1049) carrying the spec plus all packages, +The owner's standing instruction is one rc PR carrying spec plus work; +the concrete shape below (which adds the forced `main` containment PR and +package checkpoints) still awaits explicit confirmation - open question 7 +blocks implementation until it is given, and its answer is recorded with +owner and date. The shape: a single rc PR (#1049) carrying the spec plus +all packages, one reviewable commit (or small series) per package, with package-level review checkpoints (acceptance evidence recorded before the next package lands), generated-output changes in their own commits, and no squash on @@ -245,6 +257,12 @@ the owner to confirm this shape. the `test-prebid-eids.sh` comment. `service_id` stays under its allowlist entry pending the ops migration (open question 1). - `docs/package.json`: `"private": true`, license Apache-2.0. +- `CLAUDE.md` policy amendment lands HERE (not WP6): the sensitive-data + policy gains the owner-approved exception taxonomy (vendor URL, + hash-pinned fake-credential fixture, historical example, service ID, + project-owned public domain - each with owner, rationale, expiry), so + the scanner and every later checkpoint rely on a policy that is already + canonical rather than knowingly violating the written one until WP6. - `.github/pull_request_template.md`: `tracing` → `log`; test-plan gates become a link to the canonical gate region (link-only mode). - `.claude/commands/*.md` converted to link-only gate references; @@ -258,8 +276,10 @@ every command file links to (not copies) the canonical gates. ### WP2: Truth pass over existing content -Disposition-based: every document in the active public and maintained -internal sets gets verified / rewrite / retire / created recorded in a +Disposition-based: every document in all three active sets (public, +repo, and maintained internal - root markdown, config templates, +`edgezero.toml`, env files, and command files included) gets +verified / rewrite / retire / created recorded in a checked inventory under `docs/internal/audits/`, stamped with the audited merge-base SHA, with source anchors; non-page surfaces get region-level dispositions. The inventory is an audit record; the WP8 gates are the @@ -305,11 +325,13 @@ type-check` and the `settings_data::get_settings` example (the exported - Roadmap status pass (shipped/active/deferred; also reconcile the malvertising-detection claim with `business-use-cases.md`). -Acceptance: checked-in inventory complete; greps over the active sets -(historical exempt) for `RequestWrapper`, `equativ`, `with_asset`, +Acceptance: checked-in inventory complete over all three active sets; +the retired-token scan is case-insensitive and whitespace-tolerant +(`rg -i` for prose identifiers; pattern classes for spacing variants such +as `mock\s*=\s*true`) for `RequestWrapper`, `Equativ`, `with_asset`, `type-check`, `settings_data::get_settings`, `SEQUENCE.md`, -`synthetic_id` (outside shipped changelog entries), and `mock = true` (APS -context) return nothing; the all-tracked privacy scan is clean modulo the +`synthetic_id` (outside shipped changelog entries), and the APS `mock` +key, returning nothing; the all-tracked privacy scan is clean modulo the allowlist; checkpoint scope = surfaces this package touches, full-set greps re-run at final HEAD. @@ -327,17 +349,42 @@ provider profile schemas, and the secret model. typed profile schemas (each profile's fields, defaults, timeout defaults, byte/depth/key limits, endpoint canonicalization, reserved fields). -- `trusted-server.example.toml`: add the missing commented blocks - (`[consent]`, `[rewrite]`, `[tester_cookie]`, `[image_optimizer]`, - `[[proxy.asset_routes]]`, osano; extend `[tinybird]` if incomplete), - preserving the template's contract: placeholder strings must stay in - the rejection constants' exact forms (three test call sites splice on - the literal strings and the `# [header]` comment style), and - secret-reference fields carry key names, never values. -- Secret-model documentation: classify every `Redacted` path as - store-resolved (the 11 `secret_fields()` entries) or deliberately - inline (`trusted_client_ip.shared_secret`, - `tinybird.access_token_secret`) with explicit exposure guidance; the +- `trusted-server.example.toml`: the template already carries commented + blocks for every root (`[consent]` L151, `[rewrite]` L141, + `[tester_cookie]` L147, `[image_optimizer]` L209, + `[[proxy.asset_routes]]` L183, osano L507 at the baseline), so the work + is an exact present/missing/repair inventory, not block creation: + validate every block against the field inventory, and REMOVE the stale + recommendations - the template still suggests the four legacy store + selectors (`proxy.asset_routes.auth.secret_store`, + `tinybird.secret_store`, + `integrations.datadome.server_side_key_secret_store`, + `integrations.datadome.protection_test_bypass.credential_secret_store`) + that the code accepts-and-discards and the migration guide tells + operators to delete. Preserve the template's contract: placeholder + strings stay in the rejection constants' exact forms and secret + references carry key names, never values. The literal-string consumer + surface is wider than three call sites since `985ff2298`: the config.rs + template tests, the CLI ad_templates substitutions, `config init`, the + audit `generate`/`validate` commands, and + `scripts/template-cache-local-test.sh` all splice on exact strings - + the WP8a harness enumerates these consumers and CI fails when a + placeholder change strands one. +- Directional field dispositions: the inventory classifies every path as + one of canonical-runtime-active, accepted-deprecated-alias, + accepted-but-normalized-away, deserialization-only, + skipped-at-serialization, store-resolved secret, deliberately-inline + secret, or rejected. `tinybird.access_token_secret` is + accepted-but-normalized-away (deserialized, then set to `None` and + never serialized) - not a deliberately inline secret; only + `trusted_client_ip.shared_secret` currently holds that classification. + Deprecated, ignored, and deserialization-only paths are documented as + such and never promoted into the template as normal fields. +- Secret-model documentation: classify every `Redacted` path with its + directional disposition - store-resolved (the 11 `secret_fields()` + entries), deliberately inline (`trusted_client_ip.shared_secret`, with + exposure guidance), or accepted-but-discarded + (`tinybird.access_token_secret`); the migration section already on rc gets a verified disposition. CLI/config pages warn that `ts config diff`/`--dry-run`/push output can print inline-secret values. @@ -349,9 +396,11 @@ provider profile schemas, and the secret model. functions; `serde(skip)` fields never become documented paths; canonical keys vs deprecated aliases (`pub_id`, `s3_sig_v4`). -Acceptance: every inventory field path appears in template and reference; -every deploy-validated ID has a config subsection matching its struct; -the WP8a harness passes; the parity checklist (Appendix B) is in the PR +Acceptance: every canonical-runtime-active field path appears in +template and reference; deprecated/ignored/deserialization-only paths +appear only with their directional disposition stated; every +deploy-validated ID has a config subsection matching its struct; the +WP8a harness passes; the parity checklist (Appendix B) is in the PR description. ### WP4: API reference completion @@ -393,23 +442,37 @@ contract checklist satisfied; manual-ownership markers present. secret store) from clean state: - `fastly.md` additions + quick start: init/validate → `ts config push --adapter fastly --local` → seed the three required - `ts_secrets` keys → `fastly compute serve` → `/health` → publisher - request against a stub origin → restore the mutated `fastly.toml`. - - `cloudflare.md`: the nested `{"app_config": ""}` wrapper, - the documented envelope-transfer bridge (push writes KV that the - Worker never reads - warn that a green push does not configure the - Worker), `wrangler secret put` for the required key names, the - `wrangler.ci.toml`/generated-manifest pattern, no `/health` - (readiness via another route), the single-provider restriction, and - the unwired stores (`TRUSTED_SERVER_KV` never opened) per the - follow-up. + `ts_secrets` keys (exact copyable edit of + `[local_server.secret_stores.ts_secrets]` entries) → + `fastly compute serve` → `/health` → publisher request against a + stub origin → restore the mutated `fastly.toml`. All guide commands + are exact and copyable; "seed the keys" prose is not acceptance. + - `cloudflare.md`: an executable bridge, not a concept. Decided + retrieval path: after `ts config push --adapter cloudflare --local`, + read the envelope back from local Wrangler KV + (`wrangler kv key get trusted_server_config --local` against the + manifest's namespace), double-encode it into + `{"app_config": ""}` with `jq`, and write it into a + gitignored generated manifest (the `wrangler.ci.toml` + + single-placeholder substitution pattern the integration harness + uses) or `.dev.vars` for `wrangler dev`; remote deploys set the var + via the dashboard/`wrangler` and provision secrets with + `wrangler secret put ` (a production flow - local secrets come + from generated `[vars]`/`.dev.vars`, never `secret put`). `--local` + variants are the documented default to prevent accidental remote + writes; cleanup removes the generated files. The guide carries the + exact copyable commands, warns that a green push does not configure + the Worker, and notes no `/health`, the single-provider restriction, + and the unwired `TRUSTED_SERVER_KV`. The preferred end state is the + CLI envelope-export follow-up, which retires the KV read-back. - `spin.md` (now writable - the runtime fix landed): `ts config push --adapter spin --local` with the required `EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME=default` mapping (push writes SQLite under `.spin/`; runtime reads store label `default` - the mismatch footgun is documented), Spin variable names generated from the operator's key names via the encoder (empty - defaults fail closed), `spin up`, a non-health request, cleanup. + defaults fail closed; exact copyable `SPIN_VARIABLE_*` exports or + provider config), `spin up`, a non-health request, cleanup. Maturity label from current evidence: experimental - no integration-test environment, single-provider only, request-time config/KV stores unwired. @@ -437,9 +500,19 @@ contract checklist satisfied; manual-ownership markers present. - CLI reference from the two-platform help union; description text passes the internal-term gate with the expiring override table. +- The four first-success journeys become recurring smoke scripts + (`scripts/smoke-{axum,fastly,cloudflare,spin}.sh`), each starting from + the documented CLI commands (not the harness's internal shortcuts - + today's Cloudflare integration coverage bypasses the push path), wired + into the integration-test workflow where runnable (Axum, Fastly, + Cloudflare). If Spin cannot run in CI, its manual evidence carries a + named owner, tested SHA and tool versions, and an expiry date - one PR + run is not continuing enforcement. + Acceptance: every ID documented and nav-reachable; every adapter guide consistent with the support record; snippets compile; `vitepress build` -green; search/banner/diagram/journey assertions recorded; each adapter +green; search/banner/diagram/journey assertions recorded; the smoke +scripts pass in CI (Spin per its evidence contract); each adapter smoke's exact commands and cleanup recorded in the PR description. ### WP6: Root markdown and crate READMEs @@ -460,8 +533,9 @@ recorded; quick-start journeys proven. ### WP7: In-code documentation -Worklist (acceptance scope): core `lib.rs` module index (12 of 37 listed -today); `platform/` docs (4 of 10 files documented on rc; test-only +Worklist (acceptance scope): core `lib.rs` module index (13 bullets for +38 `pub mod`s today); `platform/` docs (4 of 10 files documented on rc; +test-only excluded); crate-level headers for fastly/cloudflare/js/cli; module docs for the undocumented core files (settings, settings_data, http_util, proxy, auth, tsjs, openrtb, price_bucket, rsc_flight, host_rewrite, @@ -470,9 +544,20 @@ storage, html_processor, registry, prebid, nextjs/, datadome/); complete `core/types.ts`), `build-prebid-external.mjs` header. Plus the known-false rustdoc repairs: the Cloudflare `platform.rs:579-592` stores claim (and its Spin sibling) rewritten to match the unwired reality, -citing the follow-up. Rustdoc command matrix as before (CI triple -`x86_64-unknown-linux-gnu`; pinned Node for the js build script; doctest -job gets the same Node setup). +citing the follow-up. + +Rustdoc command matrix (self-contained; run with +`RUSTDOCFLAGS="-D warnings"`; the core/js documentation and native +doctest jobs need pinned Node because the js crate's build script runs +npm): + +- `cargo doc --no-deps -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1` +- `cargo doc --no-deps -p trusted-server-adapter-fastly --target wasm32-wasip1` +- `cargo doc --no-deps -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare` +- `cargo doc --no-deps -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin` +- `cargo doc --no-deps -p trusted-server-adapter-axum` +- `cargo doc --no-deps -p trusted-server-cli -p trusted-server-openrtb-codegen --target x86_64-unknown-linux-gnu` (CI; locally substitute the host triple) +- Native doctests: `cargo test --doc -p trusted-server-core` (host). Acceptance: worklist complete; matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the WP8 jsdoc lint (mandatory, scoped) @@ -495,14 +580,27 @@ WP8a (lands right after WP1): rationale/expiry/source-text staleness check), snippet-manifest tooling (all languages; graded modes incl. `expected_compile_failure` / `expected_validation_failure` / `illustrative_fragment`; expiring - waivers), the domain/credential scanner + typed allowlist, the - maintained-source manifest checker, and the gate manifest. -- The example harness, redesigned around the secret model: validate the - template as a deploy-time, key-name-bearing `TrustedServerAppConfig` - (placeholder strings intact - the template must keep failing deploy - until customized); serialize the blob envelope; resolve through a fake - secret store; run runtime validation post-resolution; never substitute - plaintext into the template itself. Enumerate every `[integrations.*]` + waivers), the domain/credential scanner + typed allowlist (with a + defined input contract: text files detected by content, invalid UTF-8 + and binaries scanned as metadata/path only, a size cap, generated + lockfiles and image assets excluded structurally by path+type rather + than hash-by-hash allowlisting, cryptographic checksums and integrity + strings recognized structurally, and base64 test fixtures + distinguished via the hash-pinned fixture allowlist class - results + must be deterministic across platforms), the maintained-source + manifest checker, and the gate manifest. +- The example harness, in eight explicit phases (secret resolution + alone cannot make the template valid: placeholder rejection also + covers the non-secret publisher fields `domain`, `cookie_domain`, and + `origin_url`): (1) parse the unchanged source template; (2) assert it + FAILS deploy validation for exactly the expected placeholder set; + (3) apply deterministic non-secret customization in memory, never by + editing the template; (4) assert the customized config passes deploy + validation with secret key names intact; (5) serialize and verify the + blob envelope; (6) resolve through a fake secret store; (7) assert + runtime validation passes post-resolution; (8) run each optional + block and profile through isolated positive and negative probes. + Enumerate every `[integrations.*]` subtree (grouped by first-segment ID, nested tables in their parent) and every `[auction.providers.]` entry - commented or active, enabled or disabled - deserializing into the typed structs with @@ -515,6 +613,17 @@ WP8a (lands right after WP1): registries (builders, plan registrations, profile registry, mediator, JS module sets - replacing the deploy-ID constant's one-directional subset check) against the same checked records the tool renders from. + Capability parity is behavioral, not ID-level: the tests instantiate + each integration under the predicate fixture matrix and inspect the + resulting registration state (proxy routes, rewriters, injectors, + post-processors, request filters, JS mode), comparing observed + capabilities against the record - so a record claiming a capability + after its `with_*` call changes fails. APS plan-dependent registration + and the DataDome filter are asserted in both predicate states. In the + capability and adapter-support records, executable columns (fan-out, + routes, status behavior) are test-backed; "operational support" and + "release status" are manually owned fields with a named owner and + review date, never implied to be code-derived. WP8b (lands last): @@ -526,9 +635,12 @@ WP8b (lands last): - Workflow edits: CodeQL `rc/*` PR triggers; `.tool-versions` in deploy-docs paths; normalized setup-node cache keys (all workflows, to lockfiles); Dependabot roots (github-actions, browser and Next.js - fixture npm, docs-parity cargo); pinned Wrangler; the scheduled - external link check (pinned checker, job-scoped `issues: write`, - dedup, auto-close, named owner, fixture-tested); + fixture npm, docs-parity cargo); Wrangler pinned in `.tool-versions` + (the same pin consumed locally and in CI); the scheduled external link + check using a checker pinned by action SHA (versions chosen from + current stable at implementation and recorded where pinned; + job-scoped `issues: write`, dedup, auto-close, named owner, + fixture-tested); `[lints] workspace = true` for openrtb-codegen. Where a governance value can be asserted deterministically (a YAML-parsing static test over workflow triggers, cache keys, Dependabot roots), it is; the @@ -614,9 +726,10 @@ baseline assertion (merge base equals the recorded SHA) passing. - `ec.partners[*].ts_pull_token` template placeholder (`replace-with-partner-ts-pull-token`) is in no rejection constant list. -- `trusted_client_ip.shared_secret` and `tinybird.access_token_secret` - are `Redacted` but inline in the blob (not in `secret_fields()`) - - confirm intended or migrate to store references. +- `trusted_client_ip.shared_secret` is `Redacted` but inline in the + blob (not in `secret_fields()`) - confirm intended or migrate to a + store reference. (`tinybird.access_token_secret` is accepted and + discarded at normalization; its only fix is documentation.) - The deploy-ID constant is `#[cfg(test)]` with a one-directional subset check (a stale extra entry passes) - superseded by the WP8a set-equality test, but the constant itself should be fixed or removed. @@ -637,32 +750,52 @@ outermost on Axum/Cloudflare/Spin; Fastly sanitizes in `main.rs`; only Fastly resolves the client IP from it). Spin adds an innermost `NormalizeMiddleware` (spin-header derivation). -| Route | Methods | Fastly | Axum | Cloudflare | Spin | -| -------------------------------------- | ------------------------------- | ------------------------------------ | ------------- | ------------------------------- | ---------------------------------- | -| `/health` | GET | pre-router, survives startup failure | real | **absent** (falls to publisher) | real; also alive in the 503 router | -| `/_ts/debug/ja4` | GET | pre-router, config-gated | - | - | - | -| `/.well-known/trusted-server.json` | GET | real | real | real | real | -| `/verify-signature` | POST | real | real | real | real | -| `/_ts/admin/keys/{rotate,deactivate}` | POST | real | 501 | 501 | 501 | -| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | real (EC KV) | not-supported | not-supported | not-supported | -| `/_ts/admin/eids` | GET | real | real | real | real | -| `/admin/keys/*` (legacy) | 7 methods | 404 deny | 404 deny | 404 deny | 404 deny | -| `/_ts/api/v1/batch-sync` | POST | real (Bearer + rate limit) | - | - | - | -| `/_ts/api/v1/identify` | GET, OPTIONS | real | - | - | - | -| `/_ts/set-tester`, `/_ts/clear-tester` | GET | real (gated) | - | - | - | -| `/auction` | POST | real | real | real | real | -| `/_ts/page-bids`, `/__ts/page-bids` | GET; OPTIONS denied in-handler | real | real | real | real | -| `/first-party/{proxy,click}` | GET | real | real | real | real | -| `/first-party/{sign,proxy-rebuild}` | GET, POST | real | real | real | real | -| `/static/tsjs=` | GET (fallback chain) | real | real | real | real | -| `/integrations//...` | varies; families per Appendix C | real | real | real | real | -| asset route prefixes | GET, HEAD | Fastly only | - | - | - | -| publisher fallback | 7 explicit methods | real | real | real | real | +| Route | Methods | Fastly | Axum | Cloudflare | Spin | +| -------------------------------------- | ------------------------------------ | ------------------------------------ | ------------- | ------------------------------- | ---------------------------------- | +| `/health` | GET | pre-router, survives startup failure | real | **absent** (falls to publisher) | real; also alive in the 503 router | +| `/_ts/debug/ja4` | GET | pre-router, config-gated | - | - | - | +| `/.well-known/trusted-server.json` | GET | real | real | real | real | +| `/verify-signature` | POST | real | real | real | real | +| `/_ts/admin/keys/{rotate,deactivate}` | POST | real | 501 | 501 | 501 | +| `/_ts/admin/ec`, `/_ts/admin/ec/{id}` | GET | real (EC KV) | not-supported | not-supported | not-supported | +| `/_ts/admin/eids` | GET | real | real | real | real | +| `/admin/keys/*` (legacy) | 7 methods | 404 deny | 404 deny | 404 deny | 404 deny | +| `/_ts/api/v1/batch-sync` | POST | real (Bearer + rate limit) | - | - | - | +| `/_ts/api/v1/identify` | GET, OPTIONS | real | - | - | - | +| `/_ts/set-tester`, `/_ts/clear-tester` | GET | real (gated) | - | - | - | +| `/auction` | POST | real | real | real | real | +| `/_ts/page-bids`, `/__ts/page-bids` | GET; OPTIONS denied in-handler | real | real | real | real | +| `/first-party/{proxy,click}` | GET | real | real | real | real | +| `/first-party/{sign,proxy-rebuild}` | GET, POST | real | real | real | real | +| `/static/tsjs=` | GET (fallback chain) | real | real | real | real | +| `/integrations//...` | per the route-family inventory below | real | real | real | real | +| asset route prefixes | GET, HEAD | Fastly only | - | - | - | +| publisher fallback | 7 explicit methods | real | real | real | real | + +Integration route families (the checked inventory WP4 generates from; +disabled integrations register nothing and their paths fall through to +the publisher proxy; first registration wins on collisions): + +- Fixed: `prebid` `GET /integrations/prebid/bundle.js`; `aps` + `GET /integrations/aps/renderer` (conditional: only in + `trusted_server` rendering mode); `testlight` + `POST /integrations/testlight/auction`; `didomi` configurable-prefix + loader/api routes; `datadome` `tags.js`, `/js/`, `/js/*`; + `google_tag_manager` `gtm.js`, `gtag/js`, `gtag.js`, `collect`, + `g/collect`; `gpt` `script`, `pagead/*`, `tag/*`; `sourcepoint` + `cdn/*`; `permutive` `api/*`, `secure-signal/*`, `events/*`, `sync/*`, + `cdn/*`, `sdk`; `lockr` `sdk`, `api/*`. +- Prefix-overridable: every integration's default + `/integrations/` prefix can be overridden by its registration. +- Config-derived: `prebid` additionally neutralizes operator-configured + `script_patterns` plus `/static/prebid/{*rest}`. +- Methods are per-registration (GET for scripts/CDN, GET+POST for + beacon/api paths); the WP8a route snapshot records each. Startup failure: Spin installs a hardened 503 router (generic body, `/health` 200, all fallback methods); Cloudflare and Fastly serve 500 -from the error router; the Cloudflare and Axum degraded routers answer on -every path. EC partner API, tester cookies, and JA4 remain Fastly-only +from the error router; the Cloudflare and Axum degraded routers answer +the underlying error status on every path (no `/health` carve-out). EC partner API, tester cookies, and JA4 remain Fastly-only (they need platform KV and entry-point wiring). Fastly-only capabilities: asset routes, image optimizer, request filters (DataDome pre-route), Tinybird emission. Provider fan-out: Fastly and Axum allow multiple @@ -701,7 +834,9 @@ resolution flow is verify → `remove_inactive_secret_references` → `resolve_secret_references` → `from_json_value` → `validate_settings_for_runtime`; deploy validation excludes secret-leaf attributes and requires key names conditionally. Redacted-but-inline: -`trusted_client_ip.shared_secret`, `tinybird.access_token_secret`. +`trusted_client_ip.shared_secret` only; `tinybird.access_token_secret` +is accepted-but-normalized-away (set to `None`, never serialized). +Directional dispositions (WP3) classify every path. Template placeholder constants and their three literal-string test call sites (the config.rs template tests, the CLI ad_templates substitutions, the `config init` byte source) are load-bearing. Validator inventory for From 91f4e6d54660e3116d246b175717fda3e89c7d3a Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:57:48 -0700 Subject: [PATCH 17/33] Rework spec after tenth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 153 ++++++++++++------ 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 6c251024e..9084c4829 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-27 (round 9) +**Revised:** 2026-08-27 (round 10) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `985ff2298` ("Restore secret-store key @@ -13,9 +13,12 @@ literal-placeholder consumer surface; folded into WP3/WP8a below). Baseline contract - a merge-base check alone false-greens when the target advances without a rebase, so the guard is exact-tip: after fetching, `origin/rc/202608` must equal the recorded audited_target_tip AND the -implementation branch must contain that commit; the same assertion repeats -at the final PR HEAD. If either fails, rebase, re-audit the delta, and -update this spec first. Rounds 1-8 of review history live in git; this +implementation branch must contain that commit; the same exact-tip +assertion (never a merge-base comparison) repeats at the final rc-PR +HEAD. The containment work targets `main`, so an audited_main_tip is +recorded when the containment PR is cut and the same exact-tip assertion +runs before that PR merges. If any assertion fails, rebase, re-audit the +delta, and update this spec first. Rounds 1-8 of review history live in git; this revision supersedes their inventories. ## Context @@ -248,10 +251,14 @@ the owner to confirm this shape. content. - `business-use-cases.md` gains its source-level unverified banner (asserted by WP1 acceptance). -- Resolve `docs/public/CNAME` in its own follow-up commit (open question - 2; both branches specified: delete and re-smoke project URLs, or custom - domain with `base: '/'`, Pages/DNS/TLS setup, canonical+asset smokes, - and a project-owned-public-domain allowlist classification). +- Resolve `docs/public/CNAME` (open question 2; both branches specified: + delete and re-smoke project URLs, or custom domain with `base: '/'`, + Pages/DNS/TLS setup, canonical+asset smokes, and a + project-owned-public-domain allowlist classification). Because only + `main` deploys, an rc-only edit never reaches the live site: the + resolved disposition ships inside the containment PR if question 2 is + answered by then, otherwise as a second `main`-target PR. Question 2 + plus its live smoke is a completion gate for this refresh either way. - `fastly.toml`: empty `authors` list; label the key fixtures consistently as local test fixtures; comment the four KV stores; remove the `test-prebid-eids.sh` comment. `service_id` stays under its @@ -330,8 +337,10 @@ the retired-token scan is case-insensitive and whitespace-tolerant (`rg -i` for prose identifiers; pattern classes for spacing variants such as `mock\s*=\s*true`) for `RequestWrapper`, `Equativ`, `with_asset`, `type-check`, `settings_data::get_settings`, `SEQUENCE.md`, -`synthetic_id` (outside shipped changelog entries), and the APS `mock` -key, returning nothing; the all-tracked privacy scan is clean modulo the +`synthetic_id` (outside shipped changelog entries), +`TRUSTED_SERVER__SYNTHETIC__` (the retired env-overlay root in +`.env.example`/`.env.dev`, with its own synthesized regression fixture), +and the APS `mock` key, returning nothing; the all-tracked privacy scan is clean modulo the allowlist; checkpoint scope = surfaces this package touches, full-set greps re-run at final HEAD. @@ -370,11 +379,14 @@ provider profile schemas, and the secret model. `scripts/template-cache-local-test.sh` all splice on exact strings - the WP8a harness enumerates these consumers and CI fails when a placeholder change strands one. -- Directional field dispositions: the inventory classifies every path as - one of canonical-runtime-active, accepted-deprecated-alias, - accepted-but-normalized-away, deserialization-only, - skipped-at-serialization, store-resolved secret, deliberately-inline - secret, or rejected. `tinybird.access_token_secret` is +- Directional field dispositions as independent axes, not a flat + "one of" (real fields overlap: `S3SigV4AuthConfig.secret_store` is + simultaneously deprecated, skip-serializing, and normalized away) - + lifecycle (canonical / deprecated-alias / rejected), serialization + (serialized / skipped), runtime (active / normalized-away / + deserialization-only), and secret handling (store-resolved / + deliberately-inline / none); the generated reference renders every + applicable axis. `tinybird.access_token_secret` is accepted-but-normalized-away (deserialized, then set to `None` and never serialized) - not a deliberately inline secret; only `trusted_client_ip.shared_secret` currently holds that classification. @@ -427,10 +439,17 @@ contract standard and binds it to generated regions. live `/health`; Cloudflare/Fastly 500; degraded-router differences). - `trusted_client_ip` documented as middleware (sanitization on all adapters, IP resolution only on Fastly), not a route. -- Cloudflare route parity source: `docs-parity` parses the `build_router` - chain with a fail-closed grammar that expands the known constants and - loops (path arrays, `publisher_fallback_methods()`); an unrecognized - construct fails the check rather than undercounting. +- Route parity is mechanically closed over all four adapters, not just + Cloudflare: Fastly, Axum, and Spin expose named route collections that + the per-adapter tests snapshot directly; Cloudflare's inline + `build_router` chain is parsed by `docs-parity` with a fail-closed + grammar that expands the known constants and loops (path arrays, + `publisher_fallback_methods()`) - an unrecognized construct fails the + check rather than undercounting. Each adapter's snapshot covers the + full route set, methods, predicates, unsupported/guarded semantics, + and the startup-error router behavior, asserted as set equality + against the checked inventory - a new route on any adapter fails CI + until the inventory and generated regions update. Acceptance: generated regions match the checked inventory per adapter; contract checklist satisfied; manual-ownership markers present. @@ -449,9 +468,15 @@ contract checklist satisfied; manual-ownership markers present. are exact and copyable; "seed the keys" prose is not acceptance. - `cloudflare.md`: an executable bridge, not a concept. Decided retrieval path: after `ts config push --adapter cloudflare --local`, - read the envelope back from local Wrangler KV - (`wrangler kv key get trusted_server_config --local` against the - manifest's namespace), double-encode it into + read the envelope back from local Wrangler KV with an explicit + binding selector (the guide first defines the binding strategy: the + config store gets its own provisioned namespace mapped via + `EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME`, or reuses + the existing `TRUSTED_SERVER_KV` namespace with that mapping - one + of the two is chosen and provisioned before the first push, so the + clean-state journey never pushes into an unmapped namespace): + `wrangler kv key get trusted_server_config --binding +--local` (or `--namespace-id`), then double-encode it into `{"app_config": ""}` with `jq`, and write it into a gitignored generated manifest (the `wrangler.ci.toml` + single-placeholder substitution pattern the integration harness @@ -463,8 +488,12 @@ contract checklist satisfied; manual-ownership markers present. writes; cleanup removes the generated files. The guide carries the exact copyable commands, warns that a green push does not configure the Worker, and notes no `/health`, the single-provider restriction, - and the unwired `TRUSTED_SERVER_KV`. The preferred end state is the - CLI envelope-export follow-up, which retires the KV read-back. + and the unwired request-time `TRUSTED_SERVER_KV`. The smoke + terminates with a publisher request asserting EXPECTED rewritten + content, not merely a response - the degraded error router also + answers, so status-only checks false-green. The preferred end state + is the CLI envelope-export follow-up, which retires the KV + read-back. - `spin.md` (now writable - the runtime fix landed): `ts config push --adapter spin --local` with the required `EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME=default` @@ -551,17 +580,30 @@ Rustdoc command matrix (self-contained; run with doctest jobs need pinned Node because the js crate's build script runs npm): -- `cargo doc --no-deps -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1` +- `cargo doc --no-deps --all-features -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1` - `cargo doc --no-deps -p trusted-server-adapter-fastly --target wasm32-wasip1` - `cargo doc --no-deps -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare` - `cargo doc --no-deps -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin` -- `cargo doc --no-deps -p trusted-server-adapter-axum` -- `cargo doc --no-deps -p trusted-server-cli -p trusted-server-openrtb-codegen --target x86_64-unknown-linux-gnu` (CI; locally substitute the host triple) +- `cargo doc --no-deps --all-features -p trusted-server-adapter-axum` +- `cargo doc --no-deps --all-features -p trusted-server-cli -p trusted-server-openrtb-codegen --target x86_64-unknown-linux-gnu` (CI; locally substitute the host triple) +- The adapter invocations deliberately pin explicit features instead of + `--all-features` because their features are target-gated (the + cloudflare feature carries a non-wasm32 `compile_error!`); everywhere + else `--all-features` applies, matching the repo's documented rustdoc + rule and covering core's test-utils feature. - Native doctests: `cargo test --doc -p trusted-server-core` (host). +The JSDoc contract is explicit: the ESLint jsdoc rule set applies to +the WP7 file globs (`lib/src/core/render.ts`, `lib/src/core/types.ts`, +`lib/src/core/registry.ts`, `lib/src/shared/globals.ts`, +`lib/src/integrations/creative/**`), requiring a file-header block plus +JSDoc on every exported declaration (functions, classes, interfaces, +type aliases) via `jsdoc/require-jsdoc` with those contexts, plus +`jsdoc/check-alignment` and `jsdoc/check-types`. + Acceptance: worklist complete; matrix builds warning-free with -`RUSTDOCFLAGS="-D warnings"`; the WP8 jsdoc lint (mandatory, scoped) -green. +`RUSTDOCFLAGS="-D warnings"`; the WP8 jsdoc lint (mandatory, scoped as +above) green. ### WP8: Enforcement (WP8a scaffolding early, WP8b activation last) @@ -581,13 +623,16 @@ WP8a (lands right after WP1): (all languages; graded modes incl. `expected_compile_failure` / `expected_validation_failure` / `illustrative_fragment`; expiring waivers), the domain/credential scanner + typed allowlist (with a - defined input contract: text files detected by content, invalid UTF-8 - and binaries scanned as metadata/path only, a size cap, generated - lockfiles and image assets excluded structurally by path+type rather - than hash-by-hash allowlisting, cryptographic checksums and integrity - strings recognized structurally, and base64 test fixtures - distinguished via the hash-pinned fixture allowlist class - results - must be deterministic across platforms), the maintained-source + defined input contract: text files detected by content; an + expected-text file that is oversized or invalid UTF-8 FAILS the scan + rather than being skipped (thresholds must not become evasion + mechanisms); generated lockfiles are not blanket-excluded - their + structured URL/source/registry fields are scanned while checksum and + integrity strings are recognized structurally; known media assets get + metadata-string inspection (or are individually manifest-listed with + rationale); true binaries are scanned by path/metadata; base64 test + fixtures distinguish via the hash-pinned fixture allowlist class - + results must be deterministic across platforms), the maintained-source manifest checker, and the gate manifest. - The example harness, in eight explicit phases (secret resolution alone cannot make the template valid: placeholder rejection also @@ -632,6 +677,12 @@ WP8b (lands last): jsdoc lint, gate-manifest check across every surface in its mode, repo/orphan/tombstone inventory, disposition-set equality, maintained-source manifest equality. +- Link enforcement per set: VitePress covers only the built public set + (and after `srcExclude`, nothing internal), so `docs-parity` adds a + repository-relative path and anchor check over the active repo and + maintained internal sets; the scheduled external-link check's input + is all three active sets; each set gets its own synthesized dead-link + negative fixture. - Workflow edits: CodeQL `rc/*` PR triggers; `.tool-versions` in deploy-docs paths; normalized setup-node cache keys (all workflows, to lockfiles); Dependabot roots (github-actions, browser and Next.js @@ -685,8 +736,11 @@ matrix locally; acceptance greps over the defined sets with output in the PR description; the four adapter first-success smokes (Axum env bridge, Fastly local push + secrets, Cloudflare envelope transfer, Spin local push + variables) executed as documented with commands and cleanup -recorded; the `main` containment PR merged with its positive smoke; the -baseline assertion (merge base equals the recorded SHA) passing. +recorded; the `main` containment PR merged with its positive smoke (its +audited_main_tip assertion having passed); and the exact-tip baseline +assertion for `origin/rc/202608` (equal to the recorded +audited_target_tip, contained in the branch) passing at the final HEAD - +not a merge-base comparison. ## Open questions @@ -774,7 +828,8 @@ Fastly resolves the client IP from it). Spin adds an innermost Integration route families (the checked inventory WP4 generates from; disabled integrations register nothing and their paths fall through to -the publisher proxy; first registration wins on collisions): +the publisher proxy; a duplicate route registration is a startup +configuration error, not silently resolved): - Fixed: `prebid` `GET /integrations/prebid/bundle.js`; `aps` `GET /integrations/aps/renderer` (conditional: only in @@ -785,8 +840,9 @@ the publisher proxy; first registration wins on collisions): `g/collect`; `gpt` `script`, `pagead/*`, `tag/*`; `sourcepoint` `cdn/*`; `permutive` `api/*`, `secure-signal/*`, `events/*`, `sync/*`, `cdn/*`, `sdk`; `lockr` `sdk`, `api/*`. -- Prefix-overridable: every integration's default - `/integrations/` prefix can be overridden by its registration. +- Prefix-overridable: the trait default `/integrations/` can be + overridden by an integration's implementation; the operator- + configurable case is Didomi's `proxy_path` config field. - Config-derived: `prebid` additionally neutralizes operator-configured `script_patterns` plus `/static/prebid/{*rest}`. - Methods are per-registration (GET for scripts/CDN, GET+POST for @@ -837,12 +893,17 @@ attributes and requires key names conditionally. Redacted-but-inline: `trusted_client_ip.shared_secret` only; `tinybird.access_token_secret` is accepted-but-normalized-away (set to `None`, never serialized). Directional dispositions (WP3) classify every path. -Template placeholder constants and their three literal-string test call -sites (the config.rs template tests, the CLI ad_templates substitutions, -the `config init` byte source) are load-bearing. Validator inventory for +Template placeholder constants and their consumer surface are +load-bearing; the WP8a-enumerated consumer manifest is canonical (it +distinguishes literal-substitution consumers - config.rs template tests, +CLI ad_templates and audit generate/validate substitutions, the +template-cache script - from include-only consumers such as +`config init`), and this appendix does not restate its count. Validator inventory for companion entries: 2 struct-level schema validators -(`validate_trusted_client_ip`, APS inventory-identity override), 23 -field-level custom-validator sites, the imperative +(`validate_trusted_client_ip`, APS inventory-identity override), the +field-level custom-validator sites (enumerated canonically by the WP8a +companion manifest, which fails closed on unclassified validators, +rather than by a count here), the imperative `finalize_deserialized` pipeline (normalize → prepare_runtime → derive validate → admin coverage → placeholder rejection), the plan-compiler family (`ProviderId`/`BidderId` grammars, `canonicalize_endpoint`, From b55e573f6b7effb6379dc0d63a45fe15f74e974d Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:06:26 -0700 Subject: [PATCH 18/33] Rework spec after eleventh pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 125 ++++++++++++------ 1 file changed, 86 insertions(+), 39 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 9084c4829..e5338fa6c 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-27 (round 10) +**Revised:** 2026-08-28 (round 11) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `985ff2298` ("Restore secret-store key @@ -157,9 +157,14 @@ Truth-pass acceptance and parity checks operate on defined source sets: (including the moved onboarding page), `docs/epics/**` (maintained internal records), `docs/business-use-cases.md` while excluded-but- tracked, `scripts/README.md`, `tinybird/README.md`, and - `tools/docs-parity/README.md` once created, the human-facing comments of - `.github/workflows/**`, `.github/actions/**`, and `scripts/*.sh` usage - headers, the comment surfaces of the adapter manifests (`fastly.toml`, + `tools/docs-parity/README.md` once created, and human-facing comment + regions DISCOVERED across all tracked operational files rather than a + narrow directory list: `.github/workflows/**`, `.github/actions/**`, + issue forms, every tracked `**/*.sh` usage header (crate-local scripts + included - `crates/trusted-server-openrtb/generate.sh:23` already + carries a false claim that `.cargo/config.toml` defaults to wasm32, + which WP2 repairs), `.cargo/config.toml`, and the comment surfaces of + the adapter manifests (`fastly.toml`, `wrangler.toml`, `wrangler.ci.toml`, `spin.toml`, `axum.toml`, and `cloudflare.toml` until retired), `.claude/skills/**`, `.claude/agents/**`, and `.github/pull_request_template.md`. A checked @@ -220,8 +225,19 @@ The owner's standing instruction is one rc PR carrying spec plus work; the concrete shape below (which adds the forced `main` containment PR and package checkpoints) still awaits explicit confirmation - open question 7 blocks implementation until it is given, and its answer is recorded with -owner and date. The shape: a single rc PR (#1049) carrying the spec plus -all packages, +owner and date. The delivery graph is exactly four PRs: (a) the single rc PR (#1049) +carrying the spec plus all packages; (b) the `main` containment PR; +(c) a `main` automation PR - scheduled workflows and Dependabot read +their configuration from the DEFAULT branch, so the scheduled +external-link workflow lives on `main` (explicitly checking out +`rc/202608` while that branch is live) and `dependabot.yml` lives on +`main` with `target-branch: rc/202608` where updates should land on rc; +final acceptance includes a successful `workflow_dispatch` of the +scheduled job and Dependabot config validation; (d) the CNAME resolution +PR to `main`, cut when open question 2 resolves (the containment PR +NEVER carries it - WP1's earlier allowance is superseded). Every +`main`-target PR records its own fresh audited_main_tip and runs the +exact-tip assertion before merge. The rc PR carries one reviewable commit (or small series) per package, with package-level review checkpoints (acceptance evidence recorded before the next package lands), generated-output changes in their own commits, and no squash on @@ -256,9 +272,11 @@ the owner to confirm this shape. Pages/DNS/TLS setup, canonical+asset smokes, and a project-owned-public-domain allowlist classification). Because only `main` deploys, an rc-only edit never reaches the live site: the - resolved disposition ships inside the containment PR if question 2 is - answered by then, otherwise as a second `main`-target PR. Question 2 - plus its live smoke is a completion gate for this refresh either way. + resolved disposition ships as its own `main`-target PR (delivery graph + item (d); the containment PR never carries it). The custom-domain + branch also inventories hard-coded Pages URLs (e.g. `README.md:11`) + rather than testing only canonical and asset responses. Question 2 + plus its live smoke is a completion gate for this refresh. - `fastly.toml`: empty `authors` list; label the key fixtures consistently as local test fixtures; comment the four KV stores; remove the `test-prebid-eids.sh` comment. `service_id` stays under its @@ -325,10 +343,12 @@ type-check` and the `settings_data::get_settings` example (the exported remediation of non-document fixtures (e.g. the scraped `html_processor.test.html`) is owned here with regression tests re-run; rotation/history-rewrite decisions escalate per finding. -- Human-facing workflow comment repairs: the Spin release-build comment - (claims env overrides fix embedded settings; startup now reads the KV - store) and the `test-cli` comment (claims a workspace default target - that `.cargo/config.toml` does not set). +- Human-facing workflow/script comment repairs: the Spin release-build + comment (claims env overrides fix embedded settings; startup now reads + the KV store), the `test-cli` comment, and + `crates/trusted-server-openrtb/generate.sh:23` (both claim a workspace + default wasm32 target that `.cargo/config.toml` explicitly does not + set). - Roadmap status pass (shipped/active/deferred; also reconcile the malvertising-detection claim with `business-use-cases.md`). @@ -382,11 +402,15 @@ provider profile schemas, and the secret model. - Directional field dispositions as independent axes, not a flat "one of" (real fields overlap: `S3SigV4AuthConfig.secret_store` is simultaneously deprecated, skip-serializing, and normalized away) - - lifecycle (canonical / deprecated-alias / rejected), serialization - (serialized / skipped), runtime (active / normalized-away / - deserialization-only), and secret handling (store-resolved / - deliberately-inline / none); the generated reference renders every - applicable axis. `tinybird.access_token_secret` is + lifecycle (canonical / deprecated / rejected), key identity + (canonical / alias, with `alias_of` - `s3_sig_v4` and `pub_id` are + aliases, while `S3SigV4AuthConfig.secret_store` is a deprecated + accepted field, not an alias), serialization (serialized / skipped), + runtime (active / normalized-away / deserialization-only), and secret + handling (store-resolved / deliberately-inline / none); the generated + reference renders every applicable axis, so + `tinybird.access_token_secret` reads as deprecated + skipped + + normalized-away simultaneously. `tinybird.access_token_secret` is accepted-but-normalized-away (deserialized, then set to `None` and never serialized) - not a deliberately inline secret; only `trusted_client_ip.shared_secret` currently holds that classification. @@ -593,13 +617,16 @@ npm): rule and covering core's test-utils feature. - Native doctests: `cargo test --doc -p trusted-server-core` (host). -The JSDoc contract is explicit: the ESLint jsdoc rule set applies to -the WP7 file globs (`lib/src/core/render.ts`, `lib/src/core/types.ts`, -`lib/src/core/registry.ts`, `lib/src/shared/globals.ts`, -`lib/src/integrations/creative/**`), requiring a file-header block plus -JSDoc on every exported declaration (functions, classes, interfaces, -type aliases) via `jsdoc/require-jsdoc` with those contexts, plus -`jsdoc/check-alignment` and `jsdoc/check-types`. +The JSDoc contract is explicit and config-relative (ESLint runs from +`crates/trusted-server-js/lib`, so globs are `src/core/render.ts`, +`src/core/types.ts`, `src/core/registry.ts`, `src/shared/globals.ts`, +`src/integrations/creative/**`): `jsdoc/require-file-overview` enforces +the file-header block (`require-jsdoc` checks declarations, not +headers), and `jsdoc/require-jsdoc` covers every exported declaration +form - functions, classes, interfaces, type aliases, exported +variables/consts, default exports, and re-exports - plus +`jsdoc/check-alignment` and `jsdoc/check-types`. Each declaration form +and the file-overview rule get separate synthesized negative fixtures. Acceptance: worklist complete; matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the WP8 jsdoc lint (mandatory, scoped as @@ -623,16 +650,23 @@ WP8a (lands right after WP1): (all languages; graded modes incl. `expected_compile_failure` / `expected_validation_failure` / `illustrative_fragment`; expiring waivers), the domain/credential scanner + typed allowlist (with a - defined input contract: text files detected by content; an - expected-text file that is oversized or invalid UTF-8 FAILS the scan - rather than being skipped (thresholds must not become evasion - mechanisms); generated lockfiles are not blanket-excluded - their - structured URL/source/registry fields are scanned while checksum and - integrity strings are recognized structurally; known media assets get - metadata-string inspection (or are individually manifest-listed with - rationale); true binaries are scanned by path/metadata; base64 test - fixtures distinguish via the hash-pinned fixture allowlist class - - results must be deterministic across platforms), the maintained-source + defined input contract: text-vs-binary classification comes from path + plus a checked classification manifest, never content sniffing alone - + a tracked file the manifest does not classify FAILS the scan (so a new + binary cannot appear unreviewed, and text renamed under an unknown + extension cannot dodge content inspection); an expected-text file that + is oversized or invalid UTF-8 FAILS rather than being skipped + (thresholds must not become evasion mechanisms); classified binaries + get an ASCII/UTF-8 strings scan, or a reviewed hash-pinned manifest + entry where strings scanning is meaningless; generated lockfiles are + not blanket-excluded - their structured URL/source/registry fields are + scanned while checksum and integrity strings are recognized + structurally; known media assets get metadata-string inspection; + base64 test fixtures distinguish via the hash-pinned fixture allowlist + class - results must be deterministic across platforms. Every detector + and encoding class (domain, email, credential shape, service ID, + encoded token, binary strings) gets both a synthesized positive + fixture and an allowlisted fixture proving the allowlist path), the maintained-source manifest checker, and the gate manifest. - The example harness, in eight explicit phases (secret resolution alone cannot make the template valid: placeholder rejection also @@ -736,8 +770,11 @@ matrix locally; acceptance greps over the defined sets with output in the PR description; the four adapter first-success smokes (Axum env bridge, Fastly local push + secrets, Cloudflare envelope transfer, Spin local push + variables) executed as documented with commands and cleanup -recorded; the `main` containment PR merged with its positive smoke (its -audited_main_tip assertion having passed); and the exact-tip baseline +recorded; the `main` containment PR merged with its positive smoke and the +`main` automation PR merged with a successful `workflow_dispatch` of the +scheduled job and validated Dependabot config (each `main` PR's +audited_main_tip assertion having passed); every follow-up filed with a +recorded URL or disposition; and the exact-tip baseline assertion for `origin/rc/202608` (equal to the recorded audited_target_tip, contained in the branch) passing at the final HEAD - not a merge-base comparison. @@ -753,13 +790,23 @@ not a merge-base comparison. evidence-based rewrite in this pass. 5. CHANGELOG release cut (out of scope; deterministic no-release edit defined in Non-goals). -6. Governance ownership (CODEOWNERS/minutes) - blocks the WP6 governance - edit only. +6. Governance ownership (CODEOWNERS/minutes). Terminal disposition: if + no owner is named by the time WP6 lands, the fallback executes - the + governance document is corrected to current evidence (no minutes + exist; releases are not continuous) without adding CODEOWNERS or + minutes commitments - and the refresh completes; naming owners + remains a follow-up for maintainers. 7. Delivery shape confirmation (blocks starting implementation). 8. CodeQL `push` coverage for `rc/*` (non-blocking). ## Follow-up issues to file (code, not docs) +Filing is owned work, not an aspiration: WP8b deduplicates this list +against the existing tracker and files each item (or records an +existing-issue disposition), with issue URLs, owner, and labels recorded +in the rc PR description; final verification checks that every entry +below has a URL or disposition. + - `Hooks::stores()` unimplemented on Cloudflare, Spin, and Axum: request- time config/KV registries are empty, the declared `TRUSTED_SERVER_KV` binding is never opened, Spin's request-signing kid variables are From 0ddbb8864133345ac104f67258a2aa6724795854 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:28:28 -0700 Subject: [PATCH 19/33] Rework spec after twelfth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 184 +++++++++++++----- 1 file changed, 137 insertions(+), 47 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index e5338fa6c..5999a21c2 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,15 +1,24 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-28 (round 11) +**Revised:** 2026-08-28 (round 12) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. -**Baseline:** audited_target_tip `985ff2298` ("Restore secret-store key -references in the config template", 2026-08-27). The bulk inventories were -regenerated by a four-track delta audit at `a163367b3` (the main→rc merge -that landed #870); the one commit between them was reviewed individually -(it restored secret-store key references in the template and widened the -literal-placeholder consumer surface; folded into WP3/WP8a below). +**Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk +inventories were regenerated by a four-track delta audit at `a163367b3` +(the main→rc merge that landed #870); the ranges `a163367b3..985ff2298` +and `985ff2298..07dfc1c6d` (56 commits total) were each audited as +focused deltas and folded in below (notably: template secret references +restored and the placeholder consumer surface widened; a duplicate +commented `[trusted_client_ip]` template block introduced; +`proxy.allowed_domains` semantics broadened to signing + initial fetch + +redirects with rc's docs updated in the same commits; the +`validate_proxy_secret_strength` runtime validator REMOVED; `--scroll` +added to `ts audit ad-templates generate`; `browser_family` added to +device signals, auction telemetry, and the Tinybird datasource; the +`EdgeTerminatedAuthorization` digest for template-cache reuse; JS +`types.ts` renamed `suppressionConsumed` to `publisherRegistrationClosed` +and `proxy_sign` returns an outcome union). Baseline contract - a merge-base check alone false-greens when the target advances without a rebase, so the guard is exact-tip: after fetching, `origin/rc/202608` must equal the recorded audited_target_tip AND the @@ -36,7 +45,7 @@ runtime model underneath the rest. Current state at the baseline: documents `POST /auction` and the `/_ts/admin/ec`, `/_ts/admin/ec/{id}`, and `/_ts/admin/eids` diagnostics with an auth-coverage contract. - `format.yml` now runs `vitepress build` on PRs (dead internal links fail - CI). Note the interaction: with no `srcExclude`, CI now builds all 127 + CI). Note the interaction: with no `srcExclude`, CI now builds all 133 internal `docs/superpowers/**` files as site pages. - `cli.md` covers `active-version`, `healthcheck`, `rollback`, and `config gc`; `configuration.md` documents the secret-store migration @@ -50,7 +59,7 @@ runtime model underneath the rest. Current state at the baseline: **Still open (verified at the baseline):** 1. **Publishing and policy hygiene.** No `srcExclude` in - `docs/.vitepress/config.mts` (127 internal spec/plan files build into + `docs/.vitepress/config.mts` (133 internal spec/plan files build into the public site); `docs/guide/index.md` is 0 bytes; the nav Guide link targets `/guide/getting-started` and a Business Value nav item points at `business-use-cases.md` (uncited quantitative claims; presents planned @@ -69,7 +78,7 @@ runtime model underneath the rest. Current state at the baseline: `FAQ_POC.md`; it is gone from `integration-guide.md`); `.with_asset(...)` remains in `creative-processing.md:808` and `integration-guide.md:84,248`; `error-reference.md:614` still says - `npm run type-check`; `configuration.md:2287` still imports the + `npm run type-check`; `configuration.md:2301` still imports the nonexistent `settings_data::get_settings`; the auction README's rotted route table, `providers/` directory, and APS `mock` sections; `onboarding.md`'s dead `SEQUENCE.md` links; `TESTING.md` is still the @@ -82,7 +91,9 @@ runtime model underneath the rest. Current state at the baseline: lists 10 of 17 roots. Docs claim reserved-field protection for both `request_ext` and `imp_ext` while `reject_reserved_fields` guards only `request_ext`. `adserver_mock` is doubly stranded: rc deleted its old - config subsection without a replacement page. The CHANGELOG carries 8 + config subsection without a replacement page. The template now + carries a DUPLICATE commented `[trusted_client_ip]` block (lines + 73-80 and 145-151, near-identical). The CHANGELOG carries 8 breaking `[Unreleased]` entries with inconsistent `**Breaking**` formatting and two dead `v1.2.0` compare links (no tag exists). 4. **Adapter truth gaps.** `/health` is not registered on Cloudflare; @@ -167,10 +178,18 @@ Truth-pass acceptance and parity checks operate on defined source sets: the adapter manifests (`fastly.toml`, `wrangler.toml`, `wrangler.ci.toml`, `spin.toml`, `axum.toml`, and `cloudflare.toml` until retired), `.claude/skills/**`, - `.claude/agents/**`, and `.github/pull_request_template.md`. A checked - maintained-source manifest enumerates entries as `{path, mode, -selector}` (whole-file vs comment-region), and the WP8b inventory gate - asserts final set equality against it. + `.claude/agents/**`, and `.github/pull_request_template.md`. The + candidate universe is mechanical, not semantic: a tracked-path rule + set (every `**/*.sh`, `**/*.toml` manifest, workflow/action/issue-form + YAML, Dockerfile-like files, and markdown outside the other sets) + generates the candidate list, and EVERY candidate must carry an + explicit include or typed-exclude disposition in the checked + maintained-source manifest (`{path, mode, selector}`; whole-file vs + comment-region) - an unclassified candidate fails the gate, so a new + Dockerfile or unfamiliar operational format cannot silently fall + outside the universe. The WP8b inventory gate asserts final set + equality, with negative fixtures for a new operational extension and + a comment outside an existing selector. - **Historical set:** `docs/superpowers/**` and shipped `CHANGELOG.md` release entries. Exempt from retired-term greps only; privacy/secret scanning covers ALL tracked files (see WP2). @@ -229,11 +248,19 @@ owner and date. The delivery graph is exactly four PRs: (a) the single rc PR (#1 carrying the spec plus all packages; (b) the `main` containment PR; (c) a `main` automation PR - scheduled workflows and Dependabot read their configuration from the DEFAULT branch, so the scheduled -external-link workflow lives on `main` (explicitly checking out -`rc/202608` while that branch is live) and `dependabot.yml` lives on -`main` with `target-branch: rc/202608` where updates should land on rc; -final acceptance includes a successful `workflow_dispatch` of the -scheduled job and Dependabot config validation; (d) the CNAME resolution +external-link workflow lives on `main` (checking out `rc/202608` while +that branch is live) and `dependabot.yml` lives on `main` with +`target-branch: rc/202608` for version updates (security updates always +target the default branch; the config validation covers both kinds and +the wording distinguishes them). Pre-merge acceptance is not circular: +the workflow takes a `workflow_dispatch` input accepting an immutable +SHA (the rc PR head), so the dispatch validates the WP8 tooling on that +exact commit BEFORE the rc PR merges; post-merge, a second dispatch +against the merged tip is recorded. The PR also defines the +end-of-release handoff: when rc/202608 merges or is deleted, the +checkout target and Dependabot `target-branch` switch back to `main` +(an enumerated, owned edit - the automation must not keep pointing at a +dead branch); (d) the CNAME resolution PR to `main`, cut when open question 2 resolves (the containment PR NEVER carries it - WP1's earlier allowance is superseded). Every `main`-target PR records its own fresh audited_main_tip and runs the @@ -351,6 +378,12 @@ type-check` and the `settings_data::get_settings` example (the exported set). - Roadmap status pass (shipped/active/deferred; also reconcile the malvertising-detection claim with `business-use-cases.md`). +- Verified dispositions for the docs rc updated inside the audited + deltas (`allowed_domains` semantics in `configuration.md` and + `first-party-proxy.md`, the `/first-party/sign` 403 + `href`/`base` + contract in `api-reference.md`, the softened `proxy-signing.md` + recommendation, the cli.md `--staging` known-limitation callout): + confirm each against code rather than re-writing. Acceptance: checked-in inventory complete over all three active sets; the retired-token scan is case-insensitive and whitespace-tolerant @@ -390,7 +423,9 @@ provider profile schemas, and the secret model. `integrations.datadome.server_side_key_secret_store`, `integrations.datadome.protection_test_bypass.credential_secret_store`) that the code accepts-and-discards and the migration guide tells - operators to delete. Preserve the template's contract: placeholder + operators to delete, and remove the duplicate commented + `[trusted_client_ip]` block (the delta introduced a second, + near-identical copy). Preserve the template's contract: placeholder strings stay in the rejection constants' exact forms and secret references carry key names, never values. The literal-string consumer surface is wider than three call sites since `985ff2298`: the config.rs @@ -407,8 +442,12 @@ provider profile schemas, and the secret model. aliases, while `S3SigV4AuthConfig.secret_store` is a deprecated accepted field, not an alias), serialization (serialized / skipped), runtime (active / normalized-away / deserialization-only), and secret - handling (store-resolved / deliberately-inline / none); the generated - reference renders every applicable axis, so + handling (store-resolved / deliberately-inline / none); removed + validators get a `rejected`/removed disposition too - the delta + deleted `validate_proxy_secret_strength` (no 32-byte runtime + enforcement remains; `proxy-signing.md` was already softened to a + recommendation on rc, which WP2 verifies rather than rewrites); the + generated reference renders every applicable axis, so `tinybird.access_token_secret` reads as deprecated + skipped + normalized-away simultaneously. `tinybird.access_token_secret` is accepted-but-normalized-away (deserialized, then set to `None` and @@ -536,7 +575,9 @@ contract checklist satisfied; manual-ownership markers present. everywhere adapter status is stated), including provider fan-out capability and startup-failure behavior columns. - `edgezero.md` (manifest, stores, blob flow, lifecycle commands), - `telemetry.md` (+ `tinybird/README.md`; emission Fastly-only), + `telemetry.md` (+ `tinybird/README.md`; emission Fastly-only; covers + the new `browser_family` column through device signals, auction rows, + and the Tinybird datasource), `tsjs.md` (module system including the third loading mode: `gpt_diagnostics` standalone tag; 12 modules / 13 bundles), `integrations/adserver_mock.md` (as the mediator, with @@ -553,6 +594,17 @@ contract checklist satisfied; manual-ownership markers present. - CLI reference from the two-platform help union; description text passes the internal-term gate with the expiring override table. +- Every smoke shares one strong oracle - exact expected status, a + stub-origin sentinel present in the response, and an expected Trusted + Server rewrite or header proving the app (not a degraded router) + served it - plus a negative run per adapter proving that removing the + config blob or a required secret makes the smoke FAIL. This matters + concretely: Fastly's `/health` short-circuits before app construction + and succeeds while startup is broken, and Spin's degraded router + answers every publisher path with a valid 503, so status-or-response + checks false-green on both. Axum's smoke is the same full clean-state + sequence (export config + secret vars, launch, assert), not a list of + configuration facts. - The four first-success journeys become recurring smoke scripts (`scripts/smoke-{axum,fastly,cloudflare,spin}.sh`), each starting from the documented CLI commands (not the harness's internal shortcuts - @@ -647,8 +699,15 @@ WP8a (lands right after WP1): grammar above), CLI help goldens (both platforms, merged platform-annotated union, description override table with owner/ rationale/expiry/source-text staleness check), snippet-manifest tooling - (all languages; graded modes incl. `expected_compile_failure` / - `expected_validation_failure` / `illustrative_fragment`; expiring + (all languages; graded modes incl. `expected_compile_failure` and + `expected_validation_failure`, each carrying a REQUIRED stable + diagnostic matcher - error class/path or message pattern - and + expected phase, so a typo, missing dependency, wrong working + directory, or unrelated parser error cannot satisfy the gate, with a + fixture proving that a correct nonzero exit with the WRONG diagnostic + fails, and a negative example failing CI when it unexpectedly becomes + valid; `illustrative_fragment` entries carry owner, rationale, and + expiry like manual waivers, not a permanent escape hatch; expiring waivers), the domain/credential scanner + typed allowlist (with a defined input contract: text-vs-binary classification comes from path plus a checked classification manifest, never content sniffing alone - @@ -666,7 +725,16 @@ WP8a (lands right after WP1): class - results must be deterministic across platforms. Every detector and encoding class (domain, email, credential shape, service ID, encoded token, binary strings) gets both a synthesized positive - fixture and an allowlisted fixture proving the allowlist path), the maintained-source + fixture and an allowlisted fixture proving the allowlist path. The + scanner also carries a reviewed identifier denylist class - known + organization names, personal handles, chat channels, project IDs, and + access-instruction phrases - seeded from what WP2's scrubbing removes, + because the pattern detectors alone cannot catch policy-relevant + content like internal handles once common hosts are allowlisted. The + stated guarantee is honest about the residual: semantic sensitivity + beyond the detector and denylist classes remains a human review + control exercised through the WP2 disposition process, not a scanner + promise), the maintained-source manifest checker, and the gate manifest. - The example harness, in eight explicit phases (secret resolution alone cannot make the template valid: placeholder rejection also @@ -734,6 +802,15 @@ WP8b (lands last): - `CLAUDE.md`/`AGENTS.md`/`TESTING.md`/`docs/guide/testing.md` gate regions regenerated from the manifest in the same commit. +Merge-blocking is a repository setting, not a workflow property: a +failing check blocks merges only when the ruleset/branch protection +requires it. WP8b therefore carries an externally owned acceptance item +recording: the required check names and their GitHub App, the +ruleset/branch-protection evidence for BOTH `rc/202608` and `main`, +the bypass policy, one demonstrated failing check actually preventing a +merge, and `merge_group` triggers on the new workflows if the +repository adopts merge queues. + Acceptance: every runtime gate has a synthesized negative fixture (dead link, broken intra-doc link, failing doctest, invalid or unknown-keyed example block including a disabled integration table and a bad @@ -746,18 +823,20 @@ regions and goldens at final HEAD produces no diff. ## Sequencing -| Order | Package | Size | Depends on | -| ----- | ---------------------------------- | ---- | ------------------------- | -| 0 | WP1 containment subset → `main` PR | XS | - | -| 1 | WP1 hygiene (full, rc PR) | S | - | -| 2 | WP8a scaffolding | L | - | -| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | -| 4 | WP3 config reference | L | WP8a (extractor, harness) | -| 5 | WP4 API reference | M | WP2, WP8a | -| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | -| 7 | WP6 root + READMEs | M | WP2 | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 | +| Order | Package | Size | Depends on | +| ----- | -------------------------------------- | ---- | ---------------------------------------------------------------- | +| 0 | WP1 containment subset → `main` PR (b) | XS | - | +| 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | +| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | +| 1 | WP1 hygiene (full, rc PR) | S | - | +| 2 | WP8a scaffolding | L | - | +| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | +| 4 | WP3 config reference | L | WP8a (extractor, harness) | +| 5 | WP4 API reference | M | WP2, WP8a | +| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | +| 7 | WP6 root + READMEs | M | WP2 | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 | ## Verification @@ -840,6 +919,9 @@ below has a URL or disposition. auction emission Fastly-only. - `.env.dev` references an `opid_store` that `fastly.toml` does not declare. +- Fastly `--staging` deploys load the PRODUCTION config blob (the entry + point does not read the `edgezero_runtime_env` store-name selectors); + rc's cli.md documents it as a known limitation - file the code fix. ## Appendix A: Route inventory (regenerated at `a163367b3`) @@ -936,7 +1018,9 @@ The docs "Key Sections" table lists 10 of 17. Secret model: 11 resolution flow is verify → `remove_inactive_secret_references` → `resolve_secret_references` → `from_json_value` → `validate_settings_for_runtime`; deploy validation excludes secret-leaf -attributes and requires key names conditionally. Redacted-but-inline: +attributes and requires key names conditionally +(`validate_proxy_secret_strength` was removed in the audited delta - no +runtime length enforcement on the resolved proxy secret remains). Redacted-but-inline: `trusted_client_ip.shared_secret` only; `tinybird.access_token_secret` is accepted-but-normalized-away (set to `None`, never serialized). Directional dispositions (WP3) classify every path. @@ -1009,14 +1093,19 @@ creative JS-only, always injected. ## Appendix D: CLI and per-adapter configuration handoff `ts` (the two-platform help union is canonical; macOS adds `dev proxy`): -`audit page|generate|ad-templates generate|verify`, `active-version`, +`audit page|generate|ad-templates generate[--scroll]|verify`, +`active-version`, `auth login|logout|status`, `build`, `config init|diff|push|validate|gc|ad-templates lint|match|check|explain`, `deploy`, `healthcheck`, `prebid bundle`, `provision`, `rollback`, `serve`, `dev proxy [ca ...]`. `--version` is available. Drift-detecting -commands use a stable drift exit code. rc's cli.md already covers the -lifecycle commands; the remaining work is the generated-region conversion -and description gating. +commands use a stable drift exit code. `config gc --store ` is owned +by the pinned `edgezero-cli` (newly documented, not newly added); the +deploy-lifecycle commands (`active-version`, `healthcheck`, `rollback`) +are Fastly-only per rc's own docs; cli.md documents a known limitation +that `--staging` loads the production config blob (follow-up below). +rc's cli.md already covers the lifecycle commands; the remaining work is +the generated-region conversion and description gating. Per-adapter configuration handoff (deployment-guide truth): @@ -1041,7 +1130,7 @@ documented as separate surfaces (WP2). ## Appendix E: Still-open finding index (verified 2026-08-27) -- `srcExclude` absent; 127 superpowers files in the CI-built site; empty +- `srcExclude` absent; 133 superpowers files in the CI-built site; empty `guide/index.md`; nav Guide/Business Value links; CNAME placeholder. - `fastly.toml:4,10,38` + inconsistent fixture labels; `docs/package.json` ISC/not-private; onboarding published. @@ -1050,12 +1139,13 @@ documented as separate surfaces (WP2). `.claude/agents/issue-creator.md:85`, `FAQ_POC.md`); `.with_asset` (`creative-processing.md:808`, `integration-guide.md:84,248`); `npm run type-check` (`error-reference.md:614`); - `settings_data::get_settings` (`configuration.md:2287`); auction README + `settings_data::get_settings` (`configuration.md:2301`); auction README rot; `SEQUENCE.md` links; `TESTING.md` runbook. - `[consent]`/`[debug]` sections missing; `[tinybird]` Quick-Start-only; Key Sections 10/17; reserved-field `imp_ext` docs/code mismatch; integration subsections 5/14; `adserver_mock` stranded. -- CHANGELOG: 8 breaking entries, inconsistent markers, dead v1.2.0 links. +- CHANGELOG: 8 breaking entries (one `**Breaking:**`, seven + `**Breaking** -` variants), dead v1.2.0 links. - Stale rustdoc: the Cloudflare `platform.rs:579-592` stores claim (and its Spin sibling); `cloudflare.toml` dead; false workflow comments (Spin release build, test-cli default target). From 2ff5b89e0c1a07279fcc6385e910e7e28261bfcf Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 28 Aug 2026 18:00:18 -0700 Subject: [PATCH 20/33] Rework spec after thirteenth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 5999a21c2..ad26d9b50 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-28 (round 12) +**Revised:** 2026-08-28 (round 13) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk @@ -179,15 +179,18 @@ Truth-pass acceptance and parity checks operate on defined source sets: `wrangler.toml`, `wrangler.ci.toml`, `spin.toml`, `axum.toml`, and `cloudflare.toml` until retired), `.claude/skills/**`, `.claude/agents/**`, and `.github/pull_request_template.md`. The - candidate universe is mechanical, not semantic: a tracked-path rule - set (every `**/*.sh`, `**/*.toml` manifest, workflow/action/issue-form - YAML, Dockerfile-like files, and markdown outside the other sets) - generates the candidate list, and EVERY candidate must carry an - explicit include or typed-exclude disposition in the checked - maintained-source manifest (`{path, mode, selector}`; whole-file vs - comment-region) - an unclassified candidate fails the gate, so a new - Dockerfile or unfamiliar operational format cannot silently fall - outside the universe. The WP8b inventory gate asserts final set + candidate universe is mechanical and complete: it is derived from the + all-tracked text/binary classification manifest (WP8a) - EVERY tracked + file classified as text is a candidate (a path whitelist cannot + recognize an extension it does not know; `.mjs` build scripts and the + human-facing `.proto` documentation are real examples outside any + whitelist), and every candidate must carry an explicit include or + typed-exclude disposition in the checked maintained-source manifest + (`{path, mode, selector}`; whole-file vs comment-region). An + unclassified candidate fails the gate, so a new Dockerfile or + unfamiliar operational format cannot silently fall outside the + universe; the path rules below are the default include hints, not the + universe. The WP8b inventory gate asserts final set equality, with negative fixtures for a new operational extension and a comment outside an existing selector. - **Historical set:** `docs/superpowers/**` and shipped `CHANGELOG.md` @@ -224,8 +227,12 @@ Truth-pass acceptance and parity checks operate on defined source sets: tool and the SHA-pinned external link-checker action. - No rewrite of `business-use-cases.md` marketing copy: it is excluded from the build via `srcExclude` and carries a source-level unverified - banner (WP1) until an evidence-based rewrite happens (open question 4). - `roadmap.md` gets a factual status pass only. + banner (WP1). Open question 4 is closed to this disposition - an + evidence-based rewrite is not an option inside this refresh, because + the delivery graph allocates no PR to remove the exclusion, restore + navigation, and smoke the page; republishing it is a separate future + effort with its own publishing PR and acceptance. `roadmap.md` gets a + factual status pass only. - No release management. The 8 breaking `[Unreleased]` entries are a maintainer decision; the deterministic no-release CHANGELOG edit is: normalize the `**Breaking**` marker formatting, keep `[1.2.0]` with a @@ -244,7 +251,7 @@ The owner's standing instruction is one rc PR carrying spec plus work; the concrete shape below (which adds the forced `main` containment PR and package checkpoints) still awaits explicit confirmation - open question 7 blocks implementation until it is given, and its answer is recorded with -owner and date. The delivery graph is exactly four PRs: (a) the single rc PR (#1049) +owner and date. The delivery graph is exactly five PRs: (a) the single rc PR (#1049) carrying the spec plus all packages; (b) the `main` containment PR; (c) a `main` automation PR - scheduled workflows and Dependabot read their configuration from the DEFAULT branch, so the scheduled @@ -252,15 +259,25 @@ external-link workflow lives on `main` (checking out `rc/202608` while that branch is live) and `dependabot.yml` lives on `main` with `target-branch: rc/202608` for version updates (security updates always target the default branch; the config validation covers both kinds and -the wording distinguishes them). Pre-merge acceptance is not circular: -the workflow takes a `workflow_dispatch` input accepting an immutable -SHA (the rc PR head), so the dispatch validates the WP8 tooling on that -exact commit BEFORE the rc PR merges; post-merge, a second dispatch -against the merged tip is recorded. The PR also defines the -end-of-release handoff: when rc/202608 merges or is deleted, the -checkout target and Dependabot `target-branch` switch back to `main` -(an enumerated, owned edit - the automation must not keep pointing at a -dead branch); (d) the CNAME resolution +the wording distinguishes them). Pre-merge acceptance is not circular +and not privileged: the workflow is split into two jobs. A dispatch +validation job runs with `contents: read` only, no secrets, and no +mutating steps; it checks out and executes the supplied SHA, which must +be a full 40-character SHA that the job verifies via the API to be +either the approved same-repository rc PR's current head or the merged +rc tip (arbitrary refs, stale SHAs, and non-approved commits are +rejected, proven by a negative workflow fixture). A separate +schedule-only issue-management job holds the job-scoped `issues: write` +and never executes code from a supplied SHA - it checks out only the +branch tip it is configured for. Pre-merge, the validation job runs on +the rc PR head; post-merge, a dispatch against the merged tip is +recorded. (e) A fifth, release-triggered handoff PR to `main`: when +rc/202608 merges or is deleted, the workflow checkout and Dependabot +`target-branch` switch back to `main` - the checkout could fall back +dynamically but Dependabot's target is static, so a `main` edit is +unavoidable. It has a named owner, a tracked issue filed in WP8b, a +sequencing row, and a verification item (the automation must not keep +pointing at a dead branch); (d) the CNAME resolution PR to `main`, cut when open question 2 resolves (the containment PR NEVER carries it - WP1's earlier allowance is superseded). Every `main`-target PR records its own fresh audited_main_tip and runs the @@ -322,7 +339,11 @@ the owner to confirm this shape. that cannot read `CLAUDE.md`, so it carries the list). Acceptance: `vitepress build` output contains none of the excluded pages; -the `main` containment PR is merged and its smoke passes; the banner is +the `main` containment PR is merged and its smoke passes; the CNAME +disposition (open question 2) is selected and PR (d) is merged with its +branch-specific live smoke (project-URL re-smoke, or for the custom +domain: DNS/TLS evidence plus canonical, asset, and hard-coded-URL +checks) and its own audited_main_tip assertion; the banner is present; no internal contacts or access instructions anywhere in the repo; every command file links to (not copies) the canonical gates. @@ -597,8 +618,13 @@ contract checklist satisfied; manual-ownership markers present. - Every smoke shares one strong oracle - exact expected status, a stub-origin sentinel present in the response, and an expected Trusted Server rewrite or header proving the app (not a degraded router) - served it - plus a negative run per adapter proving that removing the - config blob or a required secret makes the smoke FAIL. This matters + served it - plus INDEPENDENT negative cases per adapter, each with a + diagnostic matcher: (i) missing config blob fails with the expected + startup/config diagnostic; (ii) each required secret key, missing or + unresolved, fails for its expected reason; and a failure caused by an + unrelated launcher, origin, or port error satisfies neither case. One + negative run cannot stand in for both halves - both the config + handoff and the secret handoff must be proven live. This matters concretely: Fastly's `/health` short-circuits before app construction and succeeds while startup is broken, and Spin's degraded router answers every publisher path with a valid 503, so status-or-response @@ -849,10 +875,12 @@ matrix locally; acceptance greps over the defined sets with output in the PR description; the four adapter first-success smokes (Axum env bridge, Fastly local push + secrets, Cloudflare envelope transfer, Spin local push + variables) executed as documented with commands and cleanup -recorded; the `main` containment PR merged with its positive smoke and the -`main` automation PR merged with a successful `workflow_dispatch` of the -scheduled job and validated Dependabot config (each `main` PR's -audited_main_tip assertion having passed); every follow-up filed with a +recorded; the `main` containment PR merged with its positive smoke, the +`main` automation PR merged with a successful read-only validation +dispatch against the rc PR head and validated Dependabot config, and the +CNAME PR (d) merged with its branch-specific live smoke (each `main` +PR's audited_main_tip assertion having passed); the release-handoff +PR (e) has its owner and tracked issue recorded; every follow-up filed with a recorded URL or disposition; and the exact-tip baseline assertion for `origin/rc/202608` (equal to the recorded audited_target_tip, contained in the branch) passing at the final HEAD - @@ -865,8 +893,8 @@ not a merge-base comparison. 2. CNAME: delete (recommended) or custom domain (fully specified branch). 3. `FAQ_POC.md` retirement; gam/kargo tombstones (routes preserved either way). -4. `business-use-cases.md`: excluded-with-banner default vs an - evidence-based rewrite in this pass. +4. `business-use-cases.md`: CLOSED - excluded with a source-level + banner; republishing is a separate future effort (see Non-goals). 5. CHANGELOG release cut (out of scope; deterministic no-release edit defined in Non-goals). 6. Governance ownership (CODEOWNERS/minutes). Terminal disposition: if From 08f339fc3fc79e539be0e08ed97f010c9aa487db Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 28 Aug 2026 18:00:32 -0700 Subject: [PATCH 21/33] Add release-handoff PR sequencing row --- ...2026-08-19-documentation-refresh-design.md | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index ad26d9b50..fea3c3d84 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -849,20 +849,21 @@ regions and goldens at final HEAD produces no diff. ## Sequencing -| Order | Package | Size | Depends on | -| ----- | -------------------------------------- | ---- | ---------------------------------------------------------------- | -| 0 | WP1 containment subset → `main` PR (b) | XS | - | -| 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | -| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | -| 1 | WP1 hygiene (full, rc PR) | S | - | -| 2 | WP8a scaffolding | L | - | -| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | -| 4 | WP3 config reference | L | WP8a (extractor, harness) | -| 5 | WP4 API reference | M | WP2, WP8a | -| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | -| 7 | WP6 root + READMEs | M | WP2 | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 | +| Order | Package | Size | Depends on | +| ----- | -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------- | +| 0 | WP1 containment subset → `main` PR (b) | XS | - | +| 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | +| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | +| 10 | Release handoff PR (e) | XS | rc merge or deletion; owned, tracked issue; its filing is part of this refresh's completion, its merge is not | +| 1 | WP1 hygiene (full, rc PR) | S | - | +| 2 | WP8a scaffolding | L | - | +| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | +| 4 | WP3 config reference | L | WP8a (extractor, harness) | +| 5 | WP4 API reference | M | WP2, WP8a | +| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | +| 7 | WP6 root + READMEs | M | WP2 | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 | ## Verification From 60b6334329d3273aeabe9162221a696fb28645e5 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 28 Aug 2026 23:53:05 -0700 Subject: [PATCH 22/33] Rework spec after fourteenth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 97 +++++++++++++------ 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index fea3c3d84..c994c7092 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-28 (round 13) +**Revised:** 2026-08-28 (round 14) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk @@ -186,8 +186,14 @@ Truth-pass acceptance and parity checks operate on defined source sets: human-facing `.proto` documentation are real examples outside any whitelist), and every candidate must carry an explicit include or typed-exclude disposition in the checked maintained-source manifest - (`{path, mode, selector}`; whole-file vs comment-region). An - unclassified candidate fails the gate, so a new Dockerfile or + (`{path, mode, selector}`; whole-file vs comment-region). Comment + regions are closed the same way files are: every comment-region file + class needs a supported comment extractor (fail closed when none + exists for a class), and EVERY extracted comment span must be + included or typed-excluded - so a new human-facing comment added + outside an existing selector fails the gate rather than passing + because its file is already classified. An unclassified candidate or + span fails the gate, so a new Dockerfile or unfamiliar operational format cannot silently fall outside the universe; the path rules below are the default include hints, not the universe. The WP8b inventory gate asserts final set @@ -213,8 +219,10 @@ Truth-pass acceptance and parity checks operate on defined source sets: live site (Pages deploys only from `main`), and internal details are scrubbed from the public repository regardless of build exclusion. - Sensitive real-world values are removed or covered by the typed, - expiring allowlist (the `fastly.toml` `service_id` is its one - owner-approved, time-bounded entry pending the ops-owned migration). + expiring allowlist (the `fastly.toml` `service_id` is its one PROPOSED + exception, pending the ops-owned migration; it becomes an approved, + time-bounded entry only once open question 1 records an owner and + review date, which gates scanner activation). - CI catches regressions: docs build (already live on rc), rustdoc with broken-intra-doc-link denial, doctests, and semantic parity bound to the reader-facing markdown. @@ -251,33 +259,57 @@ The owner's standing instruction is one rc PR carrying spec plus work; the concrete shape below (which adds the forced `main` containment PR and package checkpoints) still awaits explicit confirmation - open question 7 blocks implementation until it is given, and its answer is recorded with -owner and date. The delivery graph is exactly five PRs: (a) the single rc PR (#1049) +owner and date. The delivery graph is four PRs delivered by this refresh - (a)-(d) - +plus one mandatory future PR (e) whose tracked issue and draft this +refresh must produce: (a) the single rc PR (#1049) carrying the spec plus all packages; (b) the `main` containment PR; -(c) a `main` automation PR - scheduled workflows and Dependabot read -their configuration from the DEFAULT branch, so the scheduled -external-link workflow lives on `main` (checking out `rc/202608` while -that branch is live) and `dependabot.yml` lives on `main` with -`target-branch: rc/202608` for version updates (security updates always +(c) a `main` automation PR, which lands the workflow in +DISPATCH-ONLY form (no `schedule:` trigger, no rc-targeted Dependabot +entries) because scheduled runs and Dependabot would otherwise inspect +pre-refresh rc content and a docs-parity cargo root that does not yet +exist on any target branch. Activation is a separate, owned post-merge +checkpoint (see (c2)) that adds the `schedule:` trigger and the +rc-targeted Dependabot entries once #1049 has merged. Scheduled +workflows and Dependabot read their configuration from the DEFAULT +branch, so both live on `main`; version updates use +`target-branch: rc/202608` (security updates always target the default branch; the config validation covers both kinds and the wording distinguishes them). Pre-merge acceptance is not circular and not privileged: the workflow is split into two jobs. A dispatch validation job runs with `contents: read` only, no secrets, and no mutating steps; it checks out and executes the supplied SHA, which must -be a full 40-character SHA that the job verifies via the API to be -either the approved same-repository rc PR's current head or the merged -rc tip (arbitrary refs, stale SHAs, and non-approved commits are -rejected, proven by a negative workflow fixture). A separate +be a full 40-character SHA satisfying an exact machine predicate: the API +reports it as `head.sha` of pull request **#1049** in this same +repository, with `base.ref == "rc/202608"`, state open, and not a +draft - or it equals the current `origin/rc/202608` tip. No other +notion of "approved" is used (so the job needs no +`pull-requests: read` beyond the public metadata read it already has; +if a review-state predicate is ever added, that permission is added +with it). Arbitrary refs, stale SHAs, other PRs' heads, and +fork-repository SHAs are rejected, proven by negative workflow +fixtures, one per rejected class. A separate schedule-only issue-management job holds the job-scoped `issues: write` and never executes code from a supplied SHA - it checks out only the -branch tip it is configured for. Pre-merge, the validation job runs on -the rc PR head; post-merge, a dispatch against the merged tip is -recorded. (e) A fifth, release-triggered handoff PR to `main`: when -rc/202608 merges or is deleted, the workflow checkout and Dependabot -`target-branch` switch back to `main` - the checkout could fall back -dynamically but Dependabot's target is static, so a `main` edit is -unavoidable. It has a named owner, a tracked issue filed in WP8b, a -sequencing row, and a verification item (the automation must not keep -pointing at a dead branch); (d) the CNAME resolution +branch tip it is configured for. Pre-merge, the validation job runs on the rc PR head. +(c2) is an owned post-merge activation checkpoint, tracked as an issue +filed in WP8b with a named owner: after #1049 merges into rc, dispatch +the exact merged rc tip and record it, then land the activation edit +(schedule trigger + rc-targeted Dependabot roots); after rc lands on +`main`, validate the retargeted schedule, the Dependabot roots, live +Pages containment, and the selected CNAME behavior. (e) A fifth, release-triggered handoff PR to `main`, with two distinct +paths. Normal path - rc/202608 is deleted AFTER a verified merge into +`main`: the workflow checkout and Dependabot `target-branch` switch to +`main` (the checkout could fall back dynamically but Dependabot's +target is static, so a `main` edit is unavoidable), an XS retarget-only +edit. Abandonment path - rc is deleted WITHOUT merging: retargeting is +invalid because `main` then holds neither the refreshed docs nor +`tools/docs-parity` and its cargo root, so the handoff instead DISABLES +and removes the automation (schedule trigger, rc-targeted Dependabot +entries, link workflow) rather than pointing it at content that does +not exist; transplanting the tooling to `main` is a separate, +explicitly sized effort, never a silent XS edit. Both paths have a named owner, a tracked issue filed in WP8b, a +sequencing row, and a verification item (the automation must never keep +pointing at a dead or content-less branch); (d) the CNAME resolution PR to `main`, cut when open question 2 resolves (the containment PR NEVER carries it - WP1's earlier allowance is superseded). Every `main`-target PR records its own fresh audited_main_tip and runs the @@ -853,6 +885,7 @@ regions and goldens at final HEAD produces no diff. | ----- | -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------- | | 0 | WP1 containment subset → `main` PR (b) | XS | - | | 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | +| 9b | Post-merge activation checkpoint (c2) | XS | #1049 merged into rc; adds schedule trigger + rc Dependabot roots | | 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | | 10 | Release handoff PR (e) | XS | rc merge or deletion; owned, tracked issue; its filing is part of this refresh's completion, its merge is not | | 1 | WP1 hygiene (full, rc PR) | S | - | @@ -880,8 +913,11 @@ recorded; the `main` containment PR merged with its positive smoke, the `main` automation PR merged with a successful read-only validation dispatch against the rc PR head and validated Dependabot config, and the CNAME PR (d) merged with its branch-specific live smoke (each `main` -PR's audited_main_tip assertion having passed); the release-handoff -PR (e) has its owner and tracked issue recorded; every follow-up filed with a +PR's audited_main_tip assertion having passed); the post-merge +activation checkpoint (c2) recorded with its dispatch against the exact +merged rc tip; the release-handoff PR (e) existing as a draft PR URL in +a defined readiness state, with its owner, tracked issue, and both +path conditions recorded; every follow-up filed with a recorded URL or disposition; and the exact-tip baseline assertion for `origin/rc/202608` (equal to the recorded audited_target_tip, contained in the branch) passing at the final HEAD - @@ -892,8 +928,13 @@ not a merge-base comparison. 1. `fastly.toml` `service_id` allowlist owner and review date (blocks WP8a scanner activation); the ops migration itself blocks nothing. 2. CNAME: delete (recommended) or custom domain (fully specified branch). -3. `FAQ_POC.md` retirement; gam/kargo tombstones (routes preserved either - way). +3. `FAQ_POC.md` retirement (owner: the maintainer driving this refresh; + decide before WP2 starts). Deterministic default if undecided by + then: archive it under the historical tree (not a rewrite), leaving + no active-set page and no route to preserve. The gam/kargo + tombstones are NOT part of this question - WP2 applies them + unconditionally, with routes preserved, sidebar entries removed, and + old-route smokes asserting the tombstones serve. 4. `business-use-cases.md`: CLOSED - excluded with a source-level banner; republishing is a separate future effort (see Non-goals). 5. CHANGELOG release cut (out of scope; deterministic no-release edit From 306e1063cf4de0076bde5d9bba1b7a389c9820e0 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sat, 29 Aug 2026 00:08:10 -0700 Subject: [PATCH 23/33] Rework spec after fifteenth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 153 ++++++++++++------ 1 file changed, 103 insertions(+), 50 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index c994c7092..37400e33b 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-28 (round 14) +**Revised:** 2026-08-29 (round 15) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk @@ -259,20 +259,31 @@ The owner's standing instruction is one rc PR carrying spec plus work; the concrete shape below (which adds the forced `main` containment PR and package checkpoints) still awaits explicit confirmation - open question 7 blocks implementation until it is given, and its answer is recorded with -owner and date. The delivery graph is four PRs delivered by this refresh - (a)-(d) - -plus one mandatory future PR (e) whose tracked issue and draft this -refresh must produce: (a) the single rc PR (#1049) +owner and date. The delivery graph is four PRs delivered inside this refresh - +(a)-(d) - plus two named follow-on `main` PRs it must set up: (c2), the +post-merge activation PR, and (e), the release handoff. Each is a real +PR with its own audited_main_tip, because each mutates default-branch +files: (a) the single rc PR (#1049) carrying the spec plus all packages; (b) the `main` containment PR; (c) a `main` automation PR, which lands the workflow in DISPATCH-ONLY form (no `schedule:` trigger, no rc-targeted Dependabot entries) because scheduled runs and Dependabot would otherwise inspect pre-refresh rc content and a docs-parity cargo root that does not yet -exist on any target branch. Activation is a separate, owned post-merge -checkpoint (see (c2)) that adds the `schedule:` trigger and the -rc-targeted Dependabot entries once #1049 has merged. Scheduled +exist on any target branch. Activation is a separate NAMED `main` PR (c2) - it mutates +default-branch files, so it is a PR under this design's own protection +model, not a checkpoint - that adds the `schedule:` trigger and the +rc-targeted Dependabot entries once #1049 has merged, with its own +audited_main_tip. Scheduled workflows and Dependabot read their configuration from the DEFAULT branch, so both live on `main`; version updates use -`target-branch: rc/202608` (security updates always +`target-branch: rc/202608`. Security updates always target the DEFAULT +branch, and the dependency graph analyzes manifests from it, so while +`tools/docs-parity` exists only on rc it receives version-update PRs +but NO Dependabot alerts or security-update PRs - an explicit, +time-bounded risk closing at the release merge (temporary dependency +submission for rc is the mitigation if that window proves long); the +config validation covers both kinds and says so rather than +overstating coverage (security updates always target the default branch; the config validation covers both kinds and the wording distinguishes them). Pre-merge acceptance is not circular and not privileged: the workflow is split into two jobs. A dispatch @@ -291,23 +302,40 @@ fixtures, one per rejected class. A separate schedule-only issue-management job holds the job-scoped `issues: write` and never executes code from a supplied SHA - it checks out only the branch tip it is configured for. Pre-merge, the validation job runs on the rc PR head. -(c2) is an owned post-merge activation checkpoint, tracked as an issue -filed in WP8b with a named owner: after #1049 merges into rc, dispatch -the exact merged rc tip and record it, then land the activation edit -(schedule trigger + rc-targeted Dependabot roots); after rc lands on -`main`, validate the retargeted schedule, the Dependabot roots, live -Pages containment, and the selected CNAME behavior. (e) A fifth, release-triggered handoff PR to `main`, with two distinct -paths. Normal path - rc/202608 is deleted AFTER a verified merge into -`main`: the workflow checkout and Dependabot `target-branch` switch to -`main` (the checkout could fall back dynamically but Dependabot's +(c2) is the owned post-merge activation PR to `main`, tracked as an +issue filed in WP8b with a named owner: after #1049 merges into rc, +record a `merged_rc_tip`, dispatch that exact SHA, then land the +activation edit (schedule trigger + rc-targeted Dependabot roots). +Because `main` does not yet contain `tools/docs-parity` or the WP8 +workflows, c2's own checks cannot come from `main`: its validation job +checks out the trusted merged rc tip for the TOOL and a second checkout +of the c2 head for the FILES UNDER TEST, statically validating the +actual changed workflow and Dependabot file, and c2's acceptance +additionally requires a successful real scheduled run after activation +(including the schedule-only issue job). Required-check activation on +`main` is deferred until the release merge puts the tooling there - +that deferral is recorded in the WP8b branch-protection acceptance item +rather than leaving c2 blocked on checks that cannot report. (e) The handoff PR to `main`, which GATES branch deletion rather than +being triggered by it - deleting rc first would leave the schedule and +Dependabot pointing at a dead branch, the exact state this design +forbids. Ordering, normal release: verify rc landed on `main` -> merge +(e) retargeting to `main` -> verify -> only then delete rc. Ordering, +abandonment: merge (e) disabling/removing the automation -> verify -> +only then delete rc. Normal path - the workflow checkout and Dependabot +`target-branch` switch to `main` (the checkout could fall back dynamically but Dependabot's target is static, so a `main` edit is unavoidable), an XS retarget-only -edit. Abandonment path - rc is deleted WITHOUT merging: retargeting is +edit. Abandonment path - rc is abandoned WITHOUT merging: retargeting is invalid because `main` then holds neither the refreshed docs nor `tools/docs-parity` and its cargo root, so the handoff instead DISABLES and removes the automation (schedule trigger, rc-targeted Dependabot entries, link workflow) rather than pointing it at content that does not exist; transplanting the tooling to `main` is a separate, -explicitly sized effort, never a silent XS edit. Both paths have a named owner, a tracked issue filed in WP8b, a +explicitly sized effort, never a silent XS edit. The two paths are mutually exclusive diffs, so this refresh does not +open a single speculative draft: it produces a tracked issue plus two +REVIEWED patch templates/runbooks (retarget, and disable/remove), and +the concrete PR is opened from the matching runbook once the outcome is +known, with the same dual-checkout validation c2 uses applied to the +concrete (e) diff before handoff. Both paths have a named owner, a sequencing row, and a verification item (the automation must never keep pointing at a dead or content-less branch); (d) the CNAME resolution PR to `main`, cut when open question 2 resolves (the containment PR @@ -407,7 +435,11 @@ type-check` and the `settings_data::get_settings` example (the exported - Auction README repairs (route table by symbol name, real provider layout: `AuctionPlan`, `PROFILE_REGISTRATIONS`, `GenericOpenRtbProvider`, mediator; remove the removed-`mock` sections). -- Retire `FAQ_POC.md` (fallback if rejected: archive or factual rewrite); +- `FAQ_POC.md` is a three-way decision (open question 3): retire, + archive under the historical tree, or factual rewrite - archive is + the deadline fallback; a rewrite carries its own acceptance (every + answer verified against code, page kept in the active public set), + while retire and archive leave no active-set page; replace `gam.md`/`kargo.md` with tombstone content (routes preserved unconditionally, `tombstone` orphan-allowlist kind, old-route smokes). - `TESTING.md` rewritten as the test-matrix index; the auction runbook @@ -881,26 +913,30 @@ regions and goldens at final HEAD produces no diff. ## Sequencing -| Order | Package | Size | Depends on | -| ----- | -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------- | -| 0 | WP1 containment subset → `main` PR (b) | XS | - | -| 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | -| 9b | Post-merge activation checkpoint (c2) | XS | #1049 merged into rc; adds schedule trigger + rc Dependabot roots | -| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | -| 10 | Release handoff PR (e) | XS | rc merge or deletion; owned, tracked issue; its filing is part of this refresh's completion, its merge is not | -| 1 | WP1 hygiene (full, rc PR) | S | - | -| 2 | WP8a scaffolding | L | - | -| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | -| 4 | WP3 config reference | L | WP8a (extractor, harness) | -| 5 | WP4 API reference | M | WP2, WP8a | -| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | -| 7 | WP6 root + READMEs | M | WP2 | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 | +| Order | Package | Size | Depends on | +| ----- | -------------------------------------- | ---- | ----------------------------------------------------------------------------------------------- | +| 0 | WP1 containment subset → `main` PR (b) | XS | - | +| 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | +| 9b | Post-merge activation PR (c2) → `main` | XS | #1049 merged into rc; own audited_main_tip; dual-checkout validation; real scheduled run | +| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | +| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion (merge e, verify, then delete); issue + two runbooks produced by this refresh | +| 1 | WP1 hygiene (full, rc PR) | S | - | +| 2 | WP8a scaffolding | L | - | +| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | +| 4 | WP3 config reference | L | WP8a (extractor, harness) | +| 5 | WP4 API reference | M | WP2, WP8a | +| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | +| 7 | WP6 root + READMEs | M | WP2 | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 | ## Verification -At the final rc-PR HEAD: all GitHub checks green (CodeQL with rc +Verification runs in three epochs, because a single state cannot hold +both "target still equals the audited baseline" and "#1049 has merged": + +**Epoch 1 - pre-merge, at the final rc-PR HEAD** (`origin/rc/202608` +still equals audited_target_tip `07dfc1c6d`): all GitHub checks green (CodeQL with rc triggers, format including the docs build, the seven test.yml jobs, the four integration-test jobs, release builds, JS build/test); the WP8 parity suite and negative fixtures green; regeneration produces no diff; @@ -913,25 +949,42 @@ recorded; the `main` containment PR merged with its positive smoke, the `main` automation PR merged with a successful read-only validation dispatch against the rc PR head and validated Dependabot config, and the CNAME PR (d) merged with its branch-specific live smoke (each `main` -PR's audited_main_tip assertion having passed); the post-merge -activation checkpoint (c2) recorded with its dispatch against the exact -merged rc tip; the release-handoff PR (e) existing as a draft PR URL in -a defined readiness state, with its owner, tracked issue, and both -path conditions recorded; every follow-up filed with a -recorded URL or disposition; and the exact-tip baseline -assertion for `origin/rc/202608` (equal to the recorded -audited_target_tip, contained in the branch) passing at the final HEAD - -not a merge-base comparison. +PR's audited_main_tip assertion having passed); every follow-up filed with a recorded URL or +disposition; the release-handoff work (e) existing as a tracked issue +plus two reviewed runbooks (retarget / disable-and-remove), with its +owner and both path conditions recorded and branch deletion documented +as gated by (e); and the exact-tip baseline assertion for +`origin/rc/202608` (equal to the recorded audited_target_tip, contained +in the branch) passing at this HEAD - not a merge-base comparison. + +**Epoch 2 - after #1049 merges into rc**: record a `merged_rc_tip`; +dispatch that exact SHA through the read-only validation job and record +the result; open and merge the c2 activation PR to `main` (its own +audited_main_tip; dual-checkout validation - trusted tool from the +merged rc tip, files under test from the c2 head - covering the actual +changed workflow and Dependabot file); then observe one successful real +scheduled run, including the schedule-only issue job. + +**Epoch 3 - at release, before rc deletion**: verify rc landed on +`main` (or that it is being abandoned); open the concrete (e) PR from +the matching runbook, validated the same dual-checkout way against its +real diff; merge it; re-verify the retargeted (or removed) schedule and +Dependabot roots, live Pages containment, and the selected CNAME +behavior; only then is rc deleted. If the release merge happened, +`main` now carries the tooling, so the deferred required-check +activation on `main` is completed here per the WP8b protection item. ## Open questions 1. `fastly.toml` `service_id` allowlist owner and review date (blocks WP8a scanner activation); the ops migration itself blocks nothing. 2. CNAME: delete (recommended) or custom domain (fully specified branch). -3. `FAQ_POC.md` retirement (owner: the maintainer driving this refresh; - decide before WP2 starts). Deterministic default if undecided by - then: archive it under the historical tree (not a rewrite), leaving - no active-set page and no route to preserve. The gam/kargo +3. `FAQ_POC.md`: three-way - retire, archive, or factual rewrite + (owner: the maintainer driving this refresh; decide before WP2 + starts). Deterministic fallback if undecided by then: archive under + the historical tree, leaving no active-set page and no route to + preserve. "Do not retire" alone is not an answer - it selects + between archive and rewrite, and rewrite has its own acceptance. The gam/kargo tombstones are NOT part of this question - WP2 applies them unconditionally, with routes preserved, sidebar entries removed, and old-route smokes asserting the tombstones serve. From 2daa370721dc36b6f96b018a4081559ce3d1b6a2 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sat, 29 Aug 2026 09:51:34 -0700 Subject: [PATCH 24/33] Rework spec after sixteenth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 146 ++++++++++++------ 1 file changed, 97 insertions(+), 49 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 37400e33b..43fb3ab1e 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-29 (round 15) +**Revised:** 2026-08-29 (round 16) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk @@ -259,7 +259,15 @@ The owner's standing instruction is one rc PR carrying spec plus work; the concrete shape below (which adds the forced `main` containment PR and package checkpoints) still awaits explicit confirmation - open question 7 blocks implementation until it is given, and its answer is recorded with -owner and date. The delivery graph is four PRs delivered inside this refresh - +owner and date. + +Milestones (so "complete" has one meaning): **implementation-ready** +after Epoch 1 - PRs (a)-(d) merged and every content/enforcement +acceptance met; **activated** after Epoch 2 - c2 merged and a real +scheduled run observed; **lifecycle-closed** after Epoch 3 - (e) +merged and rc deleted. THIS REFRESH CLOSES AT "activated"; Epoch 3 is +owned, tracked, and specified here but belongs to release management, +which is out of scope. The delivery graph is four PRs delivered inside this refresh - (a)-(d) - plus two named follow-on `main` PRs it must set up: (c2), the post-merge activation PR, and (e), the release handoff. Each is a real PR with its own audited_main_tip, because each mutates default-branch @@ -279,26 +287,57 @@ branch, so both live on `main`; version updates use `target-branch: rc/202608`. Security updates always target the DEFAULT branch, and the dependency graph analyzes manifests from it, so while `tools/docs-parity` exists only on rc it receives version-update PRs -but NO Dependabot alerts or security-update PRs - an explicit, -time-bounded risk closing at the release merge (temporary dependency -submission for rc is the mitigation if that window proves long); the -config validation covers both kinds and says so rather than -overstating coverage (security updates always -target the default branch; the config validation covers both kinds and -the wording distinguishes them). Pre-merge acceptance is not circular +but NO Dependabot alerts or security-update PRs - an explicit risk with a +real bound: owner = the maintainer driving this refresh, review date +recorded when c2 merges, and a maximum window of 60 days, after which +the mitigation is mandatory rather than optional. The mitigation is a +SEPARATE, minimally permissioned dependency-submission job (snapshot +creation needs `contents: write`, which the read-only validation job +must never hold). The rc-targeted entry configures version updates +only; it does not configure security updates, and the validation +asserts exactly that rather than claiming coverage of both. +Pre-merge acceptance is not circular and not privileged: the workflow is split into two jobs. A dispatch validation job runs with `contents: read` only, no secrets, and no -mutating steps; it checks out and executes the supplied SHA, which must -be a full 40-character SHA satisfying an exact machine predicate: the API -reports it as `head.sha` of pull request **#1049** in this same -repository, with `base.ref == "rc/202608"`, state open, and not a -draft - or it equals the current `origin/rc/202608` tip. No other -notion of "approved" is used (so the job needs no +mutating steps. Its inputs are TWO independent, separately authorized +SHAs, because one predicate cannot cover both the trusted tool and the +files being inspected: + +- `tool_sha` - the code that EXECUTES. A full 40-character SHA that the + API reports as `head.sha` of pull request **#1049** in this same + repository with `base.ref == "rc/202608"`, open and non-draft; or the + current `origin/rc/202608` tip; or (from Epoch 2 on) the recorded + `merged_rc_tip`, which the job independently verifies is still an + ancestor of `origin/rc/202608` - if rc has advanced past it in a way + that breaks reachability, the job fails and the tip is re-audited. +- `files_sha` - data that is NEVER executed. A full 40-character SHA + that the API reports as the current `head.sha` of a named, + same-repository, OPEN pull request targeting `main` (the c2 or e PR). + Its checkout uses `persist-credentials: false`, is restricted to an + allowed-file set (the workflow file and `.github/dependabot.yml`), + canonicalizes paths and rejects symlinks escaping the tree, and is + read statically only - "nothing from the files checkout is executed" + is an explicit workflow invariant, since executing an untrusted PR + checkout in a privileged context is the exact pattern GitHub warns + against. + +No other notion of "approved" is used (so the job needs no `pull-requests: read` beyond the public metadata read it already has; if a review-state predicate is ever added, that permission is added -with it). Arbitrary refs, stale SHAs, other PRs' heads, and -fork-repository SHAs are rejected, proven by negative workflow -fixtures, one per rejected class. A separate +with it). Arbitrary refs, stale heads, other PRs' heads, fork SHAs, +and a `files_sha` from a closed or non-`main`-targeting PR are +rejected, proven by negative workflow fixtures, one per rejected class. + +Trust and binding: the run is always orchestrated by the dispatcher +workflow version already merged on `main` (never the version at the +PR head - a PR must not be able to edit or skip its own validator), so +the run associates with `main` rather than the PR head. The job +therefore records `validated_files_sha`, publishes a Check Run/status +against exactly that SHA, and the PR carries a just-before-merge +assertion that its head still equals `validated_files_sha`; where a +Check Run cannot be attached, the fallback is recorded manual evidence +plus a branch rule requiring approval of the latest push. This binding +applies identically to c2 and to the abandonment form of e. A separate schedule-only issue-management job holds the job-scoped `issues: write` and never executes code from a supplied SHA - it checks out only the branch tip it is configured for. Pre-merge, the validation job runs on the rc PR head. @@ -307,10 +346,10 @@ issue filed in WP8b with a named owner: after #1049 merges into rc, record a `merged_rc_tip`, dispatch that exact SHA, then land the activation edit (schedule trigger + rc-targeted Dependabot roots). Because `main` does not yet contain `tools/docs-parity` or the WP8 -workflows, c2's own checks cannot come from `main`: its validation job -checks out the trusted merged rc tip for the TOOL and a second checkout -of the c2 head for the FILES UNDER TEST, statically validating the -actual changed workflow and Dependabot file, and c2's acceptance +workflows, c2's own checks cannot come from `main`: its validation job runs the +dual-ref flow above - `tool_sha` = the trusted merged rc tip, +`files_sha` = the c2 PR head - statically validating the actual changed +workflow and Dependabot file and binding the result to that SHA, and c2's acceptance additionally requires a successful real scheduled run after activation (including the schedule-only issue job). Required-check activation on `main` is deferred until the release merge puts the tooling there - @@ -334,8 +373,8 @@ explicitly sized effort, never a silent XS edit. The two paths are mutually excl open a single speculative draft: it produces a tracked issue plus two REVIEWED patch templates/runbooks (retarget, and disable/remove), and the concrete PR is opened from the matching runbook once the outcome is -known, with the same dual-checkout validation c2 uses applied to the -concrete (e) diff before handoff. Both paths have a named owner, a +known, with the same dual-ref validation and SHA binding c2 uses applied to +the concrete (e) diff before handoff. Both paths have a named owner, a sequencing row, and a verification item (the automation must never keep pointing at a dead or content-less branch); (d) the CNAME resolution PR to `main`, cut when open question 2 resolves (the containment PR @@ -894,12 +933,18 @@ WP8b (lands last): Merge-blocking is a repository setting, not a workflow property: a failing check blocks merges only when the ruleset/branch protection -requires it. WP8b therefore carries an externally owned acceptance item -recording: the required check names and their GitHub App, the -ruleset/branch-protection evidence for BOTH `rc/202608` and `main`, -the bypass policy, one demonstrated failing check actually preventing a -merge, and `merge_group` triggers on the new workflows if the -repository adopts merge queues. +requires it. WP8b therefore carries an externally owned acceptance item that is +PATH-SPECIFIC, because `main` cannot require checks whose tooling it +does not yet contain: (i) Epoch 1 - the new checks are required on +`rc/202608`, with one demonstrated failing check actually preventing an +rc merge, and the `main` deferral explicitly recorded; (ii) normal +Epoch 3, after the release merge puts the tooling on `main` - the +checks are activated there, with a separate demonstrated `main` block; +(iii) abandonment - `main` activation is marked INAPPLICABLE, with +evidence that no required check referencing a nonexistent workflow +remains configured on `main`. Every path records the required check +names and their GitHub App, the bypass policy, and `merge_group` +triggers on the new workflows if the repository adopts merge queues. Acceptance: every runtime gate has a synthesized negative fixture (dead link, broken intra-doc link, failing doctest, invalid or unknown-keyed @@ -913,22 +958,22 @@ regions and goldens at final HEAD produces no diff. ## Sequencing -| Order | Package | Size | Depends on | -| ----- | -------------------------------------- | ---- | ----------------------------------------------------------------------------------------------- | -| 0 | WP1 containment subset → `main` PR (b) | XS | - | -| 0b | `main` automation PR (c) | XS | WP8a workflow content; dispatch acceptance vs the rc PR head SHA | -| 9b | Post-merge activation PR (c2) → `main` | XS | #1049 merged into rc; own audited_main_tip; dual-checkout validation; real scheduled run | -| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | -| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion (merge e, verify, then delete); issue + two runbooks produced by this refresh | -| 1 | WP1 hygiene (full, rc PR) | S | - | -| 2 | WP8a scaffolding | L | - | -| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | -| 4 | WP3 config reference | L | WP8a (extractor, harness) | -| 5 | WP4 API reference | M | WP2, WP8a | -| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | -| 7 | WP6 root + READMEs | M | WP2 | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 | +| Order | Package | Size | Depends on | +| ----- | --------------------------------------- | ---- | ------------------------------------------------------------------------------------------------- | +| 0 | WP1 containment subset → `main` PR (b) | XS | - | +| 9b | Post-merge activation PR (c2) → `main` | S | #1049 merged into rc; own audited_main_tip; dual-ref validation + SHA binding; real scheduled run | +| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | +| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion (merge e, verify, then delete); issue + two runbooks produced by this refresh | +| 1 | WP1 hygiene (full, rc PR) | S | - | +| 2 | WP8a scaffolding | L | - | +| 2b | `main` automation PR (c), dispatch-only | XS | WP8a (workflow content exists); validation dispatch vs the #1049 head | +| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | +| 4 | WP3 config reference | L | WP8a (extractor, harness) | +| 5 | WP4 API reference | M | WP2, WP8a | +| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | +| 7 | WP6 root + READMEs | M | WP2 | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 | ## Verification @@ -957,9 +1002,12 @@ as gated by (e); and the exact-tip baseline assertion for `origin/rc/202608` (equal to the recorded audited_target_tip, contained in the branch) passing at this HEAD - not a merge-base comparison. -**Epoch 2 - after #1049 merges into rc**: record a `merged_rc_tip`; -dispatch that exact SHA through the read-only validation job and record -the result; open and merge the c2 activation PR to `main` (its own +**Epoch 2 - after #1049 merges into rc**: record `merged_rc_tip` as the +exact merge result of #1049 and verify it is reachable from +`origin/rc/202608` (if rc has since advanced such that it is not, or +the predicate no longer accepts it, re-audit and use the new rc tip); +dispatch that exact SHA as `tool_sha` through the read-only validation +job and record the result; open and merge the c2 activation PR to `main` (its own audited_main_tip; dual-checkout validation - trusted tool from the merged rc tip, files under test from the c2 head - covering the actual changed workflow and Dependabot file); then observe one successful real From b404a36d0e9a54cb65fb2c417fae07c68e58f90a Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sat, 29 Aug 2026 09:51:55 -0700 Subject: [PATCH 25/33] Order sequencing table by epoch --- .../specs/2026-08-19-documentation-refresh-design.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 43fb3ab1e..e7b4750f6 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -961,9 +961,7 @@ regions and goldens at final HEAD produces no diff. | Order | Package | Size | Depends on | | ----- | --------------------------------------- | ---- | ------------------------------------------------------------------------------------------------- | | 0 | WP1 containment subset → `main` PR (b) | XS | - | -| 9b | Post-merge activation PR (c2) → `main` | S | #1049 merged into rc; own audited_main_tip; dual-ref validation + SHA binding; real scheduled run | | 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | -| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion (merge e, verify, then delete); issue + two runbooks produced by this refresh | | 1 | WP1 hygiene (full, rc PR) | S | - | | 2 | WP8a scaffolding | L | - | | 2b | `main` automation PR (c), dispatch-only | XS | WP8a (workflow content exists); validation dispatch vs the #1049 head | @@ -974,6 +972,12 @@ regions and goldens at final HEAD produces no diff. | 7 | WP6 root + READMEs | M | WP2 | | 8 | WP7 in-code docs | M | - | | 9 | WP8b gate activation | M | WP2-WP7 | +| 9b | Post-merge activation PR (c2) → `main` | S | #1049 merged into rc; own audited_main_tip; dual-ref validation + SHA binding; real scheduled run | +| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion (merge e, verify, then delete); issue + two runbooks produced by this refresh | + +Orders 0-9 are Epoch 1 (this refresh; closes at implementation-ready +once they land, then activated after 9b). 9b is Epoch 2. 10 is Epoch 3, +owned and specified here but executed at release. ## Verification From 92a3a334feeb8e68f350d19f59b5a06915d404bb Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:02:37 -0700 Subject: [PATCH 26/33] Rework spec after seventeenth pre-implementation review --- ...2026-08-19-documentation-refresh-design.md | 67 ++++++++++++++----- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index e7b4750f6..4943cee18 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,7 +1,7 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-29 (round 16) +**Revised:** 2026-08-29 (round 17) **Status:** Draft, pending review **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk @@ -262,10 +262,15 @@ blocks implementation until it is given, and its answer is recorded with owner and date. Milestones (so "complete" has one meaning): **implementation-ready** -after Epoch 1 - PRs (a)-(d) merged and every content/enforcement -acceptance met; **activated** after Epoch 2 - c2 merged and a real -scheduled run observed; **lifecycle-closed** after Epoch 3 - (e) -merged and rc deleted. THIS REFRESH CLOSES AT "activated"; Epoch 3 is +after Epoch 1 - the `main` PRs (b)-(d) merged, and (a) #1049 complete, +approved, all checks green, and ready to merge (it is not yet merged; +Epoch 1 runs at its final pre-merge head while rc still equals the +audited baseline); **activated** after Epoch 2 - (a) merged, then c2 +merged and a real scheduled run observed; **lifecycle-closed** after +Epoch 3 - (e) merged and rc deleted. Enforcement acceptance is +epoch-scoped: WP8b's Epoch 1 items (rc-required checks, demonstrated rc +block, recorded `main` deferral) gate implementation-ready; its Epoch 3 +items do not. THIS REFRESH CLOSES AT "activated"; Epoch 3 is owned, tracked, and specified here but belongs to release management, which is out of scope. The delivery graph is four PRs delivered inside this refresh - (a)-(d) - plus two named follow-on `main` PRs it must set up: (c2), the @@ -287,21 +292,34 @@ branch, so both live on `main`; version updates use `target-branch: rc/202608`. Security updates always target the DEFAULT branch, and the dependency graph analyzes manifests from it, so while `tools/docs-parity` exists only on rc it receives version-update PRs -but NO Dependabot alerts or security-update PRs - an explicit risk with a -real bound: owner = the maintainer driving this refresh, review date -recorded when c2 merges, and a maximum window of 60 days, after which -the mitigation is mandatory rather than optional. The mitigation is a -SEPARATE, minimally permissioned dependency-submission job (snapshot -creation needs `contents: write`, which the read-only validation job -must never hold). The rc-targeted entry configures version updates +but NO Dependabot alerts or security-update PRs. The mitigation is not deferred to a +deadline this refresh would close before reaching: c2 installs a +SEPARATE, minimally permissioned dependency-submission job immediately +(snapshot creation needs `contents: write`, which the read-only +validation job must never hold), submitting the `tools/docs-parity` +manifest for rc so alerts and security updates work from day one; it +recurs on rc lockfile changes, and (e) removes it on both paths. c2's +acceptance includes first-successful-snapshot evidence, and the +Dependabot risk is thereby closed at activation rather than carried as +a 60-day exposure. The rc-targeted entry configures version updates only; it does not configure security updates, and the validation asserts exactly that rather than claiming coverage of both. Pre-merge acceptance is not circular and not privileged: the workflow is split into two jobs. A dispatch validation job runs with `contents: read` only, no secrets, and no -mutating steps. Its inputs are TWO independent, separately authorized -SHAs, because one predicate cannot cover both the trusted tool and the -files being inspected: +mutating steps. It has two mutually exclusive, fail-closed dispatch MODES, because not +every dispatch inspects a `main` PR: + +- `validate_rc` - requires `tool_sha` ONLY (no `files_sha`; supplying + one is rejected). Used for the Epoch 1 dispatch against #1049's head + and the first Epoch 2 dispatch of `merged_rc_tip`: it exercises the + WP8 tooling itself. +- `validate_main_pr` - requires `tool_sha`, `files_sha`, the PR number, + and the authenticated `base_sha` (see below). Used for c2 and e. + +Its inputs are TWO independent, separately authorized SHAs, because one +predicate cannot cover both the trusted tool and the files being +inspected: - `tool_sha` - the code that EXECUTES. A full 40-character SHA that the API reports as `head.sha` of pull request **#1049** in this same @@ -313,8 +331,20 @@ files being inspected: - `files_sha` - data that is NEVER executed. A full 40-character SHA that the API reports as the current `head.sha` of a named, same-repository, OPEN pull request targeting `main` (the c2 or e PR). - Its checkout uses `persist-credentials: false`, is restricted to an - allowed-file set (the workflow file and `.github/dependabot.yml`), + Validation is over the DIFF, not just head contents: the job + authenticates `base_sha` (the PR's current base, equal to the + recorded `audited_main_tip`) and inspects `base_sha...files_sha`, + because a restricted checkout cannot prove the PR changes nothing + else, and the abandonment form of e deletes the workflow outright so + there is no head file to read. It enforces (i) an exact changed-file + allowlist, rejecting every extra path; (ii) a path-specific expected + patch shape - c2: only the schedule activation plus the named + Dependabot additions; normal e: only the rc-to-main retarget; + abandonment e: only the reviewed removals; and (iii) workflow AST + invariants over the resulting file where one exists - permissions, + triggers, `uses` pins, `run` steps, secrets access, local actions, + cache/artifact usage, and checkout refs - not merely the presence of + an expected schedule. Its checkout uses `persist-credentials: false`, canonicalizes paths and rejects symlinks escaping the tree, and is read statically only - "nothing from the files checkout is executed" is an explicit workflow invariant, since executing an untrusted PR @@ -346,7 +376,8 @@ issue filed in WP8b with a named owner: after #1049 merges into rc, record a `merged_rc_tip`, dispatch that exact SHA, then land the activation edit (schedule trigger + rc-targeted Dependabot roots). Because `main` does not yet contain `tools/docs-parity` or the WP8 -workflows, c2's own checks cannot come from `main`: its validation job runs the +workflows, the TOOL cannot come from `main` (the dispatcher/controller +still does): its validation job runs the dual-ref flow above - `tool_sha` = the trusted merged rc tip, `files_sha` = the c2 PR head - statically validating the actual changed workflow and Dependabot file and binding the result to that SHA, and c2's acceptance From b904b3aeb5af26a536afadcbfb2d70af36bca5a2 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:02:57 -0700 Subject: [PATCH 27/33] Split attestation job from read-only validation --- ...2026-08-19-documentation-refresh-design.md | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 4943cee18..c36ac093e 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -361,13 +361,22 @@ rejected, proven by negative workflow fixtures, one per rejected class. Trust and binding: the run is always orchestrated by the dispatcher workflow version already merged on `main` (never the version at the PR head - a PR must not be able to edit or skip its own validator), so -the run associates with `main` rather than the PR head. The job -therefore records `validated_files_sha`, publishes a Check Run/status -against exactly that SHA, and the PR carries a just-before-merge -assertion that its head still equals `validated_files_sha`; where a -Check Run cannot be attached, the fallback is recorded manual evidence -plus a branch rule requiring approval of the latest push. This binding -applies identically to c2 and to the abandonment form of e. A separate +the run associates with `main` rather than the PR head. Because the +validation job holds `contents: read` and no write token it cannot +itself publish a status, so permissions are split by job: the +validation job (`contents: read`, no secrets, no mutating steps); an +ATTESTATION job with `statuses: write` only, no checkout, `needs:` the +validation job, publishing a COMMIT STATUS (not a Check Run) with a +fixed context string from a fixed app identity against exactly +`validated_files_sha`; the schedule-only issue job (`issues: write` +only); and the dependency-submission job (`contents: write` only). +That fixed status context is what branch protection requires on the c2 +and e PRs, and each PR carries a just-before-merge assertion that its +head still equals `validated_files_sha` AND its base still equals the +authenticated `base_sha`. Manual evidence is NOT an equivalent +fallback - it cannot block a merge; if the status cannot be published, +the PR does not merge. This binding +applies identically to c2 and to the abandonment form of e. The schedule-only issue-management job holds the job-scoped `issues: write` and never executes code from a supplied SHA - it checks out only the branch tip it is configured for. Pre-merge, the validation job runs on the rc PR head. From 8588391b9e9d6f02d886c519836eedb84a37abd8 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:02:06 -0700 Subject: [PATCH 28/33] Approve documentation refresh delivery plan --- .../audits/documentation-refresh-decisions.md | 132 ++ .../audits/documentation-refresh-evidence.md | 408 ++++ .../plans/2026-08-30-documentation-refresh.md | 1896 +++++++++++++++++ ...2026-08-19-documentation-refresh-design.md | 843 ++++++-- 4 files changed, 3056 insertions(+), 223 deletions(-) create mode 100644 docs/internal/audits/documentation-refresh-decisions.md create mode 100644 docs/internal/audits/documentation-refresh-evidence.md create mode 100644 docs/superpowers/plans/2026-08-30-documentation-refresh.md diff --git a/docs/internal/audits/documentation-refresh-decisions.md b/docs/internal/audits/documentation-refresh-decisions.md new file mode 100644 index 000000000..15ba3c574 --- /dev/null +++ b/docs/internal/audits/documentation-refresh-decisions.md @@ -0,0 +1,132 @@ +# Documentation Refresh Decisions + +- **Decision date:** 2026-08-31 +- **Approver:** `aram356` +- **Status:** Approved for implementation + +This record fixes the owner-gated choices for the documentation refresh. It +does not record operational receipts; those belong in +`documentation-refresh-evidence.md` or the named post-merge tracking issues. + +## Audited tips + +| Name | Full commit SHA | Use | +| --------------------------- | ------------------------------------------ | ------------------------------------------------------------------ | +| `audited_target_tip` | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Exact required `origin/rc/202608` tip for Epoch 1 | +| `implementation_start_head` | `b904b3aeb5af26a536afadcbfb2d70af36bca5a2` | Task 1 worktree HEAD before this approval commit | +| Starting `audited_main_tip` | `d516a9e94249e10cbc36e41beb4269f9255cf407` | Initial reference only; each later `main` PR records a fresh value | + +Task 1 fetched `origin/rc/202608` and `origin/main` on 2026-08-31. The rc ref +equaled `audited_target_tip`, and the implementation branch contained that +commit. A later `main` PR must not reuse the starting `audited_main_tip` after +`main` advances. + +## Owner-gated decisions + +### 1. Temporary Fastly service-ID exception + +- Selection: retain the checked-in `fastly.toml` `service_id` only through a + typed, temporary scanner allowlist entry. +- Owner: `aram356`. +- Next review: 2026-09-30. +- Control: the review date requires renewal or expiry of the exception. It is + not an ops migration deadline, and the migration does not block this refresh. + +### 2. CNAME + +- Selection: delete `docs/public/CNAME` and retain the project-path base. +- Owner: `aram356`. +- Decision date: 2026-08-31. +- Rejected alternative: adopt a custom domain with `base: '/'`, verified + Pages/DNS/TLS configuration, URL inventory changes, and live smokes. +- Rollback: never restore the placeholder CNAME. Keep the CNAME deleted and + re-smoke project URLs, or restore only a previously verified custom-domain + DNS/CNAME/TLS tuple without weakening containment exclusions. + +### 3. `FAQ_POC.md` + +- Selection: move it to `docs/superpowers/archive/FAQ_POC.md`. +- Owner: `aram356`. +- Decision date: 2026-08-31. +- Rejected alternatives: delete it, or rewrite it as the active public page + `docs/guide/faq.md` with the rewrite-specific verification contract. +- Result: no active-set FAQ page and no FAQ route to preserve. The independent + gam/kargo tombstone requirements remain unchanged. + +### 4. `business-use-cases.md` + +- State: closed. +- Selection: exclude it from the public build and add the source-level + unverified banner. +- Rejected alternative: rewrite and republish it inside this refresh. + +### 5. CHANGELOG release cut + +- State: explicitly non-blocking and out of scope. +- Selection: apply the deterministic no-release edit in the design unless a + release lands first; a release requires rebase and focused re-audit. + +### 6. Governance ownership + +- Owner: none named. +- Selection: factual-governance fallback. +- Decision date: 2026-08-31. +- Required edit: correct `ProjectGovernance.md` to current evidence—no minutes + exist and releases are not continuous—without adding CODEOWNERS or minutes + commitments. Naming owners remains a maintainer follow-up. + +### 7. Delivery shape and external controls + +`aram356` approved the following on 2026-08-31: + +- five PRs through activation: rc PR (a), containment PR (b), validation-only + controller PR (c), CNAME deletion PR (d), and activation PR (c2); +- the separately owned release-handoff PR (e) and both reviewed outcome paths; +- temporary `main` branch protection requiring the literal + `docs/automation-delta` status with strict/up-to-date enforcement; +- merge queues disabled on `main` through PR (e); and +- the external dependency-snapshot retirement API call under the design's + freeze, drain/cancel, same-identity empty-snapshot, receipt, graph-check, and + branch-deletion controls. + +The rollback and exact-SHA binding requirements in the design remain +mandatory. This approval does not authorize weaker substitutes or link-only +evidence. + +### 8. CodeQL rc push coverage + +- State: explicitly non-blocking. +- Selection: no additional decision is required for implementation to start. + +## Delivery records + +Populate each pending field with the exact URL and immutable identifiers at +the named checkpoint. Do not infer a value from a branch name. + +| Item | Target | URL | Fresh audited base | Head or merge SHA | State | +| --------------------------------- | ----------- | -------------------------------- | ------------------------------------------ | ----------------- | ------------- | +| (a) rc implementation PR | `rc/202608` | Pending verification of PR #1049 | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Pending | Open | +| (b) containment PR | `main` | Pending | Pending | Pending | Not started | +| (c) validation-only controller PR | `main` | Pending | Pending | Pending | Not started | +| (d) CNAME deletion PR | `main` | Pending | Pending | Pending | Not started | +| (c2) activation PR | `main` | Pending | Pending | Pending | Not started | +| (e) release-handoff PR | `main` | Pending | Pending | Pending | Release-owned | + +## External issue records + +| Record | Canonical URL | Owner | State | +| ------------------------------ | ------------- | -------------------------------- | --------- | +| c2 activation tracking issue | Pending | Pending before c2 opens | Not filed | +| Release-handoff tracking issue | Pending | Pending before activation closes | Not filed | + +## Branch-protection and ruleset records + +The detailed JSON and body hashes belong in the evidence record or canonical +post-merge issue. Mirror only the decision-relevant values here. + +| Checkpoint | Capture reference | Required contexts | Strict/up-to-date | Merge queue | Bypass policy | +| ------------------------- | ----------------- | ------------------------------------------- | ---------------------------------- | ------------------------- | ------------- | +| Before PR (c) | Pending | Pending inventory | Pending | Must be disabled | Pending | +| After PR (c) | Pending | Includes `docs/automation-delta` | Required | Disabled | Pending | +| After PR (e), normal path | Pending | `docs/automation-delta` plus full WP8 suite | Required | Pending owner disposition | Pending | +| After PR (e), abandonment | Pending | No nonreporting automation context | Restored from captured prior state | Pending owner disposition | Pending | diff --git a/docs/internal/audits/documentation-refresh-evidence.md b/docs/internal/audits/documentation-refresh-evidence.md new file mode 100644 index 000000000..665f70eba --- /dev/null +++ b/docs/internal/audits/documentation-refresh-evidence.md @@ -0,0 +1,408 @@ +# Documentation Refresh Evidence + +- **Schema version:** 1 +- **Created:** 2026-08-31 +- **Epoch 1 system of record:** this append-only repository ledger +- **Epoch 2 system of record:** canonical c2 tracking issue, URL pending +- **Epoch 3 system of record:** canonical release-handoff issue, URL pending + +This ledger records evidence, not plans or inferred outcomes. A pending field +is not proof. Command output may be summarized only when the command, exact +commit, result, and authoritative raw-capture location are also recorded. + +## Durable capture contract + +Every Epoch 2 or Epoch 3 issue capture, and every Epoch 1 external handoff +import, must be append-only and timestamped in UTC. Each capture includes: + +- schema version, actor, operation, and timestamp; +- exact commit and ref, plus PR number and exact head, base, and trusted-tool + SHAs when a PR or validator is involved; +- workflow run ID, attempt, job name, and job URL when automation is involved; +- redacted request method, endpoint, and body; +- response status and redacted response body; +- dependency snapshot detector, correlator, snapshot ID, ref, and SHA when + applicable; and +- the applicable dependency-graph, ruleset, branch-protection, merge-queue, or + branch API JSON. + +Tokens, credential-bearing headers, cookies, and unredacted secrets are never +captured. Every request body, response body, and API JSON body carries its own +SHA-256. A capture larger than 60 KiB is split into ordered chunks; each chunk +records its byte length and SHA-256, and the capture records the aggregate +byte length and SHA-256. Workflow, PR, issue, and artifact URLs are navigation +aids only; pasted redacted bodies plus hashes are authoritative. Corrections +append a new comment that names the superseded comment URL and capture ID; +existing comments are never edited or deleted. + +### Capture template + +```text +Capture ID: +Schema version: 1 +Timestamp (UTC): +Actor: +Operation: +Commit SHA: +Ref: +PR number / URL: +PR head SHA: +PR base SHA: +Trusted-tool SHA: +Run ID / attempt / URL: +Job name / URL: +Request method / endpoint: +Redacted request body: +Request-body bytes / SHA-256: +Response status: +Redacted response body: +Response-body bytes / SHA-256: +Snapshot detector / correlator / ID / ref / SHA: +Graph API JSON bytes / SHA-256: +Ruleset API JSON bytes / SHA-256: +Protection and merge-queue API JSON bytes / SHA-256: +Branch API JSON bytes / SHA-256: +Chunk index / total, chunk bytes / SHA-256, aggregate bytes / SHA-256: +Navigation URLs: +Supersedes capture/comment: +Result: +``` + +## Package checkpoint template + +Copy this block into the matching Epoch 1 section before each package starts. +One block covers one reviewed package or adjacent evidence-only commit. + +```text +Task / package: +Package start HEAD: +Timestamp (UTC): +Actor: +Approved path list: +Failing fixture or pre-change proof: +Focused red command / expected diagnostic: +Minimal change: +Focused green command / result: +Affected regressions / result: +Exact staged name-status from package start HEAD: +Untracked-path check / result: +Unstaged tracked-byte check / result: +Generated command / second-run no-diff proof: +Docs-parity checks / result: +Live smoke or external receipt: +Exception / owner / rationale / expiry: +Evidence-ledger restage and repeated checks: +git diff --cached --check result: +Commit SHA / message: +Post-commit clean-status result: +Correction reference: +``` + +## Epoch 1: pre-merge implementation evidence + +Epoch 1 retains exact evidence while `origin/rc/202608` equals +`07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` and the implementation branch +contains that commit. Any rc advance requires a focused delta audit and an +updated approved baseline before work continues. + +### Task 1: decisions and immutable tips + +- Capture timestamp: 2026-08-31T22:55:52Z. +- Actor: agent acting for approver `aram356`. +- Operation: fetch refs, verify exact rc tip and ancestry, record starting + `main` tip, and establish decision/evidence records. +- Implementation start HEAD: + `b904b3aeb5af26a536afadcbfb2d70af36bca5a2`. +- `git fetch origin rc/202608 main`: passed. +- `git rev-parse origin/rc/202608`: + `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`. +- Ancestry check for `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` + against `HEAD`: exited 0. +- `git rev-parse origin/main`: + `d516a9e94249e10cbc36e41beb4269f9255cf407`. +- Approved paths: the design, implementation plan, decision record, and this + evidence record. +- `cd docs && npm run format`: passed. +- `cd docs && npm run lint`: passed. +- `cd docs && npm run build`: passed; generated `.vitepress/.temp` output was + removed and not staged. +- `git diff --check` and `git diff --cached --check`: passed after formatting. +- Cached path review: exactly the four approved paths above; no unintended + untracked file or unstaged tracked byte remained. +- Commit: the enclosing commit uses + `Approve documentation refresh delivery plan`; its SHA and post-commit clean + status are recorded in the execution handoff because a commit cannot contain + its own SHA. + +### External delivery URLs and immutable identifiers + +| Item | PR or issue URL | Target | Fresh audited base | Validated head/tool | Merge SHA | Evidence state | +| --------------------------------- | -------------------------------- | ----------- | ------------------------------------------ | ------------------- | --------- | -------------- | +| (a) rc implementation PR | Pending verification of PR #1049 | `rc/202608` | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Pending | Pending | Pending | +| (b) containment PR | Pending | `main` | Pending | Pending | Pending | Pending | +| (c) validation-only controller PR | Pending | `main` | Pending | Pending | Pending | Pending | +| (d) CNAME deletion PR | Pending | `main` | Pending | Pending | Pending | Pending | +| (c2) activation PR | Pending | `main` | Pending | Pending | Pending | Epoch 2 | +| (e) release-handoff PR | Pending | `main` | Pending | Pending | Pending | Epoch 3 | + +### Cross-worktree handoff: PRs (b), (c), and (d) + +While each tightly scoped `main` branch is open, its branch-specific evidence +stays in its PR description or named tracking issue. Do not add rc-only audit +records to that branch. The next named rc checkpoint imports every field below +by both URL and literal value. + +```text +Handoff ID / PR label: (b), (c), or (d) +Source PR / tracking issue URL: +Source capture IDs: +Source worktree path: +Branch name: +Fresh audited_main_tip: +Exact PR base SHA: +Exact PR head SHA: +Merge SHA: +Changed paths and modes: +Local commands and results: +Hosted check names, apps, run IDs, attempts, jobs, and results: +Ruleset / protection / merge-queue capture IDs and body SHA-256 values: +Live receipt or smoke commands, endpoints, expected content, and results: +Rollback owner and tested rollback path: +Imported by rc task / package start HEAD: +Import timestamp / actor: +Imported literal values checked against source: +Import commit SHA: +``` + +Required import checkpoints: + +- PR (b): import into Task 4 after live containment smoke. +- PR (d): import into Task 4 after project-URL smoke. +- PR (c): import in the adjacent Task 11 rc evidence checkpoint. + +### Branch-protection, ruleset, and queue snapshots + +| Checkpoint | Capture ID / URL | Exact `main` SHA | Required contexts and apps | Strict | Merge queue | Bypass policy | Body SHA-256 | +| ------------------------ | ---------------- | ---------------- | ---------------------------------------------------- | -------- | -------------------- | ------------- | ------------ | +| Before PR (c) | Pending | Pending | Pending | Pending | Must report disabled | Pending | Pending | +| Immediately after PR (c) | Pending | Pending | Includes `docs/automation-delta` from GitHub Actions | Required | Disabled | Pending | Pending | +| Epoch 1 final reproval | Pending | Pending | Pending exact inventory | Required | Disabled | Pending | Pending | + +### First-success smokes and public delivery + +| Surface | Exact commit/ref | Command or operation | Expected oracle | Receipt / result | +| ------------------------------------ | ---------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------- | +| Pages containment | PR (b) merge | Live URL matrix | Excluded paths 404; root, Guide, and reference page return expected 200 content | Pending | +| CNAME deletion | PR (d) merge | Project-path live URL matrix | Canonical page and assets resolve under project path; placeholder absent | Pending | +| Axum | Pending | `scripts/smoke-axum.sh` | Non-health publisher response satisfies the documented strong oracle | Pending | +| Fastly | Pending | `scripts/smoke-fastly.sh` | Local push and required secrets yield a non-health publisher response | Pending | +| Cloudflare | Pending | `scripts/smoke-cloudflare.sh` | Envelope transfer yields a non-health publisher response | Pending | +| Spin | Pending | `scripts/smoke-spin.sh` or time-bounded manual contract | Local push and variables yield a non-health publisher response | Pending | +| Controller read-only dispatch | Pending | `validate_rc` | Exact trusted rc SHA passes; no writer or attestation job runs | Pending | +| Controller protected-delta rejection | Pending | Unauthorized protected-file fixture | Required status blocks merge | Pending | +| Controller unrelated-PR pass | Pending | Net-empty protected-delta fixture | Required status reports success without privileged PR checkout | Pending | + +### Generated-diff proof + +Each generator records its command, first-run changed paths, second-run exit +status, and exact clean-diff assertion. “Generated” without a second-run +no-diff proof is incomplete. + +| Generator / region | Source SHA | First-run output | Second-run command | Clean-diff assertion | Result | +| -------------------------------- | ---------- | ---------------- | ------------------ | -------------------- | ------- | +| Tracked/source classification | Pending | Pending | Pending | Pending | Pending | +| Settings reference | Pending | Pending | Pending | Pending | Pending | +| Route/API reference | Pending | Pending | Pending | Pending | Pending | +| Integration/support matrix | Pending | Pending | Pending | Pending | Pending | +| CLI help goldens | Pending | Pending | Pending | Pending | Pending | +| Gate consumers | Pending | Pending | Pending | Pending | Pending | +| c2 and inverse patches | Pending | Pending | Pending | Pending | Pending | +| Release retarget/disable patches | Pending | Pending | Pending | Pending | Pending | + +### Exceptions and waivers + +Every exception requires an owner, narrow rationale, and review or expiry date. +Expired or ownerless entries fail the checkpoint. + +| Type / path | Value classification | Owner | Rationale | Review or expiry | State | +| ------------------------------------------- | ---------------------- | --------------------- | ---------------------------------------------- | -------------------------------- | -------- | +| `fastly.toml` `service_id` | Service ID | `aram356` | Temporary checked-in operational identifier | 2026-09-30 review/expiry control | Approved | +| Task 15 temporary public-page ownership | Page/orphan transition | Pending Task 15 owner | Page registered before Task 16 final ownership | Expires at Task 16 | Pending | +| Spin manual smoke, only if CI cannot run it | Manual evidence | Pending | Runner capability gap | Time-bounded date required | Pending | + +### Follow-up issues + +Each row requires a deduplicated issue URL or explicit existing-issue +disposition, owner, and labels. Do not replace these rows with umbrella issues. + +| Finding | Issue or disposition URL | Owner | Labels | State | +| -------------------------------------------------- | ------------------------ | ------- | ------- | ------- | +| Adapter `Hooks::stores()` and dead store manifests | Pending | Pending | Pending | Pending | +| Cloudflare config-store / CLI envelope bridge | Pending | Pending | Pending | Pending | +| Axum local config-store / env bridge | Pending | Pending | Pending | Pending | +| Cross-adapter health and startup-failure contract | Pending | Pending | Pending | Pending | +| `imp_ext` reserved-field protection | Pending | Pending | Pending | Pending | +| Partner pull-token placeholder rejection | Pending | Pending | Pending | Pending | +| Inline `trusted_client_ip.shared_secret` | Pending | Pending | Pending | Pending | +| Deploy-ID constant and set equality | Pending | Pending | Pending | Pending | +| Vendored CLI help internal references | Pending | Pending | Pending | Pending | +| Tinybird telemetry runtime support | Pending | Pending | Pending | Pending | +| `.env.dev` undeclared `opid_store` | Pending | Pending | Pending | Pending | +| Fastly staging config-blob selection | Pending | Pending | Pending | Pending | + +### Package sections + +Every section below receives a completed package checkpoint block. A task with +an isolated `main` PR records its rc import checkpoint separately. + +#### Task 2 — PR (b) public-site containment + +Pending. Evidence source: cross-worktree handoff; import at Task 4. + +#### Task 3 — PR (d) CNAME deletion + +Pending. Evidence source: cross-worktree handoff; import at Task 4. + +#### Task 4 — WP1 rc import and hygiene + +Pending. Must import PRs (b) and (d), prove byte identity, and complete the +Task 1 bootstrap exception checks. + +#### Task 5 — docs-parity model and repository scaffolding + +Pending. Record each atomic fixture cycle and the bootstrap exception checks. + +#### Task 6 — exhaustive classification and sensitive scanner + +Pending. Must bootstrap the complete then-current repository and enforce the +approved service-ID exception. + +#### Task 7 — Markdown ownership, links, and generated regions + +Pending. Record each atomic fixture cycle. + +#### Task 8 — settings extraction and template harness + +Pending. Record each atomic fixture cycle. + +#### Task 9 — integrations, routes, and adapter support + +Pending. Record each atomic fixture cycle and any behavior-preserving private +route seam. + +#### Task 10 — CLI help, snippets, gates, workflows, and snapshots + +Pending. Preserve the planned adjacent source and golden commits and record +both clean checkpoints. + +#### Task 11 — PR (c) validation-only controller + +Pending. Keep branch evidence external while open, then import exact values in +the adjacent rc evidence commit before Task 12. + +#### Task 12 — WP2 truth pass and FAQ archive + +Pending. Record full source/disposition equality, scanner results, tombstone +smokes, and the selected archive move. + +#### Task 13 — WP3 configuration reference + +Pending. Record generated reference equality, compiled probes, and template +round-trip proof. + +#### Task 14 — WP4 API contracts + +Pending. Record route-set equality, adapter predicates, generated no-diff, and +adapter regression suites for any private seam. + +#### Task 15 — deployment guides and first-success smokes + +Pending. Record all four smoke contracts, exact tool versions, cleanup, and +any time-bounded Spin exception. + +#### Task 16 — WP5 product coverage and navigation + +Pending. Record page/orphan ownership, diagram prose equivalents, snippet +checks, and removal of the Task 15 transition exception. + +#### Task 17 — WP6 root and crate documentation + +Pending. Apply and verify the factual-governance fallback. + +#### Task 18 — WP7 rustdoc and JSDoc + +Pending. Record the rustdoc matrix, doctests, JSDoc fixtures, and JS checks. + +#### Task 19 — WP8b enforcement and release controls + +Pending. Record every workflow negative fixture, generated consumer proof, +follow-up issue row, c2 issue URL, release-handoff issue URL, and reviewed +patch/runbook hashes. + +#### Task 20 — final Epoch 1 acceptance + +Pending. Record the exact final rc baseline, full local and hosted gates, +required-check topology, generated no-diff, smokes, all external handoffs, +clean package shape, final PR head, and implementation-ready approval. + +## Epoch 2: activation evidence schema + +- **Canonical c2 tracking issue URL:** Pending; Task 19 must populate it before + activation. +- **Owner:** Pending before c2 opens. + +The issue uses the durable capture contract above. Required captures: + +1. #1049 merge result, `merged_rc_tip`, current `origin/rc/202608`, and any + focused delta audit producing `validated_rc_tip`. +2. Read-only `validate_rc` dispatch at `ref: main`, exact `tool_sha`, run and + job identity, and proof that no writer or attestation ran. +3. c2 PR URL, fresh `audited_main_tip`, exact base/head/tool SHAs, protected + path modes/blob IDs, cached candidate diff, and trusted validation result. +4. Automatic pending status and exact manual success replacement, including + context, source app, head/base binding, and just-before-merge reassertion. +5. c2 merge SHA and first `refresh_dependency_snapshot` dispatch. +6. Redacted snapshot request, 201 response, stable detector/correlator, + snapshot ID, authenticated rc ref/SHA, graph API JSON, alert-triage owner, + runbook URL, and two-business-day SLA. +7. One genuine scheduled run, including link reader, schedule-only issue + writer, snapshot reader/writer, reconciliation, timeout/concurrency state, + and resulting issue/snapshot state. +8. Reverse-c2 patch proof for both protected modes/blobs and, if invoked, + drain/cancel and same-identity empty-snapshot receipts. +9. Activation decision. It must not claim lifecycle closure. + +No Epoch 2 repository evidence-only branch or commit is permitted. + +## Epoch 3: release-handoff evidence schema + +- **Canonical release-handoff issue URL:** Pending; Task 19 must populate it + before activation closes. +- **Owner:** Pending before activation closes. + +The issue uses the durable capture contract above. Required captures: + +1. Selected normal or abandonment path, reviewed patch/runbook hashes, owner, + exact `validated_rc_tip`, freeze timestamp, bypass policy, and repeated + freeze checks. +2. On normal release, current-main base versus frozen-rc-head modes/blob IDs + for both protected paths and the net-empty result; otherwise the separately + reviewed repair/sync PR evidence. +3. PR (e) URL, fresh `audited_main_tip`, exact head/base/tool SHAs, automatic + pending status, manual validation success, and just-before-merge binding. +4. PR (e) merge SHA and proof that no further temporary snapshot submission + path remains. +5. Enumeration of every queued or in-progress same-identity run, wait/cancel + result, optional separately scoped `actions: write` actor, and proof the + empty same-identity snapshot is the final submission. +6. Redacted retirement request and 201 response, exact merge SHA/ref, + detector/correlator/snapshot ID, dependency-graph replacement or + disappearance, and all body hashes. +7. Final ruleset, required-context, strictness, merge-queue, live Pages/CNAME, + branch API, and rc-deletion captures. +8. Normal-path full WP8 activation and maintenance allow/reject proofs, or + abandonment-path removal of the nonreporting context plus the next-PR + non-stranding proof. +9. Lifecycle-closed decision only after every selected-path gate passes. + +No Epoch 3 repository evidence-only branch or commit is permitted. diff --git a/docs/superpowers/plans/2026-08-30-documentation-refresh.md b/docs/superpowers/plans/2026-08-30-documentation-refresh.md new file mode 100644 index 000000000..536d8ca8f --- /dev/null +++ b/docs/superpowers/plans/2026-08-30-documentation-refresh.md @@ -0,0 +1,1896 @@ +# Documentation Refresh (Full Surface) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Refresh every maintained documentation surface, make reader-facing inventories derive from checked records, and activate enforcement without exposing write credentials to pull-request-controlled code. + +**Architecture:** One rc implementation PR supplies eight package checkpoints and the canonical `docs-parity` tooling. Four small `main` PRs contain the public site, install a base-controlled validation controller, resolve CNAME, and activate scheduled automation after the rc merge; a separately owned release handoff closes the temporary branch lifecycle. Checked manifests connect code inventories, generated Markdown, source classification, examples, and CI so each fact has one source of truth. + +**Tech Stack:** Rust 1.95 (`syn`, Serde, `error-stack`, Cargo), VitePress/Node 24, ESLint/JSDoc, GitHub Actions and REST APIs, shell smoke scripts, Fastly Viceroy, Wrangler, Spin, Axum + +**Revised:** 2026-08-31 after full spec/plan review (round 21) + +--- + +## Execution gate + +**Gate status:** Satisfied by `aram356` on 2026-08-31. The approval covers the +five-PR-through-activation delivery shape, temporary `main` +`docs/automation-delta` required-status and strict/up-to-date protection +change, merge queues disabled on `main` through PR (e), and the external +dependency-snapshot retirement call under the specified runbook and controls. +Task 2 may begin only after Task 1 commits this approval. + +The narrower owner gates are also resolved: + +- `aram356` owns the temporary `fastly.toml` `service_id` allowlist exception; + its 2026-09-30 review date controls review or expiry, not the ops migration. +- Task 3 deletes `docs/public/CNAME` and retains the project-path base. PR (d) + must merge before Task 4 imports the live publishing deltas into rc. +- Task 12 archives `FAQ_POC.md` at + `docs/superpowers/archive/FAQ_POC.md`. +- Task 17 uses the factual-governance fallback; no governance owner was named. +- Questions 5 and 8 remain explicitly non-blocking; question 4 remains closed. + +## File map + +### Program records and release controls + +| File | Responsibility | +| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| `docs/superpowers/specs/2026-08-19-documentation-refresh-design.md` | Approved design, immutable baseline contract, owner decisions, and epoch definitions. | +| `docs/superpowers/plans/2026-08-30-documentation-refresh.md` | This execution plan and package checkpoints. | +| `docs/internal/audits/documentation-refresh-decisions.md` | Owners, dates, selected open-question branches, audited tips, ruleset snapshot, and PR URLs. | +| `docs/internal/audits/documentation-refresh-inventory.toml` | Per-file or per-region WP2 dispositions and source anchors. | +| `docs/internal/audits/documentation-refresh-evidence.md` | Epoch 1 commands/proofs/smokes plus schemas and issue links for post-merge evidence. | +| `docs/internal/runbooks/documentation-automation-release.md` | Normal and abandonment release sequencing, snapshot retirement, and branch deletion gate. | +| `docs/internal/runbooks/documentation-automation-rollback.md` | Controller, c2, Pages, and CNAME rollback procedures. | +| `docs/internal/runbooks/patches/docs-links-c2.patch` | Reviewed activation delta from validation-only controller to the rc-final workflow. | +| `docs/internal/runbooks/patches/docs-links-rollback-c2.patch` | Exact inverse of c2 without overwriting unrelated base changes. | +| `docs/internal/runbooks/patches/docs-links-release-retarget.patch` | Normal release retarget/removal template. | +| `docs/internal/runbooks/patches/docs-links-release-disable.patch` | Abandonment removal template. | + +### `docs-parity` crate and checked records + +`tools/docs-parity` is a standalone Cargo workspace with its own committed lockfile; it is not added to the repository workspace members. + +| File | Responsibility | +| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `tools/docs-parity/Cargo.toml` | Standalone binary/library metadata, `[workspace]`, dependencies, and lint policy. | +| `tools/docs-parity/Cargo.lock` | Reproducible host-tool dependency graph. | +| `tools/docs-parity/README.md` | Subcommands, manifest ownership, update/check flow, and failure semantics. | +| `tools/docs-parity/src/main.rs` | Thin CLI parsing and exit-code mapping. | +| `tools/docs-parity/src/lib.rs` | Subcommand dispatch and shared `Report` API. | +| `tools/docs-parity/src/model.rs` | Checked record schemas, ownership/expiry types, and generated-region markers. | +| `tools/docs-parity/src/repository.rs` | Repository-root discovery, tracked-file enumeration, safe paths, exact Git object reads, and atomic writes. | +| `tools/docs-parity/src/classification.rs` | Text/binary classification and exhaustive candidate/span closure. | +| `tools/docs-parity/src/scanner.rs` | Domain, email, credential, identifier, encoded-token, lockfile, binary-string, and media-metadata scanners. | +| `tools/docs-parity/src/markdown.rs` | Link/anchor parsing, fence inventory, ownership markers, orphan/tombstone checks, and generated regions. | +| `tools/docs-parity/src/settings.rs` | Serde-aware settings extractor, companion semantics, compiled probes, and template harness. | +| `tools/docs-parity/src/integrations.rs` | Integration/provider inventory and capability-record checks. | +| `tools/docs-parity/src/routes.rs` | Route record checks, Cloudflare fail-closed parser, and adapter-support rendering. | +| `tools/docs-parity/src/cli_help.rs` | Linux/macOS help capture, annotated union, overrides, and golden comparison. | +| `tools/docs-parity/src/snippets.rs` | Fence manifest, diagnostic matching, isolated execution, and waiver expiry. | +| `tools/docs-parity/src/gates.rs` | Canonical gate manifest and link-only/generated consumer checks. | +| `tools/docs-parity/src/workflow.rs` | YAML AST policy, dispatch/diff authentication fixtures, and PR-status state machine. | +| `tools/docs-parity/src/dependency_snapshot.rs` | Schema-validated Cargo dependency snapshot generation only; submission stays in the no-checkout workflow writer. | + +Checked records live under `tools/docs-parity/manifests/`: `tracked-files.toml`, `maintained-sources.toml`, `sensitive-allowlist.toml`, `retired-identifiers.toml`, `snippets.toml`, `settings-companions.toml`, `routes.toml`, `integrations.toml`, `adapter-support.toml`, `cli-overrides.toml`, `gates.toml`, `pages.toml`, `diagrams.toml`, and `orphans.toml`. CLI goldens live at `tools/docs-parity/goldens/cli-linux.txt` and `tools/docs-parity/goldens/cli-macos.txt`. Synthetic fixtures live under `tools/docs-parity/tests/fixtures/`; never add a live secret, internal contact, or real customer value as a fixture. + +### Existing surfaces with known edits + +- Publishing/policy: `docs/.vitepress/config.mts`, `docs/guide/index.md`, `docs/guide/onboarding.md`, `docs/internal/onboarding.md`, `docs/business-use-cases.md`, `docs/public/CNAME`, `docs/package.json`, `docs/package-lock.json`, `fastly.toml`, `CLAUDE.md`, `AGENTS.md`, `.github/pull_request_template.md`, and `.claude/commands/{check-ci,review-changes,test-all,test-crate,verify}.md`. +- Truth pass: the active sets defined by the spec, with named repairs in `docs/guide/{ad-serving,architecture,configuration,creative-processing,error-reference,integration-guide,roadmap}.md`, `docs/guide/integrations/{gam,kargo}.md`, `crates/trusted-server-core/src/auction/README.md`, `TESTING.md`, `FAQ_POC.md`, `CHANGELOG.md`, `.env.example`, `.env.dev`, `.claude/agents/{code-architect,issue-creator}.md`, `crates/trusted-server-openrtb/generate.sh`, and the human-facing workflow/script comments recorded in the inventory. +- Configuration/API: `trusted-server.example.toml`, `docs/guide/configuration.md`, `docs/guide/api-reference.md`, and generated/check seams in `crates/trusted-server-core/src/{config,settings,auction_config_types}.rs`, `crates/trusted-server-core/src/auction/{plan,profile}.rs`, `crates/trusted-server-core/src/integrations/*.rs`, the four adapter `src/app.rs` files, and their route tests. +- New coverage: `docs/guide/{auction-testing,axum-dev,cloudflare,edgezero,fastly,spin,telemetry,tsjs}.md`, `docs/guide/integrations/{adserver_mock,gpt,testlight}.md`, `tinybird/README.md`, `scripts/smoke-{axum,fastly,cloudflare,spin}.sh`, and `.github/workflows/integration-tests.yml`. +- README/rustdoc/JSDoc: the seven missing crate READMEs named in Task 17, their Cargo manifests, `scripts/README.md`, the WP7 Rust worklist, `crates/trusted-server-js/lib/eslint.config.js`, and the scoped TypeScript/MJS files named in the spec. +- Automation: `.github/workflows/{codeql,deploy-docs,docs-links,format,integration-tests,test}.yml`, `.github/dependabot.yml`, `.tool-versions`, and `crates/trusted-server-openrtb-codegen/Cargo.toml`. + +## Package checkpoint rule + +After each rc package task: + +1. Before editing, record `package_start_head="$(git rev-parse HEAD)"`. Make the + package edits, generate candidate outputs, and create the package's evidence + section. +2. Fully stage every intended add/modify/delete with the task's exact + pathspecs before classification or parity checks. Never use `git add -N`: + intent-to-add has no candidate blob. Review + `git diff --cached --name-status "$package_start_head"` and reject every + changed path outside the task's file list. Require + `git ls-files --others --exclude-standard` to print nothing, and require + `git diff --quiet` to exit 0 so no unstaged tracked byte anywhere can affect + a repository-wide check. Ignored dependency/build output remains unstaged. +3. Run the focused tests and package acceptance commands against that fully + staged universe. Regenerate checked outputs, restage only their exact paths, + run `docs-parity check`, and require a clean generated diff. + Bootstrap exception: Tasks 1, 4, and 5 run every staged-universe check above + but cannot regenerate `tracked-files.toml` or `maintained-sources.toml` + because Task 6 creates them. Task 6's initial bootstrap must classify the + complete then-current repository, including every path those tasks added, + moved, or deleted. From the Task 6 commit onward, every package that creates, + moves, or deletes a tracked path must regenerate and stage both manifests; + public-page changes must also regenerate the applicable page/orphan records. +4. Record commands/results in + `docs/internal/audits/documentation-refresh-evidence.md` and stage that + exact file. Because that mutation changes the candidate universe, repeat any + classification/scanner check that consumes the ledger, restage any generated + output, and again require global `git diff --quiet` so every tracked + working-tree byte equals the index/HEAD candidate. Run + `git diff --cached --check` and review the cached + name/status and content diff from `package_start_head` against only this + package. +5. Commit with the exact imperative message listed in the task. If recording + final commit/run identifiers requires a follow-up, make one immediately + adjacent evidence-only commit before starting the next package. Never let a + later directory-wide `git add` absorb earlier evidence, and do not squash + package or evidence commits. +6. Require `git status --porcelain` to be empty except for explicitly named, + reviewed state before advancing to the next package. + +### Atomic execution rule + +This is the master program plan. Composite implementation checkpoints in +Tasks 5-10, 15, and 19 are not single coding actions. Before changing a +component, copy its next fixture from the task's enumerated negative matrix +into the evidence checklist and execute one leaf cycle at a time: + +1. add one named failing fixture/test; +2. run its exact focused command and record the expected diagnostic; +3. implement the smallest production/tool change for that fixture; +4. rerun the focused command and its immediately affected regression set; +5. mark that leaf complete, then continue to the next named fixture. + +Do not batch multiple parser classes, workflow rejection classes, scanners, or +adapter seams into one unreviewed edit. The package checkpoint commit happens +only after every enumerated leaf is green; the evidence ledger is the resumable +leaf-task list. + +### Task 1: Record decisions and revalidate immutable tips + +**Files:** + +- Modify: `docs/superpowers/specs/2026-08-19-documentation-refresh-design.md` +- Modify: `docs/superpowers/plans/2026-08-30-documentation-refresh.md` +- Create: `docs/internal/audits/documentation-refresh-decisions.md` +- Create: `docs/internal/audits/documentation-refresh-evidence.md` + +- [x] **Step 1: Fetch and verify the rc baseline** + +Run: + +```bash +git fetch origin rc/202608 main +git rev-parse origin/rc/202608 +git merge-base --is-ancestor 07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf HEAD +``` + +Expected: the first command succeeds, `origin/rc/202608` prints exactly `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`, and the ancestry command exits 0. Stop and re-audit every new rc commit if either assertion changes. + +- [x] **Step 2: Record the current default-branch tip** + +Run `git rev-parse origin/main` and record the full SHA as the starting `audited_main_tip`; do not reuse it for a later `main` PR after `main` advances. + +- [x] **Step 3: Resolve the owner gates** + +Record owner/date/answer for questions 1, 2, 3, and 7. Record whether question 6 has an owner or will take the deterministic fallback. Leave questions 5 and 8 explicitly non-blocking and preserve question 4 as closed. + +- [x] **Step 4: Make the spec state executable** + +Change the spec status only after question 7 is explicit. Replace resolved open-question prose with the selected branch plus owner/date; do not erase the rejected alternatives or rollback requirements. + +- [x] **Step 5: Establish evidence templates** + +Add the complete Epoch 1/package evidence sections, PR/issue URLs for (a)-(e), +ruleset snapshots, first-success smokes, generated-diff proof, follow-up +issues, and exceptions with owner/expiry. For Epochs 2 and 3, record the +required evidence schema and canonical c2/release-handoff issue URLs. The +schema must require append-only timestamped captures of actor/operation, exact +commit/ref and PR head/base/tool SHAs, run IDs/attempts/jobs, redacted request +method/endpoint/body, response status/body, snapshot identity, and applicable +graph/ruleset/protection/branch API JSON. Hash every body with SHA-256; split +captures over 60 KiB into ordered hashed chunks; make links navigational rather +than authoritative; append corrections that name the superseded comment. +Actual post-merge receipts are captured under that schema in those issues +rather than committed later. + +Also define the cross-worktree handoff template for PRs (b), (c), and (d): +their branch-specific tips, checks, live receipts, and ruleset snapshots stay in +the PR description or named tracking issue while the tightly scoped `main` +branch is open. The next named rc checkpoint imports those captures by URL and +value into the rc evidence/decision records; the records never ride in a +scope-limited `main` PR. + +- [x] **Step 6: Verify and commit the approved handoff** + +Run `cd docs && npm run format`, then `git diff --check`. + +Expected: both commands pass and only the spec, plan, and new audit records are in this checkpoint. + +```bash +git add docs/superpowers/specs/2026-08-19-documentation-refresh-design.md docs/superpowers/plans/2026-08-30-documentation-refresh.md docs/internal/audits/documentation-refresh-decisions.md docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Approve documentation refresh delivery plan" +``` + +### Task 2: Ship the public-site containment PR (b) + +**Files:** + +- Modify: `docs/.vitepress/config.mts` +- Modify: `docs/guide/index.md` +- Move/Modify: `docs/guide/onboarding.md` → `docs/internal/onboarding.md` + +- [ ] **Step 1: Create an isolated branch from the fresh `origin/main` tip** + +Use `@superpowers:using-git-worktrees`. Record that PR's new +`audited_main_tip` in its PR-description handoff block; the containment branch +must contain exactly the four containment concerns below. Do not add the rc-only +decision/evidence records to this branch. + +- [ ] **Step 2: Prove the current build leaks excluded pages** + +Run `cd docs && npm ci && npm run build`, then assert that at least one `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.html`, `README.html`, or `business-use-cases.html` artifact exists. + +Expected: the assertion demonstrates the pre-change leak. Save the exact artifact path as failing evidence. + +- [ ] **Step 3: Add the minimal containment configuration** + +Set `srcExclude` to `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, `README.md`, and `business-use-cases.md`. Fill `docs/guide/index.md`, point the Guide nav item at `/guide/`, remove Business Value navigation, move/scrub onboarding, and remove every built-page link to an excluded source. Do not include CNAME, package metadata, marketing-copy edits, or unrelated navigation work. + +- [ ] **Step 4: Rebuild and prove the boundary** + +Run `cd docs && npm run lint && npm run format && npm run build`. + +Expected: all commands pass; the six excluded path families produce no output; `/index.html`, `/guide/index.html`, and `/guide/api-reference.html` exist and contain their expected headings. + +- [ ] **Step 5: Review and commit the XS diff** + +```bash +git add docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md +git diff --cached --check +git diff --cached --name-status "$AUDITED_MAIN_TIP" +git diff --cached "$AUDITED_MAIN_TIP" -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md +git commit -m "Contain internal documentation pages" +``` + +Reject any cached path beyond those four. Require `git diff --quiet` before the +commit so the reviewed index is the complete candidate. + +- [ ] **Step 6: Merge and smoke the live Pages deployment** + +Immediately before merge, refetch `main` and require the PR base to equal its +`audited_main_tip`; otherwise rebase, re-review, and record the new tip. After +merge, set a task-specific `DOCS_BASE_URL` from the selected project URL and +assert excluded URLs return 404 while site root, Guide, and API reference return +200 with expected text. Put response headers, deployment SHA, URLs, merge SHA, +and authenticated PR base in the external handoff block for Task 4 to import. + +### Task 3: Resolve CNAME in independent PR (d) + +**Files:** + +- Delete or Modify: `docs/public/CNAME` +- Modify only on custom-domain path: `docs/.vitepress/config.mts`, `README.md`, and every hard-coded Pages URL found by the checked inventory + +- [ ] **Step 1: Cut a new isolated branch from the then-current `origin/main`** + +Record a fresh `audited_main_tip` in the PR-description handoff block; never +stack this on containment or automation PRs and do not add rc-only audit records. + +- [ ] **Step 2: Execute exactly the selected branch** + +Delete path: remove `docs/public/CNAME` and keep the project-path `base`. +Custom-domain path: first run +`git grep -l -F 'https://iabtechlab.github.io/trusted-server'`; at the audited +baseline the exact URL-bearing path list is `README.md`. Record that list in the +external handoff block. If it differs, stop and amend this task's exact allowlist +and staging command before editing. Replace the placeholder with the approved +project-owned public domain, set `base: '/'`, update that exact URL set, and +attach owner/DNS/TLS evidence. + +- [ ] **Step 3: Build and test locally** + +Run `cd docs && npm ci && npm run lint && npm run format && npm run build`. + +Expected: the build is green and assets resolve under the selected base. + +- [ ] **Step 4: Commit, merge, and run branch-specific smokes** + +```bash +# Delete path: +git add -A -- docs/public/CNAME + +# Custom-domain path instead: +git add -A -- docs/public/CNAME docs/.vitepress/config.mts README.md + +git diff --cached --check +git diff --cached --name-status "$AUDITED_MAIN_TIP" +git commit -m "Resolve documentation site domain" +``` + +Run exactly one staging branch. The delete path's cached set is exactly CNAME; +the custom path's is exactly CNAME, config, and the recorded URL path. Review +the full cached content and require `git diff --quiet` before committing. + +Immediately before merge, assert the exact recorded base. After deploy, the +delete path re-smokes project URLs; the custom path records DNS, TLS, canonical +page, asset, and former hard-coded URL results. Put the merge SHA, base, and all +receipts in the external handoff block for Task 4. Never restore the placeholder +during rollback. + +### Task 4: Complete WP1 hygiene on rc + +**Files:** + +- Import exactly from PR (b): `docs/.vitepress/config.mts`, + `docs/guide/index.md`, `docs/guide/onboarding.md` → + `docs/internal/onboarding.md` +- Import exactly from selected PR (d): `docs/public/CNAME` plus, only on the + custom-domain path, `docs/.vitepress/config.mts`, `README.md`, and each + checked URL path in that PR +- Modify: `docs/business-use-cases.md` +- Modify: `fastly.toml` +- Modify: `docs/package.json` +- Modify: `docs/package-lock.json` +- Modify: `CLAUDE.md` +- Modify: `AGENTS.md` +- Modify: `.github/pull_request_template.md` +- Modify: `.claude/commands/check-ci.md` +- Modify: `.claude/commands/review-changes.md` +- Modify: `.claude/commands/test-all.md` +- Modify: `.claude/commands/test-crate.md` +- Modify: `.claude/commands/verify.md` +- Modify: `docs/internal/audits/documentation-refresh-evidence.md` +- Modify: `docs/internal/audits/documentation-refresh-decisions.md` + +- [ ] **Step 1: Import the live publishing deltas into rc** + +Fetch `main`, authenticate the recorded merge commits for (b) and (d), and +authenticate each PR's recorded base SHA. Do not merge a moving `main` +wholesale. First require each authenticated base-to-merge name/status to match +its PR allowlist. Then import the final modes/blobs for only those paths directly +from the merge tree and commit the two path sets separately. This intentionally +handles an rc path whose unrelated bytes diverged from `main`; later named rc +packages, not the import, reapply any intended content. Stop on any tree or +allowlist mismatch: + +```bash +b_import_start="$(git rev-parse HEAD)" +git diff --name-status "$B_BASE_SHA" "$B_MERGE_SHA" -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md +git restore --source="$B_MERGE_SHA" --staged --worktree -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md +git diff --cached --name-status "$b_import_start" +git diff --cached --check +git diff --quiet +git diff --quiet "$B_MERGE_SHA" -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md +git commit -m "Import public documentation containment" + +``` + +Then run exactly one d block. Delete path: + +```bash +d_import_start="$(git rev-parse HEAD)" +git diff --name-status "$D_BASE_SHA" "$D_MERGE_SHA" -- docs/public/CNAME +git restore --source="$D_MERGE_SHA" --staged --worktree -- docs/public/CNAME +git diff --cached --name-status "$d_import_start" +git diff --cached --check +git diff --quiet +git diff --quiet "$D_MERGE_SHA" -- docs/public/CNAME +git commit -m "Import documentation site domain" +git status --porcelain +``` + +Custom-domain path (the authenticated scan must still have exactly this path +set; otherwise update the plan before applying): + +```bash +d_import_start="$(git rev-parse HEAD)" +git diff --name-status "$D_BASE_SHA" "$D_MERGE_SHA" -- docs/public/CNAME docs/.vitepress/config.mts README.md +git restore --source="$D_MERGE_SHA" --staged --worktree -- docs/public/CNAME docs/.vitepress/config.mts README.md +git diff --cached --name-status "$d_import_start" +git diff --cached --check +git diff --quiet +git diff --quiet "$D_MERGE_SHA" -- docs/public/CNAME docs/.vitepress/config.mts README.md +git commit -m "Import documentation site domain" +git status --porcelain +``` + +Expected: each cached name/status is exactly its authenticated PR delta; every +imported existing path has the merge commit's mode/blob and every imported +deletion is absent; final status is empty. On the custom path, this direct +README import replaces any divergent rc bytes; Task 17 performs the later WP6 +README rewrite from that imported state. Record +both source base/merge pairs and resulting rc commit SHAs. Only after these two +clean import commits set the Task 4 `package_start_head` and begin hygiene edits. + +- [ ] **Step 2: Add assertions for the policy state** + +Use temporary `rg` assertions to show the banner, package privacy/license, empty authors, fixture labels, KV comments, canonical gate link, generated AGENTS gate region, and exception taxonomy are absent or stale before editing. + +- [ ] **Step 3: Apply the policy and hygiene edits** + +Add the unverified marketing banner; scrub `fastly.toml` as specified while preserving the time-bounded service-ID entry; set the docs package private/Apache-2.0 and refresh its lockfile metadata; add the exception taxonomy to `CLAUDE.md`; make command files and the PR template link-only gate consumers; generate the AGENTS fallback region. + +- [ ] **Step 4: Prove contacts/access guidance are absent** + +Search all tracked files for every removed onboarding contact, handle, channel, and access phrase. Expected: no matches outside an explicit typed exception in the decision record. + +- [ ] **Step 5: Verify and checkpoint WP1** + +Run: + +```bash +cd docs && npm ci && npm run lint && npm run format && npm run build +git diff --check +``` + +Expected: all commands pass and the containment/CNAME live evidence is linked from the rc evidence record. + +Import the complete external handoff blocks for (b) and (d), including their +audited bases, merge SHAs, live receipts, and URLs, into the rc evidence and +decision records before staging this checkpoint. + +```bash +git add docs/business-use-cases.md fastly.toml docs/package.json docs/package-lock.json CLAUDE.md AGENTS.md .github/pull_request_template.md .claude/commands/check-ci.md .claude/commands/review-changes.md .claude/commands/test-all.md .claude/commands/test-crate.md .claude/commands/verify.md docs/internal/audits/documentation-refresh-evidence.md docs/internal/audits/documentation-refresh-decisions.md +git commit -m "Clean documentation publishing policy" +``` + +### Task 5: Scaffold the standalone `docs-parity` crate + +**Files:** + +- Create: `tools/docs-parity/Cargo.toml` +- Create: `tools/docs-parity/Cargo.lock` +- Create: `tools/docs-parity/README.md` +- Create: `tools/docs-parity/src/main.rs` +- Create: `tools/docs-parity/src/lib.rs` +- Create: `tools/docs-parity/src/model.rs` +- Create: `tools/docs-parity/src/repository.rs` +- Create/Test: `tools/docs-parity/tests/cli.rs` + +- [ ] **Step 1: Write the failing CLI contract tests** + +Cover repository-root discovery from nested directories, `--help`, unknown subcommands, check-vs-update exit codes, paths outside the repository, unsafe relative paths, atomic-update interruption, and stable ordering. Expect the binary to be absent. + +- [ ] **Step 2: Create the independent Cargo root** + +Add `[workspace]`, package metadata, the repository's lint policy, `error-stack` error flow, and only the dependencies required by the checked formats. Do not add the tool to root `workspace.members`. + +- [ ] **Step 3: Implement the minimal shared model and repository boundary** + +The model must make ownership and expiry structurally mandatory where the spec requires them. Repository APIs accept normalized relative paths, reject symlink escapes/unsafe modes, enumerate Git-tracked paths, and write generated files atomically. + +- [ ] **Step 4: Run the focused tests** + +```bash +cargo test --manifest-path tools/docs-parity/Cargo.toml --test cli +cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check +cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings +``` + +Expected: all pass, `tools/docs-parity/Cargo.lock` exists, and root `Cargo.lock` is unchanged. + +- [ ] **Step 5: Document the update/check contract and commit the foundation** + +```bash +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/README.md tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/tests/cli.rs docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Add documentation parity tool foundation" +``` + +### Task 6: Close tracked-file classification and sensitive-data scanning + +**Files:** + +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.toml` +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.lock` +- Create: `tools/docs-parity/src/classification.rs` +- Create: `tools/docs-parity/src/scanner.rs` +- Modify: `tools/docs-parity/src/main.rs` +- Modify: `tools/docs-parity/src/lib.rs` +- Modify: `tools/docs-parity/src/model.rs` +- Modify: `tools/docs-parity/src/repository.rs` +- Create: `tools/docs-parity/manifests/tracked-files.toml` +- Create: `tools/docs-parity/manifests/maintained-sources.toml` +- Create: `tools/docs-parity/manifests/sensitive-allowlist.toml` +- Create: `tools/docs-parity/manifests/retired-identifiers.toml` +- Create/Test: `tools/docs-parity/tests/classification.rs` +- Create/Test: `tools/docs-parity/tests/scanner.rs` + +- [ ] **Step 1: Write exhaustive-classification failures** + +Add synthesized repositories proving each of these fails: an unknown text extension, an unknown binary, invalid UTF-8 in an expected-text file, oversized expected text, a new Dockerfile, a `.mjs` file, a `.proto` file, a human-facing comment outside an existing selector, a comment syntax without an extractor, a symlink escape, and an unclassified extracted comment span. + +- [ ] **Step 2: Implement the checked classification contract** + +Start from `git ls-files -z`; classify every path as text or binary without treating content sniffing as the authority. Require each text path to have a whole-file include/exclude or comment-region selector and each extracted comment span to have a disposition. Fail closed on new paths, selectors, or comment syntaxes. + +- [ ] **Step 3: Write scanner detector and allowlist tests** + +For domain, email, credential shape, service ID, encoded token, binary strings, lockfile structured fields, media metadata, and identifier/access-phrase denylist, add both a positive fixture and an owner/rationale/expiry allowlisted fixture. Prove expired entries, stale hashes, renamed files, and broad domain exemptions fail. + +- [ ] **Step 4: Implement deterministic scanning** + +Scan all tracked files. Parse lockfile source/registry/URL fields structurally, inspect binary strings and media metadata, and support only the five typed exception classes approved in WP1. Report semantic sensitivity outside detector classes as a required human disposition, not as a scanner guarantee. + +- [ ] **Step 5: Bootstrap and review the real manifests** + +Generate candidate entries, then manually disposition every path and comment span. Seed the identifier denylist from the WP1/WP2 removals. Record the `fastly.toml` exception owner/date from Task 1; do not enable check mode if that entry is incomplete. + +- [ ] **Step 6: Run the negative matrix and real scan** + +Add each scanner/classification dependency only in the standalone manifest and +regenerate its lockfile. Require `git diff --quiet -- Cargo.lock` so the root +workspace lockfile is unchanged. + +```bash +cargo test --manifest-path tools/docs-parity/Cargo.toml classification +cargo test --manifest-path tools/docs-parity/Cargo.toml scanner +cargo run --manifest-path tools/docs-parity/Cargo.toml -- classify --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- scan --check +``` + +Expected: synthesized violations fail for the intended diagnostic; the repository scan passes only with typed, unexpired entries. + +- [ ] **Step 7: Commit the closed universe** + +```bash +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/classification.rs tools/docs-parity/src/scanner.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/sensitive-allowlist.toml tools/docs-parity/manifests/retired-identifiers.toml tools/docs-parity/tests/classification.rs tools/docs-parity/tests/scanner.rs docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Enforce documentation source classification" +``` + +### Task 7: Implement generated regions, Markdown ownership, and link checks + +**Files:** + +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.toml` +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.lock` +- Create: `tools/docs-parity/src/markdown.rs` +- Modify: `tools/docs-parity/src/main.rs` +- Modify: `tools/docs-parity/src/lib.rs` +- Modify: `tools/docs-parity/src/model.rs` +- Modify: `tools/docs-parity/src/repository.rs` +- Create: `tools/docs-parity/manifests/pages.toml` +- Create: `tools/docs-parity/manifests/diagrams.toml` +- Create: `tools/docs-parity/manifests/orphans.toml` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Create/Test: `tools/docs-parity/tests/markdown.rs` +- Create/Test: `tools/docs-parity/tests/links.rs` + +- [ ] **Step 1: Write generated-region failure tests** + +Cover duplicate/missing markers, unknown record names, hand-edited output, unstable ordering, update mode changing bytes outside markers, interrupted writes, and a second update producing a diff. + +- [ ] **Step 2: Implement deterministic region updates** + +Require named start/end markers, render from typed records, update atomically, and make `generate --check` fail on any byte drift. Manual endpoint prose must carry ownership markers that are separately checked. + +- [ ] **Step 3: Write set-specific Markdown tests** + +Add one dead-link fixture for each active set; include missing relative files, missing anchors, duplicate headings, percent-encoded fragments, tombstone routes, an unlisted orphan, and a built page that links to an excluded source. + +- [ ] **Step 4: Implement local and external link contracts** + +Local checks cover active repo/maintained-internal path and anchor links. +External checks cover all active sets with final HTTPS/status validation, at +most five redirects, HEAD→GET fallback, and at most three total attempts for +429/5xx with 1-second then 2-second delays. Honor `Retry-After` only up to 30 +seconds; otherwise use the bounded local delay. Exact-URL exceptions require +owner/reason/expiry. Add fixtures for allowlisted URL, expiry, redirect loop, +redirect-depth overflow, malformed/oversized `Retry-After`, retry exhaustion, +and credentials accidentally embedded in a URL. + +- [ ] **Step 5: Check page/nav/orphan/diagram records** + +Make `pages.toml` the intended VitePress publication/nav inventory, `orphans.toml` carry only typed tombstone/manual exceptions, and `diagrams.toml` require a prose equivalent plus owner for every diagram. + +- [ ] **Step 6: Verify** + +Add each Markdown/link dependency only in the standalone manifest, regenerate +its lockfile, and require `git diff --quiet -- Cargo.lock` before the commands +below. + +```bash +cargo test --manifest-path tools/docs-parity/Cargo.toml markdown +cargo test --manifest-path tools/docs-parity/Cargo.toml links +cargo run --manifest-path tools/docs-parity/Cargo.toml -- links --local --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- generate --check +``` + +Expected: focused tests and current local repository checks pass; external network checks remain scheduled/manual, not a required per-PR network gate. + +- [ ] **Step 7: Commit** + +```bash +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/markdown.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/diagrams.toml tools/docs-parity/manifests/orphans.toml tools/docs-parity/tests/markdown.rs tools/docs-parity/tests/links.rs docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Add checked documentation regions and links" +``` + +### Task 8: Extract settings semantics and execute the example harness + +**Files:** + +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.toml` +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.lock` +- Create: `tools/docs-parity/src/settings.rs` +- Modify: `tools/docs-parity/src/main.rs` +- Modify: `tools/docs-parity/src/lib.rs` +- Modify: `tools/docs-parity/src/model.rs` +- Modify: `tools/docs-parity/src/repository.rs` +- Create: `tools/docs-parity/manifests/settings-companions.toml` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Create/Test: `tools/docs-parity/tests/settings.rs` +- Modify/Test: `crates/trusted-server-core/src/config.rs` +- Modify/Test: `crates/trusted-server-core/src/settings.rs` +- Modify/Test: `crates/trusted-server-core/src/auction/profile.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/aps.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/prebid.rs` + +- [ ] **Step 1: Write extractor grammar fixtures** + +Cover Serde field/container/variant attributes (`rename`, `rename_all`, `alias`, `tag`, `content`, `untagged`, `flatten`, `skip`, `skip_serializing`), literal/nonliteral defaults, custom deserializers, `Option`, validation ranges, and an unknown shape-changing attribute. Expected: unclassified custom behavior fails closed. + +- [ ] **Step 2: Implement the AST plus companion chain** + +Resolve literal defaults from AST, require companion entries for custom deserializers/nonliteral defaults/validator functions, and verify companion claims with compiled positive and negative probes. Emit independent lifecycle, key identity, serialization, runtime, and secret-handling axes; never collapse overlapping dispositions. + +- [ ] **Step 3: Write the eight-phase template harness failures** + +Prove the unmodified template fails for the exact placeholder set; prove a typo, unknown disabled integration key, bad profile config, unresolved secret, stranded literal substitution, inactive-block shortcut, missing profile compiler probe, and wrong failure diagnostic do not pass. + +- [ ] **Step 4: Implement the harness through production APIs** + +Parse the source template, customize non-secret values in memory, preserve secret key names through deploy validation/blob serialization, resolve with a fake store, run runtime validation, and probe every optional integration/provider block in isolation with it forced enabled. Enumerate exact-string consumers in a checked record. + +- [ ] **Step 5: Add visibility-local set-equality seams** + +Replace the one-directional deploy-ID assertion with equality against the checked record. Keep production behavior unchanged and expose no new public API solely for the tool; put private-registry assertions in module-local `#[cfg(test)]` tests. + +- [ ] **Step 6: Run focused and target-matched tests** + +Add each AST/settings dependency only in the standalone manifest, regenerate +its lockfile, and require `git diff --quiet -- Cargo.lock` before the commands +below. + +```bash +cargo test --manifest-path tools/docs-parity/Cargo.toml settings +cargo test-fastly config +cargo test-fastly settings +cargo test-fastly profile +``` + +Expected: extractor/harness tests pass and core behavior is unchanged. + +- [ ] **Step 7: Commit the extractor checkpoint** + +```bash +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/settings.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/settings-companions.toml tools/docs-parity/tests/settings.rs crates/trusted-server-core/src/config.rs crates/trusted-server-core/src/settings.rs crates/trusted-server-core/src/auction/profile.rs crates/trusted-server-core/src/integrations/aps.rs crates/trusted-server-core/src/integrations/prebid.rs docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Check configuration documentation semantics" +``` + +### Task 9: Check integration capabilities and adapter routes + +**Files:** + +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.toml` +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.lock` +- Create: `tools/docs-parity/src/integrations.rs` +- Create: `tools/docs-parity/src/routes.rs` +- Modify: `tools/docs-parity/src/main.rs` +- Modify: `tools/docs-parity/src/lib.rs` +- Modify: `tools/docs-parity/src/model.rs` +- Modify: `tools/docs-parity/src/repository.rs` +- Create: `tools/docs-parity/manifests/integrations.toml` +- Create: `tools/docs-parity/manifests/routes.toml` +- Create: `tools/docs-parity/manifests/adapter-support.toml` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Create/Test: `tools/docs-parity/tests/integrations.rs` +- Create/Test: `tools/docs-parity/tests/routes.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/mod.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/registry.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/aps.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/datadome.rs` +- Modify/Test: `crates/trusted-server-core/src/integrations/prebid.rs` +- Modify/Test: `crates/trusted-server-core/src/auction/mod.rs` +- Modify/Test: `crates/trusted-server-core/src/auction/profile.rs` +- Modify/Test: `crates/trusted-server-adapter-fastly/src/app.rs` +- Modify/Test: `crates/trusted-server-adapter-axum/src/app.rs` +- Modify/Test: `crates/trusted-server-adapter-cloudflare/src/app.rs` +- Modify/Test: `crates/trusted-server-adapter-spin/src/app.rs` +- Modify/Test: `crates/trusted-server-adapter-axum/tests/routes.rs` +- Modify/Test: `crates/trusted-server-adapter-cloudflare/tests/routes.rs` +- Modify/Test: `crates/trusted-server-adapter-spin/tests/routes.rs` + +- [ ] **Step 1: Write inventory equality failures** + +Prove missing and extra deploy IDs, builders, plan registrations, profiles, mediator, JS module/bundle/loading-mode entries, route/method/predicate rows, and startup-router semantics all fail. + +- [ ] **Step 2: Add behavioral capability probes** + +Instantiate every integration across its predicate matrix and compare observed proxy routes, rewriters, injectors, post-processors, filters, and JS modes. Exercise APS rendering and DataDome protection in both states. Keep operational/release status manual with owner/review date. + +- [ ] **Step 3: Add complete adapter route seams** + +Snapshot Fastly, Axum, and Spin through named private test-only route +collections. Parse Cloudflare's builder with a closed grammar that expands only +the known constants, loops, method arrays, and publisher fallback helper; make +an unknown builder construct fail rather than undercount. If obtaining a +collection requires a production-source extraction, keep it private and +behavior-preserving: capture the pre-change route/method/predicate/status and +startup-router sets, then require exact equality after the extraction. Add no +public API. + +- [ ] **Step 4: Compare checked records as sets** + +Assert methods, literal/template/config-derived/conditional predicates, +unsupported/guarded semantics, fan-out capability, and degraded startup +routers. Every adapter regression suite plus the before/after equality proof +must pass; do not change routing behavior to make the records convenient. + +- [ ] **Step 5: Verify every affected target** + +Add each integration/route dependency only in the standalone manifest, +regenerate its lockfile, and require `git diff --quiet -- Cargo.lock` before the +commands below. + +```bash +cargo test --manifest-path tools/docs-parity/Cargo.toml integrations +cargo test --manifest-path tools/docs-parity/Cargo.toml routes +cargo test-fastly +cargo test-axum +cargo test-cloudflare +cargo test-spin +cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity +``` + +Expected: all pass and a generated route/capability update followed by `generate --check` is clean. + +- [ ] **Step 6: Commit** + +```bash +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/integrations.rs tools/docs-parity/src/routes.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/integrations.toml tools/docs-parity/manifests/routes.toml tools/docs-parity/manifests/adapter-support.toml tools/docs-parity/tests/integrations.rs tools/docs-parity/tests/routes.rs crates/trusted-server-core/src/integrations/mod.rs crates/trusted-server-core/src/integrations/registry.rs crates/trusted-server-core/src/integrations/aps.rs crates/trusted-server-core/src/integrations/datadome.rs crates/trusted-server-core/src/integrations/prebid.rs crates/trusted-server-core/src/auction/mod.rs crates/trusted-server-core/src/auction/profile.rs crates/trusted-server-adapter-fastly/src/app.rs crates/trusted-server-adapter-axum/src/app.rs crates/trusted-server-adapter-axum/tests/routes.rs crates/trusted-server-adapter-cloudflare/src/app.rs crates/trusted-server-adapter-cloudflare/tests/routes.rs crates/trusted-server-adapter-spin/src/app.rs crates/trusted-server-adapter-spin/tests/routes.rs docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Check integration and adapter inventories" +``` + +### Task 10: Check CLI help, snippets, gates, workflows, and snapshots + +**Files:** + +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.toml` +- Modify as dependencies are introduced: `tools/docs-parity/Cargo.lock` +- Create: `tools/docs-parity/src/cli_help.rs` +- Create: `tools/docs-parity/src/snippets.rs` +- Create: `tools/docs-parity/src/gates.rs` +- Create: `tools/docs-parity/src/workflow.rs` +- Create: `tools/docs-parity/src/dependency_snapshot.rs` +- Modify: `tools/docs-parity/src/main.rs` +- Modify: `tools/docs-parity/src/lib.rs` +- Modify: `tools/docs-parity/src/model.rs` +- Modify: `tools/docs-parity/src/repository.rs` +- Create: `tools/docs-parity/manifests/{cli-overrides,snippets,gates}.toml` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Create: `tools/docs-parity/goldens/{cli-linux,cli-macos}.txt` +- Create/Test: `tools/docs-parity/tests/{cli_help,snippets,gates,workflow,dependency_snapshot}.rs` +- Create: `docs/internal/runbooks/documentation-automation-rollback.md` +- Create: `docs/internal/runbooks/patches/docs-links-c2.patch` +- Create: `docs/internal/runbooks/patches/docs-links-rollback-c2.patch` +- Create: `.github/workflows/docs-links.yml` + +- [ ] **Step 1: Capture and union CLI help** + +Add test seams if required so recursive Clap help can be captured without +process termination. Implement a capture command that detects the compiled +host OS and has no caller-supplied platform override. After the capture-ready +source commit in Step 8, check out that exact 40-character SHA on one native +Linux runner and one native macOS runner and run the same recursive capture +command. For each raw result record runner identity, `uname -a`, `rustc -vV`, +Node version, source SHA, and SHA-256. A Linux VM/container is acceptable only +when it executes the Linux-target binary; the macOS capture must execute on +macOS. Never copy or infer one platform's output from the other. Import the two +hashed raw captures through the deterministic tool command, generate +`cli-linux.txt` and `cli-macos.txt`, annotate platform-only commands, and require +every prose override to carry owner/rationale/expiry plus an exact source-text +staleness fingerprint. + +- [ ] **Step 2: Write snippet-mode tests** + +Cover every language and mode: executable, compile/validation expected failure with phase and stable diagnostic, illustrative fragment with expiring owner waiver, missing classification, wrong diagnostic despite nonzero exit, and a formerly invalid example becoming valid. + +- [ ] **Step 3: Implement the canonical gate manifest** + +Define each command once with its runner/target/mode and generate or check every consumer region. Link-only consumers must contain no copied command bodies; AGENTS and canonical test docs use generated regions. + +- [ ] **Step 4: Write workflow security fixtures first** + +Add positive fixtures for an ordinary net-empty PR, a +divergent-history/net-identical rc release PR, c2, normal e, abandonment e, +rollback-c2, same-lifecycle repair/sync, and post-handoff maintenance. Add one +negative fixture per spec class, including a repair that changes lifecycle +state, pre-handoff maintenance, caller-selected maintenance tool, maintenance +AST weakening, an unexpected `merge_group` trigger, non-`main` +dispatch, stale `main` controller, stale base/head, fork executable SHA, extra +path, PR-files truncation attempt, unsafe mode/symlink, mixed inputs, open #1049 +SHA used by `validate_main_pr`, pending candidate, failed validation attestation, +checkout/cache/service-container/secret escalation, stale snapshot, malformed +or oversized artifact, and caller-supplied refresh SHA. + +- [ ] **Step 5: Implement exact-diff and workflow AST policy** + +Use a separate bare object store, fetch the authenticated base/head objects, +and never check out or execute the files object. Classify the merge result with +a NUL-delimited two-tree `git diff --name-status `, never a +merge-base/three-dot diff, and compare mode/blob IDs for both protected paths. +Ordinary and rc release PRs pass only when those paths are net-identical even if +history diverged. c2/e/rollback-c2/same-lifecycle repair-sync/post-handoff +maintenance use the full two-tree candidate diff, change at most the two named protected files, and +change no other path; each resulting blob is at most 384 KiB and the protected +blobs at most 512 KiB total. Require exact lifecycle patch shapes; require +repair/sync to equal the authenticated rc protected blobs without changing the +validation-only or active-rc state; require +maintenance to use the authenticated current-main tool and preserve every trust +and AST invariant; require action SHA pins, least privilege, safe events, and +byte equality between c2 result and the authenticated rc workflow. +Expose the same policy through a local-index subcommand so a trusted tool +worktree can validate another worktree's fully staged candidate as inert Git +objects before its first commit; this local form never executes candidate +files. Its `candidate-kind` argument is an assertion checked against the +inferred shape, never a selector that relaxes checks, and the hosted workflow +accepts no caller-supplied candidate kind. + +- [ ] **Step 6: Implement the controller state machine** + +The validation-only workflow has `validate_rc`, `validate_main_pr`, +`validate_main_maintenance`, and base-controlled `pull_request_target`; it +asserts the current `main` controller SHA before inputs. Maintenance accepts no +`tool_sha`, executes only the authenticated current-main tool, and fails until +the base has tooling, main-targeted automation, and no temporary rc snapshot +jobs/refresh. Validation has `contents: read`/`pull-requests: read`; +attestation has only `statuses: write`, no checkout, fixed +`docs/automation-delta`, authenticated 40-hex head, and fixed result enum. +`validate_rc` cannot reach attestation or any writer. Materialize the complete +link reader/issue writer jobs in their final dormant form now: final +permissions/conditions, 30/5-minute timeouts, fixed schedule/refresh +concurrency with no cancellation, bounded artifact schema, dedup/auto-close +logic, and pinned action references. They remain unreachable solely because +the validation-only workflow has no `schedule:` trigger. + +Enforce the exact artifact bounds before any writer starts. Each archive has +exactly one regular member, respectively `link-results.json` or +`dependency-snapshot.json`, and rejects links, traversal, and extra members. +Link results use a 2 MiB maximum archive, 1 MiB decoded JSON, 500 findings, and +2,048-byte strings; dependency snapshots use a 4 MiB archive, 2 MiB decoded +JSON, 5,000 records, and 2,048-byte strings. Both schemas reject unknown fields +and every overflow. + +- [ ] **Step 7: Implement snapshot generation and templates** + +Generate the schema-versioned snapshot from the exact authenticated rc tip with fixed detector/correlator/ref. The future writer template has only `contents: write`, no checkout or repository code, revalidates schema and current rc SHA, and submits only after validation. Produce and test c2 plus inverse rollback patch templates; keep schedule/snapshot jobs unreachable in the rc workflow's validation-only copy used by PR (c). + +- [ ] **Step 8: Verify the whole WP8a tool** + +Add each CLI/workflow/snapshot dependency only in the standalone manifest, +regenerate its lockfile, and require `git diff --quiet -- Cargo.lock` before the +commands below. First stage the complete capture-ready Task 10 source except the +two not-yet-generated golden files, review it under the package checkpoint rule, +and commit the authenticated source used by both native runners: + +```bash +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/cli_help.rs tools/docs-parity/src/snippets.rs tools/docs-parity/src/gates.rs tools/docs-parity/src/workflow.rs tools/docs-parity/src/dependency_snapshot.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/cli-overrides.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/gates.toml tools/docs-parity/tests/cli_help.rs tools/docs-parity/tests/snippets.rs tools/docs-parity/tests/gates.rs tools/docs-parity/tests/workflow.rs tools/docs-parity/tests/dependency_snapshot.rs .github/workflows/docs-links.yml docs/internal/runbooks/documentation-automation-rollback.md docs/internal/runbooks/patches/docs-links-c2.patch docs/internal/runbooks/patches/docs-links-rollback-c2.patch docs/internal/audits/documentation-refresh-evidence.md +git diff --cached --check +git commit -m "Add documentation enforcement scaffolding" +``` + +Run the two native captures at that exact commit, import them without hand +editing, regenerate `tracked-files.toml`/`maintained-sources.toml`, and append +the complete provenance and raw/output hashes to the evidence ledger. Fully +stage those five exact paths, require `git diff --quiet`, and then run: + +```bash +./scripts/test-cli.sh +cargo test --manifest-path tools/docs-parity/Cargo.toml +cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check +cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings +cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all +``` + +Expected: all positive and negative fixtures pass for their intended reasons; update mode followed by check mode yields no diff. + +- [ ] **Step 9: Commit WP8a** + +```bash +git add tools/docs-parity/goldens/cli-linux.txt tools/docs-parity/goldens/cli-macos.txt tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml docs/internal/audits/documentation-refresh-evidence.md +git diff --cached --check +git commit -m "Record cross-platform CLI help goldens" +``` + +Expected: Task 10 is the adjacent two-commit series above; both capture +provenance records name the first commit, all final checks run with the second +commit's candidate bytes, and neither commit is squashed. + +### Task 11: Install the validation-only `main` controller in PR (c) + +**Files on the isolated `main` branch:** + +- Create: `.github/workflows/docs-links.yml` + +- [ ] **Step 1: Cut from a fresh `origin/main` and record the PR-specific tip** + +Use `@superpowers:using-git-worktrees`; record the full `audited_main_tip` and +the rc PR #1049 head used as the reviewed source in PR (c)'s external handoff +block. Attach the complete pre-change `main` ruleset/branch-protection JSON +there. Do not add the rc-only audit records to this isolated branch. + +- [ ] **Step 2: Materialize only the validation form** + +Copy the complete reviewed validation-only workflow: the base-controlled PR +gate, manual `validate_rc`/`validate_main_pr`/`validate_main_maintenance`, status +attestation, and the schedule-only link reader/issue writer definitions, which +remain unreachable because there is no `schedule:` trigger. The maintenance +operation is also present but fail-closed until the normal post-(e) steady-state +predicate is true. Omit `schedule:`, +`refresh_dependency_snapshot`, snapshot jobs, and rc-targeted Dependabot +entries. Record the full workflow blob hash; the controller, attestation, and +unreachable link/issue definitions are frozen through c2. + +- [ ] **Step 3: Validate the exact candidate statically** + +Run the rc PR tool against the candidate workflow as data. Expected: +controller-current-main assertions, event guards, exact permissions, no +untrusted checkout/execution, action pins, dispatch input closure, +base-vs-head net-equality handling, c2/e/rollback templates, and the +same-lifecycle repair/sync plus pre-/post-handoff maintenance predicates all +pass. + +- [ ] **Step 4: Commit and open PR (c)** + +```bash +git add .github/workflows/docs-links.yml +git diff --cached --check +git diff --cached --name-status "$AUDITED_MAIN_TIP" +git commit -m "Install documentation automation controller" +``` + +Before merge, refetch and require the PR's current base to equal its recorded +tip. Put review and a successful manual `validate_rc` dispatch using `tool_sha` +equal to the current #1049 head and `ref: main` in the external handoff block. + +- [ ] **Step 5: Seed all existing `main` PRs before requiring the context** + +Inventory every open `main` PR and authenticate its current head/base. Retrigger +the automatic net-empty-protected-delta path or use the authenticated bootstrap +path; record a `docs/automation-delta` result for every head. Do not enable the +required context while any existing PR is missing it. + +- [ ] **Step 6: Enable and prove branch protection** + +Require `docs/automation-delta` from the GitHub Actions app for every `main` PR +and enable strict/up-to-date enforcement. Demonstrate an unauthorized +protected-file delta is blocked and an unrelated PR receives success without a +privileged checkout. Record that merge queues are disabled for `main`; if they +are active, stop until the approved question-7 branch disables them or a +separate reviewed design updates the spec, plan, controller, and fixtures. +Record queue adoption as blocked through PR (e). + +- [ ] **Step 7: Exercise controller rollback on paper** + +Review the exact procedure: remove only the new required context and restore prior strictness before disabling/reverting the controller; repair and re-prove block/pass before re-enabling. Attach the ruleset IDs and owner. + +- [ ] **Step 8: Import controller rollout evidence into rc** + +After PR (c), status seeding, and protection proofs complete, return to the rc +worktree. Append the external handoff block, authenticated merge/base/rc-source +SHAs, full before/after ruleset captures, seeded PR results, block/pass proof, +queue state, and rollback owner to the rc records. Commit this adjacent evidence +checkpoint before Task 12: + +```bash +git add docs/internal/audits/documentation-refresh-evidence.md docs/internal/audits/documentation-refresh-decisions.md +git diff --cached --check +git diff --cached --name-status HEAD +git commit -m "Record documentation controller rollout" +git status --porcelain +``` + +Expected: only the two rc audit records are committed and final status is empty. + +### Task 12: Complete WP2 truth pass and dispositions + +**Files:** + +- Create/Modify: `docs/internal/audits/documentation-refresh-inventory.toml` +- Modify: `docs/guide/ad-serving.md` +- Modify: `docs/guide/architecture.md` +- Modify: `docs/guide/configuration.md` +- Modify: `docs/guide/creative-processing.md` +- Modify: `docs/guide/error-reference.md` +- Modify: `docs/guide/integration-guide.md` +- Modify: `docs/guide/roadmap.md` +- Modify: `docs/guide/integrations/gam.md` +- Modify: `docs/guide/integrations/kargo.md` +- Create: `docs/guide/auction-testing.md` +- Modify: `crates/trusted-server-core/src/auction/README.md` +- Modify: `TESTING.md` +- Retire/Move/Modify: `FAQ_POC.md` +- Create only on FAQ archive path: `docs/superpowers/archive/FAQ_POC.md` +- Create only on FAQ rewrite path: `docs/guide/faq.md` +- Modify only on FAQ rewrite path: `docs/guide/index.md` +- Modify only on FAQ rewrite path: `docs/.vitepress/config.mts` +- Modify: `CHANGELOG.md` +- Modify: `.env.example` +- Modify: `.env.dev` +- Modify: `.claude/agents/code-architect.md` +- Modify: `.claude/agents/issue-creator.md` +- Modify: `.github/workflows/test.yml` +- Modify: `scripts/test-cli.sh` +- Modify: `crates/trusted-server-openrtb/generate.sh` +- Modify: `crates/trusted-server-core/src/html_processor.test.html` only if the scanner finds a real value +- Modify: human-facing script/workflow comments selected by the checked inventory +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Modify: `tools/docs-parity/manifests/sensitive-allowlist.toml` +- Modify: `tools/docs-parity/manifests/retired-identifiers.toml` +- Modify: `tools/docs-parity/manifests/snippets.toml` +- Modify: `tools/docs-parity/manifests/pages.toml` +- Modify: `tools/docs-parity/manifests/orphans.toml` + +- [ ] **Step 1: Generate and fail the initial disposition inventory** + +Run the inventory command over all three active sets. Expected: check mode fails for every missing whole-file/region disposition and records the audited merge-base SHA plus candidate source anchors. + +- [ ] **Step 2: Disposition every candidate before rewriting** + +Choose verified/rewrite/retire/create for each file and region. Manually review semantic sensitivity beyond scanner patterns. The inventory is complete only when set equality holds; do not use a wildcard disposition. + +- [ ] **Step 3: Remove the named fabricated/retired content** + +Replace `RequestWrapper` with real platform traits; remove Equativ, `.with_asset`, `npm run type-check`, `settings_data::get_settings`, dead `SEQUENCE.md`, APS `mock`, stale auction provider layout/routes, and retired env overlay keys. Fix only `request_ext` reserved-field protection in docs and record the `imp_ext` code follow-up. + +- [ ] **Step 4: Resolve FAQ and tombstones** + +Execute exactly one FAQ branch: retire deletes `FAQ_POC.md`; archive moves it +to `docs/superpowers/archive/FAQ_POC.md`; rewrite moves it to +`docs/guide/faq.md`, verifies every answer against code, links it from the +Guide landing page and Reference navigation, registers `/guide/faq` in +`pages.toml`, and adds a built-page smoke for `guide/faq.html`. Every branch +removes the root path from the active-repo inventory; only rewrite adds an +active public page. Independently replace GAM/Kargo with route-preserving +tombstones, remove sidebar reachability, and add old-route/tombstone smokes to +`pages.toml`. + +- [ ] **Step 5: Rewrite testing and operator records** + +Make root `TESTING.md` the test-matrix index, move the verified auction runbook into `docs/guide/auction-testing.md`, normalize the deterministic no-release CHANGELOG form, distinguish runtime env from CLI overlay, fix roadmap status, and repair the three known workflow/script comments. + +- [ ] **Step 6: Reverify rc-delta content instead of blindly changing it** + +Check allowed-domain semantics, `/first-party/sign` 403 plus `href`/`base`, proxy-signing recommendation, and `--staging` limitation against code. Mark verified with anchors when correct; edit only proven drift. + +- [ ] **Step 7: Run full-set acceptance** + +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- inventory --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- scan --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- retired --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- pages --check +cd docs && npm run lint && npm run format && npm run build +``` + +On the FAQ rewrite branch, additionally assert +`docs/.vitepress/dist/guide/faq.html` exists and contains the expected FAQ +heading and that `/guide/faq` is present in generated navigation. On retire and +archive, assert that artifact and route are absent. + +Expected: set equality passes; retired terms are absent from active sets with only the spec-defined historical exceptions; every executable fence has a valid manifest entry and diagnostic. + +- [ ] **Step 8: Run regression tests for any non-doc fixture changed** + +If `html_processor.test.html` changes, run `cargo test-fastly html_processor`. Run focused tests for every other non-Markdown fixture touched. + +- [ ] **Step 9: Commit WP2** + +Stage the common WP2 paths first, then exactly one FAQ branch and only the +conditional fixture paths that actually changed: + +```bash +git add docs/internal/audits/documentation-refresh-inventory.toml docs/internal/audits/documentation-refresh-evidence.md docs/guide/ad-serving.md docs/guide/architecture.md docs/guide/configuration.md docs/guide/creative-processing.md docs/guide/error-reference.md docs/guide/integration-guide.md docs/guide/roadmap.md docs/guide/integrations/gam.md docs/guide/integrations/kargo.md docs/guide/auction-testing.md TESTING.md CHANGELOG.md .env.example .env.dev .claude/agents/code-architect.md .claude/agents/issue-creator.md .github/workflows/test.yml scripts/test-cli.sh crates/trusted-server-core/src/auction/README.md crates/trusted-server-openrtb/generate.sh tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/sensitive-allowlist.toml tools/docs-parity/manifests/retired-identifiers.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/orphans.toml +# Retire branch: +git add -A -- FAQ_POC.md +# Archive branch instead: +git add -A -- FAQ_POC.md docs/superpowers/archive/FAQ_POC.md +# Rewrite branch instead: +git add -A -- FAQ_POC.md docs/guide/faq.md docs/guide/index.md docs/.vitepress/config.mts +# Only when the scanner required this fixture edit: +git add crates/trusted-server-core/src/html_processor.test.html +git commit -m "Correct maintained documentation truth" +``` + +The comments label mutually exclusive branch commands; execute one, not all. +If the mechanical inventory selects another human-facing comment path, add +that one exact path to the reviewed list before running the checkpoint—never +replace this list with `git add docs`, `git add .github`, or another directory. + +### Task 13: Complete WP3 configuration reference and template + +**Files:** + +- Modify: `docs/guide/configuration.md` +- Modify: `docs/guide/cli.md` +- Modify: `trusted-server.example.toml` +- Modify: `tools/docs-parity/manifests/settings-companions.toml` + +- [ ] **Step 1: Make parity fail on the baseline gaps** + +Run settings check before edits. Expected diagnostics: missing `[consent]`, `[debug]`, and standalone `[tinybird]`; 10/17 key-section rows; 5/14 integration subsections; missing profile schemas; stale template store selectors; duplicate `[trusted_client_ip]`; incomplete directional/secret dispositions. + +- [ ] **Step 2: Generate canonical field/profile regions** + +Render all 17 roots, 14 deploy IDs, three profile configs, resolved defaults/requiredness/grammars/ranges/limits, and every independent disposition axis. Manual prose stays outside markers with an ownership marker. + +- [ ] **Step 3: Repair the example template conservatively** + +Audit all existing root blocks, remove the four accepted-and-discarded store selectors and duplicate trusted-client-IP block, preserve exact placeholder strings/key references, and do not normalize deprecated/ignored fields into recommended examples. + +- [ ] **Step 4: Document the secret model and CLI exposure** + +Classify the 11 store-resolved paths, inline trusted-client-IP secret, and discarded Tinybird secret. Warn that config diff/dry-run/push output can expose inline values. + +- [ ] **Step 5: Run extractor, harness, and docs gates** + +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- settings --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- examples --check +cargo test-fastly config +cd docs && npm run lint && npm run format && npm run build +``` + +Expected: every active canonical field appears in reference/template, noncanonical paths are labeled only, all literal consumers remain connected, and all eight example-harness phases pass. + +- [ ] **Step 6: Commit WP3** + +```bash +git add docs/guide/configuration.md docs/guide/cli.md trusted-server.example.toml tools/docs-parity/manifests/settings-companions.toml docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Complete configuration documentation" +``` + +### Task 14: Complete WP4 generated API contracts + +**Files:** + +- Modify: `docs/guide/api-reference.md` +- Modify: `tools/docs-parity/manifests/routes.toml` +- Modify: `tools/docs-parity/manifests/adapter-support.toml` +- Modify/Test only for test seams: the four adapter `src/app.rs` files and route tests from Task 9 + +- [ ] **Step 1: Make route generation fail on reader drift** + +Temporarily alter one checked record in a test fixture and prove check mode rejects an unregenerated region. Prove an unknown Cloudflare route-builder construct fails parsing. + +- [ ] **Step 2: Generate the route/availability regions** + +Render all adapters, methods, route families, predicates, Fastly-only routes, guarded/unsupported admin behavior, publisher fallback, startup failure, middleware facts, and fan-out support from the checked records. + +- [ ] **Step 3: Complete manually owned endpoint contracts** + +For every endpoint, cover auth, schemas, status codes, cache/CORS, config gates, rate limits, and examples or mark a typed not-applicable value. Keep minting (`/first-party/sign`) distinct from validation (`/proxy`, `/click`, `/proxy-rebuild`). + +- [ ] **Step 4: Verify set equality and rendered prose** + +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- routes --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- generate --check +cargo test-fastly +cargo test-axum +cargo test-cloudflare +cargo test-spin +cd docs && npm run lint && npm run format && npm run build +``` + +Expected: no adapter can add/remove/change a route without a record and generated diff; all ownership markers are present. + +- [ ] **Step 5: Commit WP4** + +```bash +git add docs/guide/api-reference.md tools/docs-parity/manifests/routes.toml tools/docs-parity/manifests/adapter-support.toml crates/trusted-server-adapter-fastly/src/app.rs crates/trusted-server-adapter-axum/src/app.rs crates/trusted-server-adapter-axum/tests/routes.rs crates/trusted-server-adapter-cloudflare/src/app.rs crates/trusted-server-adapter-cloudflare/tests/routes.rs crates/trusted-server-adapter-spin/src/app.rs crates/trusted-server-adapter-spin/tests/routes.rs docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Generate adapter API documentation" +``` + +### Task 15: Add deployment guides and recurring first-success smokes + +**Files:** + +- Modify: `docs/guide/fastly.md` +- Create: `docs/guide/cloudflare.md` +- Create: `docs/guide/spin.md` +- Create: `docs/guide/axum-dev.md` +- Create: `scripts/smoke-fastly.sh` +- Create: `scripts/smoke-cloudflare.sh` +- Create: `scripts/smoke-spin.sh` +- Create: `scripts/smoke-axum.sh` +- Modify: `.tool-versions` +- Modify: `.github/workflows/integration-tests.yml` +- Modify as fixtures, not operator sources: `crates/trusted-server-integration-tests/fixtures/configs/trusted-server.integration.toml` +- Modify as needed: `crates/trusted-server-adapter-cloudflare/wrangler.ci.toml` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Modify: `tools/docs-parity/manifests/pages.toml` +- Modify: `tools/docs-parity/manifests/orphans.toml` + +- [ ] **Step 1: Write smoke failures before guides** + +For each adapter, add a clean-state positive scenario with exact status, stub-origin sentinel, and Trusted Server rewrite/header; add independent missing-config and per-required-secret failures with specific diagnostics. Prove health-only/status-only/degraded-router responses do not satisfy the oracle. + +Select the current stable Wrangler version at implementation time, record its +source/version, add it to `.tool-versions`, and make the Cloudflare smoke/CI +fixture fail if that pin is absent or a different executable is used. + +- [ ] **Step 2: Implement Axum and Fastly scripts** + +Axum exports the exact config/secret bridge and exercises a publisher request. Fastly runs config init/validate/local push, seeds all three `ts_secrets` entries, serves through Viceroy, asserts health plus publisher behavior, and restores `fastly.toml` in a trap. + +- [ ] **Step 3: Implement the Cloudflare bridge exactly** + +Provision/map the selected KV binding before push, run local push, read the envelope with explicit binding/namespace, double-encode with `jq`, write only gitignored generated vars/manifest files, run Wrangler, assert rewritten content, and clean up. Keep local and remote secret instructions separate. + +- [ ] **Step 4: Implement the Spin path** + +Set the required store mapping to `default`, local-push into `.spin/`, encode/export every secret variable name, run `spin up`, assert a non-health publisher response with the strong oracle, and clean all local state. If CI cannot run Spin, record owner/SHA/tool versions/expiry for recurring manual evidence. + +- [ ] **Step 5: Write the guides from the scripts** + +Every guide command must be copyable and remain in the same order as the recurring script. State maturity, fan-out, health/startup, and unwired-store limitations from `adapter-support.toml`; a successful push is not described as a configured runtime where the bridge is still required. + +Register the four new public pages immediately. Until Task 16 adds their final +navigation, give any genuinely unreachable page a typed temporary orphan entry +owned by WP5 and expiring at Task 16; Task 16 must remove that entry. + +- [ ] **Step 6: Wire runnable scripts into integration CI** + +Run `chmod +x scripts/smoke-axum.sh scripts/smoke-fastly.sh +scripts/smoke-cloudflare.sh scripts/smoke-spin.sh`. Run Axum, Fastly, and +Cloudflare smokes in the existing integration workflow after their artifacts +are prepared. Consume Wrangler from the Task 15 `.tool-versions` pin, not an +unpinned global latest. Preserve existing integration suites. + +- [ ] **Step 7: Verify focused journeys** + +```bash +bash -n scripts/smoke-axum.sh scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh +./scripts/smoke-axum.sh +./scripts/smoke-fastly.sh +./scripts/smoke-cloudflare.sh +``` + +Run Spin or attach its time-bounded evidence. Then run the integration-test parity target and docs build. + +- [ ] **Step 8: Commit the deployment half of WP5** + +```bash +git add docs/guide/fastly.md docs/guide/cloudflare.md docs/guide/spin.md docs/guide/axum-dev.md scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh scripts/smoke-axum.sh .tool-versions .github/workflows/integration-tests.yml crates/trusted-server-integration-tests/fixtures/configs/trusted-server.integration.toml crates/trusted-server-adapter-cloudflare/wrangler.ci.toml tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/orphans.toml docs/internal/audits/documentation-refresh-evidence.md +git ls-files --stage scripts/smoke-axum.sh scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh | awk '$1 != "100755" { bad=1 } END { exit bad }' +git commit -m "Document adapter deployment journeys" +``` + +### Task 16: Complete WP5 product coverage and navigation + +**Files:** + +- Create: `docs/guide/edgezero.md` +- Create: `docs/guide/telemetry.md` +- Create: `docs/guide/tsjs.md` +- Modify: `docs/guide/cli.md` +- Create: `docs/guide/integrations/adserver_mock.md` +- Modify: `docs/guide/integrations/gpt.md` +- Create: `docs/guide/integrations/testlight.md` +- Modify: `docs/guide/integrations-overview.md` +- Modify: `docs/guide/integration-guide.md` +- Create: `tinybird/README.md` +- Modify: `docs/.vitepress/config.mts` +- Modify: `docs/package.json` +- Modify: `.github/workflows/deploy-docs.yml` +- Read/verify: `.tool-versions` (Wrangler pin established in Task 15) +- Create/Test: `crates/trusted-server-integration-tests/tests/documentation_snippets.rs` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Modify: `tools/docs-parity/manifests/integrations.toml` +- Modify: `tools/docs-parity/manifests/adapter-support.toml` +- Modify: `tools/docs-parity/manifests/cli-overrides.toml` +- Modify: `tools/docs-parity/manifests/snippets.toml` +- Modify: `tools/docs-parity/manifests/pages.toml` +- Modify: `tools/docs-parity/manifests/diagrams.toml` +- Modify: `tools/docs-parity/manifests/orphans.toml` + +- [ ] **Step 1: Generate the support matrix and inventory regions** + +Render adapter status/fan-out/startup rows and all 14 deploy IDs plus creative from checked records. Prove repeated status prose cannot diverge from the matrix. + +- [ ] **Step 2: Add missing platform/system pages** + +Write EdgeZero lifecycle/store/blob flow, telemetry plus `browser_family`/Tinybird/Fastly-only emission, tsjs 12-module/13-bundle/three-loading-mode model (including the standalone `gpt_diagnostics` tag), and adserver-mock mediator semantics from their truth sources. Render `docs/guide/cli.md` from the checked two-platform help union while preserving owned explanatory prose outside the generated region. + +- [ ] **Step 3: Complete integration journeys** + +Document GPT slot handoff, script guards, and Testlight. Replace broken integration-guide snippets with one compiling, core-neutral fixture using complete `RuntimeServices`; register every fence and expected diagnostic. + +- [ ] **Step 4: Restructure discoverability** + +Create Operator/Deployment/Reference navigation groups, make every ID reachable, enable local search and `lastUpdated`, and add prose equivalents for every diagram. Keep tombstones out of navigation and containment exclusions intact. + +- [ ] **Step 5: Add rolling-main provenance** + +Inject `GITHUB_SHA` into the build without exposing secrets, render a rolling-main banner, and make deploy-docs paths include `.tool-versions`. Add a built-output assertion for the exact supplied SHA. + +- [ ] **Step 6: Verify journeys, snippets, and publication** + +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- integrations --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- pages --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- generate --check +cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test documentation_snippets +cd docs && npm run lint && npm run format && GITHUB_SHA="$(git rev-parse HEAD)" npm run build +``` + +Expected: nav/page set equality passes, banner contains the current SHA, excluded pages remain absent, all journeys/diagrams have owners, and the compiling fixture passes. + +- [ ] **Step 7: Commit the remaining WP5 checkpoint** + +```bash +git add docs/guide/edgezero.md docs/guide/telemetry.md docs/guide/tsjs.md docs/guide/cli.md docs/guide/integrations/adserver_mock.md docs/guide/integrations/gpt.md docs/guide/integrations/testlight.md docs/guide/integrations-overview.md docs/guide/integration-guide.md tinybird/README.md docs/.vitepress/config.mts docs/package.json .github/workflows/deploy-docs.yml crates/trusted-server-integration-tests/tests/documentation_snippets.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/integrations.toml tools/docs-parity/manifests/adapter-support.toml tools/docs-parity/manifests/cli-overrides.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/diagrams.toml tools/docs-parity/manifests/orphans.toml docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Add full documentation product coverage" +``` + +### Task 17: Complete WP6 root and crate documentation + +**Files:** + +- Modify: `README.md` +- Modify: `CONTRIBUTING.md` +- Modify: `CLAUDE.md` +- Modify: `ProjectGovernance.md` +- Modify: `crates/trusted-server-core/README.md` +- Modify: `crates/trusted-server-integration-tests/README.md` +- Create: `crates/trusted-server-adapter-axum/README.md` +- Create: `crates/trusted-server-adapter-cloudflare/README.md` +- Create: `crates/trusted-server-adapter-fastly/README.md` +- Create: `crates/trusted-server-adapter-spin/README.md` +- Create: `crates/trusted-server-cli/README.md` +- Create: `crates/trusted-server-js/README.md` +- Create: `crates/trusted-server-openrtb-codegen/README.md` +- Create: `scripts/README.md` +- Modify: each corresponding crate `Cargo.toml` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` + +- [ ] **Step 1: Add the failing README equality test** + +Use `cargo metadata --no-deps` to enumerate every package. Expected before edits: seven missing README files and/or missing `readme =` metadata. Include an extra/unlisted README negative fixture. + +- [ ] **Step 2: Correct canonical contributor/operator prose** + +Make root quick starts satisfy the first-success scripts; make contributing link to canonical gates; correct target/integration-model/example policy in CLAUDE; apply the selected factual-governance fallback. + +- [ ] **Step 3: Write responsibility-focused READMEs** + +Each crate README states purpose, runtime/target, important boundaries, build/test command, and links to canonical guides without copying volatile matrices. Rewrite core as an actual module overview and correct integration-test scope. Add a scripts index with inputs/side effects/cleanup. + +- [ ] **Step 4: Connect Cargo metadata** + +Add exact `readme = "README.md"` entries to the seven new crate manifests and any existing package missing the metadata. Do not alter dependency or feature resolution. + +- [ ] **Step 5: Verify** + +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- readmes --check +cargo metadata --no-deps --format-version 1 +cargo fmt --all -- --check +cd docs && npm run lint && npm run format && npm run build +``` + +Expected: every package maps to an existing README and all active root/crate/skill/agent dispositions remain closed. + +- [ ] **Step 6: Commit WP6** + +```bash +git add README.md CONTRIBUTING.md CLAUDE.md ProjectGovernance.md crates/trusted-server-core/README.md crates/trusted-server-core/Cargo.toml crates/trusted-server-integration-tests/README.md crates/trusted-server-integration-tests/Cargo.toml crates/trusted-server-adapter-axum/README.md crates/trusted-server-adapter-cloudflare/README.md crates/trusted-server-adapter-fastly/README.md crates/trusted-server-adapter-spin/README.md crates/trusted-server-cli/README.md crates/trusted-server-js/README.md crates/trusted-server-openrtb-codegen/README.md crates/trusted-server-adapter-axum/Cargo.toml crates/trusted-server-adapter-cloudflare/Cargo.toml crates/trusted-server-adapter-fastly/Cargo.toml crates/trusted-server-adapter-spin/Cargo.toml crates/trusted-server-cli/Cargo.toml crates/trusted-server-js/Cargo.toml crates/trusted-server-openrtb-codegen/Cargo.toml scripts/README.md tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Refresh contributor and crate documentation" +``` + +### Task 18: Complete WP7 rustdoc and JSDoc + +**Files:** + +- Modify: `crates/trusted-server-core/src/lib.rs` +- Modify: the ten production files in `crates/trusted-server-core/src/platform/`: `backend_naming.rs`, `error.rs`, `http.rs`, `image_optimizer.rs`, `kv.rs`, `mod.rs`, `template_assembly.rs`, `template_cache.rs`, `traits.rs`, and `types.rs` +- Modify: `crates/trusted-server-core/src/auth.rs` +- Modify: `crates/trusted-server-core/src/constants.rs` +- Modify: `crates/trusted-server-core/src/host_rewrite.rs` +- Modify: `crates/trusted-server-core/src/html_processor.rs` +- Modify: `crates/trusted-server-core/src/http_util.rs` +- Modify: `crates/trusted-server-core/src/openrtb.rs` +- Modify: `crates/trusted-server-core/src/price_bucket.rs` +- Modify: `crates/trusted-server-core/src/proxy.rs` +- Modify: `crates/trusted-server-core/src/rsc_flight.rs` +- Modify: `crates/trusted-server-core/src/settings.rs` +- Modify: `crates/trusted-server-core/src/settings_data.rs` +- Modify: `crates/trusted-server-core/src/tsjs.rs` +- Modify: `crates/trusted-server-core/src/storage/kv_store.rs` +- Modify: `crates/trusted-server-core/src/storage/mod.rs` +- Modify: `crates/trusted-server-core/src/integrations/datadome.rs` +- Modify: `crates/trusted-server-core/src/integrations/prebid.rs` +- Modify: `crates/trusted-server-core/src/integrations/registry.rs` +- Modify: all six files under `crates/trusted-server-core/src/integrations/nextjs/`: `html_post_process.rs`, `mod.rs`, `rsc.rs`, `rsc_placeholders.rs`, `script_rewriter.rs`, and `shared.rs` +- Modify: `crates/trusted-server-adapter-fastly/src/main.rs` +- Modify: `crates/trusted-server-adapter-cloudflare/src/lib.rs` +- Modify: `crates/trusted-server-adapter-cloudflare/src/platform.rs` +- Modify: `crates/trusted-server-adapter-spin/src/lib.rs` +- Modify: `crates/trusted-server-adapter-spin/src/platform.rs` +- Modify: `crates/trusted-server-adapter-axum/src/lib.rs` when the checked WP7 inventory marks its existing crate header incomplete +- Modify: `crates/trusted-server-cli/src/lib.rs` +- Modify: `crates/trusted-server-cli/src/run.rs` +- Modify: undocumented CLI command modules recorded by the exact WP2 inventory before this task starts +- Modify: `crates/trusted-server-js/src/lib.rs` +- Modify: `crates/trusted-server-js/lib/src/core/{registry,render,types}.ts` +- Modify: `crates/trusted-server-js/lib/src/shared/globals.ts` +- Modify: `crates/trusted-server-js/lib/src/integrations/creative/*.ts` +- Modify: `crates/trusted-server-js/lib/build-prebid-external.mjs` +- Modify: `crates/trusted-server-js/lib/eslint.config.js` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/file-overview.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/exported-function.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/exported-class.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/exported-interface.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/exported-type-alias.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/exported-variable.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/default-export.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/re-export.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/alignment.ts` +- Create/Test: `tools/docs-parity/tests/fixtures/jsdoc/types.ts` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` + +- [ ] **Step 1: Capture failing rustdoc/JSDoc evidence** + +Run the complete rustdoc matrix with `RUSTDOCFLAGS="-D warnings"`, native core doctests, and current JS lint. Record every existing failure; do not suppress a warning solely to get green. + +- [ ] **Step 2: Complete the exact Rust worklist** + +Add crate/module/item docs with correct errors/panics/examples only where they earn their keep. Repair Cloudflare/Spin store claims to state the unwired reality and link the follow-up. Keep test-only modules excluded from coverage counts. + +- [ ] **Step 3: Activate scoped JSDoc rules test-first** + +Add separate synthesized failures for file overview, exported function/class/interface/type alias/variable/default export/re-export, alignment, and types. Configure paths relative to `crates/trusted-server-js/lib`; do not accidentally impose this scope on generated/vendor files. + +- [ ] **Step 4: Document the scoped TS/MJS files** + +Add file headers and declaration docs, especially complete `core/types.ts`; document behavior, not TypeScript syntax. Keep runtime code unchanged. + +- [ ] **Step 5: Run the exact rustdoc matrix** + +```bash +RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features -p trusted-server-core -p trusted-server-js -p trusted-server-openrtb --target wasm32-wasip1 +RUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p trusted-server-adapter-fastly --target wasm32-wasip1 +RUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p trusted-server-adapter-cloudflare --target wasm32-unknown-unknown --features cloudflare +RUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p trusted-server-adapter-spin --target wasm32-wasip1 --features spin +RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features -p trusted-server-adapter-axum +RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features -p trusted-server-cli -p trusted-server-openrtb-codegen --target "$(rustc -vV | sed -n 's/host: //p')" +cargo test --doc -p trusted-server-core +``` + +Expected: warning-free docs and passing native doctests with Node available for the JS build script. + +- [ ] **Step 6: Run JSDoc and target regression suites** + +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- jsdoc-fixtures --check +cd crates/trusted-server-js/lib && npm run lint && npm run format && npx vitest run && npm run build +cargo test-fastly +cargo test-axum +cargo test-cloudflare +cargo test-spin +``` + +Expected: all pass with no runtime behavior diff. + +- [ ] **Step 7: Commit WP7** + +Stage only the exact modified paths enumerated in this task plus the ten named +JSDoc fixtures and the evidence ledger. The WP2 inventory may add individual +CLI command modules to this list; record and stage each path explicitly. +Directory-wide `git add crates` is forbidden. + +```bash +git add crates/trusted-server-core/src/lib.rs crates/trusted-server-core/src/platform/backend_naming.rs crates/trusted-server-core/src/platform/error.rs crates/trusted-server-core/src/platform/http.rs crates/trusted-server-core/src/platform/image_optimizer.rs crates/trusted-server-core/src/platform/kv.rs crates/trusted-server-core/src/platform/mod.rs crates/trusted-server-core/src/platform/template_assembly.rs crates/trusted-server-core/src/platform/template_cache.rs crates/trusted-server-core/src/platform/traits.rs crates/trusted-server-core/src/platform/types.rs crates/trusted-server-core/src/auth.rs crates/trusted-server-core/src/constants.rs crates/trusted-server-core/src/host_rewrite.rs crates/trusted-server-core/src/html_processor.rs crates/trusted-server-core/src/http_util.rs crates/trusted-server-core/src/openrtb.rs crates/trusted-server-core/src/price_bucket.rs crates/trusted-server-core/src/proxy.rs crates/trusted-server-core/src/rsc_flight.rs crates/trusted-server-core/src/settings.rs crates/trusted-server-core/src/settings_data.rs crates/trusted-server-core/src/tsjs.rs crates/trusted-server-core/src/storage/kv_store.rs crates/trusted-server-core/src/storage/mod.rs crates/trusted-server-core/src/integrations/datadome.rs crates/trusted-server-core/src/integrations/prebid.rs crates/trusted-server-core/src/integrations/registry.rs crates/trusted-server-core/src/integrations/nextjs/html_post_process.rs crates/trusted-server-core/src/integrations/nextjs/mod.rs crates/trusted-server-core/src/integrations/nextjs/rsc.rs crates/trusted-server-core/src/integrations/nextjs/rsc_placeholders.rs crates/trusted-server-core/src/integrations/nextjs/script_rewriter.rs crates/trusted-server-core/src/integrations/nextjs/shared.rs crates/trusted-server-adapter-fastly/src/main.rs crates/trusted-server-adapter-cloudflare/src/lib.rs crates/trusted-server-adapter-cloudflare/src/platform.rs crates/trusted-server-adapter-spin/src/lib.rs crates/trusted-server-adapter-spin/src/platform.rs crates/trusted-server-cli/src/lib.rs crates/trusted-server-cli/src/run.rs crates/trusted-server-js/src/lib.rs crates/trusted-server-js/lib/src/core/registry.ts crates/trusted-server-js/lib/src/core/render.ts crates/trusted-server-js/lib/src/core/types.ts crates/trusted-server-js/lib/src/shared/globals.ts crates/trusted-server-js/lib/src/integrations/creative/click.ts crates/trusted-server-js/lib/src/integrations/creative/dynamic_src_guard.ts crates/trusted-server-js/lib/src/integrations/creative/iframe.ts crates/trusted-server-js/lib/src/integrations/creative/image.ts crates/trusted-server-js/lib/src/integrations/creative/index.ts crates/trusted-server-js/lib/src/integrations/creative/proxy_sign.ts crates/trusted-server-js/lib/build-prebid-external.mjs crates/trusted-server-js/lib/eslint.config.js tools/docs-parity/tests/fixtures/jsdoc/file-overview.ts tools/docs-parity/tests/fixtures/jsdoc/exported-function.ts tools/docs-parity/tests/fixtures/jsdoc/exported-class.ts tools/docs-parity/tests/fixtures/jsdoc/exported-interface.ts tools/docs-parity/tests/fixtures/jsdoc/exported-type-alias.ts tools/docs-parity/tests/fixtures/jsdoc/exported-variable.ts tools/docs-parity/tests/fixtures/jsdoc/default-export.ts tools/docs-parity/tests/fixtures/jsdoc/re-export.ts tools/docs-parity/tests/fixtures/jsdoc/alignment.ts tools/docs-parity/tests/fixtures/jsdoc/types.ts tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml docs/internal/audits/documentation-refresh-evidence.md +# Append `crates/trusted-server-adapter-axum/src/lib.rs` only if selected and +# each exact CLI command-module path named by the WP2 inventory. +git commit -m "Complete in-code documentation" +``` + +### Task 19: Activate WP8b CI gates and release controls + +**Files:** + +- Modify: `.github/workflows/format.yml` +- Modify: `.github/workflows/test.yml` +- Modify: `.github/workflows/integration-tests.yml` +- Modify: `.github/workflows/codeql.yml` +- Modify: `.github/workflows/deploy-docs.yml` +- Modify: `.github/workflows/docs-links.yml` +- Modify: `.github/dependabot.yml` +- Modify: `.tool-versions` +- Modify: `crates/trusted-server-openrtb-codegen/Cargo.toml` +- Modify: `CLAUDE.md` +- Modify: `AGENTS.md` +- Modify: `TESTING.md` +- Modify: `docs/guide/testing.md` +- Create: `docs/internal/runbooks/documentation-automation-release.md` +- Create: `docs/internal/runbooks/patches/docs-links-release-retarget.patch` +- Create: `docs/internal/runbooks/patches/docs-links-release-disable.patch` +- Modify: `tools/docs-parity/src/gates.rs` +- Modify: `tools/docs-parity/src/workflow.rs` +- Modify: `tools/docs-parity/src/dependency_snapshot.rs` +- Modify/Test: `tools/docs-parity/tests/gates.rs` +- Modify/Test: `tools/docs-parity/tests/workflow.rs` +- Modify/Test: `tools/docs-parity/tests/dependency_snapshot.rs` +- Modify: `tools/docs-parity/manifests/tracked-files.toml` +- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Modify: `tools/docs-parity/manifests/gates.toml` + +- [ ] **Step 1: Make static workflow fixtures fail for current gaps** + +Assert missing rc CodeQL triggers, rustdoc/doctest/docs-parity jobs, nested lockfile cache keys, setup-node lockfile paths, Dependabot roots, Wrangler pin, gate-region equality, schedule trust split, release patches, and openrtb-codegen workspace lints. + +- [ ] **Step 2: Wire blocking deterministic checks** + +Add host docs-parity fmt/clippy/test/check, generated clean-diff, settings/examples/inventory/snippets/scanner/local links/readmes/jsdoc/workflow fixtures, rustdoc matrix, and native doctests. Pin Node wherever the JS build script runs. Keep external network links scheduled, not a flaky PR dependency. + +- [ ] **Step 3: Normalize existing automation** + +Add CodeQL `rc/*` PR triggers, `.tool-versions` deploy paths, lockfile-based +setup-node cache keys, pinned Wrangler, GitHub +Actions/browser/Next.js/docs-parity Dependabot roots, and +`[lints] workspace = true`. Choose current stable action versions at +implementation time and pin every new `uses` reference by full SHA with its +source version recorded. The `docs-links.yml` action pins were selected and +frozen in Tasks 10-11; verify them here but do not repin or otherwise change a +non-activation byte. A required repin takes the controller-repair path. + +- [ ] **Step 4: Finalize the rc workflow's activated form** + +Starting from the exact workflow hash recorded in Task 11, add only the +reviewed c2 activation regions: weekly `17 9 * * 1`, the fixed stateful +schedule/refresh path already configured on the dormant link jobs, and the +split snapshot generator/writer plus closed refresh operation with their +20/5-minute timeouts. Preserve every +non-activation byte, including per-PR serialization, controller, +attestation, current-`main` assertion, fail-closed future maintenance mode, and +unreachable link/issue definitions. +The issue writer schema-validates bounded results, deduplicates the single +owned report issue, and auto-closes it after a clean scheduled run. Generate a +fresh c2 patch and its inverse covering both protected files. Record the final +rc mode/blob IDs for `.github/workflows/docs-links.yml` and +`.github/dependabot.yml`; prove the c2 base workflow hashes to the Task 11 blob, +and prove applying c2 produces mode/blob identity with rc for both protected +paths. The inverse must restore both base blobs without overwriting unrelated +base changes. If a non-activation byte must change, stop and ship a separately +reviewed controller-repair PR before continuing; do not hide it in c2. + +- [ ] **Step 5: Finalize release and rollback runbooks** + +Provide normal and abandonment patch templates, freeze semantics, queued-run +enumeration, optional separately scoped `actions: write` cancellation token, +empty same-identity retirement request, 201 receipt fields, +automatic-main-graph verification, branch deletion gate, CNAME fallback, and +branch-protection restoration ordering. The normal runbook requires +base-vs-rc-head mode/blob identity for both protected paths before the broad +release merge and a separately reviewed sync/repair PR if they differ; it also +proves post-(e) maintenance before closure. Add an rc dependency-change +checklist with a named owner: every merge changing +`tools/docs-parity/Cargo.toml` or `tools/docs-parity/Cargo.lock` remains +incomplete until a post-merge `refresh_dependency_snapshot` receipt is +attached; missed/failed refreshes are security-coverage incidents and the +weekly run is only a reconciliation backstop. + +- [ ] **Step 6: Generate all gate consumers** + +Regenerate CLAUDE/AGENTS/TESTING/guide testing regions from `gates.toml`; prove command files and CONTRIBUTING remain link-only. A second generation must produce no diff. + +- [ ] **Step 7: Run WP8b focused acceptance** + +```bash +cargo test --manifest-path tools/docs-parity/Cargo.toml +cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all +cargo run --manifest-path tools/docs-parity/Cargo.toml -- generate --check +cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check +cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings +cargo fmt --all -- --check +cd docs && npm run lint && npm run format && npm run build +``` + +Expected: all static/runtime negative fixtures fail for their intended diagnostic, then the real repository passes; generation is idempotent. + +- [ ] **Step 8: Deduplicate and file every code follow-up** + +Search the tracker for each item in the spec's “Follow-up issues to file” section. File or record an existing-issue disposition for all twelve items, with URL, owner, and labels in the evidence record; do not collapse the adapter-store, config-bridge, health-contract, reserved-field, placeholder, inline-secret, deploy-ID, CLI-help, telemetry, env-store, or staging-blob issues into vague umbrella tickets. + +- [ ] **Step 9: Commit WP8b** + +```bash +git add .github/workflows/format.yml .github/workflows/test.yml .github/workflows/integration-tests.yml .github/workflows/codeql.yml .github/workflows/deploy-docs.yml .github/workflows/docs-links.yml .github/dependabot.yml .tool-versions crates/trusted-server-openrtb-codegen/Cargo.toml CLAUDE.md AGENTS.md TESTING.md docs/guide/testing.md docs/internal/runbooks/documentation-automation-release.md docs/internal/runbooks/patches/docs-links-release-retarget.patch docs/internal/runbooks/patches/docs-links-release-disable.patch tools/docs-parity/src/gates.rs tools/docs-parity/src/workflow.rs tools/docs-parity/src/dependency_snapshot.rs tools/docs-parity/tests/gates.rs tools/docs-parity/tests/workflow.rs tools/docs-parity/tests/dependency_snapshot.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/gates.toml docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Activate documentation enforcement gates" +``` + +### Task 20: Close Epoch 1 and make PR (a) implementation-ready + +**Files:** + +- Modify: `docs/internal/audits/documentation-refresh-evidence.md` +- Modify: `docs/internal/audits/documentation-refresh-decisions.md` + +- [ ] **Step 1: Reassert the exact rc baseline at final PR head** + +Fetch and require `origin/rc/202608` still equals `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`; require the implementation branch contains it. Any advance triggers a focused delta audit, spec update, regenerated records, and re-review before continuing. + +- [ ] **Step 2: Run the complete local CI gate list** + +```bash +cargo fmt --all -- --check +cargo clippy-fastly +cargo clippy-axum +cargo clippy-cloudflare +cargo clippy-cloudflare-wasm +cargo clippy-spin-native +cargo clippy-spin-wasm +cargo clippy --package trusted-server-cli --target "$(rustc -vV | sed -n 's/host: //p')" --all-targets --all-features -- -D warnings +cargo clippy --package trusted-server-openrtb-codegen --target "$(rustc -vV | sed -n 's/host: //p')" --all-targets -- -D warnings +cargo test-fastly +cargo test-axum +cargo test-cloudflare +cargo test-spin +cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity +./scripts/test-cli.sh +cargo test --package trusted-server-openrtb-codegen --target "$(rustc -vV | sed -n 's/host: //p')" +cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1 +cargo build --package trusted-server-adapter-spin --target wasm32-wasip1 --features spin --release +cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check +cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings +cargo test --manifest-path tools/docs-parity/Cargo.toml +cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all +cd crates/trusted-server-js/lib +npm ci +npm run lint +npx vitest run +npm run format +npm run build +cd ../../.. +cd docs +npm ci +npm run lint +npm run format +npm run build +cd .. +``` + +Also run Task 18's rustdoc matrix, all four Task 15 smokes/evidence paths, and the integration workflow's runnable smoke targets. + +- [ ] **Step 3: Confirm the hosted check topology provisionally** + +Confirm CodeQL with rc triggers, all format jobs, all seven `test.yml` jobs, +all four `integration-tests.yml` jobs, release builds, JS build/test, docs build, +and the new WP8 jobs report with the expected names and GitHub App identities. +Record provisional run URLs and the current head, but do not call this the final +hosted proof because Step 8 may create one last evidence commit. Local +substitutes do not replace hosted evidence. + +- [ ] **Step 4: Prove every acceptance surface** + +Record: generated no-diff; source/disposition equality; all retired/privacy scans; dead-link negatives; route/settings/integration equality; snippet diagnostics; README/JSDoc/rustdoc gates; Pages containment/CNAME smokes; all follow-up issue URLs/dispositions; PR (b), (c), and (d) URLs; c2 issue/owner; e issue plus reviewed runbooks. + +- [ ] **Step 5: Activate required checks on rc** + +Require the full WP8 suite on `rc/202608`, record context names/source apps/bypass policy, and demonstrate one planted failure actually prevents an rc merge. Remove the planted failure and show success. + +- [ ] **Step 6: Reprove the active `main` controller** + +Record strict/up-to-date protection, every pre-existing PR seed result, unauthorized protected-file block, unrelated-PR success, and full-suite `main` deferral. Do not claim the full suite is required on `main` yet. + +- [ ] **Step 7: Review commit/package shape** + +Require one reviewable commit or small series per package, generated-output commits separated where review needs it, no unrelated runtime changes, and no squash-on-merge. Run `git diff --check` and review `git diff origin/rc/202608...HEAD` path by path. + +- [ ] **Step 8: Commit the final Epoch 1 records and prove a clean tree** + +```bash +git add docs/internal/audits/documentation-refresh-evidence.md docs/internal/audits/documentation-refresh-decisions.md +git diff --cached --check +git commit -m "Record documentation refresh acceptance" +git status --porcelain +``` + +Expected: the status command prints nothing. If the final records do not +change, omit the empty commit but still require the clean status. + +- [ ] **Step 9: Re-run hosted checks on the exact final head and mark implementation-ready** + +After Step 8, record the new exact PR head in the PR description and require +every hosted check named in Step 3 to report green on that SHA. Record the +final run URLs and app identities in the PR description, which can be updated +without advancing the commit. Reassert the rc baseline and clean-tree proof. +Only then may the owner mark PR (a) #1049 implementation-ready. All approvals +must be current and the PR ready to merge. Record that activation still +requires the rc merge plus Task 21. + +### Task 21: Execute Epoch 2 activation PR (c2) + +**Files on the isolated `main` branch:** + +- Modify: `.github/workflows/docs-links.yml` +- Modify: `.github/dependabot.yml` + +**External system of record:** the named c2 tracking issue and c2 PR timeline. +The Epoch 1 repository evidence ledger already contains the issue URL and +evidence schema. Do not create an evidence-only branch or advance rc merely to +store receipts that exist only after c2 merges. Use append-only issue comments +under the Task 1 schema: paste the redacted request/response and graph/ruleset +captures with body hashes and exact SHAs; links alone are not evidence. + +- [ ] **Step 1: Record and authenticate the rc merge** + +After #1049 merges, record its exact merge result as `merged_rc_tip` and initial `validated_rc_tip`. Require the latter equals current `origin/rc/202608`; if rc advanced, audit the full delta and record a new exact `validated_rc_tip`. + +- [ ] **Step 2: Dispatch read-only post-merge validation** + +Dispatch `.github/workflows/docs-links.yml` at `ref: main` with `validate_rc` and exact `tool_sha`. Assert the run's `github.sha` was the authenticated current `main` tip and no writer/attestation job ran. + +- [ ] **Step 3: Cut c2 from a fresh `main` tip** + +Record c2's own `audited_main_tip`. Apply only the reviewed c2 patch: weekly +schedule, rc-targeted Dependabot version roots, snapshot reader/writer, and +`refresh_dependency_snapshot`. Require both resulting protected paths, +including modes and blob IDs, to equal their counterparts at exact +`validated_rc_tip`; workflow-only equality is not sufficient. + +- [ ] **Step 4: Stage, statically validate, and commit the exact c2 candidate** + +Use a detached trusted-tool worktree at exact `validated_rc_tip`. In the c2 +worktree run: + +```bash +git add .github/workflows/docs-links.yml .github/dependabot.yml +git diff --cached --check +git diff --cached --name-status "$AUDITED_MAIN_TIP" +git diff --quiet +git ls-files --stage .github/workflows/docs-links.yml .github/dependabot.yml +git ls-tree "$VALIDATED_RC_TIP" .github/workflows/docs-links.yml .github/dependabot.yml +test "$(git -C "$TRUSTED_RC_WORKTREE" rev-parse HEAD)" = "$VALIDATED_RC_TIP" +cargo run --manifest-path "$TRUSTED_RC_WORKTREE/tools/docs-parity/Cargo.toml" -- workflow validate-local-index --repository "$C2_WORKTREE" --base "$AUDITED_MAIN_TIP" --candidate-kind c2 +git commit -m "Activate documentation automation" +``` + +Expected: cached name/status is exactly the two protected files, byte/count +budgets pass, the two `ls-*` outputs have identical modes/blob IDs by path, and +the trusted validator accepts the reviewed c2 shape without executing candidate +content. Record the commit SHA before opening the PR. + +- [ ] **Step 5: Let the automatic gate publish pending** + +Open c2 and record that the base controller authenticates the candidate set but publishes `docs/automation-delta: pending`, never success. + +- [ ] **Step 6: Run exact dual-ref validation** + +Dispatch `validate_main_pr` at `ref: main` with exact `validated_rc_tip`, c2 head, PR number, and base SHA. Require the local uncapped diff and AST policy pass; verify the manual run replaces pending with success on exactly that head/base. + +- [ ] **Step 7: Reassert head/base and merge** + +Immediately before merge, require current PR head/base equal the validated pair and the base equals recorded `audited_main_tip`. Merge without extra paths. + +- [ ] **Step 8: Submit and verify the first dependency snapshot** + +Dispatch `refresh_dependency_snapshot` with no SHA/PR inputs. Record in the c2 +tracking issue the authenticated rc SHA/ref, fixed detector/correlator, +external snapshot ID, 201 receipt, dependency-graph visibility, and +alert-triage owner/runbook/two-business-day SLA. Paste and hash the redacted +request, 201 response, and graph API capture; link the exact workflow run for +navigation. + +- [ ] **Step 9: Observe a real scheduled run** + +Require a genuine cron run (not manual emulation) to complete the link reader, +schedule-only issue writer, snapshot reader/writer, and same-identity +reconciliation. Record timeouts/concurrency behavior and resulting +issue/snapshot state in the c2 tracking issue and link the run; no repository +evidence commit follows. + +- [ ] **Step 10: Prove rollback readiness** + +Validate the reverse-c2 patch for both protected files against the current base, +including restored mode/blob IDs. If activation is unhealthy, merge only that +inverse, stop resubmission, drain/cancel prior snapshot runs, submit an empty +same-identity snapshot, and reopen the activated milestone until a repaired c2 +passes again. + +- [ ] **Step 11: Mark this refresh activated** + +Activation closes this refresh. Do not claim lifecycle closure; Task 22 remains release-owned. + +### Task 22: Hand off the Epoch 3 release lifecycle + +**Files:** + +- Use: `docs/internal/runbooks/documentation-automation-release.md` +- Use: `docs/internal/runbooks/patches/docs-links-release-retarget.patch` +- Use: `docs/internal/runbooks/patches/docs-links-release-disable.patch` +- Modify later in PR (e): `.github/workflows/docs-links.yml` +- Modify later in PR (e): `.github/dependabot.yml` + +**External system of record:** the named release-handoff issue and PR (e) +timeline. Post-merge retirement, ruleset, graph, and deletion receipts are +captured there under the Task 1 append-only schema, with exact SHAs, redacted +bodies, statuses, and SHA-256 hashes; URLs are navigation only. No follow-on +repository evidence PR is part of this lifecycle. + +- [ ] **Step 1: Verify the tracked issue, owner, and both reviewed paths exist before activation closes** + +The normal path retargets to `main` and removes temporary snapshot jobs/refresh. The abandonment path removes the workflow, schedule, Dependabot entries, snapshot jobs, and refresh; it never points automation at missing tooling. + +- [ ] **Step 2: At release, freeze and authenticate rc** + +Pause queue/direct writes, record bypass policy and exact tip, audit any delta into `validated_rc_tip`, and recheck the freeze immediately before validation, retirement, and deletion. + +- [ ] **Step 3: Prove the normal release merge is net-empty on protected paths** + +On the normal path, authenticate the current-main base and frozen rc head and +compare modes/blob IDs for `.github/workflows/docs-links.yml` and +`.github/dependabot.yml`; require both identical. Demonstrate that the +base-vs-head protected delta is empty while the three-dot history may contain +the earlier independent copies, then merge the rc release PR with +`docs/automation-delta` green. If either path differs, stop and use a separately +reviewed sync/repair PR before retrying. On abandonment, record this step as +inapplicable and do not merge rc. + +- [ ] **Step 4: Open the concrete PR (e) from the matching template** + +Record a fresh `audited_main_tip`; use the active base controller's automatic pending state and exact dual-ref manual validation. Reassert head/base before merge. + +- [ ] **Step 5: Drain and retire the temporary snapshot** + +After (e) stops resubmission, enumerate every queued/in-progress same-identity run, wait or cancel with a separate `actions: write` token, and submit an empty snapshot at the exact (e) merge SHA/ref `refs/heads/main`. Record the 201 receipt and prove it is the final submission. + +Attach the run enumeration, cancellation/drain result, request body hash, 201 +response, and final-submission proof to the release-handoff issue. + +- [ ] **Step 6: Close branch and protection state in order** + +Normal: verify automatic `main` dependency parsing, activate the deferred full +WP8 suite on `main`, and run both a permitted action-repin maintenance fixture +and an AST-weakening rejection through `validate_main_maintenance` using the +authenticated current-main tool. Keep `docs/automation-delta` required, +re-smoke Pages/CNAME, then delete rc. Abandonment: remove the nonreporting +automation-delta required context immediately after (e), prove the next `main` +PR is not stranded, then delete rc. Never delete rc first. + +Attach the final graph, ruleset, live-site, and branch-deletion receipts to the +release-handoff issue and close it only after the selected path is complete. + +## Final plan-to-spec traceability + +| Spec surface | Plan tasks | +| --------------------------------------- | ---------- | +| Owner questions and immutable tips | 1 | +| WP1 containment/CNAME/hygiene | 2–4 | +| WP8a tool, manifests, controller design | 5–10 | +| `main` validation controller PR (c) | 11 | +| WP2 truth pass | 12 | +| WP3 configuration | 13 | +| WP4 API/routes | 14 | +| WP5 deployment and coverage | 15–16 | +| WP6 root/crate docs | 17 | +| WP7 in-code docs | 18 | +| WP8b blocking enforcement | 19 | +| Epoch 1 / PR (a) | 20 | +| Epoch 2 / activation PR (c2) | 21 | +| Epoch 3 / release PR (e) | 22 | + +The implementation is complete only at Task 21's **activated** milestone. Task 22 is deliberately specified and owned but is not part of this refresh's implementation completion claim. diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index c36ac093e..0e76662e8 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,10 +1,11 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-29 (round 17) -**Status:** Draft, pending review +**Revised:** 2026-08-31 (round 21) +**Status:** Approved for implementation; owner decisions recorded 2026-08-31 **Scope:** Documentation and doc tooling. No runtime behavior changes. -**Baseline:** audited_target_tip `07dfc1c6d` (2026-08-28). The bulk +**Baseline:** audited_target_tip `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` +(2026-08-28). The bulk inventories were regenerated by a four-track delta audit at `a163367b3` (the main→rc merge that landed #870); the ranges `a163367b3..985ff2298` and `985ff2298..07dfc1c6d` (56 commits total) were each audited as @@ -160,8 +161,9 @@ Truth-pass acceptance and parity checks operate on defined source sets: - **Active public set:** everything VitePress builds (`docs/**` minus the WP1 `srcExclude` list). - **Active repo set:** root markdown (`README.md`, `CONTRIBUTING.md`, - `TESTING.md`, `CHANGELOG.md`, `FAQ_POC.md` until actually retired, - `ProjectGovernance.md`, `AGENTS.md`, `CLAUDE.md`), crate READMEs, config + `TESTING.md`, `CHANGELOG.md`, `FAQ_POC.md` until its selected WP2 + disposition moves or removes it, `ProjectGovernance.md`, `AGENTS.md`, + `CLAUDE.md`), crate READMEs, config templates (`trusted-server.example.toml`, `fastly.toml`, `edgezero.toml`, `.env.example`, `.env.dev`), and `.claude/commands/*.md`. - **Active maintained internal set:** `docs/README.md`, `docs/internal/**` @@ -199,6 +201,22 @@ Truth-pass acceptance and parity checks operate on defined source sets: universe. The WP8b inventory gate asserts final set equality, with negative fixtures for a new operational extension and a comment outside an existing selector. +- **Pre-commit set semantics:** "tracked" means visible in the Git index. + Record `package_start_head = HEAD` before editing a package. Before any + package checkpoint runs classification, generation, or parity checks, fully + stage every intended add/modify/delete with exact pathspecs; intent-to-add is + forbidden because it does not expose the candidate blob CI will see. Derive + the package delta from + `git diff --cached --name-status "$package_start_head"`, not from the set of + all index entries, and reject every changed path outside the package's + reviewed path list. Reject every non-ignored untracked path and every + unstaged tracked change anywhere in the repository (`git diff --quiet` must + pass). Checks may read working-tree bytes, but those bytes must equal the + index/HEAD candidate for the complete repository. After + generated output or the evidence ledger changes, restage the exact paths and + repeat the affected checks and cached-diff review. This makes the local + pre-commit candidate identical to the committed universe CI sees; checks + must never run against placeholder index entries or stale staged content. - **Historical set:** `docs/superpowers/**` and shipped `CHANGELOG.md` release entries. Exempt from retired-term greps only; privacy/secret scanning covers ALL tracked files (see WP2). @@ -219,23 +237,28 @@ Truth-pass acceptance and parity checks operate on defined source sets: live site (Pages deploys only from `main`), and internal details are scrubbed from the public repository regardless of build exclusion. - Sensitive real-world values are removed or covered by the typed, - expiring allowlist (the `fastly.toml` `service_id` is its one PROPOSED - exception, pending the ops-owned migration; it becomes an approved, - time-bounded entry only once open question 1 records an owner and - review date, which gates scanner activation). + expiring allowlist. The `fastly.toml` `service_id` is its one approved, + time-bounded exception: `aram356` owns it through its 2026-09-30 review. + That date controls review or expiry of the exception; it is not an ops + migration deadline. - CI catches regressions: docs build (already live on rc), rustdoc with broken-intra-doc-link denial, doctests, and semantic parity bound to the reader-facing markdown. ## Non-goals -- No runtime changes. Code defects found by the audits are follow-ups - (list below), not in-scope work; parity checks add tests/tools only. +- No runtime behavior or public-API changes. Code defects found by the audits + are follow-ups (list below), not in-scope work. A behavior-preserving private + test seam or private route-descriptor extraction is allowed only where WP4 + needs the production route registration set to be asserted directly; it + must preserve before/after route, method, predicate, status, and startup + behavior exactly and pass every adapter regression suite. All other parity + work is tests/tooling only. - No new documentation toolchains beyond the `tools/docs-parity` dev tool and the SHA-pinned external link-checker action. - No rewrite of `business-use-cases.md` marketing copy: it is excluded from the build via `srcExclude` and carries a source-level unverified - banner (WP1). Open question 4 is closed to this disposition - an + banner (WP1). Question 4 is closed to this disposition - an evidence-based rewrite is not an option inside this refresh, because the delivery graph allocates no PR to remove the exclusion, restore navigation, and smoke the page; republishing it is a separate future @@ -255,11 +278,10 @@ Truth-pass acceptance and parity checks operate on defined source sets: ## Delivery shape -The owner's standing instruction is one rc PR carrying spec plus work; -the concrete shape below (which adds the forced `main` containment PR and -package checkpoints) still awaits explicit confirmation - open question 7 -blocks implementation until it is given, and its answer is recorded with -owner and date. +The owner's standing instruction is one rc PR carrying spec plus work. +`aram356` approved the concrete shape below on 2026-08-31, including four +`main` PRs through activation, temporary branch-protection changes, the +release follow-on, and package checkpoints. Milestones (so "complete" has one meaning): **implementation-ready** after Epoch 1 - the `main` PRs (b)-(d) merged, and (a) #1049 complete, @@ -269,145 +291,360 @@ audited baseline); **activated** after Epoch 2 - (a) merged, then c2 merged and a real scheduled run observed; **lifecycle-closed** after Epoch 3 - (e) merged and rc deleted. Enforcement acceptance is epoch-scoped: WP8b's Epoch 1 items (rc-required checks, demonstrated rc -block, recorded `main` deferral) gate implementation-ready; its Epoch 3 +block, recorded full-suite `main` deferral, and the active +automation-delta gate) gate implementation-ready; its Epoch 3 items do not. THIS REFRESH CLOSES AT "activated"; Epoch 3 is owned, tracked, and specified here but belongs to release management, -which is out of scope. The delivery graph is four PRs delivered inside this refresh - -(a)-(d) - plus two named follow-on `main` PRs it must set up: (c2), the -post-merge activation PR, and (e), the release handoff. Each is a real -PR with its own audited_main_tip, because each mutates default-branch -files: (a) the single rc PR (#1049) +which is out of scope. The delivery graph through activation is five PRs - +(a)-(d) plus the required post-merge activation PR (c2) - and one named +release follow-on, (e), whose issue and reviewed runbooks this refresh +must produce. Each default-branch PR (b), (c), (d), (c2), and (e) is a real +PR with its own `audited_main_tip`; (a) instead uses the exact recorded +`audited_target_tip` for `rc/202608`. The graph is: (a) the single rc PR (#1049) carrying the spec plus all packages; (b) the `main` containment PR; (c) a `main` automation PR, which lands the workflow in -DISPATCH-ONLY form (no `schedule:` trigger, no rc-targeted Dependabot +VALIDATION-ONLY form (`workflow_dispatch` plus a base-controlled +`pull_request_target` automation-delta gate; no `schedule:` trigger, no rc-targeted Dependabot entries) because scheduled runs and Dependabot would otherwise inspect pre-refresh rc content and a docs-parity cargo root that does not yet -exist on any target branch. Activation is a separate NAMED `main` PR (c2) - it mutates +exist on any target branch. The new workflow path is fixed as +`.github/workflows/docs-links.yml`. Activation is a separate NAMED `main` PR (c2) - it mutates default-branch files, so it is a PR under this design's own protection model, not a checkpoint - that adds the `schedule:` trigger and the -rc-targeted Dependabot entries once #1049 has merged, with its own -audited_main_tip. Scheduled +rc-targeted Dependabot entries and temporary rc dependency-snapshot jobs +once #1049 has merged, with its own +audited_main_tip. The schedule is the off-hour weekly cron +`17 9 * * 1` (09:17 UTC Monday). The stateful schedule/manual-refresh +path shares one fixed concurrency group with `cancel-in-progress: false`; +the PR gate does not share that global group and is serialized per PR +head/base pair. Explicit timeouts are 30 minutes for link checking, 20 +for validation/snapshot generation, and 5 for each write-only reporter; +the rc-targeted +Dependabot version-update entries are weekly as well. Scheduled workflows and Dependabot read their configuration from the DEFAULT branch, so both live on `main`; version updates use `target-branch: rc/202608`. Security updates always target the DEFAULT branch, and the dependency graph analyzes manifests from it, so while -`tools/docs-parity` exists only on rc it receives version-update PRs -but NO Dependabot alerts or security-update PRs. The mitigation is not deferred to a -deadline this refresh would close before reaching: c2 installs a -SEPARATE, minimally permissioned dependency-submission job immediately -(snapshot creation needs `contents: write`, which the read-only -validation job must never hold), submitting the `tools/docs-parity` -manifest for rc so alerts and security updates work from day one; it -recurs on rc lockfile changes, and (e) removes it on both paths. c2's -acceptance includes first-successful-snapshot evidence, and the -Dependabot risk is thereby closed at activation rather than carried as -a 60-day exposure. The rc-targeted entry configures version updates -only; it does not configure security updates, and the validation +`tools/docs-parity` exists only on rc, the rc-targeted entry by itself +would provide version-update PRs but NO Dependabot alerts or +security-update PRs. The c2 dependency submission below is the alert +mitigation; no part of this design claims that it makes security-update +PRs target rc. The mitigation is not deferred to a +deadline this refresh would close before reaching: c2 installs two +SEPARATE, minimally permissioned jobs plus a non-validation +`refresh_dependency_snapshot` operation. A read-only snapshot +generator authenticates and checks out only the current rc tip and uses +the trusted `docs-parity` subcommand to produce a schema-validated +dependency snapshot artifact; it has `contents: read` only. A writer has +`contents: write` as its only repository API permission, checks out no +repository and executes no repository code, revalidates the artifact's +schema and authenticated rc SHA, then submits it. This gives +`tools/docs-parity` dependency-graph visibility and alerts from activation without +putting a write token in any job that executes repository code. The +snapshot uses the stable identity `job.correlator = docs-parity-rc-temporary` +and `detector.name = trusted-server/docs-parity`, with `ref` set to +`refs/heads/rc/202608` and `sha` equal to the authenticated tip. The operation accepts +no caller-supplied SHA, authenticates the current protected rc tip, and +runs immediately after c2 and after every merge that changes +`tools/docs-parity/{Cargo.toml,Cargo.lock}`; the weekly run is a +reconciliation backstop. Immediately before submission, the writer +re-authenticates that the artifact SHA is still the current rc tip, so an +older overlapping run cannot overwrite a newer snapshot. On both paths, +(e) removes the temporary jobs and, after that merge has stopped rc snapshot +submission, the release owner submits one empty snapshot with the SAME +identity at the (e) merge SHA and `refs/heads/main`; the API receipt and +dependency-graph disappearance/replacement are branch-deletion gates. +Merely deleting the job is insufficient because GitHub retains the +latest snapshot for a detector/correlator, and a stale user submission +can outrank automatic manifest parsing. c2's +acceptance includes first-successful-snapshot evidence, so the detection +gap is closed at activation rather than carried as a 60-day exposure. +The rc dependency-change checklist has a named owner and is not complete +until that post-merge refresh receipt is attached. This is deliberately +an operational post-merge control: duplicating a write-capable push +workflow on rc would create a second controller and a cross-branch +rollback hole. If the operation is missed, the weekly reconciliation +is the backstop (subject to GitHub's normal schedule delays); a stale +snapshot SHA or missed/failed run is recorded as a security-coverage +incident, not described as continuous coverage. +GitHub still raises automatic security-update PRs against the default +branch only, where this rc-only manifest does not exist; the compensating +control is a named alert-triage owner and runbook with a two-business-day +SLA to open an rc patch PR. The rc-targeted entry configures version +updates only; it does not configure security updates, and validation asserts exactly that rather than claiming coverage of both. -Pre-merge acceptance is not circular -and not privileged: the workflow is split into two jobs. A dispatch -validation job runs with `contents: read` only, no secrets, and no -mutating steps. It has two mutually exclusive, fail-closed dispatch MODES, because not -every dispatch inspects a `main` PR: +Pre-merge acceptance is not circular and no untrusted code runs with +privilege. A validation job runs with `contents: read` and +`pull-requests: read` only, no secrets, and no mutating steps. Manual +dispatch has a closed operation enum (four values after c2). Three operations +are mutually exclusive, fail-closed validation MODES, because not every +validation inspects a `main` PR: + +PR (c)'s validation-only form exposes the first three values; c2 adds the +fourth value and its jobs atomically. - `validate_rc` - requires `tool_sha` ONLY (no `files_sha`; supplying one is rejected). Used for the Epoch 1 dispatch against #1049's head and the first Epoch 2 dispatch of `merged_rc_tip`: it exercises the WP8 tooling itself. - `validate_main_pr` - requires `tool_sha`, `files_sha`, the PR number, - and the authenticated `base_sha` (see below). Used for c2 and e. - -Its inputs are TWO independent, separately authorized SHAs, because one -predicate cannot cover both the trusted tool and the files being -inspected: + and the authenticated `base_sha` (see below). Used for c2, e, rollback-c2, + and a same-lifecycle controller repair/sync. + It also bootstraps any already-open ordinary `main` PR before the + context first becomes required; the net-empty-protected-delta fast path + still authenticates every supplied value. +- `validate_main_maintenance` - requires `files_sha`, PR number, and + `base_sha`, and rejects `tool_sha`. It is present in (c) but cannot succeed + until the authenticated current `main` base is in the normal post-(e) + steady state: `tools/docs-parity` exists on `main`, the workflow and + Dependabot roots target `main`, and the temporary rc snapshot jobs and + refresh operation are absent. It then executes only the tool at that exact + current `main` SHA and statically validates a same-repository maintenance PR + limited to the two protected paths. This is the long-lived path for action + repins and Dependabot/workflow maintenance; it cannot authorize c2, e, or an + active-rc controller change. + +The fourth operation, `refresh_dependency_snapshot`, is not a validation +mode: it rejects every SHA/PR input, derives the exact current rc tip +itself, and can reach only the split snapshot reader/writer after c2. +Missing, unknown, mixed, or operation-inapplicable inputs fail before any +job with write permission starts. + +`validate_rc` and `validate_main_pr` use TWO independent, separately authorized +SHAs, because one predicate cannot cover both the trusted tool and the files +being inspected. `validate_main_maintenance` derives its trusted tool from the +authenticated current `main` tip and accepts no caller-selected executable +SHA: - `tool_sha` - the code that EXECUTES. A full 40-character SHA that the API reports as `head.sha` of pull request **#1049** in this same repository with `base.ref == "rc/202608"`, open and non-draft; or the - current `origin/rc/202608` tip; or (from Epoch 2 on) the recorded - `merged_rc_tip`, which the job independently verifies is still an - ancestor of `origin/rc/202608` - if rc has advanced past it in a way - that breaks reachability, the job fails and the tip is re-audited. + current `origin/rc/202608` tip. From Epoch 2 on, `merged_rc_tip` is the + initial recorded value; it is accepted only while it still EQUALS the + remote tip. Any rc advance requires a focused delta audit and a new + recorded `validated_rc_tip`; ancestry alone is not sufficient because + the workflow blob and validator may have changed. + The open #1049 head is accepted ONLY by `validate_rc`. A + protected-delta `validate_main_pr` run rejects it and executes only a + protected rc tip after the API confirms #1049 merged; the + net-empty-protected-delta bootstrap path executes no supplied tool at all. - `files_sha` - data that is NEVER executed. A full 40-character SHA - that the API reports as the current `head.sha` of a named, - same-repository, OPEN pull request targeting `main` (the c2 or e PR). - Validation is over the DIFF, not just head contents: the job - authenticates `base_sha` (the PR's current base, equal to the - recorded `audited_main_tip`) and inspects `base_sha...files_sha`, - because a restricted checkout cannot prove the PR changes nothing - else, and the abandonment form of e deletes the workflow outright so + that the API reports as the current `head.sha` of a named, OPEN pull + request targeting `main`. Protected automation deltas (c2/e, the reviewed + reverse-c2 rollback, or same-lifecycle repair/sync) must be same-repository; an ordinary + net-empty-protected-delta PR may be a fork + because its files remain data and the tool fast path is not executed. + Validation is over the candidate trees, not just head contents: the job + authenticates `base_sha` as the PR's current base; for c2/e/rollback-c2 + it must also equal that delivery PR's recorded `audited_main_tip`. The + validator fetches those two exact commit objects into a separate bare object + store. Its canonical gate view is the NUL-delimited two-tree diff + `git diff --name-status base_sha files_sha` (no merge-base/three-dot + semantics), because branch histories can diverge while the proposed merge + leaves the protected blobs unchanged. It separately compares modes and blob + IDs for `.github/workflows/docs-links.yml` and `.github/dependabot.yml`. + A three-dot PR diff may be recorded for human review but never classifies + the protected merge result. An ordinary PR - including the normal + rc-to-`main` release PR - passes this gate only when both protected paths are + net-identical in base and head; unrelated unprotected changes do not turn + that into a protected delta. c2, either e path, rollback-c2, and + same-lifecycle repair/sync, and post-handoff maintenance use the two-tree full candidate diff and may change + at most the two named protected files, with no other changed paths, no + resulting blob larger than 384 KiB, and no candidate's resulting protected + blobs larger than 512 KiB in aggregate. Exceeding a count or byte limit fails + rather than truncating. The abandonment form of e deletes the workflow outright so there is no head file to read. It enforces (i) an exact changed-file allowlist, rejecting every extra path; (ii) a path-specific expected - patch shape - c2: only the schedule activation plus the named - Dependabot additions; normal e: only the rc-to-main retarget; - abandonment e: only the reviewed removals; and (iii) workflow AST - invariants over the resulting file where one exists - permissions, + patch shape - c2: only the weekly schedule activation, the named + Dependabot additions, and the two temporary dependency-snapshot jobs + plus their closed manual refresh operation, with the + resulting `.github/workflows/docs-links.yml` byte-for-byte equal to + that file at authenticated `tool_sha` so the later rc→`main` merge + cannot create an add/add workflow divergence; normal e: + only the rc-to-main retarget and removal of those two jobs and their + manual refresh operation; + abandonment e: only the reviewed removal of the workflow, Dependabot + entries, and temporary jobs; rollback-c2: the exact inverse activation + patch removing only c2's additions and restoring (c)'s validation-only + semantics without overwriting unrelated base changes; same-lifecycle + repair/sync: candidate protected blobs equal those at authenticated + `tool_sha`, the base and candidate stay in the same validation-only or + active-rc lifecycle state, and no schedule/target/snapshot/refresh transition + is allowed; post-handoff + maintenance: only the named protected paths, same-repository head, trusted + current-main tool, and no weakening of the controller-current-main check, + event/input closure, trust-zone separation, literal status context, artifact + bounds, or attestation binding; and (iii) + workflow AST invariants over the resulting file where one exists - permissions, triggers, `uses` pins, `run` steps, secrets access, local actions, cache/artifact usage, and checkout refs - not merely the presence of - an expected schedule. Its checkout uses `persist-credentials: false`, - canonicalizes paths and rejects symlinks escaping the tree, and is - read statically only - "nothing from the files checkout is executed" - is an explicit workflow invariant, since executing an untrusted PR + an expected schedule. The workflow defaults every permission to + `none`, then grants only the job scopes named here; validation has no + OIDC, cache restore/save, artifact upload, or service-container path + through which PR-controlled data can cross trust zones. The trusted + tool checkout uses `persist-credentials: false`; PR file blobs are read with `git show` + from the separate object store, never checked out. The reader + canonicalizes paths, rejects unsafe tree modes and symlinks escaping + the tree, and reads blobs statically only - "nothing from the files + object store is executed" is an explicit workflow invariant, since executing an untrusted PR checkout in a privileged context is the exact pattern GitHub warns against. -No other notion of "approved" is used (so the job needs no -`pull-requests: read` beyond the public metadata read it already has; -if a review-state predicate is ever added, that permission is added -with it). Arbitrary refs, stale heads, other PRs' heads, fork SHAs, -and a `files_sha` from a closed or non-`main`-targeting PR are -rejected, proven by negative workflow fixtures, one per rejected class. - -Trust and binding: the run is always orchestrated by the dispatcher -workflow version already merged on `main` (never the version at the -PR head - a PR must not be able to edit or skip its own validator), so -the run associates with `main` rather than the PR head. Because the -validation job holds `contents: read` and no write token it cannot -itself publish a status, so permissions are split by job: the -validation job (`contents: read`, no secrets, no mutating steps); an -ATTESTATION job with `statuses: write` only, no checkout, `needs:` the -validation job, publishing a COMMIT STATUS (not a Check Run) with a -fixed context string from a fixed app identity against exactly -`validated_files_sha`; the schedule-only issue job (`issues: write` -only); and the dependency-submission job (`contents: write` only). -That fixed status context is what branch protection requires on the c2 -and e PRs, and each PR carries a just-before-merge assertion that its -head still equals `validated_files_sha` AND its base still equals the -authenticated `base_sha`. Manual evidence is NOT an equivalent -fallback - it cannot block a merge; if the status cannot be published, -the PR does not merge. This binding -applies identically to c2 and to the abandonment form of e. The -schedule-only issue-management job holds the job-scoped `issues: write` -and never executes code from a supplied SHA - it checks out only the -branch tip it is configured for. Pre-merge, the validation job runs on the rc PR head. +No review-state notion of "approved" is used. The validation job has +`pull-requests: read` in addition to `contents: read`, because complete, +paginated PR state/authentication metadata require it; changed-file +enumeration is deliberately local and uncapped by the PR-files API. This +is read-only metadata access, not approval authority. Arbitrary refs, +stale heads, stale bases, stale or non-`main` controller refs, other PRs' +heads, a fork SHA used as executable code or for a protected delta, extra +changed paths, mixed or partially supplied dispatch-mode inputs, and a +`files_sha` from a closed or non-`main`-targeting PR are rejected, proven +by negative workflow fixtures, one per rejected class. + +Trust and binding: the controller is always the workflow version already +merged at the CURRENT `main` tip (never the version at the PR head or a +caller-selected dispatch ref - a PR or manual caller must not be able to +edit, downgrade, or skip its own validator). Every `workflow_dispatch` +request is sent with `ref: main`, and the first fail-closed bootstrap +phase independently verifies through the repository API that the default +branch is `main`, `github.ref == refs/heads/main`, and `github.sha` equals +the authenticated current `main` tip before it accepts mode inputs or +allows any write-scoped job to start. Scheduled runs get the same +current-default-branch assertion. A dispatch against rc, a tag, an older +`main` commit, or any other ref is rejected. `validate_rc` is manual, +read-only validation and publishes no PR status. For every PR targeting +`main`, the controller's base-branch `pull_request_target` gate runs for +`opened`, `reopened`, `synchronize`, `edited`, and `ready_for_review`, +with NO path filter. It never executes PR-head code or exposes PR data +to a write-scoped job. If the +net protected-file delta is empty by base-vs-head mode/blob comparison, it +authenticates the event's current head and base and succeeds even when the +three-dot history contains earlier copies of those files. If the net delta touches +`.github/workflows/docs-links.yml` or `.github/dependabot.yml`, only the +named c2/e/rollback-c2/repair-sync file sets or, only from a normal post-(e) steady-state +base, the two-path maintenance set are accepted by the base controller's +non-executing bootstrap; an obviously wrong protected-file set fails +immediately. A candidate stays `pending` rather than receiving success. Manual +`validate_main_pr` then pins the reviewed `validated_rc_tip` (initially +`merged_rc_tip`, or the newly audited exact tip if rc advanced) and applies the +full c2/e/rollback/repair-sync exact-diff and AST predicates. Manual +`validate_main_maintenance` instead authenticates the post-handoff base and +executes the tool already at that exact current `main` tip. Thus net-empty +ordinary/release PRs pass automatically, lifecycle deltas and same-state +repairs cannot merge on an unaudited rc tool revision, and later maintenance +cannot reuse the lifecycle exceptions or a caller-selected tool. + +Because validation holds `contents: read` and no write token it cannot +publish a status itself. An ATTESTATION job has `statuses: write` only, +no checkout, `needs:` validation, and `if: always()`. For every +authenticated current PR head it publishes a COMMIT STATUS (not a Check +Run) with the literal context `docs/automation-delta` from the GitHub +Actions app identity against +exactly that head: ordinary net-empty-delta automation reports success; +an invalid delta or failed manual validation reports failure; a +protected candidate awaiting manual validation reports pending; and only +a successful authenticated `validate_main_pr` or +`validate_main_maintenance`, under that operation's own predicates, can +replace that pending state with success. The validator's first fail-closed phase authenticates and +records the head/base pair, then emits only the authenticated head through +an `if: always()` output step before the attestation consumes the later +validation result, so an ordinary +predicate failure still gets a failure status. If the current head +cannot be authenticated, no status is +published and the absent required context blocks the PR. Branch +protection requires this context for ALL `main` PRs after (c) merges; +before enabling it, the owner inventories every already-open `main` PR +and retriggers or manually validates its current head so activation does +not strand pre-existing work. Required contexts cannot be scoped to only +c2 and e. While +that context is required, protection also requires PR branches to be up +to date with `main`, so a status bound to an earlier base cannot authorize +a merge after `main` advances. Each PR carries a just-before-merge +assertion that its head and base still equal the validated pair. +Out-of-band evidence is NOT an equivalent fallback - it +cannot block a merge; if the status cannot be published, the PR does not +merge. Scheduled link checking follows the same reader/writer split: +the read-only checker authenticates and checks out the configured branch +tip, while the issue-management job has `issues: write` only, checks out +no repository, executes no repository code, and consumes a bounded, +schema-validated result artifact to deduplicate/close the issue. The link +artifact is exactly one regular `link-results.json` member (no links, traversal, +or extra members): downloaded archive at most 2 MiB, decoded JSON at most 1 +MiB, at most 500 findings, strings at most 2,048 UTF-8 bytes, and no unknown +fields. The dependency-snapshot artifact is exactly one regular +`dependency-snapshot.json` member under the same structural rules and is +bounded to a 4 MiB archive, 2 MiB decoded JSON, 5,000 dependency records, +2,048-byte strings, and no unknown fields; every overflow or schema mismatch +fails before a writer starts. The +dependency snapshot generator and writer use the split trust zones +described above. Event guards are +AST-tested: attestation runs only for `pull_request_target`, manual +`validate_main_pr`, or manual `validate_main_maintenance`; issue management, +snapshot generation, and snapshot +submission are split so issue management runs only for `schedule`, while +the snapshot pair runs only for `schedule` or the closed +`refresh_dependency_snapshot` operation; +manual `validate_rc` reaches none of the write-scoped jobs. No event +payload or PR-controlled string is interpolated into a shell command; +the only PR-derived values that cross +into attestation are the validator's authenticated 40-character head SHA +and fixed result enum (`success`, `failure`, or `pending`). Pre-merge, +`validate_rc` runs the trusted tool from the rc PR head. (c2) is the owned post-merge activation PR to `main`, tracked as an issue filed in WP8b with a named owner: after #1049 merges into rc, -record a `merged_rc_tip`, dispatch that exact SHA, then land the -activation edit (schedule trigger + rc-targeted Dependabot roots). +record `merged_rc_tip` and `validated_rc_tip` as the same exact merge +result, dispatch that exact SHA, then land the +activation edit (weekly schedule + rc-targeted Dependabot roots + the +temporary snapshot generator/writer and manual refresh operation). Because `main` does not yet contain `tools/docs-parity` or the WP8 workflows, the TOOL cannot come from `main` (the dispatcher/controller still does): its validation job runs the -dual-ref flow above - `tool_sha` = the trusted merged rc tip, +dual-ref flow above - `tool_sha` = the exact audited `validated_rc_tip`, `files_sha` = the c2 PR head - statically validating the actual changed workflow and Dependabot file and binding the result to that SHA, and c2's acceptance additionally requires a successful real scheduled run after activation -(including the schedule-only issue job). Required-check activation on -`main` is deferred until the release merge puts the tooling there - -that deferral is recorded in the WP8b branch-protection acceptance item -rather than leaving c2 blocked on checks that cannot report. (e) The handoff PR to `main`, which GATES branch deletion rather than +(including the schedule-only issue job), plus the submitted snapshot's +source ref, exact rc SHA, external correlator/ID, and visible dependency +graph result and the named alert-triage owner/runbook. The +automation-delta context is already required on +`main`; only the full WP8 documentation-check suite is deferred until the +release merge puts its tooling there. That deferral is recorded in the +WP8b branch-protection acceptance item. (e) The handoff PR to `main`, which GATES branch deletion rather than being triggered by it - deleting rc first would leave the schedule and Dependabot pointing at a dead branch, the exact state this design -forbids. Ordering, normal release: verify rc landed on `main` -> merge -(e) retargeting to `main` -> verify -> only then delete rc. Ordering, -abandonment: merge (e) disabling/removing the automation -> verify -> -only then delete rc. Normal path - the workflow checkout and Dependabot -`target-branch` switch to `main` (the checkout could fall back dynamically but Dependabot's -target is static, so a `main` edit is unavoidable), an XS retarget-only +forbids. Immediately before the normal rc-to-`main` release PR merges, require +the authenticated current-main base and rc head to have identical modes and +blob IDs for both protected automation paths. The automatic net-empty gate +then succeeds even though the three-dot history shows independent copies. If +either path differs, stop: synchronize it through a separately reviewed +controller-repair/sync PR and re-audit the exact rc tip; the broad release PR +cannot carry or authorize that repair. Ordering, normal release: verify rc landed on `main` -> freeze +rc writes at `validated_rc_tip` -> merge (e) retargeting to +`main` -> drain/cancel every pre-(e) snapshot run -> submit and verify the empty retirement +snapshot -> verify the automatic `main` graph -> only then delete rc. +Ordering, abandonment: freeze rc writes at `validated_rc_tip` -> merge +(e) disabling/removing the automation -> +drain/cancel every pre-(e) snapshot run -> submit and verify the empty +retirement snapshot -> only then delete rc. +The retirement call is a reviewed release-runbook API operation using an +owner-held token scoped to `contents: write`; it executes no repository +code and records the 201 receipt, exact merge SHA, ref, detector, and +correlator. "Freeze" is an owned release operation: pause the rc merge +queue and direct updates, record the exact tip and bypass policy, and +recheck it immediately before retirement and deletion; any movement +invalidates the evidence and restarts (e) validation. The runbook lists +and records every in-progress/queued run for the stable snapshot identity; +it waits for them or uses a separately scoped `actions: write` owner token +to cancel them. The empty snapshot is always the final submission. Normal path - the workflow checkout and Dependabot +`target-branch` switch to `main`, and both temporary dependency-snapshot +jobs plus their manual refresh operation are removed (the checkout could fall back dynamically but Dependabot's +target is static, so a `main` edit is unavoidable), an XS retarget-and-cleanup edit. Abandonment path - rc is abandoned WITHOUT merging: retargeting is invalid because `main` then holds neither the refreshed docs nor `tools/docs-parity` and its cargo root, so the handoff instead DISABLES and removes the automation (schedule trigger, rc-targeted Dependabot -entries, link workflow) rather than pointing it at content that does +entries, link workflow, both dependency-snapshot jobs, and their manual +refresh operation) rather than pointing it at content that does not exist; transplanting the tooling to `main` is a separate, explicitly sized effort, never a silent XS edit. The two paths are mutually exclusive diffs, so this refresh does not open a single speculative draft: it produces a tracked issue plus two @@ -416,16 +653,31 @@ the concrete PR is opened from the matching runbook once the outcome is known, with the same dual-ref validation and SHA binding c2 uses applied to the concrete (e) diff before handoff. Both paths have a named owner, a sequencing row, and a verification item (the automation must never keep -pointing at a dead or content-less branch); (d) the CNAME resolution -PR to `main`, cut when open question 2 resolves (the containment PR +pointing at a dead or content-less branch); (d) the CNAME deletion +PR to `main`, cut after the containment PR merges (the containment PR NEVER carries it - WP1's earlier allowance is superseded). Every -`main`-target PR records its own fresh audited_main_tip and runs the -exact-tip assertion before merge. The rc PR carries +delivery PR targeting `main` records its own fresh audited_main_tip and +runs the exact-tip assertion before merge. The rc PR carries one reviewable commit (or small series) per package, with package-level -review checkpoints (acceptance evidence recorded before the next package -lands), generated-output changes in their own commits, and no squash on -merge. One exception forced by mechanics: GitHub Pages deploys only on -pushes to `main`, so the containment subset also ships as a minimal +review checkpoints: acceptance evidence is committed with the package or +in an immediately adjacent evidence-only commit before the next package +lands; generated-output changes use their own commits where review requires +it; no checkpoint leaves a dirty evidence ledger; and no package commits are +squashed on merge. Post-merge Epoch 2/3 evidence uses a durable issue-capture +contract, not links alone. The Epoch 1 ledger predeclares the c2 and release +issue URLs plus schema version. Each checkpoint appends one or more +timestamped comments containing: actor, operation, exact commit/ref and PR +head/base/tool SHAs, run ID/attempt/job URLs, request method/endpoint, redacted +request body, response status and redacted body, detector/correlator/snapshot +IDs, and the relevant dependency-graph, ruleset, protection, or branch API +JSON. Each captured body carries its SHA-256; each comment is at most 60 KiB +and larger captures are split into ordered chunks with per-chunk and aggregate +hashes. Tokens and credential-bearing headers are never captured. Workflow +and PR URLs are navigation aids; the pasted captures and hashes are the +authoritative record when logs or artifacts expire. Corrections append a new +comment naming the superseded comment and never overwrite the prior capture. +GitHub Pages deploys only on pushes to `main`, so the containment subset ships +first as a minimal separate PR straight to `main`: exactly the `srcExclude` change (covering `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, `README.md`, `business-use-cases.md`), the onboarding move/scrub, the @@ -433,11 +685,44 @@ filled Guide landing page, and the nav edits that removing pages forces (Guide link to the landing page; Business Value item dropped). Removing every link to an excluded source is a containment invariant. Neither the CNAME resolution nor the marketing-page disposition blocks or rides in it. +After (b) and (d) merge and before the rc WP1 checkpoint, (a) imports exactly +their reviewed publishing deltas from the recorded merge commits and proves +the resulting paths byte-identical to those merges. For the delete CNAME path, +this includes the deletion; for the custom-domain path, it includes the CNAME, +base configuration, README, and every checked URL changed by (d). Later rc +packages may change an imported path only where this spec names that work (for +example, WP5's additional `config.mts` navigation); they start from the +imported bytes. This prevents the eventual rc-to-main merge from silently +losing or conflicting with the already-live containment/domain state. CodeQL gets `rc/*` PR triggers (WP8b) and joins the final gate list. Rollback treats exclusions and scrubbing as non-rollbackable security invariants: recovery reverts only the causal non-security commit or -redeploys a known-good artifact that retains them. Open question 7 asks -the owner to confirm this shape. +redeploys a known-good artifact that retains them. Before (c), record the +complete prior `main` protection/ruleset state. If the controller fails, +remove only the new `docs/automation-delta` requirement and restore the +recorded strictness setting BEFORE disabling/reverting its workflow, then +land the repaired validation-only controller and re-run both block/pass +proofs before re-enabling the context. If c2's scheduled path fails, +revert only its schedule/Dependabot/snapshot activation back to the +validated (c) form; after that merge has stopped resubmission, retire any +suspect dependency snapshot only after draining/canceling earlier runs, +using the empty SAME-identity snapshot and recording its receipt. That rollback reopens the activated milestone and +blocks release/rc→`main` merge work until a repaired c2 again makes the +protected workflow byte-identical to the audited rc blob. +CNAME recovery never restores the placeholder: fall back to the project +URLs by deleting CNAME and re-running their smokes, or restore a +previously verified custom-domain DNS/CNAME/TLS tuple without weakening +the containment exclusions. `aram356` approved this delivery and rollback +shape on 2026-08-31. + +Platform semantics are implementation inputs, not folklore, and are +revalidated when (c), c2, and (e) are cut: GitHub's +[`pull_request_target` security model](https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target), +[event-associated workflow version/ref semantics](https://docs.github.com/en/actions/concepts/workflows-and-actions/workflows), +[latest-SHA and merge-queue required-check behavior](https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/troubleshooting-required-status-checks), +[PR-files API cap and permissions](https://docs.github.com/en/rest/pulls/pulls#list-pull-requests-files), +[dependency snapshot identity/permissions](https://docs.github.com/en/rest/dependency-graph/dependency-submission), +and [default-branch-only security-update PRs](https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/customizing-dependabot-prs). ## Work packages @@ -450,20 +735,21 @@ the owner to confirm this shape. content. - `business-use-cases.md` gains its source-level unverified banner (asserted by WP1 acceptance). -- Resolve `docs/public/CNAME` (open question 2; both branches specified: - delete and re-smoke project URLs, or custom domain with `base: '/'`, +- Delete `docs/public/CNAME` and re-smoke the project URLs. The rejected + custom-domain branch would require `base: '/'`, Pages/DNS/TLS setup, canonical+asset smokes, and a - project-owned-public-domain allowlist classification). Because only + project-owned-public-domain allowlist classification. Because only `main` deploys, an rc-only edit never reaches the live site: the resolved disposition ships as its own `main`-target PR (delivery graph item (d); the containment PR never carries it). The custom-domain branch also inventories hard-coded Pages URLs (e.g. `README.md:11`) - rather than testing only canonical and asset responses. Question 2 - plus its live smoke is a completion gate for this refresh. + rather than testing only canonical and asset responses. The selected + deletion plus its live smoke is a completion gate for this refresh. - `fastly.toml`: empty `authors` list; label the key fixtures consistently as local test fixtures; comment the four KV stores; remove the `test-prebid-eids.sh` comment. `service_id` stays under its - allowlist entry pending the ops migration (open question 1). + owner-controlled allowlist entry through the 2026-09-30 review; the review + date is not a migration deadline. - `docs/package.json`: `"private": true`, license Apache-2.0. - `CLAUDE.md` policy amendment lands HERE (not WP6): the sensitive-data policy gains the owner-approved exception taxonomy (vendor URL, @@ -478,11 +764,9 @@ the owner to confirm this shape. that cannot read `CLAUDE.md`, so it carries the list). Acceptance: `vitepress build` output contains none of the excluded pages; -the `main` containment PR is merged and its smoke passes; the CNAME -disposition (open question 2) is selected and PR (d) is merged with its -branch-specific live smoke (project-URL re-smoke, or for the custom -domain: DNS/TLS evidence plus canonical, asset, and hard-coded-URL -checks) and its own audited_main_tip assertion; the banner is +the `main` containment PR is merged and its smoke passes; the CNAME deletion +PR (d) is merged with its project-URL live smoke and its own +audited_main_tip assertion; the banner is present; no internal contacts or access instructions anywhere in the repo; every command file links to (not copies) the canonical gates. @@ -514,11 +798,15 @@ type-check` and the `settings_data::get_settings` example (the exported - Auction README repairs (route table by symbol name, real provider layout: `AuctionPlan`, `PROFILE_REGISTRATIONS`, `GenericOpenRtbProvider`, mediator; remove the removed-`mock` sections). -- `FAQ_POC.md` is a three-way decision (open question 3): retire, - archive under the historical tree, or factual rewrite - archive is - the deadline fallback; a rewrite carries its own acceptance (every - answer verified against code, page kept in the active public set), - while retire and archive leave no active-set page; +- Archive `FAQ_POC.md` at `docs/superpowers/archive/FAQ_POC.md`, leaving no + active-set page or route. The rejected alternatives were deletion of the + root file or a factual rewrite moved to `docs/guide/faq.md`. A rewrite would + carry its own acceptance: every answer is verified against code, `/guide/faq` + appears in `pages.toml`, is reachable from the Guide landing page and + Reference navigation, and its built HTML is smoke-tested. The root path is + removed from the active-repo inventory after any branch; rewrite moves the + document into the active public set, while retire and archive leave no + active-set page; replace `gam.md`/`kargo.md` with tombstone content (routes preserved unconditionally, `tombstone` orphan-allowlist kind, old-route smokes). - `TESTING.md` rewritten as the test-matrix index; the auction runbook @@ -550,7 +838,8 @@ type-check` and the `settings_data::get_settings` example (the exported confirm each against code rather than re-writing. Acceptance: checked-in inventory complete over all three active sets; -the retired-token scan is case-insensitive and whitespace-tolerant +the retired-token scan covers exactly those active sets (the Historical +set remains exempt as defined above) and is case-insensitive and whitespace-tolerant (`rg -i` for prose identifiers; pattern classes for spacing variants such as `mock\s*=\s*true`) for `RequestWrapper`, `Equativ`, `with_asset`, `type-check`, `settings_data::get_settings`, `SEQUENCE.md`, @@ -905,6 +1194,24 @@ WP8a (lands right after WP1): control exercised through the WP2 disposition process, not a scanner promise), the maintained-source manifest checker, and the gate manifest. +- Author `.github/workflows/docs-links.yml` and its static security + fixtures here, before (c): the link/issue jobs are present but + unreachable without `schedule`; those dormant jobs already carry their + final permissions, conditions, 30/5-minute timeouts, fixed schedule/refresh + concurrency group, artifact schema, and pinned action references. The + base-controlled PR validator and attestation are live, including the + maintenance operation that remains fail-closed until the normal post-(e) + steady-state predicate is true, and the dependency-snapshot jobs are defined as + the reviewed c2 patch template rather than active in (c), paired with + a reviewed reverse-c2 rollback fixture. PR (c) copies + this validation-only controller to `main`; WP8b may tighten fixtures + but cannot introduce a new c2/e trust model after the controller has + already landed. PR (c) records the hash of the complete validation-only + workflow. From that point through c2, all non-activation bytes - including + the controller, attestation, and unreachable link/issue job definitions - + are frozen. WP8b may change only fixtures and the reviewed c2 activation + regions. A controller defect requires a separately reviewed repair PR and + new hash before c2; it cannot ride inside the activation delta. - The example harness, in eight explicit phases (secret resolution alone cannot make the template valid: placeholder rejection also covers the non-secret publisher fields `domain`, `cookie_domain`, and @@ -953,16 +1260,30 @@ WP8b (lands last): repository-relative path and anchor check over the active repo and maintained internal sets; the scheduled external-link check's input is all three active sets; each set gets its own synthesized dead-link - negative fixture. -- Workflow edits: CodeQL `rc/*` PR triggers; `.tool-versions` in + negative fixture. External checking sends no credentials, follows at most + five redirects and validates the final HTTPS/expected status, falls back + from unsupported HEAD to GET, and makes at most three total attempts for + 429/5xx responses with 1-second then 2-second exponential delays. + `Retry-After` is honored only when it parses to at most 30 seconds; larger or + malformed values use the bounded local delay. An exact-URL/path exception + needs reason, owner, + and expiry; blanket domain skips are forbidden, expiry fails the gate, + and allowlisted/expired/redirect-loop fixtures exercise each path. +- Complete the remaining workflow edits and lock their static assertions: + CodeQL `rc/*` PR triggers; `.tool-versions` in deploy-docs paths; normalized setup-node cache keys (all workflows, to lockfiles); Dependabot roots (github-actions, browser and Next.js fixture npm, docs-parity cargo); Wrangler pinned in `.tool-versions` (the same pin consumed locally and in CI); the scheduled external link - check using a checker pinned by action SHA (versions chosen from + check already defined at `.github/workflows/docs-links.yml`, using a + checker pinned by action SHA (versions chosen from current stable at implementation and recorded where pinned; - job-scoped `issues: write`, dedup, auto-close, named owner, - fixture-tested); + split read-only checker / no-checkout `issues: write` reporter, dedup, + auto-close, named owner, + fixture-tested); its base-controlled `pull_request_target` automation + delta gate, fail-closed dispatch modes, status-only attestation job, + and temporary split dependency-snapshot generator/writer described in + Delivery shape; `[lints] workspace = true` for openrtb-codegen. Where a governance value can be asserted deterministically (a YAML-parsing static test over workflow triggers, cache keys, Dependabot roots), it is; the @@ -973,50 +1294,82 @@ WP8b (lands last): Merge-blocking is a repository setting, not a workflow property: a failing check blocks merges only when the ruleset/branch protection -requires it. WP8b therefore carries an externally owned acceptance item that is -PATH-SPECIFIC, because `main` cannot require checks whose tooling it -does not yet contain: (i) Epoch 1 - the new checks are required on -`rc/202608`, with one demonstrated failing check actually preventing an -rc merge, and the `main` deferral explicitly recorded; (ii) normal -Epoch 3, after the release merge puts the tooling on `main` - the -checks are activated there, with a separate demonstrated `main` block; -(iii) abandonment - `main` activation is marked INAPPLICABLE, with -evidence that no required check referencing a nonexistent workflow -remains configured on `main`. Every path records the required check -names and their GitHub App, the bypass policy, and `merge_group` -triggers on the new workflows if the repository adopts merge queues. - -Acceptance: every runtime gate has a synthesized negative fixture (dead +requires it. WP8b therefore carries an externally owned, PATH-SPECIFIC +acceptance item. (i) Epoch 1 - the full new WP8 checks are required on +`rc/202608`, with one demonstrated failure actually preventing an rc +merge. After (c) merges, first inventory and seed/retrigger the current +head of every already-open `main` PR; then require the fixed +`docs/automation-delta` status for every `main` PR and enable +strict/up-to-date enforcement. Acceptance demonstrates both an +unauthorized protected-file delta being blocked and an unrelated PR +receiving success without a privileged PR checkout. Deferral applies +only to the full WP8 suite on `main`, whose tool does not exist there +yet. (ii) Normal Epoch 3, after the release merge puts the tooling on +`main` - the full checks are activated there, with a separate +demonstrated `main` block; the automation-delta status remains required and +its current-main `validate_main_maintenance` path is proven with one allowed +repin fixture and one AST-weakening rejection before lifecycle closure. +(iii) Abandonment - full-suite activation is INAPPLICABLE; after (e) +merges and removes its workflow, the automation-delta required context +is removed from branch protection before any later `main` PR can merge, +with evidence that no required context referencing a nonexistent +workflow remains. Every path records the required context names and +their GitHub App and bypass policy. This refresh does not implement a +`merge_group` attestation: a merge-group status would need to bind every +constituent PR's authenticated head/base/tool tuple, and reusing the ordinary +shape check would create a bypass. Before (c), record and enforce that merge +queues are disabled for `main`, as approved by `aram356` on 2026-08-31. If they +are active, stop until they are disabled for this lifecycle or a separate +reviewed design adds that binding to this spec, plan, and fixtures. Queue +adoption stays blocked through (e). Static workflow tests +reject an unexpected `merge_group` trigger, and branch-protection acceptance +records the queue setting alongside required contexts. + +Acceptance: the workflow suite has positive fixtures for an ordinary +net-empty protected delta, a divergent-history/net-identical rc release PR, +c2, both e paths, rollback-c2, same-lifecycle repair/sync, and post-handoff +maintenance, plus negative fixtures for a repair that changes lifecycle state, +pre-handoff maintenance, and an unexpected `merge_group` trigger. +Every runtime +gate also has a synthesized negative fixture (dead link, broken intra-doc link, failing doctest, invalid or unknown-keyed example block including a disabled integration table and a bad `profile_config`, planted non-allowlisted domain, unclassified or expired-waiver snippet fence, missing scoped JSDoc, inventory change without regenerated region including a macOS-only CLI divergence, missing README or unlisted orphan, gate mismatch in any manifest surface, removed -ownership marker); the static workflow assertions pass; regenerating all +ownership marker, stale PR base, extra automation-delta path, malformed +or mixed dispatch modes, open-PR tool SHA supplied to `validate_main_pr`, +non-`main` or stale-`main` manual controller dispatch, caller-selected +maintenance tool, maintenance AST weakening, protected candidate +receiving pending (never automatic success), +stale snapshot artifact or malformed refresh operation reaching the writer, +validation failure still producing a failing +attestation, privileged job attempting a checkout or PR-code execution); +the static workflow assertions pass; regenerating all regions and goldens at final HEAD produces no diff. ## Sequencing -| Order | Package | Size | Depends on | -| ----- | --------------------------------------- | ---- | ------------------------------------------------------------------------------------------------- | -| 0 | WP1 containment subset → `main` PR (b) | XS | - | -| 0c | CNAME PR (d) | XS | open question 2; completion gate, does not block other rows | -| 1 | WP1 hygiene (full, rc PR) | S | - | -| 2 | WP8a scaffolding | L | - | -| 2b | `main` automation PR (c), dispatch-only | XS | WP8a (workflow content exists); validation dispatch vs the #1049 head | -| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | -| 4 | WP3 config reference | L | WP8a (extractor, harness) | -| 5 | WP4 API reference | M | WP2, WP8a | -| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | -| 7 | WP6 root + READMEs | M | WP2 | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 | -| 9b | Post-merge activation PR (c2) → `main` | S | #1049 merged into rc; own audited_main_tip; dual-ref validation + SHA binding; real scheduled run | -| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion (merge e, verify, then delete); issue + two runbooks produced by this refresh | - -Orders 0-9 are Epoch 1 (this refresh; closes at implementation-ready -once they land, then activated after 9b). 9b is Epoch 2. 10 is Epoch 3, +| Order | Package | Size | Depends on | +| ----- | ----------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- | +| 0 | WP1 containment subset → `main` PR (b) | XS | - | +| 0c | CNAME deletion PR (d) | XS | (b) merged | +| 1 | WP1 hygiene (full, rc PR) | S | (b) and (d) merged; import their exact publishing deltas | +| 2 | WP8a scaffolding | L | - | +| 2b | `main` automation PR (c), validation-only | S | WP8a (workflow content exists); rc dispatch + base-controlled `main` automation-delta gate | +| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | +| 4 | WP3 config reference | L | WP8a (extractor, harness) | +| 5 | WP4 API reference | M | WP2, WP8a | +| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | +| 7 | WP6 root + READMEs | M | WP2 | +| 8 | WP7 in-code docs | M | - | +| 9 | WP8b gate activation | M | WP2-WP7 | +| 9b | Post-merge activation PR (c2) → `main` | S | #1049 merged into rc; audited tip; dual-ref binding; scheduled run + first dependency snapshot | +| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion; removes temporary snapshot jobs; issue + two reviewed runbooks | + +Orders 0-9 are Epoch 1 (this refresh reaches implementation-ready once +they land). 9b is Epoch 2 and closes this refresh at activated. 10 is Epoch 3, owned and specified here but executed at release. ## Verification @@ -1025,7 +1378,8 @@ Verification runs in three epochs, because a single state cannot hold both "target still equals the audited baseline" and "#1049 has merged": **Epoch 1 - pre-merge, at the final rc-PR HEAD** (`origin/rc/202608` -still equals audited_target_tip `07dfc1c6d`): all GitHub checks green (CodeQL with rc +still equals audited_target_tip +`07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`): all GitHub checks green (CodeQL with rc triggers, format including the docs build, the seven test.yml jobs, the four integration-test jobs, release builds, JS build/test); the WP8 parity suite and negative fixtures green; regeneration produces no diff; @@ -1036,10 +1390,14 @@ Fastly local push + secrets, Cloudflare envelope transfer, Spin local push + variables) executed as documented with commands and cleanup recorded; the `main` containment PR merged with its positive smoke, the `main` automation PR merged with a successful read-only validation -dispatch against the rc PR head and validated Dependabot config, and the -CNAME PR (d) merged with its branch-specific live smoke (each `main` -PR's audited_main_tip assertion having passed); every follow-up filed with a recorded URL or -disposition; the release-handoff work (e) existing as a tracked issue +dispatch against the rc PR head and validated Dependabot config; its +fixed status then seeded/retriggered for every already-open `main` PR and +made required for every `main` PR with strict/up-to-date enforcement, +with both the unauthorized-protected-delta block and unrelated-PR pass +demonstrated; and the CNAME deletion PR (d) merged with its project-URL live +smoke (each `main` PR's audited_main_tip assertion having passed); every +follow-up filed with a recorded URL or disposition; the release-handoff work +(e) existing as a tracked issue plus two reviewed runbooks (retarget / disable-and-remove), with its owner and both path conditions recorded and branch deletion documented as gated by (e); and the exact-tip baseline assertion for @@ -1047,51 +1405,90 @@ as gated by (e); and the exact-tip baseline assertion for in the branch) passing at this HEAD - not a merge-base comparison. **Epoch 2 - after #1049 merges into rc**: record `merged_rc_tip` as the -exact merge result of #1049 and verify it is reachable from -`origin/rc/202608` (if rc has since advanced such that it is not, or -the predicate no longer accepts it, re-audit and use the new rc tip); -dispatch that exact SHA as `tool_sha` through the read-only validation -job and record the result; open and merge the c2 activation PR to `main` (its own +exact merge result of #1049 and initialize `validated_rc_tip` to it; +require that value to equal `origin/rc/202608` (if rc has advanced at +all, audit the full delta and replace `validated_rc_tip` with the new +exact tip; reachability alone does not pass); dispatch that exact SHA as +`tool_sha` through the read-only validation job and record the result; +open and merge the c2 activation PR to `main` (its own audited_main_tip; dual-checkout validation - trusted tool from the -merged rc tip, files under test from the c2 head - covering the actual -changed workflow and Dependabot file); then observe one successful real -scheduled run, including the schedule-only issue job. +exact `validated_rc_tip`, files under test from the c2 head - covering the actual +changed workflow and Dependabot file, including the temporary snapshot +jobs - and a just-before-merge manual success replacing the automatic +pending status on that exact head/base); immediately dispatch +`refresh_dependency_snapshot` and record the first snapshot, then observe +one successful real scheduled run, including the +schedule-only issue job and a dependency snapshot whose rc ref, exact +source SHA, external correlator/ID, and dependency-graph visibility are +recorded (the scheduled submission reconciles the same stable identity); +record the alert-triage owner, runbook URL, and +two-business-day rc patch SLA as well. Because the receipts exist only after +c2 merges and c2's repository diff is exactly two protected files, the +canonical Epoch 2 record is the named c2 tracking issue using the durable +capture contract above, with links to the c2 PR, workflow runs, status, +snapshot receipt, and dependency-graph evidence. +The Epoch 1 repository ledger contains the issue URL and required evidence +schema; no post-merge evidence-only branch or unmodeled rc commit is created. **Epoch 3 - at release, before rc deletion**: verify rc landed on -`main` (or that it is being abandoned); open the concrete (e) PR from +`main` (or that it is being abandoned). On the normal release PR, first prove +the current-main base and rc head have net-identical modes/blobs for both +protected paths so the required automation context succeeds without treating +their divergent history as a new delta; otherwise use the separate repair/sync +path before the release merge. Then refresh `validated_rc_tip` to +the exact current rc tip after auditing any delta and freeze all rc +writes through branch deletion; open the concrete (e) PR from the matching runbook, validated the same dual-checkout way against its -real diff; merge it; re-verify the retargeted (or removed) schedule and -Dependabot roots, live Pages containment, and the selected CNAME +real diff, with the manual status successful on its current head/base; +merge it; verify that the temporary dependency-snapshot jobs +are gone; submit the empty snapshot at the exact (e) merge SHA using the +stable temporary identity and record the 201 receipt; verify the stale rc +submission disappeared (and, on normal release, that automatic parsing +now exposes the `main` manifest); re-verify the retargeted (or removed) +schedule and Dependabot roots, live Pages containment, and the selected CNAME behavior; only then is rc deleted. If the release merge happened, -`main` now carries the tooling, so the deferred required-check -activation on `main` is completed here per the WP8b protection item. - -## Open questions - -1. `fastly.toml` `service_id` allowlist owner and review date (blocks - WP8a scanner activation); the ops migration itself blocks nothing. -2. CNAME: delete (recommended) or custom domain (fully specified branch). -3. `FAQ_POC.md`: three-way - retire, archive, or factual rewrite - (owner: the maintainer driving this refresh; decide before WP2 - starts). Deterministic fallback if undecided by then: archive under - the historical tree, leaving no active-set page and no route to - preserve. "Do not retire" alone is not an answer - it selects - between archive and rewrite, and rewrite has its own acceptance. The gam/kargo - tombstones are NOT part of this question - WP2 applies them - unconditionally, with routes preserved, sidebar entries removed, and - old-route smokes asserting the tombstones serve. -4. `business-use-cases.md`: CLOSED - excluded with a source-level - banner; republishing is a separate future effort (see Non-goals). -5. CHANGELOG release cut (out of scope; deterministic no-release edit - defined in Non-goals). -6. Governance ownership (CODEOWNERS/minutes). Terminal disposition: if - no owner is named by the time WP6 lands, the fallback executes - the - governance document is corrected to current evidence (no minutes - exist; releases are not continuous) without adding CODEOWNERS or - minutes commitments - and the refresh completes; naming owners - remains a follow-up for maintainers. -7. Delivery shape confirmation (blocks starting implementation). -8. CodeQL `push` coverage for `rc/*` (non-blocking). +`main` now carries the tooling, so the deferred full-suite required-check +activation on `main` is completed here per the WP8b protection item, and the +post-handoff current-main maintenance mode is proven without weakening any AST +invariant. If +rc was abandoned, remove the now-nonreporting automation-delta required +context immediately after (e) and verify the next `main` PR is not +stranded. Post-merge Epoch 3 receipts live in the named release-handoff issue +under the same durable capture contract and link to PR (e), workflow runs, +retirement response, graph verification, ruleset changes, and branch deletion; +they do not require a follow-on repository evidence PR. + +## Owner decisions and remaining non-blocking questions + +1. `fastly.toml` `service_id`: approved as a temporary allowlist exception. + Owner: `aram356`. Next review: 2026-09-30. The date controls review or + expiry of the exception; it is not a migration deadline. The ops migration + remains non-blocking. +2. CNAME: delete `docs/public/CNAME` and retain the project-path base. Owner: + `aram356`. Decision date: 2026-08-31. The custom-domain alternative was + rejected for this refresh. Rollback never restores the placeholder; it + either keeps the CNAME deleted or restores a previously verified + DNS/CNAME/TLS tuple. +3. `FAQ_POC.md`: archive at `docs/superpowers/archive/FAQ_POC.md`. Owner: + `aram356`. Decision date: 2026-08-31. Retiring the content and rewriting it + as `docs/guide/faq.md` were rejected. The gam/kargo tombstones are not part + of this decision: WP2 applies them unconditionally, preserves their routes, + removes sidebar entries, and smokes the old routes. +4. `business-use-cases.md`: closed. Exclude it with a source-level banner; + republishing remains a separate effort (see Non-goals). +5. CHANGELOG release cut: explicitly non-blocking and out of scope. Apply the + deterministic no-release edit defined in Non-goals. +6. Governance ownership: no owner named. Apply the factual-governance fallback + approved on 2026-08-31: correct `ProjectGovernance.md` to current evidence + (no minutes exist; releases are not continuous) without adding CODEOWNERS + or minutes commitments. Naming owners remains a maintainer follow-up. +7. Delivery shape: `aram356` approved on 2026-08-31 all five PRs through + activation, the temporary `main` `docs/automation-delta` required-status + and strict/up-to-date protection change, merge queues disabled on `main` + through (e), and the external dependency-snapshot retirement API call under + the specified runbook and control scheme. The rollback requirements in + Delivery shape remain mandatory. +8. CodeQL `push` coverage for `rc/*`: explicitly non-blocking. ## Follow-up issues to file (code, not docs) @@ -1343,7 +1740,7 @@ envelope string>"}` (nested wrapper) + `wrangler secret put `; Runtime environment variables and the `TRUSTED_SERVER__` CLI overlay are documented as separate surfaces (WP2). -## Appendix E: Still-open finding index (verified 2026-08-27) +## Appendix E: Still-open finding index (verified at the 2026-08-28 baseline) - `srcExclude` absent; 133 superpowers files in the CI-built site; empty `guide/index.md`; nav Guide/Business Value links; CNAME placeholder. From 349fd46a38fac68d803bc80e1557b9cfddba6ac6 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:15:32 -0700 Subject: [PATCH 29/33] Clarify documentation refresh evidence controls --- .../audits/documentation-refresh-decisions.md | 38 ++++++--- .../audits/documentation-refresh-evidence.md | 77 ++++++++++++------- .../plans/2026-08-30-documentation-refresh.md | 28 +++++-- ...2026-08-19-documentation-refresh-design.md | 20 +++-- 4 files changed, 110 insertions(+), 53 deletions(-) diff --git a/docs/internal/audits/documentation-refresh-decisions.md b/docs/internal/audits/documentation-refresh-decisions.md index 15ba3c574..c688eadf7 100644 --- a/docs/internal/audits/documentation-refresh-decisions.md +++ b/docs/internal/audits/documentation-refresh-decisions.md @@ -28,9 +28,10 @@ commit. A later `main` PR must not reuse the starting `audited_main_tip` after - Selection: retain the checked-in `fastly.toml` `service_id` only through a typed, temporary scanner allowlist entry. - Owner: `aram356`. -- Next review: 2026-09-30. -- Control: the review date requires renewal or expiry of the exception. It is - not an ops migration deadline, and the migration does not block this refresh. +- Expiry: `2026-09-30T00:00:00Z`. +- Control: check mode fails at or after the expiry instant. Renewal requires a + reviewed, committed replacement before expiry. This is not the ops migration + deadline, and the migration does not block this refresh. ### 2. CNAME @@ -103,14 +104,29 @@ evidence. Populate each pending field with the exact URL and immutable identifiers at the named checkpoint. Do not infer a value from a branch name. -| Item | Target | URL | Fresh audited base | Head or merge SHA | State | -| --------------------------------- | ----------- | -------------------------------- | ------------------------------------------ | ----------------- | ------------- | -| (a) rc implementation PR | `rc/202608` | Pending verification of PR #1049 | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Pending | Open | -| (b) containment PR | `main` | Pending | Pending | Pending | Not started | -| (c) validation-only controller PR | `main` | Pending | Pending | Pending | Not started | -| (d) CNAME deletion PR | `main` | Pending | Pending | Pending | Not started | -| (c2) activation PR | `main` | Pending | Pending | Pending | Not started | -| (e) release-handoff PR | `main` | Pending | Pending | Pending | Release-owned | +| Item | Target | URL | Fresh audited base | Head or merge SHA | State | +| --------------------------------- | ----------- | ------------------------------------------------------ | ------------------------------------------ | ---------------------------------------------------------- | ------------------------------- | +| (a) rc implementation PR | `rc/202608` | https://github.com/IABTechLab/trusted-server/pull/1049 | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Remote capture: `b904b3aeb5af26a536afadcbfb2d70af36bca5a2` | OPEN, draft; refresh after push | +| (b) containment PR | `main` | Pending | Pending | Pending | Not started | +| (c) validation-only controller PR | `main` | Pending | Pending | Pending | Not started | +| (d) CNAME deletion PR | `main` | Pending | Pending | Pending | Not started | +| (c2) activation PR | `main` | Pending | Pending | Pending | Not started | +| (e) release-handoff PR | `main` | Pending | Pending | Pending | Release-owned | + +### PR (a) pre-push metadata capture + +- Capture timestamp: 2026-08-31T23:12:46Z. +- URL: https://github.com/IABTechLab/trusted-server/pull/1049. +- State: OPEN, draft. +- Base: ref `rc/202608`, SHA + `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`. +- Remote head: ref `spec-docs-refresh`, captured SHA + `b904b3aeb5af26a536afadcbfb2d70af36bca5a2`. +- Author and assignee: `aram356`. + +This is a timestamped pre-push remote capture. It does not assert that Task 1 +or its follow-up commits exist on the remote head. Refresh every field after +those commits are pushed and before using PR #1049 as a validation input. ## External issue records diff --git a/docs/internal/audits/documentation-refresh-evidence.md b/docs/internal/audits/documentation-refresh-evidence.md index 665f70eba..e6c03b365 100644 --- a/docs/internal/audits/documentation-refresh-evidence.md +++ b/docs/internal/audits/documentation-refresh-evidence.md @@ -27,13 +27,15 @@ import, must be append-only and timestamped in UTC. Each capture includes: branch API JSON. Tokens, credential-bearing headers, cookies, and unredacted secrets are never -captured. Every request body, response body, and API JSON body carries its own -SHA-256. A capture larger than 60 KiB is split into ordered chunks; each chunk -records its byte length and SHA-256, and the capture records the aggregate -byte length and SHA-256. Workflow, PR, issue, and artifact URLs are navigation -aids only; pasted redacted bodies plus hashes are authoritative. Corrections -append a new comment that names the superseded comment URL and capture ID; -existing comments are never edited or deleted. +captured. Every request body, response body, and applicable API JSON body +includes its actual redacted content, byte length, and SHA-256. Each issue +comment is at most 60 KiB. A larger capture is split into ordered chunks; each +chunk includes its actual redacted content, index, byte length, and SHA-256, +and the capture records the aggregate byte length and SHA-256. Workflow, PR, +issue, and artifact URLs are navigation aids only; pasted redacted bodies plus +hashes are authoritative. Corrections append a new comment that names the +superseded comment URL and capture ID; existing comments are never edited or +deleted. ### Capture template @@ -58,11 +60,13 @@ Response status: Redacted response body: Response-body bytes / SHA-256: Snapshot detector / correlator / ID / ref / SHA: -Graph API JSON bytes / SHA-256: -Ruleset API JSON bytes / SHA-256: -Protection and merge-queue API JSON bytes / SHA-256: -Branch API JSON bytes / SHA-256: -Chunk index / total, chunk bytes / SHA-256, aggregate bytes / SHA-256: +Graph API redacted JSON body / bytes / SHA-256: +Ruleset API redacted JSON body / bytes / SHA-256: +Protection and merge-queue API redacted JSON body / bytes / SHA-256: +Branch API redacted JSON body / bytes / SHA-256: +Ordered chunk index / total: +Ordered chunk redacted content / bytes / SHA-256: +Aggregate bytes / SHA-256: Navigation URLs: Supersedes capture/comment: Result: @@ -108,7 +112,8 @@ updated approved baseline before work continues. ### Task 1: decisions and immutable tips - Capture timestamp: 2026-08-31T22:55:52Z. -- Actor: agent acting for approver `aram356`. +- Executor: `OpenAI Codex task agent task1_implementer`. +- Approver: `aram356`. - Operation: fetch refs, verify exact rc tip and ancestry, record starting `main` tip, and establish decision/evidence records. - Implementation start HEAD: @@ -136,14 +141,29 @@ updated approved baseline before work continues. ### External delivery URLs and immutable identifiers -| Item | PR or issue URL | Target | Fresh audited base | Validated head/tool | Merge SHA | Evidence state | -| --------------------------------- | -------------------------------- | ----------- | ------------------------------------------ | ------------------- | --------- | -------------- | -| (a) rc implementation PR | Pending verification of PR #1049 | `rc/202608` | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Pending | Pending | Pending | -| (b) containment PR | Pending | `main` | Pending | Pending | Pending | Pending | -| (c) validation-only controller PR | Pending | `main` | Pending | Pending | Pending | Pending | -| (d) CNAME deletion PR | Pending | `main` | Pending | Pending | Pending | Pending | -| (c2) activation PR | Pending | `main` | Pending | Pending | Pending | Epoch 2 | -| (e) release-handoff PR | Pending | `main` | Pending | Pending | Pending | Epoch 3 | +| Item | PR or issue URL | Target | Fresh audited base | Validated head/tool | Merge SHA | Evidence state | +| --------------------------------- | ------------------------------------------------------ | ----------- | ------------------------------------------ | ---------------------------------------------------------- | --------- | ------------------------------- | +| (a) rc implementation PR | https://github.com/IABTechLab/trusted-server/pull/1049 | `rc/202608` | `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` | Remote capture: `b904b3aeb5af26a536afadcbfb2d70af36bca5a2` | Pending | OPEN, draft; refresh after push | +| (b) containment PR | Pending | `main` | Pending | Pending | Pending | Pending | +| (c) validation-only controller PR | Pending | `main` | Pending | Pending | Pending | Pending | +| (d) CNAME deletion PR | Pending | `main` | Pending | Pending | Pending | Pending | +| (c2) activation PR | Pending | `main` | Pending | Pending | Pending | Epoch 2 | +| (e) release-handoff PR | Pending | `main` | Pending | Pending | Pending | Epoch 3 | + +#### PR (a) pre-push metadata capture + +- Capture timestamp: 2026-08-31T23:12:46Z. +- URL: https://github.com/IABTechLab/trusted-server/pull/1049. +- State: OPEN, draft. +- Base: ref `rc/202608`, SHA + `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`. +- Remote head: ref `spec-docs-refresh`, captured SHA + `b904b3aeb5af26a536afadcbfb2d70af36bca5a2`. +- Author and assignee: `aram356`. + +This is a timestamped pre-push remote capture. It does not assert that Task 1 +or its follow-up commits exist on the remote head. Refresh every field after +those commits are pushed and before using PR #1049 as a validation input. ### Cross-worktree handoff: PRs (b), (c), and (d) @@ -156,11 +176,16 @@ by both URL and literal value. Handoff ID / PR label: (b), (c), or (d) Source PR / tracking issue URL: Source capture IDs: +Source capture timestamp (UTC): +Source capture actor: Source worktree path: Branch name: +Source PR state / draft: Fresh audited_main_tip: Exact PR base SHA: Exact PR head SHA: +Trusted tool / source SHA: +Base-controlled controller ref / SHA: Merge SHA: Changed paths and modes: Local commands and results: @@ -224,11 +249,11 @@ no-diff proof is incomplete. Every exception requires an owner, narrow rationale, and review or expiry date. Expired or ownerless entries fail the checkpoint. -| Type / path | Value classification | Owner | Rationale | Review or expiry | State | -| ------------------------------------------- | ---------------------- | --------------------- | ---------------------------------------------- | -------------------------------- | -------- | -| `fastly.toml` `service_id` | Service ID | `aram356` | Temporary checked-in operational identifier | 2026-09-30 review/expiry control | Approved | -| Task 15 temporary public-page ownership | Page/orphan transition | Pending Task 15 owner | Page registered before Task 16 final ownership | Expires at Task 16 | Pending | -| Spin manual smoke, only if CI cannot run it | Manual evidence | Pending | Runner capability gap | Time-bounded date required | Pending | +| Type / path | Value classification | Owner | Rationale | Review or expiry | State | +| ------------------------------------------- | ---------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------- | -------- | +| `fastly.toml` `service_id` | Service ID | `aram356` | Check mode fails at or after expiry; renewal requires a reviewed committed replacement; not the ops migration deadline | `2026-09-30T00:00:00Z` | Approved | +| Task 15 temporary public-page ownership | Page/orphan transition | Pending Task 15 owner | Page registered before Task 16 final ownership | Expires at Task 16 | Pending | +| Spin manual smoke, only if CI cannot run it | Manual evidence | Pending | Runner capability gap | Time-bounded date required | Pending | ### Follow-up issues diff --git a/docs/superpowers/plans/2026-08-30-documentation-refresh.md b/docs/superpowers/plans/2026-08-30-documentation-refresh.md index 536d8ca8f..d56f22b76 100644 --- a/docs/superpowers/plans/2026-08-30-documentation-refresh.md +++ b/docs/superpowers/plans/2026-08-30-documentation-refresh.md @@ -24,7 +24,9 @@ Task 2 may begin only after Task 1 commits this approval. The narrower owner gates are also resolved: - `aram356` owns the temporary `fastly.toml` `service_id` allowlist exception; - its 2026-09-30 review date controls review or expiry, not the ops migration. + it expires at `2026-09-30T00:00:00Z`, and check mode fails at or after that + instant. Renewal requires a reviewed, committed replacement before expiry. + This is not the ops migration deadline. - Task 3 deletes `docs/public/CNAME` and retains the project-path base. PR (d) must merge before Task 4 imports the live publishing deltas into rc. - Task 12 archives `FAQ_POC.md` at @@ -279,6 +281,10 @@ stack this on containment or automation PRs and do not add rc-only audit records - [ ] **Step 2: Execute exactly the selected branch** +The selected branch is **delete**. The custom-domain instructions remain +rejected/reference-only unless Task 1's CNAME decision is formally reopened +and this plan is amended and re-approved. + Delete path: remove `docs/public/CNAME` and keep the project-path `base`. Custom-domain path: first run `git grep -l -F 'https://iabtechlab.github.io/trusted-server'`; at the audited @@ -308,8 +314,9 @@ git diff --cached --name-status "$AUDITED_MAIN_TIP" git commit -m "Resolve documentation site domain" ``` -Run exactly one staging branch. The delete path's cached set is exactly CNAME; -the custom path's is exactly CNAME, config, and the recorded URL path. Review +Run the selected delete staging branch. The delete path's cached set is exactly +CNAME; the rejected custom path's reference set is exactly CNAME, config, and +the recorded URL path. Review the full cached content and require `git diff --quiet` before committing. Immediately before merge, assert the exact recorded base. After deploy, the @@ -506,7 +513,7 @@ Start from `git ls-files -z`; classify every path as text or binary without trea - [ ] **Step 3: Write scanner detector and allowlist tests** -For domain, email, credential shape, service ID, encoded token, binary strings, lockfile structured fields, media metadata, and identifier/access-phrase denylist, add both a positive fixture and an owner/rationale/expiry allowlisted fixture. Prove expired entries, stale hashes, renamed files, and broad domain exemptions fail. +For domain, email, credential shape, service ID, encoded token, binary strings, lockfile structured fields, media metadata, and identifier/access-phrase denylist, add both a positive fixture and an owner/rationale/expiry allowlisted fixture. Prove expired entries, stale hashes, renamed files, and broad domain exemptions fail. Encode the `fastly.toml` exception expiry as `2026-09-30T00:00:00Z`; check mode must fail at or after that instant. Renewal requires a reviewed, committed replacement before expiry and is independent of the ops migration deadline. - [ ] **Step 4: Implement deterministic scanning** @@ -1050,8 +1057,11 @@ Replace `RequestWrapper` with real platform traits; remove Equativ, `.with_asset - [ ] **Step 4: Resolve FAQ and tombstones** -Execute exactly one FAQ branch: retire deletes `FAQ_POC.md`; archive moves it -to `docs/superpowers/archive/FAQ_POC.md`; rewrite moves it to +The selected FAQ branch is **archive**: move `FAQ_POC.md` to +`docs/superpowers/archive/FAQ_POC.md`. The retire and rewrite instructions +remain rejected/reference-only unless Task 1's FAQ decision is formally +reopened and this plan is amended and re-approved. For reference, retire +deletes `FAQ_POC.md`; rewrite moves it to `docs/guide/faq.md`, verifies every answer against code, links it from the Guide landing page and Reference navigation, registers `/guide/faq` in `pages.toml`, and adds a built-page smoke for `guide/faq.html`. Every branch @@ -1092,7 +1102,7 @@ If `html_processor.test.html` changes, run `cargo test-fastly html_processor`. R - [ ] **Step 9: Commit WP2** -Stage the common WP2 paths first, then exactly one FAQ branch and only the +Stage the common WP2 paths first, then the selected archive branch and only the conditional fixture paths that actually changed: ```bash @@ -1108,7 +1118,9 @@ git add crates/trusted-server-core/src/html_processor.test.html git commit -m "Correct maintained documentation truth" ``` -The comments label mutually exclusive branch commands; execute one, not all. +The comments preserve mutually exclusive reference branches; execute the +archive command only unless the decision is formally reopened and this plan is +amended and re-approved. If the mechanical inventory selects another human-facing comment path, add that one exact path to the reviewed list before running the checkpoint—never replace this list with `git add docs`, `git add .github`, or another directory. diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 0e76662e8..3af14edb9 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -238,9 +238,10 @@ Truth-pass acceptance and parity checks operate on defined source sets: scrubbed from the public repository regardless of build exclusion. - Sensitive real-world values are removed or covered by the typed, expiring allowlist. The `fastly.toml` `service_id` is its one approved, - time-bounded exception: `aram356` owns it through its 2026-09-30 review. - That date controls review or expiry of the exception; it is not an ops - migration deadline. + time-bounded exception: `aram356` owns it until it expires at + `2026-09-30T00:00:00Z`. Check mode fails at or after that instant. Renewal + requires a reviewed, committed replacement before expiry. This is not the + ops migration deadline. - CI catches regressions: docs build (already live on rc), rustdoc with broken-intra-doc-link denial, doctests, and semantic parity bound to the reader-facing markdown. @@ -748,8 +749,10 @@ and [default-branch-only security-update PRs](https://docs.github.com/en/code-se - `fastly.toml`: empty `authors` list; label the key fixtures consistently as local test fixtures; comment the four KV stores; remove the `test-prebid-eids.sh` comment. `service_id` stays under its - owner-controlled allowlist entry through the 2026-09-30 review; the review - date is not a migration deadline. + owner-controlled allowlist entry until it expires at + `2026-09-30T00:00:00Z`. Check mode fails at or after that instant; renewal + requires a reviewed, committed replacement before expiry. This is not the + ops migration deadline. - `docs/package.json`: `"private": true`, license Apache-2.0. - `CLAUDE.md` policy amendment lands HERE (not WP6): the sensitive-data policy gains the owner-approved exception taxonomy (vendor URL, @@ -1461,9 +1464,10 @@ they do not require a follow-on repository evidence PR. ## Owner decisions and remaining non-blocking questions 1. `fastly.toml` `service_id`: approved as a temporary allowlist exception. - Owner: `aram356`. Next review: 2026-09-30. The date controls review or - expiry of the exception; it is not a migration deadline. The ops migration - remains non-blocking. + Owner: `aram356`. It expires at `2026-09-30T00:00:00Z`; check mode fails at + or after that instant. Renewal requires a reviewed, committed replacement + before expiry. This is not the ops migration deadline, which remains + non-blocking. 2. CNAME: delete `docs/public/CNAME` and retain the project-path base. Owner: `aram356`. Decision date: 2026-08-31. The custom-domain alternative was rejected for this refresh. Rollback never restores the placeholder; it From 931e53e2cbd94d8a7f9fad9ec9d337d37a0f21ca Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:23:54 -0700 Subject: [PATCH 30/33] Record documentation approval evidence --- .../audits/documentation-refresh-evidence.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/internal/audits/documentation-refresh-evidence.md b/docs/internal/audits/documentation-refresh-evidence.md index e6c03b365..2b604947a 100644 --- a/docs/internal/audits/documentation-refresh-evidence.md +++ b/docs/internal/audits/documentation-refresh-evidence.md @@ -139,6 +139,31 @@ updated approved baseline before work continues. status are recorded in the execution handoff because a commit cannot contain its own SHA. +#### Task 1 completion receipt + +- Receipt timestamp: 2026-08-31T23:15:37Z. +- Executor: `OpenAI Codex task agent task1_implementer`. +- Approver: `aram356`. +- Primary implementation commit: + `8588391b9e9d6f02d886c519836eedb84a37abd8`. +- Review-fix commit: + `349fd46a38fac68d803bc80e1557b9cfddba6ac6`. +- `npx prettier --write` on the four Task 1 records: passed. +- `cd docs && npm run format`: passed. +- `cd docs && npm run lint`: passed. +- `cd docs && npm run build`: passed. VitePress emitted only the known + non-failing `vcl`-to-plain-text syntax-highlighting warning; generated + `.vitepress/.temp` output was removed and not staged. +- `git diff --check` and pre-commit `git diff --cached --check`: passed. +- Review-fix staged path set: exactly the spec, plan, decisions record, and + evidence record; no unintended untracked file or unstaged tracked byte. +- Clean-status observation immediately before this evidence-only mutation: + `git status --porcelain` printed nothing. + +This immediately adjacent evidence-only commit cannot contain its own SHA. +Its full SHA is reported in the execution handoff and independently verified +by the controller and review; no recursive receipt commit follows. + ### External delivery URLs and immutable identifiers | Item | PR or issue URL | Target | Fresh audited base | Validated head/tool | Merge SHA | Evidence state | From 3ac8bc3ee5d919b272077f2ed5288d6986de2f40 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:31:47 -0700 Subject: [PATCH 31/33] Adopt single-PR documentation refresh design --- ...2026-08-19-documentation-refresh-design.md | 1153 ++++++----------- 1 file changed, 366 insertions(+), 787 deletions(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index 3af14edb9..e214de56d 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1,8 +1,8 @@ # Documentation Refresh (Full Surface) **Date:** 2026-08-19 -**Revised:** 2026-08-31 (round 21) -**Status:** Approved for implementation; owner decisions recorded 2026-08-31 +**Revised:** 2026-08-31 (single-PR delivery revision) +**Status:** Draft revision; single-PR architecture approved, written review pending **Scope:** Documentation and doc tooling. No runtime behavior changes. **Baseline:** audited_target_tip `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` (2026-08-28). The bulk @@ -25,11 +25,9 @@ advances without a rebase, so the guard is exact-tip: after fetching, `origin/rc/202608` must equal the recorded audited_target_tip AND the implementation branch must contain that commit; the same exact-tip assertion (never a merge-base comparison) repeats at the final rc-PR -HEAD. The containment work targets `main`, so an audited_main_tip is -recorded when the containment PR is cut and the same exact-tip assertion -runs before that PR merges. If any assertion fails, rebase, re-audit the -delta, and update this spec first. Rounds 1-8 of review history live in git; this -revision supersedes their inventories. +HEAD. If either assertion fails, rebase, re-audit the delta, and update this +spec first. Rounds 1-8 of review history live in git; this revision supersedes +their inventories and the round-21 multi-PR delivery graph. ## Context @@ -261,9 +259,9 @@ Truth-pass acceptance and parity checks operate on defined source sets: from the build via `srcExclude` and carries a source-level unverified banner (WP1). Question 4 is closed to this disposition - an evidence-based rewrite is not an option inside this refresh, because - the delivery graph allocates no PR to remove the exclusion, restore - navigation, and smoke the page; republishing it is a separate future - effort with its own publishing PR and acceptance. `roadmap.md` gets a + the approved scope does not remove the exclusion, restore navigation, or + smoke the page; republishing it is a separate future publishing effort with + its own design and acceptance. `roadmap.md` gets a factual status pass only. - No release management. The 8 breaking `[Unreleased]` entries are a maintainer decision; the deterministic no-release CHANGELOG edit is: @@ -279,473 +277,167 @@ Truth-pass acceptance and parity checks operate on defined source sets: ## Delivery shape -The owner's standing instruction is one rc PR carrying spec plus work. -`aram356` approved the concrete shape below on 2026-08-31, including four -`main` PRs through activation, temporary branch-protection changes, the -release follow-on, and package checkpoints. - -Milestones (so "complete" has one meaning): **implementation-ready** -after Epoch 1 - the `main` PRs (b)-(d) merged, and (a) #1049 complete, -approved, all checks green, and ready to merge (it is not yet merged; -Epoch 1 runs at its final pre-merge head while rc still equals the -audited baseline); **activated** after Epoch 2 - (a) merged, then c2 -merged and a real scheduled run observed; **lifecycle-closed** after -Epoch 3 - (e) merged and rc deleted. Enforcement acceptance is -epoch-scoped: WP8b's Epoch 1 items (rc-required checks, demonstrated rc -block, recorded full-suite `main` deferral, and the active -automation-delta gate) gate implementation-ready; its Epoch 3 -items do not. THIS REFRESH CLOSES AT "activated"; Epoch 3 is -owned, tracked, and specified here but belongs to release management, -which is out of scope. The delivery graph through activation is five PRs - -(a)-(d) plus the required post-merge activation PR (c2) - and one named -release follow-on, (e), whose issue and reviewed runbooks this refresh -must produce. Each default-branch PR (b), (c), (d), (c2), and (e) is a real -PR with its own `audited_main_tip`; (a) instead uses the exact recorded -`audited_target_tip` for `rc/202608`. The graph is: (a) the single rc PR (#1049) -carrying the spec plus all packages; (b) the `main` containment PR; -(c) a `main` automation PR, which lands the workflow in -VALIDATION-ONLY form (`workflow_dispatch` plus a base-controlled -`pull_request_target` automation-delta gate; no `schedule:` trigger, no rc-targeted Dependabot -entries) because scheduled runs and Dependabot would otherwise inspect -pre-refresh rc content and a docs-parity cargo root that does not yet -exist on any target branch. The new workflow path is fixed as -`.github/workflows/docs-links.yml`. Activation is a separate NAMED `main` PR (c2) - it mutates -default-branch files, so it is a PR under this design's own protection -model, not a checkpoint - that adds the `schedule:` trigger and the -rc-targeted Dependabot entries and temporary rc dependency-snapshot jobs -once #1049 has merged, with its own -audited_main_tip. The schedule is the off-hour weekly cron -`17 9 * * 1` (09:17 UTC Monday). The stateful schedule/manual-refresh -path shares one fixed concurrency group with `cancel-in-progress: false`; -the PR gate does not share that global group and is serialized per PR -head/base pair. Explicit timeouts are 30 minutes for link checking, 20 -for validation/snapshot generation, and 5 for each write-only reporter; -the rc-targeted -Dependabot version-update entries are weekly as well. Scheduled -workflows and Dependabot read their configuration from the DEFAULT -branch, so both live on `main`; version updates use -`target-branch: rc/202608`. Security updates always target the DEFAULT -branch, and the dependency graph analyzes manifests from it, so while -`tools/docs-parity` exists only on rc, the rc-targeted entry by itself -would provide version-update PRs but NO Dependabot alerts or -security-update PRs. The c2 dependency submission below is the alert -mitigation; no part of this design claims that it makes security-update -PRs target rc. The mitigation is not deferred to a -deadline this refresh would close before reaching: c2 installs two -SEPARATE, minimally permissioned jobs plus a non-validation -`refresh_dependency_snapshot` operation. A read-only snapshot -generator authenticates and checks out only the current rc tip and uses -the trusted `docs-parity` subcommand to produce a schema-validated -dependency snapshot artifact; it has `contents: read` only. A writer has -`contents: write` as its only repository API permission, checks out no -repository and executes no repository code, revalidates the artifact's -schema and authenticated rc SHA, then submits it. This gives -`tools/docs-parity` dependency-graph visibility and alerts from activation without -putting a write token in any job that executes repository code. The -snapshot uses the stable identity `job.correlator = docs-parity-rc-temporary` -and `detector.name = trusted-server/docs-parity`, with `ref` set to -`refs/heads/rc/202608` and `sha` equal to the authenticated tip. The operation accepts -no caller-supplied SHA, authenticates the current protected rc tip, and -runs immediately after c2 and after every merge that changes -`tools/docs-parity/{Cargo.toml,Cargo.lock}`; the weekly run is a -reconciliation backstop. Immediately before submission, the writer -re-authenticates that the artifact SHA is still the current rc tip, so an -older overlapping run cannot overwrite a newer snapshot. On both paths, -(e) removes the temporary jobs and, after that merge has stopped rc snapshot -submission, the release owner submits one empty snapshot with the SAME -identity at the (e) merge SHA and `refs/heads/main`; the API receipt and -dependency-graph disappearance/replacement are branch-deletion gates. -Merely deleting the job is insufficient because GitHub retains the -latest snapshot for a detector/correlator, and a stale user submission -can outrank automatic manifest parsing. c2's -acceptance includes first-successful-snapshot evidence, so the detection -gap is closed at activation rather than carried as a 60-day exposure. -The rc dependency-change checklist has a named owner and is not complete -until that post-merge refresh receipt is attached. This is deliberately -an operational post-merge control: duplicating a write-capable push -workflow on rc would create a second controller and a cross-branch -rollback hole. If the operation is missed, the weekly reconciliation -is the backstop (subject to GitHub's normal schedule delays); a stale -snapshot SHA or missed/failed run is recorded as a security-coverage -incident, not described as continuous coverage. -GitHub still raises automatic security-update PRs against the default -branch only, where this rc-only manifest does not exist; the compensating -control is a named alert-triage owner and runbook with a two-business-day -SLA to open an rc patch PR. The rc-targeted entry configures version -updates only; it does not configure security updates, and validation -asserts exactly that rather than claiming coverage of both. -Pre-merge acceptance is not circular and no untrusted code runs with -privilege. A validation job runs with `contents: read` and -`pull-requests: read` only, no secrets, and no mutating steps. Manual -dispatch has a closed operation enum (four values after c2). Three operations -are mutually exclusive, fail-closed validation MODES, because not every -validation inspects a `main` PR: - -PR (c)'s validation-only form exposes the first three values; c2 adds the -fourth value and its jobs atomically. - -- `validate_rc` - requires `tool_sha` ONLY (no `files_sha`; supplying - one is rejected). Used for the Epoch 1 dispatch against #1049's head - and the first Epoch 2 dispatch of `merged_rc_tip`: it exercises the - WP8 tooling itself. -- `validate_main_pr` - requires `tool_sha`, `files_sha`, the PR number, - and the authenticated `base_sha` (see below). Used for c2, e, rollback-c2, - and a same-lifecycle controller repair/sync. - It also bootstraps any already-open ordinary `main` PR before the - context first becomes required; the net-empty-protected-delta fast path - still authenticates every supplied value. -- `validate_main_maintenance` - requires `files_sha`, PR number, and - `base_sha`, and rejects `tool_sha`. It is present in (c) but cannot succeed - until the authenticated current `main` base is in the normal post-(e) - steady state: `tools/docs-parity` exists on `main`, the workflow and - Dependabot roots target `main`, and the temporary rc snapshot jobs and - refresh operation are absent. It then executes only the tool at that exact - current `main` SHA and statically validates a same-repository maintenance PR - limited to the two protected paths. This is the long-lived path for action - repins and Dependabot/workflow maintenance; it cannot authorize c2, e, or an - active-rc controller change. - -The fourth operation, `refresh_dependency_snapshot`, is not a validation -mode: it rejects every SHA/PR input, derives the exact current rc tip -itself, and can reach only the split snapshot reader/writer after c2. -Missing, unknown, mixed, or operation-inapplicable inputs fail before any -job with write permission starts. - -`validate_rc` and `validate_main_pr` use TWO independent, separately authorized -SHAs, because one predicate cannot cover both the trusted tool and the files -being inspected. `validate_main_maintenance` derives its trusted tool from the -authenticated current `main` tip and accepts no caller-selected executable -SHA: - -- `tool_sha` - the code that EXECUTES. A full 40-character SHA that the - API reports as `head.sha` of pull request **#1049** in this same - repository with `base.ref == "rc/202608"`, open and non-draft; or the - current `origin/rc/202608` tip. From Epoch 2 on, `merged_rc_tip` is the - initial recorded value; it is accepted only while it still EQUALS the - remote tip. Any rc advance requires a focused delta audit and a new - recorded `validated_rc_tip`; ancestry alone is not sufficient because - the workflow blob and validator may have changed. - The open #1049 head is accepted ONLY by `validate_rc`. A - protected-delta `validate_main_pr` run rejects it and executes only a - protected rc tip after the API confirms #1049 merged; the - net-empty-protected-delta bootstrap path executes no supplied tool at all. -- `files_sha` - data that is NEVER executed. A full 40-character SHA - that the API reports as the current `head.sha` of a named, OPEN pull - request targeting `main`. Protected automation deltas (c2/e, the reviewed - reverse-c2 rollback, or same-lifecycle repair/sync) must be same-repository; an ordinary - net-empty-protected-delta PR may be a fork - because its files remain data and the tool fast path is not executed. - Validation is over the candidate trees, not just head contents: the job - authenticates `base_sha` as the PR's current base; for c2/e/rollback-c2 - it must also equal that delivery PR's recorded `audited_main_tip`. The - validator fetches those two exact commit objects into a separate bare object - store. Its canonical gate view is the NUL-delimited two-tree diff - `git diff --name-status base_sha files_sha` (no merge-base/three-dot - semantics), because branch histories can diverge while the proposed merge - leaves the protected blobs unchanged. It separately compares modes and blob - IDs for `.github/workflows/docs-links.yml` and `.github/dependabot.yml`. - A three-dot PR diff may be recorded for human review but never classifies - the protected merge result. An ordinary PR - including the normal - rc-to-`main` release PR - passes this gate only when both protected paths are - net-identical in base and head; unrelated unprotected changes do not turn - that into a protected delta. c2, either e path, rollback-c2, and - same-lifecycle repair/sync, and post-handoff maintenance use the two-tree full candidate diff and may change - at most the two named protected files, with no other changed paths, no - resulting blob larger than 384 KiB, and no candidate's resulting protected - blobs larger than 512 KiB in aggregate. Exceeding a count or byte limit fails - rather than truncating. The abandonment form of e deletes the workflow outright so - there is no head file to read. It enforces (i) an exact changed-file - allowlist, rejecting every extra path; (ii) a path-specific expected - patch shape - c2: only the weekly schedule activation, the named - Dependabot additions, and the two temporary dependency-snapshot jobs - plus their closed manual refresh operation, with the - resulting `.github/workflows/docs-links.yml` byte-for-byte equal to - that file at authenticated `tool_sha` so the later rc→`main` merge - cannot create an add/add workflow divergence; normal e: - only the rc-to-main retarget and removal of those two jobs and their - manual refresh operation; - abandonment e: only the reviewed removal of the workflow, Dependabot - entries, and temporary jobs; rollback-c2: the exact inverse activation - patch removing only c2's additions and restoring (c)'s validation-only - semantics without overwriting unrelated base changes; same-lifecycle - repair/sync: candidate protected blobs equal those at authenticated - `tool_sha`, the base and candidate stay in the same validation-only or - active-rc lifecycle state, and no schedule/target/snapshot/refresh transition - is allowed; post-handoff - maintenance: only the named protected paths, same-repository head, trusted - current-main tool, and no weakening of the controller-current-main check, - event/input closure, trust-zone separation, literal status context, artifact - bounds, or attestation binding; and (iii) - workflow AST invariants over the resulting file where one exists - permissions, - triggers, `uses` pins, `run` steps, secrets access, local actions, - cache/artifact usage, and checkout refs - not merely the presence of - an expected schedule. The workflow defaults every permission to - `none`, then grants only the job scopes named here; validation has no - OIDC, cache restore/save, artifact upload, or service-container path - through which PR-controlled data can cross trust zones. The trusted - tool checkout uses `persist-credentials: false`; PR file blobs are read with `git show` - from the separate object store, never checked out. The reader - canonicalizes paths, rejects unsafe tree modes and symlinks escaping - the tree, and reads blobs statically only - "nothing from the files - object store is executed" is an explicit workflow invariant, since executing an untrusted PR - checkout in a privileged context is the exact pattern GitHub warns - against. - -No review-state notion of "approved" is used. The validation job has -`pull-requests: read` in addition to `contents: read`, because complete, -paginated PR state/authentication metadata require it; changed-file -enumeration is deliberately local and uncapped by the PR-files API. This -is read-only metadata access, not approval authority. Arbitrary refs, -stale heads, stale bases, stale or non-`main` controller refs, other PRs' -heads, a fork SHA used as executable code or for a protected delta, extra -changed paths, mixed or partially supplied dispatch-mode inputs, and a -`files_sha` from a closed or non-`main`-targeting PR are rejected, proven -by negative workflow fixtures, one per rejected class. - -Trust and binding: the controller is always the workflow version already -merged at the CURRENT `main` tip (never the version at the PR head or a -caller-selected dispatch ref - a PR or manual caller must not be able to -edit, downgrade, or skip its own validator). Every `workflow_dispatch` -request is sent with `ref: main`, and the first fail-closed bootstrap -phase independently verifies through the repository API that the default -branch is `main`, `github.ref == refs/heads/main`, and `github.sha` equals -the authenticated current `main` tip before it accepts mode inputs or -allows any write-scoped job to start. Scheduled runs get the same -current-default-branch assertion. A dispatch against rc, a tag, an older -`main` commit, or any other ref is rejected. `validate_rc` is manual, -read-only validation and publishes no PR status. For every PR targeting -`main`, the controller's base-branch `pull_request_target` gate runs for -`opened`, `reopened`, `synchronize`, `edited`, and `ready_for_review`, -with NO path filter. It never executes PR-head code or exposes PR data -to a write-scoped job. If the -net protected-file delta is empty by base-vs-head mode/blob comparison, it -authenticates the event's current head and base and succeeds even when the -three-dot history contains earlier copies of those files. If the net delta touches -`.github/workflows/docs-links.yml` or `.github/dependabot.yml`, only the -named c2/e/rollback-c2/repair-sync file sets or, only from a normal post-(e) steady-state -base, the two-path maintenance set are accepted by the base controller's -non-executing bootstrap; an obviously wrong protected-file set fails -immediately. A candidate stays `pending` rather than receiving success. Manual -`validate_main_pr` then pins the reviewed `validated_rc_tip` (initially -`merged_rc_tip`, or the newly audited exact tip if rc advanced) and applies the -full c2/e/rollback/repair-sync exact-diff and AST predicates. Manual -`validate_main_maintenance` instead authenticates the post-handoff base and -executes the tool already at that exact current `main` tip. Thus net-empty -ordinary/release PRs pass automatically, lifecycle deltas and same-state -repairs cannot merge on an unaudited rc tool revision, and later maintenance -cannot reuse the lifecycle exceptions or a caller-selected tool. - -Because validation holds `contents: read` and no write token it cannot -publish a status itself. An ATTESTATION job has `statuses: write` only, -no checkout, `needs:` validation, and `if: always()`. For every -authenticated current PR head it publishes a COMMIT STATUS (not a Check -Run) with the literal context `docs/automation-delta` from the GitHub -Actions app identity against -exactly that head: ordinary net-empty-delta automation reports success; -an invalid delta or failed manual validation reports failure; a -protected candidate awaiting manual validation reports pending; and only -a successful authenticated `validate_main_pr` or -`validate_main_maintenance`, under that operation's own predicates, can -replace that pending state with success. The validator's first fail-closed phase authenticates and -records the head/base pair, then emits only the authenticated head through -an `if: always()` output step before the attestation consumes the later -validation result, so an ordinary -predicate failure still gets a failure status. If the current head -cannot be authenticated, no status is -published and the absent required context blocks the PR. Branch -protection requires this context for ALL `main` PRs after (c) merges; -before enabling it, the owner inventories every already-open `main` PR -and retriggers or manually validates its current head so activation does -not strand pre-existing work. Required contexts cannot be scoped to only -c2 and e. While -that context is required, protection also requires PR branches to be up -to date with `main`, so a status bound to an earlier base cannot authorize -a merge after `main` advances. Each PR carries a just-before-merge -assertion that its head and base still equal the validated pair. -Out-of-band evidence is NOT an equivalent fallback - it -cannot block a merge; if the status cannot be published, the PR does not -merge. Scheduled link checking follows the same reader/writer split: -the read-only checker authenticates and checks out the configured branch -tip, while the issue-management job has `issues: write` only, checks out -no repository, executes no repository code, and consumes a bounded, -schema-validated result artifact to deduplicate/close the issue. The link -artifact is exactly one regular `link-results.json` member (no links, traversal, -or extra members): downloaded archive at most 2 MiB, decoded JSON at most 1 -MiB, at most 500 findings, strings at most 2,048 UTF-8 bytes, and no unknown -fields. The dependency-snapshot artifact is exactly one regular -`dependency-snapshot.json` member under the same structural rules and is -bounded to a 4 MiB archive, 2 MiB decoded JSON, 5,000 dependency records, -2,048-byte strings, and no unknown fields; every overflow or schema mismatch -fails before a writer starts. The -dependency snapshot generator and writer use the split trust zones -described above. Event guards are -AST-tested: attestation runs only for `pull_request_target`, manual -`validate_main_pr`, or manual `validate_main_maintenance`; issue management, -snapshot generation, and snapshot -submission are split so issue management runs only for `schedule`, while -the snapshot pair runs only for `schedule` or the closed -`refresh_dependency_snapshot` operation; -manual `validate_rc` reaches none of the write-scoped jobs. No event -payload or PR-controlled string is interpolated into a shell command; -the only PR-derived values that cross -into attestation are the validator's authenticated 40-character head SHA -and fixed result enum (`success`, `failure`, or `pending`). Pre-merge, -`validate_rc` runs the trusted tool from the rc PR head. -(c2) is the owned post-merge activation PR to `main`, tracked as an -issue filed in WP8b with a named owner: after #1049 merges into rc, -record `merged_rc_tip` and `validated_rc_tip` as the same exact merge -result, dispatch that exact SHA, then land the -activation edit (weekly schedule + rc-targeted Dependabot roots + the -temporary snapshot generator/writer and manual refresh operation). -Because `main` does not yet contain `tools/docs-parity` or the WP8 -workflows, the TOOL cannot come from `main` (the dispatcher/controller -still does): its validation job runs the -dual-ref flow above - `tool_sha` = the exact audited `validated_rc_tip`, -`files_sha` = the c2 PR head - statically validating the actual changed -workflow and Dependabot file and binding the result to that SHA, and c2's acceptance -additionally requires a successful real scheduled run after activation -(including the schedule-only issue job), plus the submitted snapshot's -source ref, exact rc SHA, external correlator/ID, and visible dependency -graph result and the named alert-triage owner/runbook. The -automation-delta context is already required on -`main`; only the full WP8 documentation-check suite is deferred until the -release merge puts its tooling there. That deferral is recorded in the -WP8b branch-protection acceptance item. (e) The handoff PR to `main`, which GATES branch deletion rather than -being triggered by it - deleting rc first would leave the schedule and -Dependabot pointing at a dead branch, the exact state this design -forbids. Immediately before the normal rc-to-`main` release PR merges, require -the authenticated current-main base and rc head to have identical modes and -blob IDs for both protected automation paths. The automatic net-empty gate -then succeeds even though the three-dot history shows independent copies. If -either path differs, stop: synchronize it through a separately reviewed -controller-repair/sync PR and re-audit the exact rc tip; the broad release PR -cannot carry or authorize that repair. Ordering, normal release: verify rc landed on `main` -> freeze -rc writes at `validated_rc_tip` -> merge (e) retargeting to -`main` -> drain/cancel every pre-(e) snapshot run -> submit and verify the empty retirement -snapshot -> verify the automatic `main` graph -> only then delete rc. -Ordering, abandonment: freeze rc writes at `validated_rc_tip` -> merge -(e) disabling/removing the automation -> -drain/cancel every pre-(e) snapshot run -> submit and verify the empty -retirement snapshot -> only then delete rc. -The retirement call is a reviewed release-runbook API operation using an -owner-held token scoped to `contents: write`; it executes no repository -code and records the 201 receipt, exact merge SHA, ref, detector, and -correlator. "Freeze" is an owned release operation: pause the rc merge -queue and direct updates, record the exact tip and bypass policy, and -recheck it immediately before retirement and deletion; any movement -invalidates the evidence and restarts (e) validation. The runbook lists -and records every in-progress/queued run for the stable snapshot identity; -it waits for them or uses a separately scoped `actions: write` owner token -to cancel them. The empty snapshot is always the final submission. Normal path - the workflow checkout and Dependabot -`target-branch` switch to `main`, and both temporary dependency-snapshot -jobs plus their manual refresh operation are removed (the checkout could fall back dynamically but Dependabot's -target is static, so a `main` edit is unavoidable), an XS retarget-and-cleanup -edit. Abandonment path - rc is abandoned WITHOUT merging: retargeting is -invalid because `main` then holds neither the refreshed docs nor -`tools/docs-parity` and its cargo root, so the handoff instead DISABLES -and removes the automation (schedule trigger, rc-targeted Dependabot -entries, link workflow, both dependency-snapshot jobs, and their manual -refresh operation) rather than pointing it at content that does -not exist; transplanting the tooling to `main` is a separate, -explicitly sized effort, never a silent XS edit. The two paths are mutually exclusive diffs, so this refresh does not -open a single speculative draft: it produces a tracked issue plus two -REVIEWED patch templates/runbooks (retarget, and disable/remove), and -the concrete PR is opened from the matching runbook once the outcome is -known, with the same dual-ref validation and SHA binding c2 uses applied to -the concrete (e) diff before handoff. Both paths have a named owner, a -sequencing row, and a verification item (the automation must never keep -pointing at a dead or content-less branch); (d) the CNAME deletion -PR to `main`, cut after the containment PR merges (the containment PR -NEVER carries it - WP1's earlier allowance is superseded). Every -delivery PR targeting `main` records its own fresh audited_main_tip and -runs the exact-tip assertion before merge. The rc PR carries -one reviewable commit (or small series) per package, with package-level -review checkpoints: acceptance evidence is committed with the package or -in an immediately adjacent evidence-only commit before the next package -lands; generated-output changes use their own commits where review requires -it; no checkpoint leaves a dirty evidence ledger; and no package commits are -squashed on merge. Post-merge Epoch 2/3 evidence uses a durable issue-capture -contract, not links alone. The Epoch 1 ledger predeclares the c2 and release -issue URLs plus schema version. Each checkpoint appends one or more -timestamped comments containing: actor, operation, exact commit/ref and PR -head/base/tool SHAs, run ID/attempt/job URLs, request method/endpoint, redacted -request body, response status and redacted body, detector/correlator/snapshot -IDs, and the relevant dependency-graph, ruleset, protection, or branch API -JSON. Each captured body carries its SHA-256; each comment is at most 60 KiB -and larger captures are split into ordered chunks with per-chunk and aggregate -hashes. Tokens and credential-bearing headers are never captured. Workflow -and PR URLs are navigation aids; the pasted captures and hashes are the -authoritative record when logs or artifacts expire. Corrections append a new -comment naming the superseded comment and never overwrite the prior capture. -GitHub Pages deploys only on pushes to `main`, so the containment subset ships -first as a minimal -separate PR straight to `main`: exactly the `srcExclude` change (covering -`superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, -`README.md`, `business-use-cases.md`), the onboarding move/scrub, the -filled Guide landing page, and the nav edits that removing pages forces -(Guide link to the landing page; Business Value item dropped). Removing -every link to an excluded source is a containment invariant. Neither the -CNAME resolution nor the marketing-page disposition blocks or rides in it. -After (b) and (d) merge and before the rc WP1 checkpoint, (a) imports exactly -their reviewed publishing deltas from the recorded merge commits and proves -the resulting paths byte-identical to those merges. For the delete CNAME path, -this includes the deletion; for the custom-domain path, it includes the CNAME, -base configuration, README, and every checked URL changed by (d). Later rc -packages may change an imported path only where this spec names that work (for -example, WP5's additional `config.mts` navigation); they start from the -imported bytes. This prevents the eventual rc-to-main merge from silently -losing or conflicting with the already-live containment/domain state. -CodeQL gets `rc/*` PR triggers (WP8b) and joins the final gate list. -Rollback treats exclusions and scrubbing as non-rollbackable security -invariants: recovery reverts only the causal non-security commit or -redeploys a known-good artifact that retains them. Before (c), record the -complete prior `main` protection/ruleset state. If the controller fails, -remove only the new `docs/automation-delta` requirement and restore the -recorded strictness setting BEFORE disabling/reverting its workflow, then -land the repaired validation-only controller and re-run both block/pass -proofs before re-enabling the context. If c2's scheduled path fails, -revert only its schedule/Dependabot/snapshot activation back to the -validated (c) form; after that merge has stopped resubmission, retire any -suspect dependency snapshot only after draining/canceling earlier runs, -using the empty SAME-identity snapshot and recording its receipt. That rollback reopens the activated milestone and -blocks release/rc→`main` merge work until a repaired c2 again makes the -protected workflow byte-identical to the audited rc blob. -CNAME recovery never restores the placeholder: fall back to the project -URLs by deleting CNAME and re-running their smokes, or restore a -previously verified custom-domain DNS/CNAME/TLS tuple without weakening -the containment exclusions. `aram356` approved this delivery and rollback -shape on 2026-08-31. - -Platform semantics are implementation inputs, not folklore, and are -revalidated when (c), c2, and (e) are cut: GitHub's -[`pull_request_target` security model](https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target), -[event-associated workflow version/ref semantics](https://docs.github.com/en/actions/concepts/workflows-and-actions/workflows), -[latest-SHA and merge-queue required-check behavior](https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/troubleshooting-required-status-checks), -[PR-files API cap and permissions](https://docs.github.com/en/rest/pulls/pulls#list-pull-requests-files), -[dependency snapshot identity/permissions](https://docs.github.com/en/rest/dependency-graph/dependency-submission), -and [default-branch-only security-update PRs](https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/customizing-dependabot-prs). +All repository changes for this refresh are delivered through the existing +`spec-docs-refresh` branch and PR #1049, which continues to target +`rc/202608`. No containment, CNAME, controller, activation, or release-handoff +implementation PR is created. PR #1104 was closed as superseded; its reviewed +containment commits are transferred to #1049 before the next package starts. + +### Single-PR boundary + +PR #1049 contains: + +- this design, the implementation plan, decisions, and evidence ledger; +- public-site containment, CNAME deletion, and all WP1-WP7 documentation; +- the standalone `tools/docs-parity` tool, manifests, fixtures, and generated + records; +- the final steady-state CI, external-link, dependency-snapshot, and + Dependabot configuration; +- release-time operational instructions for effects that cannot occur while + the change exists only on `rc/202608`; and +- one reviewable commit or small non-squashed series per package, with + immediately adjacent evidence-only commits when final identifiers cannot be + recorded inside the package commit. + +Every package starts from a clean tree, records its start SHA, stages an exact +path allowlist, rejects untracked or unstaged candidate bytes, runs focused and +repository-wide checks required by that package, and ends clean. All package +commits are pushed to PR #1049. No implementation commit is routed through an +individual PR. + +The immutable implementation baseline remains +`07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`. Before the next package and at +final PR head, fetch `origin/rc/202608` and require both exact-tip equality and +ancestry. If the target advances, stop, audit the complete delta, update this +design and its checked records, and re-review before continuing. + +### Publishing and CNAME behavior + +The containment delta and CNAME deletion are ordinary commits in #1049: + +- `srcExclude` covers `superpowers/**`, `internal/**`, `epics/**`, + `guide/onboarding.md`, `README.md`, and `business-use-cases.md`; +- the Guide landing and required navigation edits ship in the same PR; +- onboarding moves to `docs/internal/onboarding.md` and is scrubbed even though + that destination is excluded; +- `docs/public/CNAME` is deleted and the project-path VitePress base is + retained; and +- the placeholder CNAME is never restored by rollback. + +GitHub Pages deploys only from `main`. Therefore #1049 acceptance proves +containment and project-path behavior from a clean local/CI VitePress build, +including exact included/excluded artifact assertions and link checks. It does +not claim a live Pages deployment while the PR targets rc. After the normal rc +release reaches `main`, the release owner runs the same URL/content matrix and +records the deployment SHA, response headers, canonical URLs, and CNAME +behavior. Those receipts are release evidence, not a prerequisite for +finishing this PR. + +### Final-state automation + +The multi-stage `main` controller, `docs/automation-delta` attestation, +activation patch, temporary rc dependency snapshot, retirement snapshot, and +release retarget/disable paths are removed. They existed only to protect +independent default-branch delivery PRs, which this design no longer uses. + +PR #1049 instead commits one final steady-state automation design: + +1. Pull-request jobs use `pull_request` and read-only permissions. They execute + deterministic docs-parity, generation, docs, rustdoc, doctest, JS, and + workflow-policy checks on #1049 without secrets or write tokens. +2. Scheduled external-link checking and dependency-snapshot submission are + defined in their final `main` form. Scheduled jobs run only from the default + branch, so they remain operationally dormant until the rc release reaches + `main`. +3. The scheduled link reader has `contents: read` only and emits a bounded, + schema-validated artifact. A separate no-checkout reporter has only the + issue permission it needs, deduplicates one owned issue, and auto-closes it + after a clean scheduled run. +4. Dependency snapshot generation reads the authenticated default-branch + source and emits a bounded, schema-validated artifact. A separate + no-checkout writer has only `contents: write` and submits the fixed + detector/correlator identity after revalidating the artifact and source SHA. +5. Manual refresh accepts no caller-supplied executable or tool SHA. It runs + the workflow and tooling committed on the authenticated default branch. +6. Every action is pinned by full SHA; event, permission, checkout, cache, + artifact, archive-member, path, mode, symlink, size, and schema invariants + are enforced by static negative fixtures. + +The final workflow has no `pull_request_target`, status-attestation writer, +caller-selected validation source, protected-file lifecycle state machine, or +merge-queue-specific bypass surface. An unexpected `pull_request_target`, +`merge_group`, status write, PR-code checkout in a writer, or caller-selected +tool SHA fails the checked workflow policy. + +The final dependency configuration targets `main`. It has no temporary +`rc/202608` Dependabot roots or snapshot identity to retire. The first real +scheduled link run and dependency submission occur only after the normal +release makes these files part of `main`. Release acceptance records the run +and graph receipts; no follow-on implementation PR is required. + +### Branch protection and merge queues + +PR #1049 does not mutate `main` branch protection, seed statuses on unrelated +PRs, disable merge queues, request a bypass, or claim that a workflow absent +from `main` is already required there. Hosted checks on the exact #1049 head +are recorded as implementation evidence. + +At the normal rc-to-`main` release, the release owner may make the final +documented check contexts required only after confirming that each context has +reported successfully from the expected GitHub App on the released commit. +Any branch-protection change is an external release operation, not an +individual implementation PR and not part of #1049 completion. Merge-queue +support remains out of scope; enabling a queue with new required-check +semantics requires its own future design. + +### Evidence and completion + +The repository evidence ledger is the system of record for #1049. It records +the exact baseline and package SHAs, commands, generated no-diff proofs, +hosted check URLs, smoke results, typed exceptions, and follow-up issues. +Fields for live default-branch behavior are explicitly labeled +`release-pending`; they are never filled with local substitutes. + +This refresh is implementation-complete when: + +- every WP1-WP8 repository package is committed and pushed to #1049; +- the final #1049 head still satisfies the exact rc-baseline contract; +- all required local and hosted checks for that head are green; +- generated output is byte-stable on a second run; +- the worktree is clean; +- all follow-up issues have exact URLs or deduplicated dispositions; and +- the release runbook contains the post-`main` Pages, scheduled-link, + dependency-graph, and optional branch-protection verification steps. + +Merging #1049, releasing rc to `main`, changing repository protection, and +observing the first default-branch schedule are external release operations. +They are not falsely reported as completed repository work. This distinction +keeps the single PR executable without weakening the evidence standard. + +Platform semantics are revalidated when the final workflows are written: +GitHub scheduled workflows use the default-branch version; pull-request jobs +receive read-only permissions unless explicitly narrowed otherwise; +dependency submission requires `contents: write`; and default-branch +Dependabot configuration becomes active only after release. The implementation +uses GitHub primary documentation as the source for those assertions. ## Work packages ### WP1: Publishing and policy hygiene - `srcExclude` + onboarding move/scrub + Guide landing page + nav edits - (contents fixed in Delivery shape); the containment PR to `main` ships - first and its post-deploy smoke asserts: excluded URLs 404; site root, - the Guide landing page, and one reference page return 200 with expected - content. + (contents fixed in Delivery shape) land directly in #1049. The package + asserts the exact excluded and required artifact sets from a clean build. - `business-use-cases.md` gains its source-level unverified banner (asserted by WP1 acceptance). - Delete `docs/public/CNAME` and re-smoke the project URLs. The rejected custom-domain branch would require `base: '/'`, Pages/DNS/TLS setup, canonical+asset smokes, and a - project-owned-public-domain allowlist classification. Because only - `main` deploys, an rc-only edit never reaches the live site: the - resolved disposition ships as its own `main`-target PR (delivery graph - item (d); the containment PR never carries it). The custom-domain - branch also inventories hard-coded Pages URLs (e.g. `README.md:11`) - rather than testing only canonical and asset responses. The selected - deletion plus its live smoke is a completion gate for this refresh. + project-owned-public-domain allowlist classification. The custom-domain + branch also inventories hard-coded Pages URLs (e.g. `README.md:11`) rather + than testing only canonical and asset responses. #1049 proves project-path + output locally and in CI; the live project-URL smoke is release-pending + until the rc changes reach `main`. - `fastly.toml`: empty `authors` list; label the key fixtures consistently as local test fixtures; comment the four KV stores; remove the `test-prebid-eids.sh` comment. `service_id` stays under its @@ -766,12 +458,12 @@ and [default-branch-only security-update PRs](https://docs.github.com/en/code-se `AGENTS.md` gets a generated gate region (it is the fallback for agents that cannot read `CLAUDE.md`, so it carries the list). -Acceptance: `vitepress build` output contains none of the excluded pages; -the `main` containment PR is merged and its smoke passes; the CNAME deletion -PR (d) is merged with its project-URL live smoke and its own -audited_main_tip assertion; the banner is -present; no internal contacts or access instructions anywhere in the repo; -every command file links to (not copies) the canonical gates. +Acceptance: a clean `vitepress build` contains none of the excluded pages and +contains the required Home, Guide, and reference artifacts with expected +content; the project-path base is preserved; the CNAME is absent; the banner +is present; no internal contacts or access instructions remain anywhere in +the repository; and every command file links to, rather than copies, the +canonical gates. The live Pages and CNAME smoke is explicitly release-pending. ### WP2: Truth pass over existing content @@ -1145,321 +837,208 @@ Acceptance: worklist complete; matrix builds warning-free with `RUSTDOCFLAGS="-D warnings"`; the WP8 jsdoc lint (mandatory, scoped as above) green. -### WP8: Enforcement (WP8a scaffolding early, WP8b activation last) - -WP8a (lands right after WP1): - -- `tools/docs-parity` (outside the workspace, own committed lockfile, - Dependabot cargo entry, nested-lockfile cache key, own README, host - fmt/clippy/test): the Serde-aware AST extractor (field and - container/variant attributes including `rename_all`/`tag`/`content`/ - `untagged`, fail-closed on unknown shape-changing attributes, companion - manifest for custom deserializers, validators, and nonliteral - defaults), generated-region generator (deterministic ordering, no-write - check mode, atomic updates), the Cloudflare builder parser (fail-closed - grammar above), CLI help goldens (both platforms, merged - platform-annotated union, description override table with owner/ - rationale/expiry/source-text staleness check), snippet-manifest tooling - (all languages; graded modes incl. `expected_compile_failure` and - `expected_validation_failure`, each carrying a REQUIRED stable - diagnostic matcher - error class/path or message pattern - and - expected phase, so a typo, missing dependency, wrong working - directory, or unrelated parser error cannot satisfy the gate, with a - fixture proving that a correct nonzero exit with the WRONG diagnostic - fails, and a negative example failing CI when it unexpectedly becomes - valid; `illustrative_fragment` entries carry owner, rationale, and - expiry like manual waivers, not a permanent escape hatch; expiring - waivers), the domain/credential scanner + typed allowlist (with a - defined input contract: text-vs-binary classification comes from path - plus a checked classification manifest, never content sniffing alone - - a tracked file the manifest does not classify FAILS the scan (so a new - binary cannot appear unreviewed, and text renamed under an unknown - extension cannot dodge content inspection); an expected-text file that - is oversized or invalid UTF-8 FAILS rather than being skipped - (thresholds must not become evasion mechanisms); classified binaries - get an ASCII/UTF-8 strings scan, or a reviewed hash-pinned manifest - entry where strings scanning is meaningless; generated lockfiles are - not blanket-excluded - their structured URL/source/registry fields are - scanned while checksum and integrity strings are recognized - structurally; known media assets get metadata-string inspection; - base64 test fixtures distinguish via the hash-pinned fixture allowlist - class - results must be deterministic across platforms. Every detector - and encoding class (domain, email, credential shape, service ID, - encoded token, binary strings) gets both a synthesized positive - fixture and an allowlisted fixture proving the allowlist path. The - scanner also carries a reviewed identifier denylist class - known - organization names, personal handles, chat channels, project IDs, and - access-instruction phrases - seeded from what WP2's scrubbing removes, - because the pattern detectors alone cannot catch policy-relevant - content like internal handles once common hosts are allowlisted. The - stated guarantee is honest about the residual: semantic sensitivity - beyond the detector and denylist classes remains a human review - control exercised through the WP2 disposition process, not a scanner - promise), the maintained-source - manifest checker, and the gate manifest. -- Author `.github/workflows/docs-links.yml` and its static security - fixtures here, before (c): the link/issue jobs are present but - unreachable without `schedule`; those dormant jobs already carry their - final permissions, conditions, 30/5-minute timeouts, fixed schedule/refresh - concurrency group, artifact schema, and pinned action references. The - base-controlled PR validator and attestation are live, including the - maintenance operation that remains fail-closed until the normal post-(e) - steady-state predicate is true, and the dependency-snapshot jobs are defined as - the reviewed c2 patch template rather than active in (c), paired with - a reviewed reverse-c2 rollback fixture. PR (c) copies - this validation-only controller to `main`; WP8b may tighten fixtures - but cannot introduce a new c2/e trust model after the controller has - already landed. PR (c) records the hash of the complete validation-only - workflow. From that point through c2, all non-activation bytes - including - the controller, attestation, and unreachable link/issue job definitions - - are frozen. WP8b may change only fixtures and the reviewed c2 activation - regions. A controller defect requires a separately reviewed repair PR and - new hash before c2; it cannot ride inside the activation delta. -- The example harness, in eight explicit phases (secret resolution - alone cannot make the template valid: placeholder rejection also - covers the non-secret publisher fields `domain`, `cookie_domain`, and - `origin_url`): (1) parse the unchanged source template; (2) assert it - FAILS deploy validation for exactly the expected placeholder set; - (3) apply deterministic non-secret customization in memory, never by - editing the template; (4) assert the customized config passes deploy - validation with secret key names intact; (5) serialize and verify the - blob envelope; (6) resolve through a fake secret store; (7) assert - runtime validation passes post-resolution; (8) run each optional - block and profile through isolated positive and negative probes. - Enumerate every `[integrations.*]` - subtree (grouped by first-segment ID, nested tables in their parent) - and every `[auction.providers.]` entry - commented or active, - enabled or disabled - deserializing into the typed structs with - ignored-key detection, running `Validate::validate`, the profile - compilers, and the deploy/startup checks via per-ID isolated `Settings` - fixtures with the integration/provider forced enabled (the runtime path - skips disabled blocks); valid and invalid compiled probes per profile. -- Inventory equality tests live where visibility allows: module-local - `#[cfg(test)]` tests inside core assert set equality for the private - registries (builders, plan registrations, profile registry, mediator, - JS module sets - replacing the deploy-ID constant's one-directional - subset check) against the same checked records the tool renders from. - Capability parity is behavioral, not ID-level: the tests instantiate - each integration under the predicate fixture matrix and inspect the - resulting registration state (proxy routes, rewriters, injectors, - post-processors, request filters, JS mode), comparing observed - capabilities against the record - so a record claiming a capability - after its `with_*` call changes fails. APS plan-dependent registration - and the DataDome filter are asserted in both predicate states. In the - capability and adapter-support records, executable columns (fan-out, - routes, status behavior) are test-backed; "operational support" and - "release status" are manually owned fields with a named owner and - review date, never implied to be code-derived. - -WP8b (lands last): - -- Wire everything as blocking CI: rustdoc matrix, native doctests (with - pinned Node), generated-region clean-diff, snippet manifest, scanner, - jsdoc lint, gate-manifest check across every surface in its mode, - repo/orphan/tombstone inventory, disposition-set equality, - maintained-source manifest equality. -- Link enforcement per set: VitePress covers only the built public set - (and after `srcExclude`, nothing internal), so `docs-parity` adds a - repository-relative path and anchor check over the active repo and - maintained internal sets; the scheduled external-link check's input - is all three active sets; each set gets its own synthesized dead-link - negative fixture. External checking sends no credentials, follows at most - five redirects and validates the final HTTPS/expected status, falls back - from unsupported HEAD to GET, and makes at most three total attempts for - 429/5xx responses with 1-second then 2-second exponential delays. - `Retry-After` is honored only when it parses to at most 30 seconds; larger or - malformed values use the bounded local delay. An exact-URL/path exception - needs reason, owner, - and expiry; blanket domain skips are forbidden, expiry fails the gate, - and allowlisted/expired/redirect-loop fixtures exercise each path. -- Complete the remaining workflow edits and lock their static assertions: - CodeQL `rc/*` PR triggers; `.tool-versions` in - deploy-docs paths; normalized setup-node cache keys (all workflows, to - lockfiles); Dependabot roots (github-actions, browser and Next.js - fixture npm, docs-parity cargo); Wrangler pinned in `.tool-versions` - (the same pin consumed locally and in CI); the scheduled external link - check already defined at `.github/workflows/docs-links.yml`, using a - checker pinned by action SHA (versions chosen from - current stable at implementation and recorded where pinned; - split read-only checker / no-checkout `issues: write` reporter, dedup, - auto-close, named owner, - fixture-tested); its base-controlled `pull_request_target` automation - delta gate, fail-closed dispatch modes, status-only attestation job, - and temporary split dependency-snapshot generator/writer described in - Delivery shape; - `[lints] workspace = true` for openrtb-codegen. Where a governance - value can be asserted deterministically (a YAML-parsing static test - over workflow triggers, cache keys, Dependabot roots), it is; the - remainder is review-time evidence explicitly listed in the PR - description. -- `CLAUDE.md`/`AGENTS.md`/`TESTING.md`/`docs/guide/testing.md` gate - regions regenerated from the manifest in the same commit. - -Merge-blocking is a repository setting, not a workflow property: a -failing check blocks merges only when the ruleset/branch protection -requires it. WP8b therefore carries an externally owned, PATH-SPECIFIC -acceptance item. (i) Epoch 1 - the full new WP8 checks are required on -`rc/202608`, with one demonstrated failure actually preventing an rc -merge. After (c) merges, first inventory and seed/retrigger the current -head of every already-open `main` PR; then require the fixed -`docs/automation-delta` status for every `main` PR and enable -strict/up-to-date enforcement. Acceptance demonstrates both an -unauthorized protected-file delta being blocked and an unrelated PR -receiving success without a privileged PR checkout. Deferral applies -only to the full WP8 suite on `main`, whose tool does not exist there -yet. (ii) Normal Epoch 3, after the release merge puts the tooling on -`main` - the full checks are activated there, with a separate -demonstrated `main` block; the automation-delta status remains required and -its current-main `validate_main_maintenance` path is proven with one allowed -repin fixture and one AST-weakening rejection before lifecycle closure. -(iii) Abandonment - full-suite activation is INAPPLICABLE; after (e) -merges and removes its workflow, the automation-delta required context -is removed from branch protection before any later `main` PR can merge, -with evidence that no required context referencing a nonexistent -workflow remains. Every path records the required context names and -their GitHub App and bypass policy. This refresh does not implement a -`merge_group` attestation: a merge-group status would need to bind every -constituent PR's authenticated head/base/tool tuple, and reusing the ordinary -shape check would create a bypass. Before (c), record and enforce that merge -queues are disabled for `main`, as approved by `aram356` on 2026-08-31. If they -are active, stop until they are disabled for this lifecycle or a separate -reviewed design adds that binding to this spec, plan, and fixtures. Queue -adoption stays blocked through (e). Static workflow tests -reject an unexpected `merge_group` trigger, and branch-protection acceptance -records the queue setting alongside required contexts. - -Acceptance: the workflow suite has positive fixtures for an ordinary -net-empty protected delta, a divergent-history/net-identical rc release PR, -c2, both e paths, rollback-c2, same-lifecycle repair/sync, and post-handoff -maintenance, plus negative fixtures for a repair that changes lifecycle state, -pre-handoff maintenance, and an unexpected `merge_group` trigger. -Every runtime -gate also has a synthesized negative fixture (dead -link, broken intra-doc link, failing doctest, invalid or unknown-keyed -example block including a disabled integration table and a bad -`profile_config`, planted non-allowlisted domain, unclassified or -expired-waiver snippet fence, missing scoped JSDoc, inventory change -without regenerated region including a macOS-only CLI divergence, missing -README or unlisted orphan, gate mismatch in any manifest surface, removed -ownership marker, stale PR base, extra automation-delta path, malformed -or mixed dispatch modes, open-PR tool SHA supplied to `validate_main_pr`, -non-`main` or stale-`main` manual controller dispatch, caller-selected -maintenance tool, maintenance AST weakening, protected candidate -receiving pending (never automatic success), -stale snapshot artifact or malformed refresh operation reaching the writer, -validation failure still producing a failing -attestation, privileged job attempting a checkout or PR-code execution); -the static workflow assertions pass; regenerating all -regions and goldens at final HEAD produces no diff. +### WP8: Enforcement (WP8a scaffolding early, WP8b final wiring) + +WP8a lands after WP1 and creates the checked foundation in #1049: + +- `tools/docs-parity` is a standalone Cargo workspace with its own committed + lockfile, README, host fmt/clippy/test commands, workspace lint policy, and + `error-stack` error flow. It is not a root-workspace member. +- Repository enumeration begins with `git ls-files -z`. A checked manifest + classifies every tracked path as text or binary and gives every expected + text file a whole-file or extracted-comment disposition. Unknown paths, + unknown comment grammars, unsafe modes, symlink escapes, oversized expected + text, and invalid UTF-8 fail closed. +- The scanner covers domain, email, credential shape, service ID, encoded + token, checked binary strings, media metadata, retired identifiers, and + structured lockfile URL/source/registry fields. Typed exceptions require a + narrow class, owner, rationale, content fingerprint, and expiry. The + `fastly.toml` service-ID entry expires at + `2026-09-30T00:00:00Z`; check mode fails at or after that instant. + Semantic sensitivity outside detector classes remains a human disposition, + not a scanner guarantee. +- The Serde-aware settings extractor handles field, container, and variant + attributes including `rename`, `rename_all`, `alias`, `tag`, `content`, + `untagged`, `flatten`, `skip`, and `skip_serializing`. Unknown + shape-changing attributes fail closed. Checked companions cover custom + deserializers, nonliteral defaults, and validators and are proved by + compiled positive and negative probes. +- Generated regions use named markers, deterministic ordering, atomic writes, + ownership markers for adjacent manual prose, a no-write check mode, and a + second-run byte-stability assertion. +- Markdown checks cover relative files and anchors over all active sets, + duplicate headings, percent-encoded fragments, intended pages/navigation, + excluded-page links, tombstones, orphans, and diagram prose equivalents. +- Integration, adapter-support, route, settings, CLI-help, README, gate, and + snippet records are checked as sets. Private registries use module-local + test seams rather than new public APIs. The Cloudflare route parser has a + closed grammar and fails on unknown builder constructs. +- CLI help is captured from the same authenticated source commit on native + Linux and native macOS. Raw runner identity, `uname -a`, `rustc -vV`, Node + version, source SHA, and SHA-256 are recorded. Platform-only commands are + annotated; prose overrides require owner, rationale, expiry, and a source + fingerprint. +- Every Markdown fence has a checked mode: executable, + expected compile/validation failure with phase and stable diagnostic, or + illustrative fragment with an expiring waiver. A nonzero exit with the + wrong diagnostic fails. +- The example-template harness performs all eight phases: unchanged parse, + exact placeholder failure, deterministic in-memory non-secret + customization, deploy validation with secret names intact, envelope + serialization, fake-store resolution, runtime validation, and isolated + positive/negative probes for every optional integration and provider + profile. + +WP8b lands last in the same PR and wires the final state: + +- Blocking PR jobs run standalone docs-parity fmt/clippy/test/check, generated + clean-diff, settings/examples/inventory/snippets/scanner/local links, + README/JSDoc/workflow fixtures, the rustdoc matrix, native doctests, docs + lint/format/build, and the existing target-specific Rust and JS gates. +- CodeQL covers `rc/*` pull requests. Node is pinned wherever Rust doc/build + paths invoke JS. Setup-node cache inputs name exact lockfiles, including the + standalone tool lockfile. `.tool-versions` pins Wrangler, and workflows + consume that pin rather than global latest. +- Dependabot covers GitHub Actions, browser/Next.js fixture npm roots, docs + npm, the root Cargo workspace, and `tools/docs-parity` Cargo. Its committed + final target is `main`; no temporary rc target is introduced. +- `.github/workflows/docs-links.yml` contains ordinary read-only + `pull_request` validation plus the final default-branch schedule and closed + manual refresh operation. It contains no `pull_request_target`, + `merge_group`, status writer, caller-selected executable SHA, or PR-code + execution in a privileged job. +- Every new `uses` reference is pinned by full SHA with its source version + recorded. Workflow-policy fixtures parse YAML and reject unpinned actions, + expanded permissions, unsafe events, checkout/cache/service-container use + in writers, caller-selected tools, unbounded artifacts, unexpected paths or + modes, symlinks, archive traversal, and unknown schema fields. +- The scheduled external-link reader uses `contents: read`, a fixed + non-canceling concurrency group, and a 30-minute timeout. It follows at most + five redirects, validates final HTTPS/status, falls back from unsupported + HEAD to GET, and makes at most three attempts for 429/5xx with 1-second then + 2-second delays. `Retry-After` is honored only when valid and at most 30 + seconds. Exact-URL exceptions require owner, reason, and expiry. +- The no-checkout issue writer has only `issues: write` and a 5-minute timeout. + Its archive has exactly one regular `link-results.json` member, at most + 2 MiB compressed and 1 MiB decoded, with at most 500 findings and + 2,048-byte strings. It validates before writing, deduplicates one owned + issue, and auto-closes it after a clean run. +- The dependency-snapshot reader uses `contents: read` and produces exactly + one regular `dependency-snapshot.json` member, at most 4 MiB compressed and + 2 MiB decoded, with at most 5,000 records and 2,048-byte strings. The + no-checkout writer has only `contents: write`, revalidates the schema and + authenticated default-branch SHA, and submits the fixed + detector/correlator identity. The manual refresh accepts no SHA or PR input. +- `CLAUDE.md`, `AGENTS.md`, `TESTING.md`, and + `docs/guide/testing.md` gate regions are generated from one checked manifest. + Command files, CONTRIBUTING, and the PR template are link-only consumers. + +The scheduled and writer paths cannot execute from #1049 merely because they +are present on rc: GitHub uses the default-branch workflow for schedules and +manual availability. Their repository acceptance is static policy coverage, +schema/parser tests, deterministic artifact fixtures, and final-state config +inspection. The first real schedule, issue reconciliation, dependency +submission, and graph proof are release-pending. + +Acceptance requires positive fixtures for the ordinary read-only PR path, +scheduled link path, clean and finding-bearing issue reconciliation, +dependency generation/submission, and manual refresh. It requires one negative +fixture per trust or bounds class, including unexpected privileged events, +status writes, caller-selected tools, untrusted checkout, stale source SHA, +extra archive members, traversal, symlink/unsafe mode, unknown fields, +oversized artifacts/strings/record counts, malformed `Retry-After`, redirect +loops, retry exhaustion, credentials embedded in a URL, expired exceptions, +wrong snippet diagnostics, generated drift, missing ownership markers, and a +new unclassified tracked path. + +All real repository checks pass from a fully staged candidate, a second +generation produces no diff, the root `Cargo.lock` remains unchanged by the +standalone tool, and the final #1049 hosted check list records exact run URLs +and GitHub App identities. Main branch-protection activation and the first +scheduled receipts remain release operations under Delivery shape. ## Sequencing -| Order | Package | Size | Depends on | -| ----- | ----------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- | -| 0 | WP1 containment subset → `main` PR (b) | XS | - | -| 0c | CNAME deletion PR (d) | XS | (b) merged | -| 1 | WP1 hygiene (full, rc PR) | S | (b) and (d) merged; import their exact publishing deltas | -| 2 | WP8a scaffolding | L | - | -| 2b | `main` automation PR (c), validation-only | S | WP8a (workflow content exists); rc dispatch + base-controlled `main` automation-delta gate | -| 3 | WP2 truth pass | M | WP8a (manifest, scanner) | -| 4 | WP3 config reference | L | WP8a (extractor, harness) | -| 5 | WP4 API reference | M | WP2, WP8a | -| 6 | WP5 pages + nav | L | WP2, WP3, WP8a | -| 7 | WP6 root + READMEs | M | WP2 | -| 8 | WP7 in-code docs | M | - | -| 9 | WP8b gate activation | M | WP2-WP7 | -| 9b | Post-merge activation PR (c2) → `main` | S | #1049 merged into rc; audited tip; dual-ref binding; scheduled run + first dependency snapshot | -| 10 | Release handoff PR (e) → `main` | XS | gates rc deletion; removes temporary snapshot jobs; issue + two reviewed runbooks | - -Orders 0-9 are Epoch 1 (this refresh reaches implementation-ready once -they land). 9b is Epoch 2 and closes this refresh at activated. 10 is Epoch 3, -owned and specified here but executed at release. +Every row is a package checkpoint on `spec-docs-refresh` and PR #1049. + +| Order | Package | Size | Depends on | +| ----- | ------------------------------------------- | ---- | ----------------------------------------------- | +| 0 | Single-PR design, plan, decisions, evidence | S | Owner approval | +| 1 | WP1 containment, CNAME, and hygiene | S | 0; transfer reviewed containment commits | +| 2 | WP8a docs-parity foundation | L | 1 | +| 3 | WP2 truth pass | M | 2 classification, scanner, snippets, page model | +| 4 | WP3 configuration reference | L | 2 settings extractor and example harness | +| 5 | WP4 API reference | M | 2 route/integration records; 3 truth pass | +| 6 | WP5 deployment and product coverage | L | 3-5 | +| 7 | WP6 root and crate documentation | M | 3 | +| 8 | WP7 rustdoc and JSDoc | M | 2 inventory/fixtures | +| 9 | WP8b final CI and scheduled automation | M | 2-8 | +| 10 | Final #1049 acceptance | S | 1-9 | + +No row waits for an individual `main` PR, imports a moving default-branch +blob, or creates a second implementation branch. Packages remain separate +commits so review and evidence can be resumed without squashing unrelated +surfaces. ## Verification -Verification runs in three epochs, because a single state cannot hold -both "target still equals the audited baseline" and "#1049 has merged": - -**Epoch 1 - pre-merge, at the final rc-PR HEAD** (`origin/rc/202608` -still equals audited_target_tip -`07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`): all GitHub checks green (CodeQL with rc -triggers, format including the docs build, the seven test.yml jobs, the -four integration-test jobs, release builds, JS build/test); the WP8 -parity suite and negative fixtures green; regeneration produces no diff; -`cd docs && npm run lint && npm run format && npm run build`; the rustdoc -matrix locally; acceptance greps over the defined sets with output in the -PR description; the four adapter first-success smokes (Axum env bridge, -Fastly local push + secrets, Cloudflare envelope transfer, Spin local -push + variables) executed as documented with commands and cleanup -recorded; the `main` containment PR merged with its positive smoke, the -`main` automation PR merged with a successful read-only validation -dispatch against the rc PR head and validated Dependabot config; its -fixed status then seeded/retriggered for every already-open `main` PR and -made required for every `main` PR with strict/up-to-date enforcement, -with both the unauthorized-protected-delta block and unrelated-PR pass -demonstrated; and the CNAME deletion PR (d) merged with its project-URL live -smoke (each `main` PR's audited_main_tip assertion having passed); every -follow-up filed with a recorded URL or disposition; the release-handoff work -(e) existing as a tracked issue -plus two reviewed runbooks (retarget / disable-and-remove), with its -owner and both path conditions recorded and branch deletion documented -as gated by (e); and the exact-tip baseline assertion for -`origin/rc/202608` (equal to the recorded audited_target_tip, contained -in the branch) passing at this HEAD - not a merge-base comparison. - -**Epoch 2 - after #1049 merges into rc**: record `merged_rc_tip` as the -exact merge result of #1049 and initialize `validated_rc_tip` to it; -require that value to equal `origin/rc/202608` (if rc has advanced at -all, audit the full delta and replace `validated_rc_tip` with the new -exact tip; reachability alone does not pass); dispatch that exact SHA as -`tool_sha` through the read-only validation job and record the result; -open and merge the c2 activation PR to `main` (its own -audited_main_tip; dual-checkout validation - trusted tool from the -exact `validated_rc_tip`, files under test from the c2 head - covering the actual -changed workflow and Dependabot file, including the temporary snapshot -jobs - and a just-before-merge manual success replacing the automatic -pending status on that exact head/base); immediately dispatch -`refresh_dependency_snapshot` and record the first snapshot, then observe -one successful real scheduled run, including the -schedule-only issue job and a dependency snapshot whose rc ref, exact -source SHA, external correlator/ID, and dependency-graph visibility are -recorded (the scheduled submission reconciles the same stable identity); -record the alert-triage owner, runbook URL, and -two-business-day rc patch SLA as well. Because the receipts exist only after -c2 merges and c2's repository diff is exactly two protected files, the -canonical Epoch 2 record is the named c2 tracking issue using the durable -capture contract above, with links to the c2 PR, workflow runs, status, -snapshot receipt, and dependency-graph evidence. -The Epoch 1 repository ledger contains the issue URL and required evidence -schema; no post-merge evidence-only branch or unmodeled rc commit is created. - -**Epoch 3 - at release, before rc deletion**: verify rc landed on -`main` (or that it is being abandoned). On the normal release PR, first prove -the current-main base and rc head have net-identical modes/blobs for both -protected paths so the required automation context succeeds without treating -their divergent history as a new delta; otherwise use the separate repair/sync -path before the release merge. Then refresh `validated_rc_tip` to -the exact current rc tip after auditing any delta and freeze all rc -writes through branch deletion; open the concrete (e) PR from -the matching runbook, validated the same dual-checkout way against its -real diff, with the manual status successful on its current head/base; -merge it; verify that the temporary dependency-snapshot jobs -are gone; submit the empty snapshot at the exact (e) merge SHA using the -stable temporary identity and record the 201 receipt; verify the stale rc -submission disappeared (and, on normal release, that automatic parsing -now exposes the `main` manifest); re-verify the retargeted (or removed) -schedule and Dependabot roots, live Pages containment, and the selected CNAME -behavior; only then is rc deleted. If the release merge happened, -`main` now carries the tooling, so the deferred full-suite required-check -activation on `main` is completed here per the WP8b protection item, and the -post-handoff current-main maintenance mode is proven without weakening any AST -invariant. If -rc was abandoned, remove the now-nonreporting automation-delta required -context immediately after (e) and verify the next `main` PR is not -stranded. Post-merge Epoch 3 receipts live in the named release-handoff issue -under the same durable capture contract and link to PR (e), workflow runs, -retirement response, graph verification, ruleset changes, and branch deletion; -they do not require a follow-on repository evidence PR. +### PR #1049 implementation acceptance + +At the final PR head, `origin/rc/202608` must still equal +`07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf` and the branch must contain that +commit. Reachability without exact equality does not pass. + +Acceptance records and verifies: + +- all hosted checks on the exact final head, including CodeQL with rc triggers, + format/docs build, every target-specific Rust test/check job, integration + tests, release builds, JS lint/test/build, docs-parity, rustdoc, and + doctests; +- standalone docs-parity fmt, clippy, full tests, `check --all`, and generated + second-run no-diff; +- docs lint, format, build, exact excluded/included artifacts, local and anchor + links, navigation/page/orphan/tombstone equality, and project-path asset + behavior with CNAME absent; +- source-classification closure and the all-tracked privacy/sensitive scan; +- settings, routes, integrations, adapter support, CLI goldens, snippets, + gates, README, JSDoc, and workflow-policy equality; +- the complete rustdoc matrix with warnings denied and native core doctests; +- JS lint, formatting, Vitest, and build; +- Axum, Fastly, Cloudflare, and Spin first-success smokes, or a named, + time-bounded manual Spin receipt only when the runner cannot execute Spin; +- every follow-up issue URL or exact existing-issue disposition; +- exact package commit/path shape, no unrelated runtime behavior change, + `git diff --check`, and a clean worktree; and +- the final PR description with immutable run URLs, app identities, current + head/base SHAs, and clearly labeled release-pending operations. + +The VitePress proof is not described as a live deployment. Scheduled workflow +fixtures are not described as a real cron run. Dependency artifact fixtures +are not described as a submitted graph. Main protection is not described as +changed. + +### Release-pending verification + +After the normal release makes the #1049 result part of `main`, the release +owner follows the committed runbook and records: + +1. the deployed `main` SHA and Pages response matrix: excluded URLs are absent, + required URLs return expected content, assets use the project path, and the + placeholder/custom CNAME is absent; +2. one real scheduled external-link run, including bounded artifact validation, + deduplicated issue behavior, concurrency, and timeout results; +3. one dependency snapshot submission from the exact authenticated `main` SHA, + its 201 receipt, fixed detector/correlator, graph visibility, and triage + owner/SLA; +4. final Dependabot roots and workflow action pins as observed on `main`; and +5. only if maintainers choose to require the new checks, the exact context + names, source GitHub Apps, strictness, bypass policy, and one planted-failure + block after all contexts have reported on `main`. + +These are release operations, not additional implementation PRs and not +conditions for claiming the repository work in #1049 complete. Any failed +release-pending check opens a focused repair through the normal repository +process; it does not retroactively authorize fabricated Epoch evidence. ## Owner decisions and remaining non-blocking questions @@ -1486,12 +1065,12 @@ they do not require a follow-on repository evidence PR. approved on 2026-08-31: correct `ProjectGovernance.md` to current evidence (no minutes exist; releases are not continuous) without adding CODEOWNERS or minutes commitments. Naming owners remains a maintainer follow-up. -7. Delivery shape: `aram356` approved on 2026-08-31 all five PRs through - activation, the temporary `main` `docs/automation-delta` required-status - and strict/up-to-date protection change, merge queues disabled on `main` - through (e), and the external dependency-snapshot retirement API call under - the specified runbook and control scheme. The rollback requirements in - Delivery shape remain mandatory. +7. Delivery shape: superseded on 2026-08-31 by `aram356`. All repository work + is committed to `spec-docs-refresh` and PR #1049; no individual containment, + CNAME, controller, activation, or release-handoff implementation PR is + created. PR #1049 remains targeted at `rc/202608`. Live Pages, scheduled + workflow, dependency-graph, and optional `main` protection verification are + release-pending operations under Delivery shape, not implementation claims. 8. CodeQL `push` coverage for `rc/*`: explicitly non-blocking. ## Follow-up issues to file (code, not docs) From 54cb66e348900fd30309093766831e64725dc76a Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:35:52 -0700 Subject: [PATCH 32/33] Remove obsolete documentation epoch terminology --- .../specs/2026-08-19-documentation-refresh-design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md index e214de56d..15dc5d7b1 100644 --- a/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md +++ b/docs/superpowers/specs/2026-08-19-documentation-refresh-design.md @@ -1038,7 +1038,7 @@ owner follows the committed runbook and records: These are release operations, not additional implementation PRs and not conditions for claiming the repository work in #1049 complete. Any failed release-pending check opens a focused repair through the normal repository -process; it does not retroactively authorize fabricated Epoch evidence. +process; it does not retroactively authorize fabricated release evidence. ## Owner decisions and remaining non-blocking questions From 01bf84a4beb4a1be4f26965478a0211f59392962 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 00:29:32 -0700 Subject: [PATCH 33/33] Rewrite documentation refresh plan for one PR --- .../plans/2026-08-30-documentation-refresh.md | 1128 ++++------------- 1 file changed, 259 insertions(+), 869 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-documentation-refresh.md b/docs/superpowers/plans/2026-08-30-documentation-refresh.md index d56f22b76..d7c66f7ab 100644 --- a/docs/superpowers/plans/2026-08-30-documentation-refresh.md +++ b/docs/superpowers/plans/2026-08-30-documentation-refresh.md @@ -2,339 +2,174 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** Refresh every maintained documentation surface, make reader-facing inventories derive from checked records, and activate enforcement without exposing write credentials to pull-request-controlled code. +**Goal:** Refresh every maintained documentation surface, derive reader-facing inventories from checked records, and commit all implementation work to PR #1049 without exposing write credentials to pull-request-controlled code. -**Architecture:** One rc implementation PR supplies eight package checkpoints and the canonical `docs-parity` tooling. Four small `main` PRs contain the public site, install a base-controlled validation controller, resolve CNAME, and activate scheduled automation after the rc merge; a separately owned release handoff closes the temporary branch lifecycle. Checked manifests connect code inventories, generated Markdown, source classification, examples, and CI so each fact has one source of truth. +**Architecture:** The existing `spec-docs-refresh` branch and PR #1049 are the only implementation branch and PR. Reviewable package commits build one standalone `docs-parity` tool and final-state documentation automation; Pages, schedules, dependency submission, and optional `main` protection effects that cannot run from rc are recorded as release-pending rather than represented by auxiliary PRs. **Tech Stack:** Rust 1.95 (`syn`, Serde, `error-stack`, Cargo), VitePress/Node 24, ESLint/JSDoc, GitHub Actions and REST APIs, shell smoke scripts, Fastly Viceroy, Wrangler, Spin, Axum -**Revised:** 2026-08-31 after full spec/plan review (round 21) +**Revised:** 2026-08-31 for the approved single-PR delivery model --- ## Execution gate -**Gate status:** Satisfied by `aram356` on 2026-08-31. The approval covers the -five-PR-through-activation delivery shape, temporary `main` -`docs/automation-delta` required-status and strict/up-to-date protection -change, merge queues disabled on `main` through PR (e), and the external -dependency-snapshot retirement call under the specified runbook and controls. -Task 2 may begin only after Task 1 commits this approval. - -The narrower owner gates are also resolved: - -- `aram356` owns the temporary `fastly.toml` `service_id` allowlist exception; - it expires at `2026-09-30T00:00:00Z`, and check mode fails at or after that - instant. Renewal requires a reviewed, committed replacement before expiry. - This is not the ops migration deadline. -- Task 3 deletes `docs/public/CNAME` and retains the project-path base. PR (d) - must merge before Task 4 imports the live publishing deltas into rc. -- Task 12 archives `FAQ_POC.md` at - `docs/superpowers/archive/FAQ_POC.md`. -- Task 17 uses the factual-governance fallback; no governance owner was named. -- Questions 5 and 8 remain explicitly non-blocking; question 4 remains closed. +- Work only in `/Users/ag/projects/iab/trusted-server/.claude/worktrees/spec-docs-refresh` on branch `spec-docs-refresh`. +- Push implementation commits only to PR #1049. Do not create containment, CNAME, controller, activation, or release-handoff implementation PRs. +- PR #1104 is closed. Its source branch remains only long enough to transfer reviewed commits `34b0613dc603ba6529396dad4dd4b7e68b1e11a9` and `e6554f24f58f6122fb806ce25432f66033765c65`. +- Before every package, fetch `origin/rc/202608` and require it to equal `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`; require the implementation branch to contain that commit. Any target advance stops execution for a full delta audit and spec/plan re-review. +- Owner decisions are fixed: delete CNAME, archive `FAQ_POC.md`, use the factual-governance fallback, and expire the Fastly service-ID exception at `2026-09-30T00:00:00Z`. +- Live Pages, real scheduled runs, dependency submission, graph visibility, and optional `main` protection changes are release-pending. Never substitute local output for those receipts or create another PR to obtain them. ## File map -### Program records and release controls - -| File | Responsibility | -| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| `docs/superpowers/specs/2026-08-19-documentation-refresh-design.md` | Approved design, immutable baseline contract, owner decisions, and epoch definitions. | -| `docs/superpowers/plans/2026-08-30-documentation-refresh.md` | This execution plan and package checkpoints. | -| `docs/internal/audits/documentation-refresh-decisions.md` | Owners, dates, selected open-question branches, audited tips, ruleset snapshot, and PR URLs. | -| `docs/internal/audits/documentation-refresh-inventory.toml` | Per-file or per-region WP2 dispositions and source anchors. | -| `docs/internal/audits/documentation-refresh-evidence.md` | Epoch 1 commands/proofs/smokes plus schemas and issue links for post-merge evidence. | -| `docs/internal/runbooks/documentation-automation-release.md` | Normal and abandonment release sequencing, snapshot retirement, and branch deletion gate. | -| `docs/internal/runbooks/documentation-automation-rollback.md` | Controller, c2, Pages, and CNAME rollback procedures. | -| `docs/internal/runbooks/patches/docs-links-c2.patch` | Reviewed activation delta from validation-only controller to the rc-final workflow. | -| `docs/internal/runbooks/patches/docs-links-rollback-c2.patch` | Exact inverse of c2 without overwriting unrelated base changes. | -| `docs/internal/runbooks/patches/docs-links-release-retarget.patch` | Normal release retarget/removal template. | -| `docs/internal/runbooks/patches/docs-links-release-disable.patch` | Abandonment removal template. | +### Program records + +| File | Responsibility | +| ------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| `docs/superpowers/specs/2026-08-19-documentation-refresh-design.md` | Approved single-PR design and immutable baseline. | +| `docs/superpowers/plans/2026-08-30-documentation-refresh.md` | This package-by-package execution plan. | +| `docs/internal/audits/documentation-refresh-decisions.md` | Owner decisions, exact #1049 identity, closed #1104 transfer, and release bounds. | +| `docs/internal/audits/documentation-refresh-inventory.toml` | Per-file or per-region WP2 dispositions and source anchors. | +| `docs/internal/audits/documentation-refresh-evidence.md` | Package evidence, hosted runs, smokes, issues, and release-pending schema. | +| `docs/internal/runbooks/documentation-automation-release.md` | Post-main Pages, schedule, snapshot, graph, and optional protection verification. | ### `docs-parity` crate and checked records `tools/docs-parity` is a standalone Cargo workspace with its own committed lockfile; it is not added to the repository workspace members. -| File | Responsibility | -| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| `tools/docs-parity/Cargo.toml` | Standalone binary/library metadata, `[workspace]`, dependencies, and lint policy. | -| `tools/docs-parity/Cargo.lock` | Reproducible host-tool dependency graph. | -| `tools/docs-parity/README.md` | Subcommands, manifest ownership, update/check flow, and failure semantics. | -| `tools/docs-parity/src/main.rs` | Thin CLI parsing and exit-code mapping. | -| `tools/docs-parity/src/lib.rs` | Subcommand dispatch and shared `Report` API. | -| `tools/docs-parity/src/model.rs` | Checked record schemas, ownership/expiry types, and generated-region markers. | -| `tools/docs-parity/src/repository.rs` | Repository-root discovery, tracked-file enumeration, safe paths, exact Git object reads, and atomic writes. | -| `tools/docs-parity/src/classification.rs` | Text/binary classification and exhaustive candidate/span closure. | -| `tools/docs-parity/src/scanner.rs` | Domain, email, credential, identifier, encoded-token, lockfile, binary-string, and media-metadata scanners. | -| `tools/docs-parity/src/markdown.rs` | Link/anchor parsing, fence inventory, ownership markers, orphan/tombstone checks, and generated regions. | -| `tools/docs-parity/src/settings.rs` | Serde-aware settings extractor, companion semantics, compiled probes, and template harness. | -| `tools/docs-parity/src/integrations.rs` | Integration/provider inventory and capability-record checks. | -| `tools/docs-parity/src/routes.rs` | Route record checks, Cloudflare fail-closed parser, and adapter-support rendering. | -| `tools/docs-parity/src/cli_help.rs` | Linux/macOS help capture, annotated union, overrides, and golden comparison. | -| `tools/docs-parity/src/snippets.rs` | Fence manifest, diagnostic matching, isolated execution, and waiver expiry. | -| `tools/docs-parity/src/gates.rs` | Canonical gate manifest and link-only/generated consumer checks. | -| `tools/docs-parity/src/workflow.rs` | YAML AST policy, dispatch/diff authentication fixtures, and PR-status state machine. | -| `tools/docs-parity/src/dependency_snapshot.rs` | Schema-validated Cargo dependency snapshot generation only; submission stays in the no-checkout workflow writer. | - -Checked records live under `tools/docs-parity/manifests/`: `tracked-files.toml`, `maintained-sources.toml`, `sensitive-allowlist.toml`, `retired-identifiers.toml`, `snippets.toml`, `settings-companions.toml`, `routes.toml`, `integrations.toml`, `adapter-support.toml`, `cli-overrides.toml`, `gates.toml`, `pages.toml`, `diagrams.toml`, and `orphans.toml`. CLI goldens live at `tools/docs-parity/goldens/cli-linux.txt` and `tools/docs-parity/goldens/cli-macos.txt`. Synthetic fixtures live under `tools/docs-parity/tests/fixtures/`; never add a live secret, internal contact, or real customer value as a fixture. - -### Existing surfaces with known edits - -- Publishing/policy: `docs/.vitepress/config.mts`, `docs/guide/index.md`, `docs/guide/onboarding.md`, `docs/internal/onboarding.md`, `docs/business-use-cases.md`, `docs/public/CNAME`, `docs/package.json`, `docs/package-lock.json`, `fastly.toml`, `CLAUDE.md`, `AGENTS.md`, `.github/pull_request_template.md`, and `.claude/commands/{check-ci,review-changes,test-all,test-crate,verify}.md`. -- Truth pass: the active sets defined by the spec, with named repairs in `docs/guide/{ad-serving,architecture,configuration,creative-processing,error-reference,integration-guide,roadmap}.md`, `docs/guide/integrations/{gam,kargo}.md`, `crates/trusted-server-core/src/auction/README.md`, `TESTING.md`, `FAQ_POC.md`, `CHANGELOG.md`, `.env.example`, `.env.dev`, `.claude/agents/{code-architect,issue-creator}.md`, `crates/trusted-server-openrtb/generate.sh`, and the human-facing workflow/script comments recorded in the inventory. -- Configuration/API: `trusted-server.example.toml`, `docs/guide/configuration.md`, `docs/guide/api-reference.md`, and generated/check seams in `crates/trusted-server-core/src/{config,settings,auction_config_types}.rs`, `crates/trusted-server-core/src/auction/{plan,profile}.rs`, `crates/trusted-server-core/src/integrations/*.rs`, the four adapter `src/app.rs` files, and their route tests. -- New coverage: `docs/guide/{auction-testing,axum-dev,cloudflare,edgezero,fastly,spin,telemetry,tsjs}.md`, `docs/guide/integrations/{adserver_mock,gpt,testlight}.md`, `tinybird/README.md`, `scripts/smoke-{axum,fastly,cloudflare,spin}.sh`, and `.github/workflows/integration-tests.yml`. -- README/rustdoc/JSDoc: the seven missing crate READMEs named in Task 17, their Cargo manifests, `scripts/README.md`, the WP7 Rust worklist, `crates/trusted-server-js/lib/eslint.config.js`, and the scoped TypeScript/MJS files named in the spec. +| File | Responsibility | +| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| `tools/docs-parity/Cargo.toml` | Standalone binary/library metadata, `[workspace]`, dependencies, and lint policy. | +| `tools/docs-parity/Cargo.lock` | Reproducible host-tool dependency graph. | +| `tools/docs-parity/README.md` | Subcommands, manifest ownership, update/check flow, and failure semantics. | +| `tools/docs-parity/src/main.rs` | Thin CLI parsing and exit-code mapping. | +| `tools/docs-parity/src/lib.rs` | Subcommand dispatch and shared `Report` API. | +| `tools/docs-parity/src/model.rs` | Checked schemas, ownership/expiry types, and generated markers. | +| `tools/docs-parity/src/repository.rs` | Root discovery, tracked files, safe paths, Git object reads, and atomic writes. | +| `tools/docs-parity/src/classification.rs` | Text/binary classification and exhaustive candidate/span closure. | +| `tools/docs-parity/src/scanner.rs` | Domain, email, credential, identifier, encoded-token, lockfile, binary-string, and metadata scanning. | +| `tools/docs-parity/src/markdown.rs` | Links, anchors, fences, ownership markers, orphan/tombstone checks, and generated regions. | +| `tools/docs-parity/src/settings.rs` | Serde-aware settings extraction, companions, compiled probes, and template harness. | +| `tools/docs-parity/src/integrations.rs` | Integration/provider inventory and behavioral capability checks. | +| `tools/docs-parity/src/routes.rs` | Route records, Cloudflare fail-closed parser, and adapter-support rendering. | +| `tools/docs-parity/src/cli_help.rs` | Native Linux/macOS capture, annotated union, overrides, and goldens. | +| `tools/docs-parity/src/snippets.rs` | Fence modes, diagnostics, isolated execution, and waiver expiry. | +| `tools/docs-parity/src/gates.rs` | Canonical gate manifest and generated/link-only consumers. | +| `tools/docs-parity/src/workflow.rs` | YAML policy for read-only PR checks and final default-branch readers/writers. | +| `tools/docs-parity/src/dependency_snapshot.rs` | Bounded dependency snapshot schema and deterministic generation. | + +Checked records live under `tools/docs-parity/manifests/`: `tracked-files.toml`, `maintained-sources.toml`, `sensitive-allowlist.toml`, `retired-identifiers.toml`, `snippets.toml`, `settings-companions.toml`, `routes.toml`, `integrations.toml`, `adapter-support.toml`, `cli-overrides.toml`, `gates.toml`, `pages.toml`, `diagrams.toml`, and `orphans.toml`. CLI goldens live at `tools/docs-parity/goldens/cli-linux.txt` and `tools/docs-parity/goldens/cli-macos.txt`. Synthetic fixtures live under `tools/docs-parity/tests/fixtures/`; never add a live secret, internal contact, or real customer value. + +### Existing product/documentation surfaces + +- Publishing/policy: `docs/.vitepress/config.mts`, `docs/guide/index.md`, `docs/internal/onboarding.md`, `docs/business-use-cases.md`, `docs/public/CNAME`, `docs/package.json`, `docs/package-lock.json`, `fastly.toml`, `CLAUDE.md`, `AGENTS.md`, `.github/pull_request_template.md`, and `.claude/commands/{check-ci,review-changes,test-all,test-crate,verify}.md`. +- Truth/config/API/product: the exact WP2-WP5 paths enumerated in Tasks 10-14. +- README/rustdoc/JSDoc: the WP6/WP7 paths enumerated in Tasks 15-16. - Automation: `.github/workflows/{codeql,deploy-docs,docs-links,format,integration-tests,test}.yml`, `.github/dependabot.yml`, `.tool-versions`, and `crates/trusted-server-openrtb-codegen/Cargo.toml`. ## Package checkpoint rule -After each rc package task: - -1. Before editing, record `package_start_head="$(git rev-parse HEAD)"`. Make the - package edits, generate candidate outputs, and create the package's evidence - section. -2. Fully stage every intended add/modify/delete with the task's exact - pathspecs before classification or parity checks. Never use `git add -N`: - intent-to-add has no candidate blob. Review - `git diff --cached --name-status "$package_start_head"` and reject every - changed path outside the task's file list. Require - `git ls-files --others --exclude-standard` to print nothing, and require - `git diff --quiet` to exit 0 so no unstaged tracked byte anywhere can affect - a repository-wide check. Ignored dependency/build output remains unstaged. -3. Run the focused tests and package acceptance commands against that fully - staged universe. Regenerate checked outputs, restage only their exact paths, - run `docs-parity check`, and require a clean generated diff. - Bootstrap exception: Tasks 1, 4, and 5 run every staged-universe check above - but cannot regenerate `tracked-files.toml` or `maintained-sources.toml` - because Task 6 creates them. Task 6's initial bootstrap must classify the - complete then-current repository, including every path those tasks added, - moved, or deleted. From the Task 6 commit onward, every package that creates, - moves, or deletes a tracked path must regenerate and stage both manifests; - public-page changes must also regenerate the applicable page/orphan records. -4. Record commands/results in - `docs/internal/audits/documentation-refresh-evidence.md` and stage that - exact file. Because that mutation changes the candidate universe, repeat any - classification/scanner check that consumes the ledger, restage any generated - output, and again require global `git diff --quiet` so every tracked - working-tree byte equals the index/HEAD candidate. Run - `git diff --cached --check` and review the cached - name/status and content diff from `package_start_head` against only this - package. -5. Commit with the exact imperative message listed in the task. If recording - final commit/run identifiers requires a follow-up, make one immediately - adjacent evidence-only commit before starting the next package. Never let a - later directory-wide `git add` absorb earlier evidence, and do not squash - package or evidence commits. -6. Require `git status --porcelain` to be empty except for explicitly named, - reviewed state before advancing to the next package. +1. Fetch and reassert the immutable rc tip. Record `package_start_head="$(git rev-parse HEAD)"` before editing. +2. Execute one test-first leaf at a time: add one named failing fixture/assertion, run the focused red command and record its diagnostic, implement the minimum change, then rerun the focused command and immediate regressions. +3. Fully stage only the package allowlist. Never use `git add -N` or directory-wide staging. Review `git diff --cached --name-status "$package_start_head"`. Require `git ls-files --others --exclude-standard` to print nothing and `git diff --quiet` to exit 0. +4. From Task 5 onward, regenerate and stage `tracked-files.toml` and `maintained-sources.toml` whenever a tracked path is created, moved, or deleted. Public-page changes also regenerate page/orphan records. Tasks 1-4 are bootstrap exceptions because Task 5 creates the classification records. +5. Run focused tests, regenerate checked outputs, restage exact output paths, run `docs-parity check` where available, and prove a second generation is byte-stable. +6. Append commands/results to `documentation-refresh-evidence.md`. Restage that exact file, rerun any scanner/classifier consuming it, and reassert no unstaged bytes. +7. Run `git diff --cached --check` and inspect the cached content. Commit with the exact task message. If final identifiers require a receipt, create one immediately adjacent evidence-only commit; do not create recursive self-SHA receipts. +8. Push the clean package commits to `origin/spec-docs-refresh` so PR #1049 is the only hosted review surface. Require clean status before advancing. ### Atomic execution rule -This is the master program plan. Composite implementation checkpoints in -Tasks 5-10, 15, and 19 are not single coding actions. Before changing a -component, copy its next fixture from the task's enumerated negative matrix -into the evidence checklist and execute one leaf cycle at a time: - -1. add one named failing fixture/test; -2. run its exact focused command and record the expected diagnostic; -3. implement the smallest production/tool change for that fixture; -4. rerun the focused command and its immediately affected regression set; -5. mark that leaf complete, then continue to the next named fixture. +Composite parser, scanner, workflow, settings, route, and smoke tasks are packages, not single coding actions. Copy each named negative fixture into the evidence checklist and complete its red/green cycle before the next leaf. Never batch multiple parser or trust classes into one unreviewed implementation change. -Do not batch multiple parser classes, workflow rejection classes, scanners, or -adapter seams into one unreviewed edit. The package checkpoint commit happens -only after every enumerated leaf is green; the evidence ledger is the resumable -leaf-task list. - -### Task 1: Record decisions and revalidate immutable tips +### Task 1: Align program records to the single PR **Files:** - Modify: `docs/superpowers/specs/2026-08-19-documentation-refresh-design.md` -- Modify: `docs/superpowers/plans/2026-08-30-documentation-refresh.md` -- Create: `docs/internal/audits/documentation-refresh-decisions.md` -- Create: `docs/internal/audits/documentation-refresh-evidence.md` +- Modify: `docs/internal/audits/documentation-refresh-decisions.md` +- Modify: `docs/internal/audits/documentation-refresh-evidence.md` -- [x] **Step 1: Fetch and verify the rc baseline** +- [ ] **Step 1: Revalidate PR #1049 and the immutable target** Run: ```bash -git fetch origin rc/202608 main -git rev-parse origin/rc/202608 +git fetch origin rc/202608 spec-docs-refresh +test "$(git rev-parse origin/rc/202608)" = 07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf git merge-base --is-ancestor 07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf HEAD +gh pr view 1049 --json url,state,isDraft,baseRefName,baseRefOid,headRefName,headRefOid ``` -Expected: the first command succeeds, `origin/rc/202608` prints exactly `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`, and the ancestry command exits 0. Stop and re-audit every new rc commit if either assertion changes. - -- [x] **Step 2: Record the current default-branch tip** - -Run `git rev-parse origin/main` and record the full SHA as the starting `audited_main_tip`; do not reuse it for a later `main` PR after `main` advances. +Expected: exact target SHA, ancestry success, and PR #1049 open from `spec-docs-refresh` to `rc/202608`. Record the current remote head as a timestamped capture, not as a permanent final SHA. -- [x] **Step 3: Resolve the owner gates** +- [ ] **Step 2: Replace obsolete delivery records** -Record owner/date/answer for questions 1, 2, 3, and 7. Record whether question 6 has an owner or will take the deterministic fallback. Leave questions 5 and 8 explicitly non-blocking and preserve question 4 as closed. +Make #1049 the only implementation row. Record PR #1104 as closed/superseded with its two reviewed source commits and no live merge/deploy receipt. Remove executable fields for PRs (b), (c), (d), (c2), and (e), all Epoch terminology, temporary protection/status changes, snapshot retirement, and cross-worktree import blocks. -- [x] **Step 4: Make the spec state executable** +- [ ] **Step 3: Define release-pending evidence** -Change the spec status only after question 7 is explicit. Replace resolved open-question prose with the selected branch plus owner/date; do not erase the rejected alternatives or rollback requirements. +Keep the durable hashed-body schema, but use it only for real external captures. Add explicit `release-pending` rows for Pages/CNAME, first schedule, dependency submission/graph, and optional `main` protection. State that local/fixture output cannot complete those rows. -- [x] **Step 5: Establish evidence templates** +- [ ] **Step 4: Mark the reviewed spec executable** -Add the complete Epoch 1/package evidence sections, PR/issue URLs for (a)-(e), -ruleset snapshots, first-success smokes, generated-diff proof, follow-up -issues, and exceptions with owner/expiry. For Epochs 2 and 3, record the -required evidence schema and canonical c2/release-handoff issue URLs. The -schema must require append-only timestamped captures of actor/operation, exact -commit/ref and PR head/base/tool SHAs, run IDs/attempts/jobs, redacted request -method/endpoint/body, response status/body, snapshot identity, and applicable -graph/ruleset/protection/branch API JSON. Hash every body with SHA-256; split -captures over 60 KiB into ordered hashed chunks; make links navigational rather -than authoritative; append corrections that name the superseded comment. -Actual post-merge receipts are captured under that schema in those issues -rather than committed later. +Change the spec status to approved for implementation and record the written-spec approval date and owner. Do not change WP scope. -Also define the cross-worktree handoff template for PRs (b), (c), and (d): -their branch-specific tips, checks, live receipts, and ruleset snapshots stay in -the PR description or named tracking issue while the tightly scoped `main` -branch is open. The next named rc checkpoint imports those captures by URL and -value into the rc evidence/decision records; the records never ride in a -scope-limited `main` PR. +- [ ] **Step 5: Verify and commit** -- [x] **Step 6: Verify and commit the approved handoff** +Run `cd docs && npm run format && npm run lint && npm run build`, remove only generated VitePress temp output, then run `git diff --check`. -Run `cd docs && npm run format`, then `git diff --check`. - -Expected: both commands pass and only the spec, plan, and new audit records are in this checkpoint. +Stage exactly the three files and commit: ```bash -git add docs/superpowers/specs/2026-08-19-documentation-refresh-design.md docs/superpowers/plans/2026-08-30-documentation-refresh.md docs/internal/audits/documentation-refresh-decisions.md docs/internal/audits/documentation-refresh-evidence.md -git commit -m "Approve documentation refresh delivery plan" +git add docs/superpowers/specs/2026-08-19-documentation-refresh-design.md docs/internal/audits/documentation-refresh-decisions.md docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Align documentation refresh records to one PR" ``` -### Task 2: Ship the public-site containment PR (b) +### Task 2: Transfer the reviewed containment commits **Files:** - Modify: `docs/.vitepress/config.mts` - Modify: `docs/guide/index.md` -- Move/Modify: `docs/guide/onboarding.md` → `docs/internal/onboarding.md` - -- [ ] **Step 1: Create an isolated branch from the fresh `origin/main` tip** - -Use `@superpowers:using-git-worktrees`. Record that PR's new -`audited_main_tip` in its PR-description handoff block; the containment branch -must contain exactly the four containment concerns below. Do not add the rc-only -decision/evidence records to this branch. - -- [ ] **Step 2: Prove the current build leaks excluded pages** - -Run `cd docs && npm ci && npm run build`, then assert that at least one `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.html`, `README.html`, or `business-use-cases.html` artifact exists. - -Expected: the assertion demonstrates the pre-change leak. Save the exact artifact path as failing evidence. - -- [ ] **Step 3: Add the minimal containment configuration** - -Set `srcExclude` to `superpowers/**`, `internal/**`, `epics/**`, `guide/onboarding.md`, `README.md`, and `business-use-cases.md`. Fill `docs/guide/index.md`, point the Guide nav item at `/guide/`, remove Business Value navigation, move/scrub onboarding, and remove every built-page link to an excluded source. Do not include CNAME, package metadata, marketing-copy edits, or unrelated navigation work. +- Delete: `docs/guide/onboarding.md` +- Create: `docs/internal/onboarding.md` +- Modify: `docs/internal/audits/documentation-refresh-evidence.md` -- [ ] **Step 4: Rebuild and prove the boundary** +- [ ] **Step 1: Authenticate the closed source PR and commits** -Run `cd docs && npm run lint && npm run format && npm run build`. +Require PR #1104 closed, base `d516a9e94249e10cbc36e41beb4269f9255cf407`, and source commits `34b0613dc603ba6529396dad4dd4b7e68b1e11a9` and `e6554f24f58f6122fb806ce25432f66033765c65`. Verify their combined base-to-head path set is exactly the four paths above. -Expected: all commands pass; the six excluded path families produce no output; `/index.html`, `/guide/index.html`, and `/guide/api-reference.html` exist and contain their expected headings. +- [ ] **Step 2: Transfer the two commits** -- [ ] **Step 5: Review and commit the XS diff** +Cherry-pick the commits in order. Any conflict outside the four authorized paths stops execution. Resolve an authorized-path conflict only by preserving the reviewed containment behavior on the rc version; record the conflict and resulting blob comparison. ```bash -git add docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md -git diff --cached --check -git diff --cached --name-status "$AUDITED_MAIN_TIP" -git diff --cached "$AUDITED_MAIN_TIP" -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md -git commit -m "Contain internal documentation pages" +git cherry-pick 34b0613dc603ba6529396dad4dd4b7e68b1e11a9 +git cherry-pick e6554f24f58f6122fb806ce25432f66033765c65 ``` -Reject any cached path beyond those four. Require `git diff --quiet` before the -commit so the reviewed index is the complete candidate. - -- [ ] **Step 6: Merge and smoke the live Pages deployment** - -Immediately before merge, refetch `main` and require the PR base to equal its -`audited_main_tip`; otherwise rebase, re-review, and record the new tip. After -merge, set a task-specific `DOCS_BASE_URL` from the selected project URL and -assert excluded URLs return 404 while site root, Guide, and API reference return -200 with expected text. Put response headers, deployment SHA, URLs, merge SHA, -and authenticated PR base in the external handoff block for Task 4 to import. - -### Task 3: Resolve CNAME in independent PR (d) - -**Files:** - -- Delete or Modify: `docs/public/CNAME` -- Modify only on custom-domain path: `docs/.vitepress/config.mts`, `README.md`, and every hard-coded Pages URL found by the checked inventory - -- [ ] **Step 1: Cut a new isolated branch from the then-current `origin/main`** - -Record a fresh `audited_main_tip` in the PR-description handoff block; never -stack this on containment or automation PRs and do not add rc-only audit records. +Expected commit subjects: `Contain internal documentation pages` and `Fix internal onboarding links`. -- [ ] **Step 2: Execute exactly the selected branch** +- [ ] **Step 3: Reprove containment on rc** -The selected branch is **delete**. The custom-domain instructions remain -rejected/reference-only unless Task 1's CNAME decision is formally reopened -and this plan is amended and re-approved. +Run `cd docs && npm ci && npm run lint && npm run format && npm run build`. Assert no output for the six excluded families, required Home/Guide/API artifacts with expected content, no excluded hrefs, and every repository-relative onboarding target exists. -Delete path: remove `docs/public/CNAME` and keep the project-path `base`. -Custom-domain path: first run -`git grep -l -F 'https://iabtechlab.github.io/trusted-server'`; at the audited -baseline the exact URL-bearing path list is `README.md`. Record that list in the -external handoff block. If it differs, stop and amend this task's exact allowlist -and staging command before editing. Replace the placeholder with the approved -project-owned public domain, set `base: '/'`, update that exact URL set, and -attach owner/DNS/TLS evidence. +- [ ] **Step 4: Record the transfer** -- [ ] **Step 3: Build and test locally** - -Run `cd docs && npm ci && npm run lint && npm run format && npm run build`. - -Expected: the build is green and assets resolve under the selected base. - -- [ ] **Step 4: Commit, merge, and run branch-specific smokes** +Append source PR URL/state, source base/head, original and resulting commit SHAs, exact path set, commands, and local-only status. Commit only the evidence ledger: ```bash -# Delete path: -git add -A -- docs/public/CNAME - -# Custom-domain path instead: -git add -A -- docs/public/CNAME docs/.vitepress/config.mts README.md - -git diff --cached --check -git diff --cached --name-status "$AUDITED_MAIN_TIP" -git commit -m "Resolve documentation site domain" +git add docs/internal/audits/documentation-refresh-evidence.md +git commit -m "Record documentation containment transfer" ``` -Run the selected delete staging branch. The delete path's cached set is exactly -CNAME; the rejected custom path's reference set is exactly CNAME, config, and -the recorded URL path. Review -the full cached content and require `git diff --quiet` before committing. - -Immediately before merge, assert the exact recorded base. After deploy, the -delete path re-smokes project URLs; the custom path records DNS, TLS, canonical -page, asset, and former hard-coded URL results. Put the merge SHA, base, and all -receipts in the external handoff block for Task 4. Never restore the placeholder -during rollback. - -### Task 4: Complete WP1 hygiene on rc +### Task 3: Complete WP1 CNAME and policy hygiene **Files:** -- Import exactly from PR (b): `docs/.vitepress/config.mts`, - `docs/guide/index.md`, `docs/guide/onboarding.md` → - `docs/internal/onboarding.md` -- Import exactly from selected PR (d): `docs/public/CNAME` plus, only on the - custom-domain path, `docs/.vitepress/config.mts`, `README.md`, and each - checked URL path in that PR +- Delete: `docs/public/CNAME` - Modify: `docs/business-use-cases.md` - Modify: `fastly.toml` - Modify: `docs/package.json` @@ -350,99 +185,38 @@ during rollback. - Modify: `docs/internal/audits/documentation-refresh-evidence.md` - Modify: `docs/internal/audits/documentation-refresh-decisions.md` -- [ ] **Step 1: Import the live publishing deltas into rc** +- [ ] **Step 1: Add failing policy assertions** -Fetch `main`, authenticate the recorded merge commits for (b) and (d), and -authenticate each PR's recorded base SHA. Do not merge a moving `main` -wholesale. First require each authenticated base-to-merge name/status to match -its PR allowlist. Then import the final modes/blobs for only those paths directly -from the merge tree and commit the two path sets separately. This intentionally -handles an rc path whose unrelated bytes diverged from `main`; later named rc -packages, not the import, reapply any intended content. Stop on any tree or -allowlist mismatch: +Prove the banner, package privacy/license, empty authors, fixture labels, KV comments, canonical gate link, generated AGENTS region, exception taxonomy, and CNAME deletion are absent or stale. -```bash -b_import_start="$(git rev-parse HEAD)" -git diff --name-status "$B_BASE_SHA" "$B_MERGE_SHA" -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md -git restore --source="$B_MERGE_SHA" --staged --worktree -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md -git diff --cached --name-status "$b_import_start" -git diff --cached --check -git diff --quiet -git diff --quiet "$B_MERGE_SHA" -- docs/.vitepress/config.mts docs/guide/index.md docs/guide/onboarding.md docs/internal/onboarding.md -git commit -m "Import public documentation containment" +- [ ] **Step 2: Delete the selected CNAME** -``` - -Then run exactly one d block. Delete path: - -```bash -d_import_start="$(git rev-parse HEAD)" -git diff --name-status "$D_BASE_SHA" "$D_MERGE_SHA" -- docs/public/CNAME -git restore --source="$D_MERGE_SHA" --staged --worktree -- docs/public/CNAME -git diff --cached --name-status "$d_import_start" -git diff --cached --check -git diff --quiet -git diff --quiet "$D_MERGE_SHA" -- docs/public/CNAME -git commit -m "Import documentation site domain" -git status --porcelain -``` - -Custom-domain path (the authenticated scan must still have exactly this path -set; otherwise update the plan before applying): +Remove `docs/public/CNAME` and retain `base: '/trusted-server'`. Assert no tracked placeholder remains and build assets use the project path. Commit the exact deletion: ```bash -d_import_start="$(git rev-parse HEAD)" -git diff --name-status "$D_BASE_SHA" "$D_MERGE_SHA" -- docs/public/CNAME docs/.vitepress/config.mts README.md -git restore --source="$D_MERGE_SHA" --staged --worktree -- docs/public/CNAME docs/.vitepress/config.mts README.md -git diff --cached --name-status "$d_import_start" +git add -A -- docs/public/CNAME git diff --cached --check -git diff --quiet -git diff --quiet "$D_MERGE_SHA" -- docs/public/CNAME docs/.vitepress/config.mts README.md -git commit -m "Import documentation site domain" -git status --porcelain +git commit -m "Resolve documentation site domain" ``` -Expected: each cached name/status is exactly its authenticated PR delta; every -imported existing path has the merge commit's mode/blob and every imported -deletion is absent; final status is empty. On the custom path, this direct -README import replaces any divergent rc bytes; Task 17 performs the later WP6 -README rewrite from that imported state. Record -both source base/merge pairs and resulting rc commit SHAs. Only after these two -clean import commits set the Task 4 `package_start_head` and begin hygiene edits. - -- [ ] **Step 2: Add assertions for the policy state** - -Use temporary `rg` assertions to show the banner, package privacy/license, empty authors, fixture labels, KV comments, canonical gate link, generated AGENTS gate region, and exception taxonomy are absent or stale before editing. +- [ ] **Step 3: Apply policy and hygiene edits** -- [ ] **Step 3: Apply the policy and hygiene edits** +Add the unverified marketing banner; scrub `fastly.toml` while preserving only the expiring service-ID record; set docs package private/Apache-2.0 and refresh lock metadata; add the typed exception taxonomy to CLAUDE; make command files and PR template link-only; generate the AGENTS fallback region. -Add the unverified marketing banner; scrub `fastly.toml` as specified while preserving the time-bounded service-ID entry; set the docs package private/Apache-2.0 and refresh its lockfile metadata; add the exception taxonomy to `CLAUDE.md`; make command files and the PR template link-only gate consumers; generate the AGENTS fallback region. +- [ ] **Step 4: Prove privacy and policy state** -- [ ] **Step 4: Prove contacts/access guidance are absent** +Search all tracked files for removed contacts, handles, channels, access phrases, placeholder CNAME, and prohibited exception shapes. Expected: no match outside a typed, unexpired decision entry. -Search all tracked files for every removed onboarding contact, handle, channel, and access phrase. Expected: no matches outside an explicit typed exception in the decision record. +- [ ] **Step 5: Verify and commit** -- [ ] **Step 5: Verify and checkpoint WP1** - -Run: - -```bash -cd docs && npm ci && npm run lint && npm run format && npm run build -git diff --check -``` - -Expected: all commands pass and the containment/CNAME live evidence is linked from the rc evidence record. - -Import the complete external handoff blocks for (b) and (d), including their -audited bases, merge SHAs, live receipts, and URLs, into the rc evidence and -decision records before staging this checkpoint. +Run `cd docs && npm ci && npm run lint && npm run format && npm run build`, exact included/excluded artifact assertions, `git diff --check`, and the package checkpoint checks. Record live Pages/CNAME as release-pending. ```bash git add docs/business-use-cases.md fastly.toml docs/package.json docs/package-lock.json CLAUDE.md AGENTS.md .github/pull_request_template.md .claude/commands/check-ci.md .claude/commands/review-changes.md .claude/commands/test-all.md .claude/commands/test-crate.md .claude/commands/verify.md docs/internal/audits/documentation-refresh-evidence.md docs/internal/audits/documentation-refresh-decisions.md git commit -m "Clean documentation publishing policy" ``` -### Task 5: Scaffold the standalone `docs-parity` crate +### Task 4: Scaffold the standalone `docs-parity` crate **Files:** @@ -484,7 +258,7 @@ git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-par git commit -m "Add documentation parity tool foundation" ``` -### Task 6: Close tracked-file classification and sensitive-data scanning +### Task 5: Close tracked-file classification and sensitive-data scanning **Files:** @@ -545,7 +319,7 @@ git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-par git commit -m "Enforce documentation source classification" ``` -### Task 7: Implement generated regions, Markdown ownership, and link checks +### Task 6: Implement generated regions, Markdown ownership, and link checks **Files:** @@ -613,7 +387,7 @@ git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-par git commit -m "Add checked documentation regions and links" ``` -### Task 8: Extract settings semantics and execute the example harness +### Task 7: Extract settings semantics and execute the example harness **Files:** @@ -676,7 +450,7 @@ git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-par git commit -m "Check configuration documentation semantics" ``` -### Task 9: Check integration capabilities and adapter routes +### Task 8: Check integration capabilities and adapter routes **Files:** @@ -761,249 +535,83 @@ git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-par git commit -m "Check integration and adapter inventories" ``` -### Task 10: Check CLI help, snippets, gates, workflows, and snapshots +### Task 9: Check CLI help, snippets, gates, and final workflow foundations **Files:** -- Modify as dependencies are introduced: `tools/docs-parity/Cargo.toml` -- Modify as dependencies are introduced: `tools/docs-parity/Cargo.lock` +- Modify: `tools/docs-parity/Cargo.toml` +- Modify: `tools/docs-parity/Cargo.lock` - Create: `tools/docs-parity/src/cli_help.rs` - Create: `tools/docs-parity/src/snippets.rs` - Create: `tools/docs-parity/src/gates.rs` - Create: `tools/docs-parity/src/workflow.rs` - Create: `tools/docs-parity/src/dependency_snapshot.rs` -- Modify: `tools/docs-parity/src/main.rs` -- Modify: `tools/docs-parity/src/lib.rs` -- Modify: `tools/docs-parity/src/model.rs` -- Modify: `tools/docs-parity/src/repository.rs` +- Modify: `tools/docs-parity/src/{main,lib,model,repository}.rs` - Create: `tools/docs-parity/manifests/{cli-overrides,snippets,gates}.toml` -- Modify: `tools/docs-parity/manifests/tracked-files.toml` -- Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Modify: `tools/docs-parity/manifests/{tracked-files,maintained-sources}.toml` - Create: `tools/docs-parity/goldens/{cli-linux,cli-macos}.txt` - Create/Test: `tools/docs-parity/tests/{cli_help,snippets,gates,workflow,dependency_snapshot}.rs` -- Create: `docs/internal/runbooks/documentation-automation-rollback.md` -- Create: `docs/internal/runbooks/patches/docs-links-c2.patch` -- Create: `docs/internal/runbooks/patches/docs-links-rollback-c2.patch` +- Modify: `.github/workflows/test.yml` - Create: `.github/workflows/docs-links.yml` +- Modify: `docs/internal/audits/documentation-refresh-evidence.md` -- [ ] **Step 1: Capture and union CLI help** - -Add test seams if required so recursive Clap help can be captured without -process termination. Implement a capture command that detects the compiled -host OS and has no caller-supplied platform override. After the capture-ready -source commit in Step 8, check out that exact 40-character SHA on one native -Linux runner and one native macOS runner and run the same recursive capture -command. For each raw result record runner identity, `uname -a`, `rustc -vV`, -Node version, source SHA, and SHA-256. A Linux VM/container is acceptable only -when it executes the Linux-target binary; the macOS capture must execute on -macOS. Never copy or infer one platform's output from the other. Import the two -hashed raw captures through the deterministic tool command, generate -`cli-linux.txt` and `cli-macos.txt`, annotate platform-only commands, and require -every prose override to carry owner/rationale/expiry plus an exact source-text -staleness fingerprint. - -- [ ] **Step 2: Write snippet-mode tests** - -Cover every language and mode: executable, compile/validation expected failure with phase and stable diagnostic, illustrative fragment with expiring owner waiver, missing classification, wrong diagnostic despite nonzero exit, and a formerly invalid example becoming valid. - -- [ ] **Step 3: Implement the canonical gate manifest** - -Define each command once with its runner/target/mode and generate or check every consumer region. Link-only consumers must contain no copied command bodies; AGENTS and canonical test docs use generated regions. - -- [ ] **Step 4: Write workflow security fixtures first** - -Add positive fixtures for an ordinary net-empty PR, a -divergent-history/net-identical rc release PR, c2, normal e, abandonment e, -rollback-c2, same-lifecycle repair/sync, and post-handoff maintenance. Add one -negative fixture per spec class, including a repair that changes lifecycle -state, pre-handoff maintenance, caller-selected maintenance tool, maintenance -AST weakening, an unexpected `merge_group` trigger, non-`main` -dispatch, stale `main` controller, stale base/head, fork executable SHA, extra -path, PR-files truncation attempt, unsafe mode/symlink, mixed inputs, open #1049 -SHA used by `validate_main_pr`, pending candidate, failed validation attestation, -checkout/cache/service-container/secret escalation, stale snapshot, malformed -or oversized artifact, and caller-supplied refresh SHA. - -- [ ] **Step 5: Implement exact-diff and workflow AST policy** - -Use a separate bare object store, fetch the authenticated base/head objects, -and never check out or execute the files object. Classify the merge result with -a NUL-delimited two-tree `git diff --name-status `, never a -merge-base/three-dot diff, and compare mode/blob IDs for both protected paths. -Ordinary and rc release PRs pass only when those paths are net-identical even if -history diverged. c2/e/rollback-c2/same-lifecycle repair-sync/post-handoff -maintenance use the full two-tree candidate diff, change at most the two named protected files, and -change no other path; each resulting blob is at most 384 KiB and the protected -blobs at most 512 KiB total. Require exact lifecycle patch shapes; require -repair/sync to equal the authenticated rc protected blobs without changing the -validation-only or active-rc state; require -maintenance to use the authenticated current-main tool and preserve every trust -and AST invariant; require action SHA pins, least privilege, safe events, and -byte equality between c2 result and the authenticated rc workflow. -Expose the same policy through a local-index subcommand so a trusted tool -worktree can validate another worktree's fully staged candidate as inert Git -objects before its first commit; this local form never executes candidate -files. Its `candidate-kind` argument is an assertion checked against the -inferred shape, never a selector that relaxes checks, and the hosted workflow -accepts no caller-supplied candidate kind. - -- [ ] **Step 6: Implement the controller state machine** - -The validation-only workflow has `validate_rc`, `validate_main_pr`, -`validate_main_maintenance`, and base-controlled `pull_request_target`; it -asserts the current `main` controller SHA before inputs. Maintenance accepts no -`tool_sha`, executes only the authenticated current-main tool, and fails until -the base has tooling, main-targeted automation, and no temporary rc snapshot -jobs/refresh. Validation has `contents: read`/`pull-requests: read`; -attestation has only `statuses: write`, no checkout, fixed -`docs/automation-delta`, authenticated 40-hex head, and fixed result enum. -`validate_rc` cannot reach attestation or any writer. Materialize the complete -link reader/issue writer jobs in their final dormant form now: final -permissions/conditions, 30/5-minute timeouts, fixed schedule/refresh -concurrency with no cancellation, bounded artifact schema, dedup/auto-close -logic, and pinned action references. They remain unreachable solely because -the validation-only workflow has no `schedule:` trigger. - -Enforce the exact artifact bounds before any writer starts. Each archive has -exactly one regular member, respectively `link-results.json` or -`dependency-snapshot.json`, and rejects links, traversal, and extra members. -Link results use a 2 MiB maximum archive, 1 MiB decoded JSON, 500 findings, and -2,048-byte strings; dependency snapshots use a 4 MiB archive, 2 MiB decoded -JSON, 5,000 records, and 2,048-byte strings. Both schemas reject unknown fields -and every overflow. - -- [ ] **Step 7: Implement snapshot generation and templates** - -Generate the schema-versioned snapshot from the exact authenticated rc tip with fixed detector/correlator/ref. The future writer template has only `contents: write`, no checkout or repository code, revalidates schema and current rc SHA, and submits only after validation. Produce and test c2 plus inverse rollback patch templates; keep schedule/snapshot jobs unreachable in the rc workflow's validation-only copy used by PR (c). - -- [ ] **Step 8: Verify the whole WP8a tool** - -Add each CLI/workflow/snapshot dependency only in the standalone manifest, -regenerate its lockfile, and require `git diff --quiet -- Cargo.lock` before the -commands below. First stage the complete capture-ready Task 10 source except the -two not-yet-generated golden files, review it under the package checkpoint rule, -and commit the authenticated source used by both native runners: +- [ ] **Step 1: Write CLI capture and snippet failures** -```bash -git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/cli_help.rs tools/docs-parity/src/snippets.rs tools/docs-parity/src/gates.rs tools/docs-parity/src/workflow.rs tools/docs-parity/src/dependency_snapshot.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/cli-overrides.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/gates.toml tools/docs-parity/tests/cli_help.rs tools/docs-parity/tests/snippets.rs tools/docs-parity/tests/gates.rs tools/docs-parity/tests/workflow.rs tools/docs-parity/tests/dependency_snapshot.rs .github/workflows/docs-links.yml docs/internal/runbooks/documentation-automation-rollback.md docs/internal/runbooks/patches/docs-links-c2.patch docs/internal/runbooks/patches/docs-links-rollback-c2.patch docs/internal/audits/documentation-refresh-evidence.md -git diff --cached --check -git commit -m "Add documentation enforcement scaffolding" -``` +Cover recursive help, host-OS detection with no caller platform override, missing native capture provenance, stale overrides, every snippet mode, wrong failure phase/diagnostic, missing classification, expired waiver, and formerly invalid examples becoming valid. -Run the two native captures at that exact commit, import them without hand -editing, regenerate `tracked-files.toml`/`maintained-sources.toml`, and append -the complete provenance and raw/output hashes to the evidence ledger. Fully -stage those five exact paths, require `git diff --quiet`, and then run: +- [ ] **Step 2: Add native capture CI** -```bash -./scripts/test-cli.sh -cargo test --manifest-path tools/docs-parity/Cargo.toml -cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check -cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings -cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all -``` - -Expected: all positive and negative fixtures pass for their intended reasons; update mode followed by check mode yields no diff. - -- [ ] **Step 9: Commit WP8a** +Add a permanent Linux/macOS PR matrix job that checks out the exact PR head, runs the same capture command, records runner/`uname`/Rust/Node/source SHA metadata, and uploads bounded raw artifacts. Commit capture-ready code before generating goldens and push that commit to #1049. -```bash -git add tools/docs-parity/goldens/cli-linux.txt tools/docs-parity/goldens/cli-macos.txt tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml docs/internal/audits/documentation-refresh-evidence.md -git diff --cached --check -git commit -m "Record cross-platform CLI help goldens" -``` +- [ ] **Step 3: Import authenticated goldens** -Expected: Task 10 is the adjacent two-commit series above; both capture -provenance records name the first commit, all final checks run with the second -commit's candidate bytes, and neither commit is squashed. +Download both artifacts from the same hosted run and exact source SHA. Verify hashes and provenance, import through the deterministic tool command, never hand-edit the goldens, and prove a second import is unchanged. -### Task 11: Install the validation-only `main` controller in PR (c) +- [ ] **Step 4: Implement snippets and canonical gates** -**Files on the isolated `main` branch:** +Define every command once with runner/target/mode; generate checked regions or enforce link-only consumers. Execute fences in isolated working directories and require stable phase/diagnostic matches. -- Create: `.github/workflows/docs-links.yml` +- [ ] **Step 5: Write final-workflow security fixtures first** -- [ ] **Step 1: Cut from a fresh `origin/main` and record the PR-specific tip** +Positive fixtures: ordinary read-only PR validation, scheduled clean/finding link paths, issue dedup/auto-close, dependency generation/submission, and closed manual refresh. Negative fixtures: `pull_request_target`, `merge_group`, status write, caller tool/SHA input, privileged PR checkout, unpinned action, expanded permissions, unsafe cache/service/local action, stale source, extra path/member, traversal, unsafe mode/symlink, mixed inputs, malformed/oversized artifacts, unknown schema fields, and write job executing repository code. -Use `@superpowers:using-git-worktrees`; record the full `audited_main_tip` and -the rc PR #1049 head used as the reviewed source in PR (c)'s external handoff -block. Attach the complete pre-change `main` ruleset/branch-protection JSON -there. Do not add the rc-only audit records to this isolated branch. +- [ ] **Step 6: Implement workflow and snapshot policy** -- [ ] **Step 2: Materialize only the validation form** +Parse YAML as data. Require read-only PR jobs, full action SHA pins, default-deny permissions, separated no-checkout writers, fixed concurrency/timeouts, exact archive member names, schema closure, and authenticated source SHA. Link bounds: 2 MiB archive, 1 MiB JSON, 500 findings, 2,048-byte strings. Snapshot bounds: 4 MiB archive, 2 MiB JSON, 5,000 records, 2,048-byte strings. -Copy the complete reviewed validation-only workflow: the base-controlled PR -gate, manual `validate_rc`/`validate_main_pr`/`validate_main_maintenance`, status -attestation, and the schedule-only link reader/issue writer definitions, which -remain unreachable because there is no `schedule:` trigger. The maintenance -operation is also present but fail-closed until the normal post-(e) steady-state -predicate is true. Omit `schedule:`, -`refresh_dependency_snapshot`, snapshot jobs, and rc-targeted Dependabot -entries. Record the full workflow blob hash; the controller, attestation, and -unreachable link/issue definitions are frozen through c2. +- [ ] **Step 7: Materialize the final workflow foundation** -- [ ] **Step 3: Validate the exact candidate statically** +Create `docs-links.yml` directly in final-state shape: ordinary read-only PR validation; default-branch schedule; split link reader/issue writer; split snapshot reader/writer; and no-input manual refresh. It contains no temporary rc target, controller attestation, protected-file lifecycle, or caller-selected executable. -Run the rc PR tool against the candidate workflow as data. Expected: -controller-current-main assertions, event guards, exact permissions, no -untrusted checkout/execution, action pins, dispatch input closure, -base-vs-head net-equality handling, c2/e/rollback templates, and the -same-lifecycle repair/sync plus pre-/post-handoff maintenance predicates all -pass. +- [ ] **Step 8: Verify and commit in two adjacent checkpoints** -- [ ] **Step 4: Commit and open PR (c)** +First commit capture-ready sources and workflow foundation: ```bash -git add .github/workflows/docs-links.yml +cargo test --manifest-path tools/docs-parity/Cargo.toml cli_help +cargo test --manifest-path tools/docs-parity/Cargo.toml snippets +cargo test --manifest-path tools/docs-parity/Cargo.toml gates +cargo test --manifest-path tools/docs-parity/Cargo.toml workflow +cargo test --manifest-path tools/docs-parity/Cargo.toml dependency_snapshot +cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check +cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings +git add tools/docs-parity/Cargo.toml tools/docs-parity/Cargo.lock tools/docs-parity/src/cli_help.rs tools/docs-parity/src/snippets.rs tools/docs-parity/src/gates.rs tools/docs-parity/src/workflow.rs tools/docs-parity/src/dependency_snapshot.rs tools/docs-parity/src/main.rs tools/docs-parity/src/lib.rs tools/docs-parity/src/model.rs tools/docs-parity/src/repository.rs tools/docs-parity/manifests/cli-overrides.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/gates.toml tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/tests/cli_help.rs tools/docs-parity/tests/snippets.rs tools/docs-parity/tests/gates.rs tools/docs-parity/tests/workflow.rs tools/docs-parity/tests/dependency_snapshot.rs .github/workflows/test.yml .github/workflows/docs-links.yml docs/internal/audits/documentation-refresh-evidence.md git diff --cached --check -git diff --cached --name-status "$AUDITED_MAIN_TIP" -git commit -m "Install documentation automation controller" +git commit -m "Add documentation enforcement foundations" +git push origin spec-docs-refresh ``` -Before merge, refetch and require the PR's current base to equal its recorded -tip. Put review and a successful manual `validate_rc` dispatch using `tool_sha` -equal to the current #1049 head and `ref: main` in the external handoff block. - -- [ ] **Step 5: Seed all existing `main` PRs before requiring the context** - -Inventory every open `main` PR and authenticate its current head/base. Retrigger -the automatic net-empty-protected-delta path or use the authenticated bootstrap -path; record a `docs/automation-delta` result for every head. Do not enable the -required context while any existing PR is missing it. - -- [ ] **Step 6: Enable and prove branch protection** - -Require `docs/automation-delta` from the GitHub Actions app for every `main` PR -and enable strict/up-to-date enforcement. Demonstrate an unauthorized -protected-file delta is blocked and an unrelated PR receives success without a -privileged checkout. Record that merge queues are disabled for `main`; if they -are active, stop until the approved question-7 branch disables them or a -separate reviewed design updates the spec, plan, controller, and fixtures. -Record queue adoption as blocked through PR (e). - -- [ ] **Step 7: Exercise controller rollback on paper** - -Review the exact procedure: remove only the new required context and restore prior strictness before disabling/reverting the controller; repair and re-prove block/pass before re-enabling. Attach the ruleset IDs and owner. - -- [ ] **Step 8: Import controller rollout evidence into rc** - -After PR (c), status seeding, and protection proofs complete, return to the rc -worktree. Append the external handoff block, authenticated merge/base/rc-source -SHAs, full before/after ruleset captures, seeded PR results, block/pass proof, -queue state, and rollback owner to the rc records. Commit this adjacent evidence -checkpoint before Task 12: +After the native artifacts return, stage only the goldens, regenerated tracked/source manifests, and evidence: ```bash -git add docs/internal/audits/documentation-refresh-evidence.md docs/internal/audits/documentation-refresh-decisions.md +git add tools/docs-parity/goldens/cli-linux.txt tools/docs-parity/goldens/cli-macos.txt tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml docs/internal/audits/documentation-refresh-evidence.md git diff --cached --check -git diff --cached --name-status HEAD -git commit -m "Record documentation controller rollout" -git status --porcelain +cargo test --manifest-path tools/docs-parity/Cargo.toml +cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all +git commit -m "Record cross-platform CLI help goldens" ``` -Expected: only the two rc audit records are committed and final status is empty. - -### Task 12: Complete WP2 truth pass and dispositions +### Task 10: Complete WP2 truth pass and dispositions **Files:** @@ -1017,14 +625,12 @@ Expected: only the two rc audit records are committed and final status is empty. - Modify: `docs/guide/roadmap.md` - Modify: `docs/guide/integrations/gam.md` - Modify: `docs/guide/integrations/kargo.md` +- Modify: `docs/.vitepress/config.mts` - Create: `docs/guide/auction-testing.md` - Modify: `crates/trusted-server-core/src/auction/README.md` - Modify: `TESTING.md` - Retire/Move/Modify: `FAQ_POC.md` - Create only on FAQ archive path: `docs/superpowers/archive/FAQ_POC.md` -- Create only on FAQ rewrite path: `docs/guide/faq.md` -- Modify only on FAQ rewrite path: `docs/guide/index.md` -- Modify only on FAQ rewrite path: `docs/.vitepress/config.mts` - Modify: `CHANGELOG.md` - Modify: `.env.example` - Modify: `.env.dev` @@ -1060,13 +666,7 @@ Replace `RequestWrapper` with real platform traits; remove Equativ, `.with_asset The selected FAQ branch is **archive**: move `FAQ_POC.md` to `docs/superpowers/archive/FAQ_POC.md`. The retire and rewrite instructions remain rejected/reference-only unless Task 1's FAQ decision is formally -reopened and this plan is amended and re-approved. For reference, retire -deletes `FAQ_POC.md`; rewrite moves it to -`docs/guide/faq.md`, verifies every answer against code, links it from the -Guide landing page and Reference navigation, registers `/guide/faq` in -`pages.toml`, and adds a built-page smoke for `guide/faq.html`. Every branch -removes the root path from the active-repo inventory; only rewrite adds an -active public page. Independently replace GAM/Kargo with route-preserving +reopened and this plan is amended and re-approved. The rejected retire and rewrite alternatives are not executable plan branches. Remove the root path from the active-repo inventory and add no active FAQ page or route. Independently replace GAM/Kargo with route-preserving tombstones, remove sidebar reachability, and add old-route/tombstone smokes to `pages.toml`. @@ -1074,6 +674,8 @@ tombstones, remove sidebar reachability, and add old-route/tombstone smokes to Make root `TESTING.md` the test-matrix index, move the verified auction runbook into `docs/guide/auction-testing.md`, normalize the deterministic no-release CHANGELOG form, distinguish runtime env from CLI overlay, fix roadmap status, and repair the three known workflow/script comments. +Record whether operator-visible CHANGELOG entries are complete for the audited range; if an entry is intentionally omitted, record the exact exclusion and source anchor. Formatting alone does not satisfy this check. + - [ ] **Step 6: Reverify rc-delta content instead of blindly changing it** Check allowed-domain semantics, `/first-party/sign` 403 plus `href`/`base`, proxy-signing recommendation, and `--staging` limitation against code. Mark verified with anchors when correct; edit only proven drift. @@ -1089,10 +691,7 @@ cargo run --manifest-path tools/docs-parity/Cargo.toml -- pages --check cd docs && npm run lint && npm run format && npm run build ``` -On the FAQ rewrite branch, additionally assert -`docs/.vitepress/dist/guide/faq.html` exists and contains the expected FAQ -heading and that `/guide/faq` is present in generated navigation. On retire and -archive, assert that artifact and route are absent. +Assert that `docs/.vitepress/dist/guide/faq.html` and `/guide/faq` navigation are absent on the selected archive path. Expected: set equality passes; retired terms are absent from active sets with only the spec-defined historical exceptions; every executable fence has a valid manifest entry and diagnostic. @@ -1106,26 +705,18 @@ Stage the common WP2 paths first, then the selected archive branch and only the conditional fixture paths that actually changed: ```bash -git add docs/internal/audits/documentation-refresh-inventory.toml docs/internal/audits/documentation-refresh-evidence.md docs/guide/ad-serving.md docs/guide/architecture.md docs/guide/configuration.md docs/guide/creative-processing.md docs/guide/error-reference.md docs/guide/integration-guide.md docs/guide/roadmap.md docs/guide/integrations/gam.md docs/guide/integrations/kargo.md docs/guide/auction-testing.md TESTING.md CHANGELOG.md .env.example .env.dev .claude/agents/code-architect.md .claude/agents/issue-creator.md .github/workflows/test.yml scripts/test-cli.sh crates/trusted-server-core/src/auction/README.md crates/trusted-server-openrtb/generate.sh tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/sensitive-allowlist.toml tools/docs-parity/manifests/retired-identifiers.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/orphans.toml -# Retire branch: -git add -A -- FAQ_POC.md -# Archive branch instead: +git add docs/internal/audits/documentation-refresh-inventory.toml docs/internal/audits/documentation-refresh-evidence.md docs/guide/ad-serving.md docs/guide/architecture.md docs/guide/configuration.md docs/guide/creative-processing.md docs/guide/error-reference.md docs/guide/integration-guide.md docs/guide/roadmap.md docs/guide/integrations/gam.md docs/guide/integrations/kargo.md docs/guide/auction-testing.md docs/.vitepress/config.mts TESTING.md CHANGELOG.md .env.example .env.dev .claude/agents/code-architect.md .claude/agents/issue-creator.md .github/workflows/test.yml scripts/test-cli.sh crates/trusted-server-core/src/auction/README.md crates/trusted-server-openrtb/generate.sh tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/sensitive-allowlist.toml tools/docs-parity/manifests/retired-identifiers.toml tools/docs-parity/manifests/snippets.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/orphans.toml git add -A -- FAQ_POC.md docs/superpowers/archive/FAQ_POC.md -# Rewrite branch instead: -git add -A -- FAQ_POC.md docs/guide/faq.md docs/guide/index.md docs/.vitepress/config.mts # Only when the scanner required this fixture edit: git add crates/trusted-server-core/src/html_processor.test.html git commit -m "Correct maintained documentation truth" ``` -The comments preserve mutually exclusive reference branches; execute the -archive command only unless the decision is formally reopened and this plan is -amended and re-approved. If the mechanical inventory selects another human-facing comment path, add that one exact path to the reviewed list before running the checkpoint—never replace this list with `git add docs`, `git add .github`, or another directory. -### Task 13: Complete WP3 configuration reference and template +### Task 11: Complete WP3 configuration reference and template **Files:** @@ -1133,6 +724,8 @@ replace this list with `git add docs`, `git add .github`, or another directory. - Modify: `docs/guide/cli.md` - Modify: `trusted-server.example.toml` - Modify: `tools/docs-parity/manifests/settings-companions.toml` +- Modify: `tools/docs-parity/manifests/snippets.toml` +- Modify: PR #1049 description through GitHub API/CLI - [ ] **Step 1: Make parity fail on the baseline gaps** @@ -1155,6 +748,7 @@ Classify the 11 store-resolved paths, inline trusted-client-IP secret, and disca ```bash cargo run --manifest-path tools/docs-parity/Cargo.toml -- settings --check cargo run --manifest-path tools/docs-parity/Cargo.toml -- examples --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check cargo test-fastly config cd docs && npm run lint && npm run format && npm run build ``` @@ -1164,18 +758,25 @@ Expected: every active canonical field appears in reference/template, noncanonic - [ ] **Step 6: Commit WP3** ```bash -git add docs/guide/configuration.md docs/guide/cli.md trusted-server.example.toml tools/docs-parity/manifests/settings-companions.toml docs/internal/audits/documentation-refresh-evidence.md +git add docs/guide/configuration.md docs/guide/cli.md trusted-server.example.toml tools/docs-parity/manifests/settings-companions.toml tools/docs-parity/manifests/snippets.toml docs/internal/audits/documentation-refresh-evidence.md git commit -m "Complete configuration documentation" ``` -### Task 14: Complete WP4 generated API contracts +- [ ] **Step 7: Push and publish the Appendix B parity checklist to #1049** + +Push the clean WP3 commit to `origin/spec-docs-refresh`, then update only the bounded `` / `` region of PR #1049's description. Render the checklist from the checked settings record and include all 17 roots, all 14 deploy IDs, all three provider profile schemas, the directional-disposition axes, the secret classifications, and the exact WP3 check results. Read the description back through the GitHub API/CLI and require the rendered rows to equal the checked record; preserve every unrelated PR-description section. + +Append the readback timestamp, PR body hash, and equality result to `documentation-refresh-evidence.md`; commit it immediately as `Record configuration checklist publication` and push it. Do not include the receipt commit's own SHA in its body. + +### Task 12: Complete WP4 generated API contracts **Files:** - Modify: `docs/guide/api-reference.md` - Modify: `tools/docs-parity/manifests/routes.toml` - Modify: `tools/docs-parity/manifests/adapter-support.toml` -- Modify/Test only for test seams: the four adapter `src/app.rs` files and route tests from Task 9 +- Modify: `tools/docs-parity/manifests/snippets.toml` +- Modify/Test only for test seams: the four adapter `src/app.rs` files and route tests from Task 8 - [ ] **Step 1: Make route generation fail on reader drift** @@ -1194,6 +795,7 @@ For every endpoint, cover auth, schemas, status codes, cache/CORS, config gates, ```bash cargo run --manifest-path tools/docs-parity/Cargo.toml -- routes --check cargo run --manifest-path tools/docs-parity/Cargo.toml -- generate --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check cargo test-fastly cargo test-axum cargo test-cloudflare @@ -1206,11 +808,11 @@ Expected: no adapter can add/remove/change a route without a record and generate - [ ] **Step 5: Commit WP4** ```bash -git add docs/guide/api-reference.md tools/docs-parity/manifests/routes.toml tools/docs-parity/manifests/adapter-support.toml crates/trusted-server-adapter-fastly/src/app.rs crates/trusted-server-adapter-axum/src/app.rs crates/trusted-server-adapter-axum/tests/routes.rs crates/trusted-server-adapter-cloudflare/src/app.rs crates/trusted-server-adapter-cloudflare/tests/routes.rs crates/trusted-server-adapter-spin/src/app.rs crates/trusted-server-adapter-spin/tests/routes.rs docs/internal/audits/documentation-refresh-evidence.md +git add docs/guide/api-reference.md tools/docs-parity/manifests/routes.toml tools/docs-parity/manifests/adapter-support.toml tools/docs-parity/manifests/snippets.toml crates/trusted-server-adapter-fastly/src/app.rs crates/trusted-server-adapter-axum/src/app.rs crates/trusted-server-adapter-axum/tests/routes.rs crates/trusted-server-adapter-cloudflare/src/app.rs crates/trusted-server-adapter-cloudflare/tests/routes.rs crates/trusted-server-adapter-spin/src/app.rs crates/trusted-server-adapter-spin/tests/routes.rs docs/internal/audits/documentation-refresh-evidence.md git commit -m "Generate adapter API documentation" ``` -### Task 15: Add deployment guides and recurring first-success smokes +### Task 13: Add deployment guides and recurring first-success smokes **Files:** @@ -1230,6 +832,8 @@ git commit -m "Generate adapter API documentation" - Modify: `tools/docs-parity/manifests/maintained-sources.toml` - Modify: `tools/docs-parity/manifests/pages.toml` - Modify: `tools/docs-parity/manifests/orphans.toml` +- Modify: `tools/docs-parity/manifests/snippets.toml` +- Modify: PR #1049 description through GitHub API/CLI - [ ] **Step 1: Write smoke failures before guides** @@ -1255,16 +859,16 @@ Set the required store mapping to `default`, local-push into `.spin/`, encode/ex Every guide command must be copyable and remain in the same order as the recurring script. State maturity, fan-out, health/startup, and unwired-store limitations from `adapter-support.toml`; a successful push is not described as a configured runtime where the bridge is still required. -Register the four new public pages immediately. Until Task 16 adds their final +Register the four new public pages immediately. Until Task 14 adds their final navigation, give any genuinely unreachable page a typed temporary orphan entry -owned by WP5 and expiring at Task 16; Task 16 must remove that entry. +owned by WP5 and expiring at Task 14; Task 14 must remove that entry. - [ ] **Step 6: Wire runnable scripts into integration CI** Run `chmod +x scripts/smoke-axum.sh scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh`. Run Axum, Fastly, and Cloudflare smokes in the existing integration workflow after their artifacts -are prepared. Consume Wrangler from the Task 15 `.tool-versions` pin, not an +are prepared. Consume Wrangler from the Task 13 `.tool-versions` pin, not an unpinned global latest. Preserve existing integration suites. - [ ] **Step 7: Verify focused journeys** @@ -1278,15 +882,27 @@ bash -n scripts/smoke-axum.sh scripts/smoke-fastly.sh scripts/smoke-cloudflare.s Run Spin or attach its time-bounded evidence. Then run the integration-test parity target and docs build. +```bash +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check +cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity +cd docs && npm run lint && npm run format && npm run build +``` + - [ ] **Step 8: Commit the deployment half of WP5** ```bash -git add docs/guide/fastly.md docs/guide/cloudflare.md docs/guide/spin.md docs/guide/axum-dev.md scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh scripts/smoke-axum.sh .tool-versions .github/workflows/integration-tests.yml crates/trusted-server-integration-tests/fixtures/configs/trusted-server.integration.toml crates/trusted-server-adapter-cloudflare/wrangler.ci.toml tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/orphans.toml docs/internal/audits/documentation-refresh-evidence.md +git add docs/guide/fastly.md docs/guide/cloudflare.md docs/guide/spin.md docs/guide/axum-dev.md scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh scripts/smoke-axum.sh .tool-versions .github/workflows/integration-tests.yml crates/trusted-server-integration-tests/fixtures/configs/trusted-server.integration.toml crates/trusted-server-adapter-cloudflare/wrangler.ci.toml tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/pages.toml tools/docs-parity/manifests/orphans.toml tools/docs-parity/manifests/snippets.toml docs/internal/audits/documentation-refresh-evidence.md git ls-files --stage scripts/smoke-axum.sh scripts/smoke-fastly.sh scripts/smoke-cloudflare.sh scripts/smoke-spin.sh | awk '$1 != "100755" { bad=1 } END { exit bad }' git commit -m "Document adapter deployment journeys" ``` -### Task 16: Complete WP5 product coverage and navigation +- [ ] **Step 9: Push and publish exact smoke journeys to #1049** + +Push the clean deployment-guide commit to `origin/spec-docs-refresh` and wait for its hosted smoke jobs. Then update only the bounded `` / `` region of PR #1049's description. For Axum, Fastly, Cloudflare, and Spin, include the exact command sequence invoked by the recurring script, its generated-state and process cleanup procedure, the strong success oracle, the two independent negative cases, and the immutable run URL or time-bounded Spin receipt. Read the description back and require each script's command and cleanup sequence to match its checked snippet records; preserve every unrelated PR-description section. + +Append the readback timestamp, PR body hash, run URLs or Spin receipt, and equality result to `documentation-refresh-evidence.md`; commit it immediately as `Record adapter smoke publication` and push it. Do not include the receipt commit's own SHA in its body. + +### Task 14: Complete WP5 product coverage and navigation **Files:** @@ -1303,7 +919,7 @@ git commit -m "Document adapter deployment journeys" - Modify: `docs/.vitepress/config.mts` - Modify: `docs/package.json` - Modify: `.github/workflows/deploy-docs.yml` -- Read/verify: `.tool-versions` (Wrangler pin established in Task 15) +- Read/verify: `.tool-versions` (Wrangler pin established in Task 13) - Create/Test: `crates/trusted-server-integration-tests/tests/documentation_snippets.rs` - Modify: `tools/docs-parity/manifests/tracked-files.toml` - Modify: `tools/docs-parity/manifests/maintained-sources.toml` @@ -1355,7 +971,7 @@ git add docs/guide/edgezero.md docs/guide/telemetry.md docs/guide/tsjs.md docs/g git commit -m "Add full documentation product coverage" ``` -### Task 17: Complete WP6 root and crate documentation +### Task 15: Complete WP6 root and crate documentation **Files:** @@ -1372,14 +988,17 @@ git commit -m "Add full documentation product coverage" - Create: `crates/trusted-server-cli/README.md` - Create: `crates/trusted-server-js/README.md` - Create: `crates/trusted-server-openrtb-codegen/README.md` +- Read/verify: `crates/trusted-server-openrtb/README.md` - Create: `scripts/README.md` - Modify: each corresponding crate `Cargo.toml` +- Modify: `crates/trusted-server-openrtb/Cargo.toml` - Modify: `tools/docs-parity/manifests/tracked-files.toml` - Modify: `tools/docs-parity/manifests/maintained-sources.toml` +- Modify: `tools/docs-parity/manifests/snippets.toml` - [ ] **Step 1: Add the failing README equality test** -Use `cargo metadata --no-deps` to enumerate every package. Expected before edits: seven missing README files and/or missing `readme =` metadata. Include an extra/unlisted README negative fixture. +Use `cargo metadata --no-deps` to enumerate every package. Expected before edits: seven missing README files and ten package manifests missing `readme =` metadata. Include an extra/unlisted README negative fixture. - [ ] **Step 2: Correct canonical contributor/operator prose** @@ -1397,6 +1016,7 @@ Add exact `readme = "README.md"` entries to the seven new crate manifests and an ```bash cargo run --manifest-path tools/docs-parity/Cargo.toml -- readmes --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check cargo metadata --no-deps --format-version 1 cargo fmt --all -- --check cd docs && npm run lint && npm run format && npm run build @@ -1407,11 +1027,11 @@ Expected: every package maps to an existing README and all active root/crate/ski - [ ] **Step 6: Commit WP6** ```bash -git add README.md CONTRIBUTING.md CLAUDE.md ProjectGovernance.md crates/trusted-server-core/README.md crates/trusted-server-core/Cargo.toml crates/trusted-server-integration-tests/README.md crates/trusted-server-integration-tests/Cargo.toml crates/trusted-server-adapter-axum/README.md crates/trusted-server-adapter-cloudflare/README.md crates/trusted-server-adapter-fastly/README.md crates/trusted-server-adapter-spin/README.md crates/trusted-server-cli/README.md crates/trusted-server-js/README.md crates/trusted-server-openrtb-codegen/README.md crates/trusted-server-adapter-axum/Cargo.toml crates/trusted-server-adapter-cloudflare/Cargo.toml crates/trusted-server-adapter-fastly/Cargo.toml crates/trusted-server-adapter-spin/Cargo.toml crates/trusted-server-cli/Cargo.toml crates/trusted-server-js/Cargo.toml crates/trusted-server-openrtb-codegen/Cargo.toml scripts/README.md tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml docs/internal/audits/documentation-refresh-evidence.md +git add README.md CONTRIBUTING.md CLAUDE.md ProjectGovernance.md crates/trusted-server-core/README.md crates/trusted-server-core/Cargo.toml crates/trusted-server-integration-tests/README.md crates/trusted-server-integration-tests/Cargo.toml crates/trusted-server-adapter-axum/README.md crates/trusted-server-adapter-cloudflare/README.md crates/trusted-server-adapter-fastly/README.md crates/trusted-server-adapter-spin/README.md crates/trusted-server-cli/README.md crates/trusted-server-js/README.md crates/trusted-server-openrtb-codegen/README.md crates/trusted-server-openrtb/Cargo.toml crates/trusted-server-adapter-axum/Cargo.toml crates/trusted-server-adapter-cloudflare/Cargo.toml crates/trusted-server-adapter-fastly/Cargo.toml crates/trusted-server-adapter-spin/Cargo.toml crates/trusted-server-cli/Cargo.toml crates/trusted-server-js/Cargo.toml crates/trusted-server-openrtb-codegen/Cargo.toml scripts/README.md tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/snippets.toml docs/internal/audits/documentation-refresh-evidence.md git commit -m "Refresh contributor and crate documentation" ``` -### Task 18: Complete WP7 rustdoc and JSDoc +### Task 16: Complete WP7 rustdoc and JSDoc **Files:** @@ -1520,7 +1140,7 @@ git add crates/trusted-server-core/src/lib.rs crates/trusted-server-core/src/pla git commit -m "Complete in-code documentation" ``` -### Task 19: Activate WP8b CI gates and release controls +### Task 17: Activate final CI and release-pending controls **Files:** @@ -1538,114 +1158,72 @@ git commit -m "Complete in-code documentation" - Modify: `TESTING.md` - Modify: `docs/guide/testing.md` - Create: `docs/internal/runbooks/documentation-automation-release.md` -- Create: `docs/internal/runbooks/patches/docs-links-release-retarget.patch` -- Create: `docs/internal/runbooks/patches/docs-links-release-disable.patch` -- Modify: `tools/docs-parity/src/gates.rs` -- Modify: `tools/docs-parity/src/workflow.rs` -- Modify: `tools/docs-parity/src/dependency_snapshot.rs` -- Modify/Test: `tools/docs-parity/tests/gates.rs` -- Modify/Test: `tools/docs-parity/tests/workflow.rs` -- Modify/Test: `tools/docs-parity/tests/dependency_snapshot.rs` -- Modify: `tools/docs-parity/manifests/tracked-files.toml` -- Modify: `tools/docs-parity/manifests/maintained-sources.toml` -- Modify: `tools/docs-parity/manifests/gates.toml` +- Modify: `tools/docs-parity/src/{gates,workflow,dependency_snapshot}.rs` +- Modify/Test: `tools/docs-parity/tests/{gates,workflow,dependency_snapshot}.rs` +- Modify: `tools/docs-parity/manifests/{tracked-files,maintained-sources,gates,snippets}.toml` +- Modify: `docs/internal/audits/documentation-refresh-evidence.md` -- [ ] **Step 1: Make static workflow fixtures fail for current gaps** +- [ ] **Step 1: Write failing automation fixtures** -Assert missing rc CodeQL triggers, rustdoc/doctest/docs-parity jobs, nested lockfile cache keys, setup-node lockfile paths, Dependabot roots, Wrangler pin, gate-region equality, schedule trust split, release patches, and openrtb-codegen workspace lints. +Assert missing rc CodeQL triggers, docs-parity jobs, rustdoc/doctest jobs, nested lockfile cache inputs, Node pins, Dependabot roots, Wrangler pin, generated gate equality, action SHA pins, final `main` targets, reader/writer separation, closed manual refresh, and release-pending runbook fields. - [ ] **Step 2: Wire blocking deterministic checks** -Add host docs-parity fmt/clippy/test/check, generated clean-diff, settings/examples/inventory/snippets/scanner/local links/readmes/jsdoc/workflow fixtures, rustdoc matrix, and native doctests. Pin Node wherever the JS build script runs. Keep external network links scheduled, not a flaky PR dependency. +Add host docs-parity fmt/clippy/test/check, generated no-diff, settings/examples/inventory/snippets/scanner/local links/readmes/JSDoc/workflow fixtures, rustdoc matrix, native doctests, docs build, and existing target regression jobs. External network links remain scheduled, not a PR dependency. - [ ] **Step 3: Normalize existing automation** -Add CodeQL `rc/*` PR triggers, `.tool-versions` deploy paths, lockfile-based -setup-node cache keys, pinned Wrangler, GitHub -Actions/browser/Next.js/docs-parity Dependabot roots, and -`[lints] workspace = true`. Choose current stable action versions at -implementation time and pin every new `uses` reference by full SHA with its -source version recorded. The `docs-links.yml` action pins were selected and -frozen in Tasks 10-11; verify them here but do not repin or otherwise change a -non-activation byte. A required repin takes the controller-repair path. - -- [ ] **Step 4: Finalize the rc workflow's activated form** - -Starting from the exact workflow hash recorded in Task 11, add only the -reviewed c2 activation regions: weekly `17 9 * * 1`, the fixed stateful -schedule/refresh path already configured on the dormant link jobs, and the -split snapshot generator/writer plus closed refresh operation with their -20/5-minute timeouts. Preserve every -non-activation byte, including per-PR serialization, controller, -attestation, current-`main` assertion, fail-closed future maintenance mode, and -unreachable link/issue definitions. -The issue writer schema-validates bounded results, deduplicates the single -owned report issue, and auto-closes it after a clean scheduled run. Generate a -fresh c2 patch and its inverse covering both protected files. Record the final -rc mode/blob IDs for `.github/workflows/docs-links.yml` and -`.github/dependabot.yml`; prove the c2 base workflow hashes to the Task 11 blob, -and prove applying c2 produces mode/blob identity with rc for both protected -paths. The inverse must restore both base blobs without overwriting unrelated -base changes. If a non-activation byte must change, stop and ship a separately -reviewed controller-repair PR before continuing; do not hide it in c2. - -- [ ] **Step 5: Finalize release and rollback runbooks** - -Provide normal and abandonment patch templates, freeze semantics, queued-run -enumeration, optional separately scoped `actions: write` cancellation token, -empty same-identity retirement request, 201 receipt fields, -automatic-main-graph verification, branch deletion gate, CNAME fallback, and -branch-protection restoration ordering. The normal runbook requires -base-vs-rc-head mode/blob identity for both protected paths before the broad -release merge and a separately reviewed sync/repair PR if they differ; it also -proves post-(e) maintenance before closure. Add an rc dependency-change -checklist with a named owner: every merge changing -`tools/docs-parity/Cargo.toml` or `tools/docs-parity/Cargo.lock` remains -incomplete until a post-merge `refresh_dependency_snapshot` receipt is -attached; missed/failed refreshes are security-coverage incidents and the -weekly run is only a reconciliation backstop. +Add CodeQL `rc/*` PR triggers, `.tool-versions` deploy paths, exact setup-node lockfile paths, pinned Wrangler, all approved Dependabot roots targeting `main`, and `[lints] workspace = true`. Choose current stable action/tool versions at implementation time, cite their primary release sources, and pin every new `uses` by full SHA. + +- [ ] **Step 4: Finalize `docs-links.yml`** + +Preserve ordinary read-only PR validation. Finalize weekly `17 9 * * 1` schedule, fixed non-canceling concurrency, 30/20/5-minute timeouts, bounded artifacts, issue dedup/auto-close, fixed snapshot identity, authenticated default-branch SHA, and no-input manual refresh. Require no `pull_request_target`, `merge_group`, status writer, temporary rc target, retirement path, or caller-selected tool. + +- [ ] **Step 5: Write the release-pending runbook** + +Document exact post-main Pages/CNAME smoke, first scheduled link run, first dependency submission and 201/graph proof, Dependabot/action-pin inspection, alert owner/SLA, and optional branch-protection activation only after contexts report from expected apps. Mark every receipt release-pending; do not create another PR or claim execution from rc. - [ ] **Step 6: Generate all gate consumers** -Regenerate CLAUDE/AGENTS/TESTING/guide testing regions from `gates.toml`; prove command files and CONTRIBUTING remain link-only. A second generation must produce no diff. +Regenerate CLAUDE/AGENTS/TESTING/guide testing from `gates.toml`. Prove command files, CONTRIBUTING, and PR template remain link-only. A second generation produces no diff. + +- [ ] **Step 7: Deduplicate all code follow-ups** + +Search the tracker for every item in the spec. File or record an exact existing-issue disposition for all twelve, with URL, owner, and labels. Do not collapse distinct adapter-store, config-bridge, health, reserved-field, placeholder, inline-secret, deploy-ID, CLI-help, telemetry, env-store, or staging-blob findings. -- [ ] **Step 7: Run WP8b focused acceptance** +- [ ] **Step 8: Run WP8 acceptance and commit** ```bash cargo test --manifest-path tools/docs-parity/Cargo.toml cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all cargo run --manifest-path tools/docs-parity/Cargo.toml -- generate --check +cargo run --manifest-path tools/docs-parity/Cargo.toml -- snippets --check cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings cargo fmt --all -- --check cd docs && npm run lint && npm run format && npm run build ``` -Expected: all static/runtime negative fixtures fail for their intended diagnostic, then the real repository passes; generation is idempotent. - -- [ ] **Step 8: Deduplicate and file every code follow-up** - -Search the tracker for each item in the spec's “Follow-up issues to file” section. File or record an existing-issue disposition for all twelve items, with URL, owner, and labels in the evidence record; do not collapse the adapter-store, config-bridge, health-contract, reserved-field, placeholder, inline-secret, deploy-ID, CLI-help, telemetry, env-store, or staging-blob issues into vague umbrella tickets. - -- [ ] **Step 9: Commit WP8b** +Stage exactly the enumerated files and commit: ```bash -git add .github/workflows/format.yml .github/workflows/test.yml .github/workflows/integration-tests.yml .github/workflows/codeql.yml .github/workflows/deploy-docs.yml .github/workflows/docs-links.yml .github/dependabot.yml .tool-versions crates/trusted-server-openrtb-codegen/Cargo.toml CLAUDE.md AGENTS.md TESTING.md docs/guide/testing.md docs/internal/runbooks/documentation-automation-release.md docs/internal/runbooks/patches/docs-links-release-retarget.patch docs/internal/runbooks/patches/docs-links-release-disable.patch tools/docs-parity/src/gates.rs tools/docs-parity/src/workflow.rs tools/docs-parity/src/dependency_snapshot.rs tools/docs-parity/tests/gates.rs tools/docs-parity/tests/workflow.rs tools/docs-parity/tests/dependency_snapshot.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/gates.toml docs/internal/audits/documentation-refresh-evidence.md +git add .github/workflows/format.yml .github/workflows/test.yml .github/workflows/integration-tests.yml .github/workflows/codeql.yml .github/workflows/deploy-docs.yml .github/workflows/docs-links.yml .github/dependabot.yml .tool-versions crates/trusted-server-openrtb-codegen/Cargo.toml CLAUDE.md AGENTS.md TESTING.md docs/guide/testing.md docs/internal/runbooks/documentation-automation-release.md tools/docs-parity/src/gates.rs tools/docs-parity/src/workflow.rs tools/docs-parity/src/dependency_snapshot.rs tools/docs-parity/tests/gates.rs tools/docs-parity/tests/workflow.rs tools/docs-parity/tests/dependency_snapshot.rs tools/docs-parity/manifests/tracked-files.toml tools/docs-parity/manifests/maintained-sources.toml tools/docs-parity/manifests/gates.toml tools/docs-parity/manifests/snippets.toml docs/internal/audits/documentation-refresh-evidence.md git commit -m "Activate documentation enforcement gates" ``` -### Task 20: Close Epoch 1 and make PR (a) implementation-ready +### Task 18: Close PR #1049 implementation **Files:** - Modify: `docs/internal/audits/documentation-refresh-evidence.md` - Modify: `docs/internal/audits/documentation-refresh-decisions.md` +- Modify: PR #1049 description through GitHub API/CLI -- [ ] **Step 1: Reassert the exact rc baseline at final PR head** +- [ ] **Step 1: Reassert immutable state** -Fetch and require `origin/rc/202608` still equals `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`; require the implementation branch contains it. Any advance triggers a focused delta audit, spec update, regenerated records, and re-review before continuing. +Fetch and require `origin/rc/202608` still equals `07dfc1c6dddf69345ded17bd2d40a3d01bb39bcf`, the branch contains it, PR #1049 targets rc from `spec-docs-refresh`, and the worktree has no unrelated bytes. -- [ ] **Step 2: Run the complete local CI gate list** +- [ ] **Step 2: Run the complete local matrix** ```bash cargo fmt --all -- --check @@ -1662,6 +1240,7 @@ cargo test-axum cargo test-cloudflare cargo test-spin cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity +cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test documentation_snippets ./scripts/test-cli.sh cargo test --package trusted-server-openrtb-codegen --target "$(rustc -vV | sed -n 's/host: //p')" cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1 @@ -1670,49 +1249,18 @@ cargo fmt --manifest-path tools/docs-parity/Cargo.toml -- --check cargo clippy --manifest-path tools/docs-parity/Cargo.toml --all-targets -- -D warnings cargo test --manifest-path tools/docs-parity/Cargo.toml cargo run --manifest-path tools/docs-parity/Cargo.toml -- check --all -cd crates/trusted-server-js/lib -npm ci -npm run lint -npx vitest run -npm run format -npm run build +cd crates/trusted-server-js/lib && npm ci && npm run lint && npx vitest run && npm run format && npm run build cd ../../.. -cd docs -npm ci -npm run lint -npm run format -npm run build -cd .. +cd docs && npm ci && npm run lint && npm run format && npm run build ``` -Also run Task 18's rustdoc matrix, all four Task 15 smokes/evidence paths, and the integration workflow's runnable smoke targets. - -- [ ] **Step 3: Confirm the hosted check topology provisionally** - -Confirm CodeQL with rc triggers, all format jobs, all seven `test.yml` jobs, -all four `integration-tests.yml` jobs, release builds, JS build/test, docs build, -and the new WP8 jobs report with the expected names and GitHub App identities. -Record provisional run URLs and the current head, but do not call this the final -hosted proof because Step 8 may create one last evidence commit. Local -substitutes do not replace hosted evidence. - -- [ ] **Step 4: Prove every acceptance surface** +Also run Task 16 rustdoc commands and all four Task 13 smoke/evidence paths. Record exact commands, tool versions, durations, and results. -Record: generated no-diff; source/disposition equality; all retired/privacy scans; dead-link negatives; route/settings/integration equality; snippet diagnostics; README/JSDoc/rustdoc gates; Pages containment/CNAME smokes; all follow-up issue URLs/dispositions; PR (b), (c), and (d) URLs; c2 issue/owner; e issue plus reviewed runbooks. +- [ ] **Step 3: Prove every acceptance surface** -- [ ] **Step 5: Activate required checks on rc** +Record generated no-diff; classification/disposition equality; retired/privacy scans; route/settings/integration equality; snippet diagnostics; README/JSDoc/rustdoc gates; local Pages/CNAME artifact proof; first-success smokes; all follow-up issue URLs/dispositions; package commit/path review; and release-pending fields without fabricated receipts. Read PR #1049's description back through the GitHub API/CLI and require the bounded settings-parity region to equal the Appendix B checklist and the bounded adapter-smokes region to contain each of the four exact script command/cleanup sequences plus its immutable run evidence. -Require the full WP8 suite on `rc/202608`, record context names/source apps/bypass policy, and demonstrate one planted failure actually prevents an rc merge. Remove the planted failure and show success. - -- [ ] **Step 6: Reprove the active `main` controller** - -Record strict/up-to-date protection, every pre-existing PR seed result, unauthorized protected-file block, unrelated-PR success, and full-suite `main` deferral. Do not claim the full suite is required on `main` yet. - -- [ ] **Step 7: Review commit/package shape** - -Require one reviewable commit or small series per package, generated-output commits separated where review needs it, no unrelated runtime changes, and no squash-on-merge. Run `git diff --check` and review `git diff origin/rc/202608...HEAD` path by path. - -- [ ] **Step 8: Commit the final Epoch 1 records and prove a clean tree** +- [ ] **Step 4: Commit final records** ```bash git add docs/internal/audits/documentation-refresh-evidence.md docs/internal/audits/documentation-refresh-decisions.md @@ -1721,188 +1269,30 @@ git commit -m "Record documentation refresh acceptance" git status --porcelain ``` -Expected: the status command prints nothing. If the final records do not -change, omit the empty commit but still require the clean status. - -- [ ] **Step 9: Re-run hosted checks on the exact final head and mark implementation-ready** - -After Step 8, record the new exact PR head in the PR description and require -every hosted check named in Step 3 to report green on that SHA. Record the -final run URLs and app identities in the PR description, which can be updated -without advancing the commit. Reassert the rc baseline and clean-tree proof. -Only then may the owner mark PR (a) #1049 implementation-ready. All approvals -must be current and the PR ready to merge. Record that activation still -requires the rc merge plus Task 21. - -### Task 21: Execute Epoch 2 activation PR (c2) - -**Files on the isolated `main` branch:** - -- Modify: `.github/workflows/docs-links.yml` -- Modify: `.github/dependabot.yml` - -**External system of record:** the named c2 tracking issue and c2 PR timeline. -The Epoch 1 repository evidence ledger already contains the issue URL and -evidence schema. Do not create an evidence-only branch or advance rc merely to -store receipts that exist only after c2 merges. Use append-only issue comments -under the Task 1 schema: paste the redacted request/response and graph/ruleset -captures with body hashes and exact SHAs; links alone are not evidence. - -- [ ] **Step 1: Record and authenticate the rc merge** - -After #1049 merges, record its exact merge result as `merged_rc_tip` and initial `validated_rc_tip`. Require the latter equals current `origin/rc/202608`; if rc advanced, audit the full delta and record a new exact `validated_rc_tip`. - -- [ ] **Step 2: Dispatch read-only post-merge validation** - -Dispatch `.github/workflows/docs-links.yml` at `ref: main` with `validate_rc` and exact `tool_sha`. Assert the run's `github.sha` was the authenticated current `main` tip and no writer/attestation job ran. - -- [ ] **Step 3: Cut c2 from a fresh `main` tip** - -Record c2's own `audited_main_tip`. Apply only the reviewed c2 patch: weekly -schedule, rc-targeted Dependabot version roots, snapshot reader/writer, and -`refresh_dependency_snapshot`. Require both resulting protected paths, -including modes and blob IDs, to equal their counterparts at exact -`validated_rc_tip`; workflow-only equality is not sufficient. - -- [ ] **Step 4: Stage, statically validate, and commit the exact c2 candidate** - -Use a detached trusted-tool worktree at exact `validated_rc_tip`. In the c2 -worktree run: - -```bash -git add .github/workflows/docs-links.yml .github/dependabot.yml -git diff --cached --check -git diff --cached --name-status "$AUDITED_MAIN_TIP" -git diff --quiet -git ls-files --stage .github/workflows/docs-links.yml .github/dependabot.yml -git ls-tree "$VALIDATED_RC_TIP" .github/workflows/docs-links.yml .github/dependabot.yml -test "$(git -C "$TRUSTED_RC_WORKTREE" rev-parse HEAD)" = "$VALIDATED_RC_TIP" -cargo run --manifest-path "$TRUSTED_RC_WORKTREE/tools/docs-parity/Cargo.toml" -- workflow validate-local-index --repository "$C2_WORKTREE" --base "$AUDITED_MAIN_TIP" --candidate-kind c2 -git commit -m "Activate documentation automation" -``` - -Expected: cached name/status is exactly the two protected files, byte/count -budgets pass, the two `ls-*` outputs have identical modes/blob IDs by path, and -the trusted validator accepts the reviewed c2 shape without executing candidate -content. Record the commit SHA before opening the PR. - -- [ ] **Step 5: Let the automatic gate publish pending** - -Open c2 and record that the base controller authenticates the candidate set but publishes `docs/automation-delta: pending`, never success. - -- [ ] **Step 6: Run exact dual-ref validation** - -Dispatch `validate_main_pr` at `ref: main` with exact `validated_rc_tip`, c2 head, PR number, and base SHA. Require the local uncapped diff and AST policy pass; verify the manual run replaces pending with success on exactly that head/base. - -- [ ] **Step 7: Reassert head/base and merge** - -Immediately before merge, require current PR head/base equal the validated pair and the base equals recorded `audited_main_tip`. Merge without extra paths. - -- [ ] **Step 8: Submit and verify the first dependency snapshot** - -Dispatch `refresh_dependency_snapshot` with no SHA/PR inputs. Record in the c2 -tracking issue the authenticated rc SHA/ref, fixed detector/correlator, -external snapshot ID, 201 receipt, dependency-graph visibility, and -alert-triage owner/runbook/two-business-day SLA. Paste and hash the redacted -request, 201 response, and graph API capture; link the exact workflow run for -navigation. - -- [ ] **Step 9: Observe a real scheduled run** - -Require a genuine cron run (not manual emulation) to complete the link reader, -schedule-only issue writer, snapshot reader/writer, and same-identity -reconciliation. Record timeouts/concurrency behavior and resulting -issue/snapshot state in the c2 tracking issue and link the run; no repository -evidence commit follows. - -- [ ] **Step 10: Prove rollback readiness** - -Validate the reverse-c2 patch for both protected files against the current base, -including restored mode/blob IDs. If activation is unhealthy, merge only that -inverse, stop resubmission, drain/cancel prior snapshot runs, submit an empty -same-identity snapshot, and reopen the activated milestone until a repaired c2 -passes again. - -- [ ] **Step 11: Mark this refresh activated** - -Activation closes this refresh. Do not claim lifecycle closure; Task 22 remains release-owned. - -### Task 22: Hand off the Epoch 3 release lifecycle - -**Files:** - -- Use: `docs/internal/runbooks/documentation-automation-release.md` -- Use: `docs/internal/runbooks/patches/docs-links-release-retarget.patch` -- Use: `docs/internal/runbooks/patches/docs-links-release-disable.patch` -- Modify later in PR (e): `.github/workflows/docs-links.yml` -- Modify later in PR (e): `.github/dependabot.yml` - -**External system of record:** the named release-handoff issue and PR (e) -timeline. Post-merge retirement, ruleset, graph, and deletion receipts are -captured there under the Task 1 append-only schema, with exact SHAs, redacted -bodies, statuses, and SHA-256 hashes; URLs are navigation only. No follow-on -repository evidence PR is part of this lifecycle. - -- [ ] **Step 1: Verify the tracked issue, owner, and both reviewed paths exist before activation closes** - -The normal path retargets to `main` and removes temporary snapshot jobs/refresh. The abandonment path removes the workflow, schedule, Dependabot entries, snapshot jobs, and refresh; it never points automation at missing tooling. - -- [ ] **Step 2: At release, freeze and authenticate rc** - -Pause queue/direct writes, record bypass policy and exact tip, audit any delta into `validated_rc_tip`, and recheck the freeze immediately before validation, retirement, and deletion. - -- [ ] **Step 3: Prove the normal release merge is net-empty on protected paths** - -On the normal path, authenticate the current-main base and frozen rc head and -compare modes/blob IDs for `.github/workflows/docs-links.yml` and -`.github/dependabot.yml`; require both identical. Demonstrate that the -base-vs-head protected delta is empty while the three-dot history may contain -the earlier independent copies, then merge the rc release PR with -`docs/automation-delta` green. If either path differs, stop and use a separately -reviewed sync/repair PR before retrying. On abandonment, record this step as -inapplicable and do not merge rc. - -- [ ] **Step 4: Open the concrete PR (e) from the matching template** - -Record a fresh `audited_main_tip`; use the active base controller's automatic pending state and exact dual-ref manual validation. Reassert head/base before merge. - -- [ ] **Step 5: Drain and retire the temporary snapshot** - -After (e) stops resubmission, enumerate every queued/in-progress same-identity run, wait or cancel with a separate `actions: write` token, and submit an empty snapshot at the exact (e) merge SHA/ref `refs/heads/main`. Record the 201 receipt and prove it is the final submission. +If records do not change, omit the empty commit but still require clean status. -Attach the run enumeration, cancellation/drain result, request body hash, 201 -response, and final-submission proof to the release-handoff issue. +- [ ] **Step 5: Push and verify hosted checks on the exact head** -- [ ] **Step 6: Close branch and protection state in order** +Push to `origin/spec-docs-refresh`. Update only the bounded `` / `` region of PR #1049's description with the exact final head/base, run URLs, job/app identities, local evidence, and release-pending operations. Preserve every unrelated region. Read the final PR description back and revalidate the settings-parity and adapter-smokes regions after the final-acceptance update. Require every expected hosted job green on that SHA; a prior head does not count. -Normal: verify automatic `main` dependency parsing, activate the deferred full -WP8 suite on `main`, and run both a permitted action-repin maintenance fixture -and an AST-weakening rejection through `validate_main_maintenance` using the -authenticated current-main tool. Keep `docs/automation-delta` required, -re-smoke Pages/CNAME, then delete rc. Abandonment: remove the nonreporting -automation-delta required context immediately after (e), prove the next `main` -PR is not stranded, then delete rc. Never delete rc first. +- [ ] **Step 6: Mark implementation complete** -Attach the final graph, ruleset, live-site, and branch-deletion receipts to the -release-handoff issue and close it only after the selected path is complete. +Review `git diff origin/rc/202608...HEAD` path by path, require no unrelated runtime change, keep package commits unsquashed, and mark PR #1049 ready for review without opening or requesting approval on any other PR. Completion means repository implementation is finished; merge and release-pending operations remain outside this plan. ## Final plan-to-spec traceability -| Spec surface | Plan tasks | -| --------------------------------------- | ---------- | -| Owner questions and immutable tips | 1 | -| WP1 containment/CNAME/hygiene | 2–4 | -| WP8a tool, manifests, controller design | 5–10 | -| `main` validation controller PR (c) | 11 | -| WP2 truth pass | 12 | -| WP3 configuration | 13 | -| WP4 API/routes | 14 | -| WP5 deployment and coverage | 15–16 | -| WP6 root/crate docs | 17 | -| WP7 in-code docs | 18 | -| WP8b blocking enforcement | 19 | -| Epoch 1 / PR (a) | 20 | -| Epoch 2 / activation PR (c2) | 21 | -| Epoch 3 / release PR (e) | 22 | - -The implementation is complete only at Task 21's **activated** milestone. Task 22 is deliberately specified and owned but is not part of this refresh's implementation completion claim. +| Spec surface | Plan tasks | +| ------------------------------------------ | ---------- | +| Single-PR decisions and evidence | 1-3 | +| WP1 containment, CNAME, policy | 2-3 | +| WP8a tool, manifests, workflow foundations | 4-9 | +| WP2 truth pass | 10 | +| WP3 configuration | 11 | +| WP4 API/routes | 12 | +| WP5 deployment and product coverage | 13-14 | +| WP6 root/crate docs | 15 | +| WP7 in-code docs | 16 | +| WP8b final CI/release-pending controls | 17 | +| Final PR #1049 acceptance | 18 | + +The implementation is complete only when Task 18 verifies the exact final #1049 head. No individual implementation PR or default-branch receipt is part of this plan.