From b2d2b3172b809318e35c15273d986e4878da5229 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/14] 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 9e85a19011f24097c2120e3fdd3044acc6ed5119 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/14] 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 4ffd5701476ed027aaa36512dc50171099206d48 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/14] 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 ad83a0d497ab12a254d654655e628679fd108426 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/14] 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 00ed524277cc4d0bc66de56778067e3e80cd7a6c 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/14] 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 65c6d7cf9014a518bab00b3e4cc02d8db22be292 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/14] 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 f2a35d9cab124368ed3070d91be473dc8b948719 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/14] 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 873d369db6b523c0c3d93ef615dc01721485cb44 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/14] 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 087e1a7b0e3b5520d99ebc3c3f2613b0728f7790 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/14] 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 ccefecdad4a6c0ab3922642506ef558e1292f993 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/14] 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 9c0b1e8943c16b3a726bbd4a9856a59e06227175 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/14] 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 1260f2cd1cb24e218aedf539150a320b87dcca34 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/14] 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 168ce81fa934878b7f2c7448daaf0f8f3a8d749f 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/14] 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 12213706032997db1821671ce6f3be0a28ab8db9 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/14] 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.