diff --git a/BACKLOG.md b/BACKLOG.md new file mode 100644 index 0000000..87b5dd6 --- /dev/null +++ b/BACKLOG.md @@ -0,0 +1,97 @@ +# BACKLOG.md — consolidated ledger (2026-07-11) — FOR GATE APPROVAL + +Sources: GitHub (0 open issues, 0 open PRs — verified via API; repo at merged +PR #55), repo grep (TODO/FIXME/HACK/XXX), docs-vs-reality diff (README, +ROADMAP, CLAUDE.md, runbook, specs), CI inventory (10 workflows, none +disabled), the Phase-1 design audit (`audit/AUDIT.md`), and the stack-currency +check. Nothing found was dropped. **Scope key** — the column the gate decides: + +- **SHIP** — proposed in-scope for this effort (buildable from the repo alone) +- **OWNER** — needs the owner's data, account access, or sign-off (not code) +- **DEFER** — documented-deliberate future work; stays deferred + +## A. Design elevation (from DESIGN.md — all SHIP) + +| ID | Item | Source/evidence | Status | Pri | Acceptance criteria | +|---|---|---|---|---|---| +| D1 | Receipt/verdict component system (the signature): shared visual language for checker verdicts, catalog verification badges, Studio policy gate, enforcement page | DESIGN §1; audit P1-4 | absent | P1 | One shared component/token set renders clean/blocked/chain verdicts; used by ≥4 surfaces; axe clean | +| D2 | Checker verdict redesign + island i18n (fixes P0-2/P0-3) | `PolicyChecker.svelte` | broken i18n | **P0** | Island honors `lang` (house STR pattern, es/ar shipped); verdict = receipt band; CLI jargon behind a disclosure; localized empty/error states | +| D3 | Catalog explorer density redesign: responsive 1→2→3-col receipt cards, designed facets w/ counts, `accent-color`, designed zero-result/empty/error states at full 1,359-entry dataset; locale-aware CTA (`:358`) | audit P1-3, P1-8 | default-ish | P1 | Desktop page no longer a 20k-px column; facets on-token; all states designed; CTA locale-aware | +| D4 | Token-only island rule: retire `--sl-*`/raw-hex/off-scale rems from BuildStudio, CostEstimator, ModelCompass, CatalogExplorer; add `--shadow-1`, verdict washes, global `accent-color` | audit P1-5 | split dialects | P1 | grep: zero `--sl-color` refs in island styles; zero raw hex; `/build/models/` axe color-contrast = 0 (closes the 89-node finding) | +| D5 | Landing: replace stock CardGrid/LinkCard sections with bespoke WorkOrderGrid; copy-register pass | `index.mdx:37-99`; audit P1-1, P1-7 | stock | P1 | No Starlight Card/LinkCard on the landing; copy passes the register rules (§4) | +| D6 | Footer override (last stock chrome) | audit P1-2 | stock | P1 | Designed, token-native, locale-aware footer on all pages | +| D7 | Copy-register pass on /build /check /build/models /start intros + island empty/error states (en/es/ar together) | audit P1-7 | jargon | P1 | Register rules met; es/ar land in the same commit as en | +| D8 | `TODO: confirm` → designed provenance chip ("unverified — awaiting human check"), same semantics | audit P1-6 | placeholder register | P1 | No user-visible "TODO"; zero-fabrication semantics preserved; localized | +| D9 | BuildStudio stray strings → STR table; AccountWidget ar placeholder; data-layer prose i18n keys (compass cautions, estimator labels) | audit P1-8 | strays | P1 | grep finds no hardcoded user-facing English in islands with i18n tables | +| D10 | `/console/` joins the token layer (raw hex → tokens, `.btn`/`.field`); stays standalone-CSP, stays English (documented operator exception) | audit P1-10 | off-system | P1 | Zero raw hex in console surfaces; CSP wss: rule unchanged (invariance test stays green) | +| D11 | Arabic display face: self-hosted subset OFL face (Readex Pro candidate) wired via `:lang(ar)` | audit P1-11; DESIGN §2/§7 | absent | P1 | ≤45 KB woff2, OFL license committed, `/ar/` headings render the face; perf budgets hold | +| D12 | Hydration pass: `client:visible` for below-fold islands; ModelCompass considered for static render + filter-only island | audit P1-9 | all-eager | P1 | No LCP regression on /catalog/; JS transferred on /build/models/ drops; e2e stays green | +| D13 | Page-weight ceilings recorded + measured (reading ≤250 KB, tool ≤450 KB, landing ≤350 KB) | DESIGN §8 | new | P2 | Measured per page type in VERIFICATION.md; over-ceiling pages fixed | + +## B. Functional backlog + +| ID | Item | Source | Status | Pri | Scope | Acceptance criteria | +|---|---|---|---|---|---|---| +| B1 | i18n freshness/governance layer: CLAUDE.md documents it (incl. a 2-review `security_sensitive` gate) but **none of it exists**; one flagged page unprotected; schema lacks the field | CLAUDE.md i18n section vs repo | absent | **P0** | **GATE DECISION** | Option 1: build it (freshness+stamp scripts, additive security-gate workflow, banner, TRANSLATING.md, CODEOWNERS, schema field). Option 2: amend CLAUDE.md to planned-not-built + add the schema field & a minimal 2-review gate for the one flagged page. Recommendation: **Option 2 now (SHIP), full layer as its own later effort** — it is a governance feature, not a design one | +| B2 | CSP Report-Only → Enforce (mechanism complete; flip is env var + log review) | ROADMAP:74; `security-headers.ts` | partial | P1 | OWNER | `[csp-report]` logs reviewed; `CSP_MODE=enforce` set; auth+console+admin verified | +| B3 | Sveltia `/admin` GitHub OAuth backend verified (editors can log in) | runbook Part A | unverifiable from repo | P1 | OWNER | An editor completes `/admin/` login end-to-end | +| B4 | Branch protection: mark `verify`,`quality`,`security-pr`,`path-a`,`e2e`,`skills` required | runbook; ROADMAP:77 | unverifiable from repo | P1 | OWNER | Required-checks list includes all six | +| B5 | Enable GitHub Dependency Graph (dependency-review currently no-ops) | `security-pr.yml:38-42` | setting off | P2 | OWNER | Review job produces results on a test PR | +| B6 | Admin phases 4–8 (portability, content-mgmt UI, moderation+NCMEC w/ legal review, analytics, CI extension) | admin-panel-spec | phases 0–3 done | P1 | DEFER (own track — active, sliced, separately gated) | Per-phase per spec | +| B7 | Staging follow-ups: publish→PR path, enforcement wiring for drafts, expiry job, unabandon decision | LOOP.md findings | deferred | P2 | DEFER (with B6) | Publish slice opens a real PR from a `ready` draft, enforcement green | +| B8 | Catalog prose translation run es+ar (pipeline built; 1 of 1,360 files translated per locale) | TRANSLATIONS.md; runbook §B5 | pipeline ready | P2 | OWNER (needs `ANTHROPIC_API_KEY` + wave sign-off) | Waves run; PRs native-reviewed; `machine_translated: true` | +| B9 | Native-speaker review of ~25 machine-translated narrative pages | TRANSLATIONS.md | open | P2 | OWNER | List emptied | +| B10 | NIP-07 one-liner in public docs (name nos2x/Alby near sign-in) | runbook Part A | open | P2 | SHIP | Line present, localized | +| B11 | Goose live agent loop (`goose serve`/ACP) | runbook §B3; PLAN §5 | deferred | P2 | DEFER | — | +| B12 | Server-side recipe signing / shareable links | runbook §B4; PLAN §5 | deferred | P2 | DEFER | — | + +## C. Content/data decisions (owner's data, not engineering) + +| ID | Item | Evidence | Pri | Scope | Acceptance criteria | +|---|---|---|---|---|---| +| C1 | Cost Estimator: 12× `unitPrice: null` + 3× `lastVerified: null` (Cloudflare/VEXXHOST/Denvr) — confirm from primary sources **or hide the module** | `providers.ts:62-97`; worksheet | P1 | OWNER | Prices transcribed + stamped, tests green — or module hidden | +| C2 | Cost Estimator tier bands / scale multipliers / `requestsPerMauPerMonth` product decisions | `tiers.ts:26-30`; `_interface.ts:10-30` | P1 | OWNER | Sign-off recorded, values updated | +| C3 | Model Compass: 7 models `score: null` + maple-ai pricing (runbook says 3 — undercount) | `models.ts:37-235` | P1 | OWNER | Filled from cited sources or annotated "vendor does not publish" | +| C4 | Alfred's PWA scope/repo decision | ROADMAP:69-70 | P2 | OWNER | Decision recorded | +| C5 | Dependabot remainder (5 dev-only) + the **Astro 7 coupled upgrade** (astro 7.0.7 + starlight 0.41.3 + @astrojs/svelte 9; breaking list verified, none of the removed APIs used here; fixes the low esbuild dev advisory) | stack report | P2 | **GATE DECISION** — recommend: not in this effort (identity work shouldn't ride a major framework jump); schedule as its own slice after | If taken later: coupled upgrade in one slice, verify:all + screens diff green | + +## D. Doc-drift cleanups (all SHIP, one commit each) + +| ID | Item | Pri | +|---|---|---| +| E1 | ROADMAP refreshed through PR #55 (Done/Decisions/date) | P2 | +| E2 | admin-panel-spec header → per-phase status ledger (0–3 built, 4–8 open) | P2 | +| E3 | PLAN.md footer → executed-status note (slices A–E shipped, broker retired) | P2 | +| E4 | README deploy section → Workers Builds topology (currently describes Pages dashboard) | P2 | +| E5 | CLAUDE.md `TRANSLATING.md` reference fixed (or file created per B1 decision) | P2 | +| E6 | One authoritative catalog count (generated figure) used everywhere (1,355 vs ~2,182 vs 1,360 on disk) | P2 | +| E7 | Runbook §B2 corrected to 7 models + maple-ai | P2 | +| E8 | MOBILE_FIXES/mobile-audit headers note "merged"; only the contrast item remains (closed by D4) | P2 | +| E9 | CLAUDE.md constraint 4: one line naming the protected workflow set {verify, security-pr, quality} (doc-only; mapping currently lives only in guard.py) | P2 | +| E10 | `LOOP.md` + `audit/` working artifacts: propose committing `audit/` (screens are the before-record) and deleting `LOOP.md` after harvesting B7 (items already captured here) | P2 | + +## D2. External backlogs, named — NOT consolidated (no silent third backlog) + +Two owner-side backlogs are referenced in session context but have **zero +matches anywhere in this repository** (grep verified 2026-07-11): +**QA-REMEDIATE-v1** (remediation backlog) and **PANEL-RESPEC-v2** (admin-panel +re-spec). They live outside the repo (owner's project workspace) and therefore +are NOT consolidated into this document. Status: **explicitly out-of-scope +until the owner supplies their contents**, at which point they merge here as +their own section with the same columns. D10 (console) is already fenced to +token-compliance-only so nothing in this effort preempts PANEL-RESPEC-v2. + +## E. Gate questions (the decisions this document exists to get) + +- **G1** — Approve scope column as marked? (SHIP = D1–D13, B1-Option-2, B10, E1–E10.) +- **G2** — B1: Option 1 (build the full i18n governance layer now) or Option 2 + (truthful CLAUDE.md + schema field + minimal gate now; full layer later)? +- **G3** — Ship-prompt DoD correction: full-tree enforcement "passes" is + unsatisfiable (engine's own fixtures); accept "zero hits outside + enforcement's own definitions/fixtures" as the criterion? +- **G4** — Name collisions: root `AUDIT.md`/`PLAN.md` are completed Goose-pivot + records. Proposal: move to `docs/archive/`, promote `audit/AUDIT.md` and the + Phase-3 PLAN.md to root. Approve the move (Rule 9: file moves listed here)? +- **G5** — C5: confirm Astro 7 upgrade is OUT of this effort. +- **G6** — DESIGN.md direction approved? (Extend civic-workshop; receipt as + signature; §10 self-critique includes the palette-honesty call.) diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..f726898 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,181 @@ +# DESIGN.md — the design elevation, decided before it is built + +Status: **PROPOSED — awaiting owner approval at the Phase-2 gate** (with BACKLOG.md). +Everything here is a decision, not an aspiration: each section maps to backlog +items with acceptance criteria. Sources: the Phase-1 audit (`audit/AUDIT.md`), +39 archived screenshots (`audit/screens/`), and the token/component inventory. + +## 1. Concept + +This project's product is **judgment you can inspect**: license receipts pinned +to commits, verdicts from an enforcement engine anyone can rerun, verification +stamps a fork inherits. The site already owns an honest identity for this — the +"civic workshop": cool green-ink paper, a structural green, one marigold spot, +crop marks and spec rails, a rubber-stamp seal. The elevation is **not a new +identity**. It is finishing the one we have where it currently stops: the +identity today lives in the *chrome* (hero, headings, docs shell) while the +*working surfaces* — the catalog at full density, the checker's verdict, the +Studio's policy gate, the console — still read as default-ish widgets. Those +surfaces are the product's thesis, so that is exactly backwards. + +**Signature element: the RECEIPT.** One visual language for every trust moment +— catalog verification badges, license-at-commit lines, checker verdicts +(clean / blocked / chain-to-excluded), the Studio's policy gate, the +enforcement page's verdict band, admin audit views. A receipt is: a stamped +edge (2px structural rule), a status color that is never decorative +(`--ok/--warn/--danger`), the evidence inline (commit SHA, org chain, license +id), and a plain-verb sentence a non-developer can act on. Boldness is spent +here — the blocked-dependency moment should feel like a customs stamp coming +down — and everywhere else stays quiet: paper, ink, structure. + +## 2. System (extend, don't replace) + +**Palette — unchanged, formalized.** The existing named tokens stay the +canon: `--bg --surface --surface-2 --ink --ink-soft --structure --signal +--edge(-strong) --control-edge --ring` + status pairs `--ok/--warn/--danger +-edge/-text` (`src/styles/tokens.css:86-126`). Additions (new tokens, not new +colors): `--verdict-ok-bg / --verdict-warn-bg / --verdict-danger-bg` +(low-chroma washes derived from the status pairs, both themes) so verdict +surfaces stop improvising, and `accent-color: var(--structure)` set globally +so native controls join the system. + +**Typography.** Display stays **Bricolage Grotesque** (self-hosted, subset, +41+18 KB woff2). Body stays the **system stack — as a choice, restated**: +organizers on cheap phones are the baseline user; zero body-font payload is a +feature, and the display face carries the identity. Two gaps close: +(1) **Arabic display**: `/ar/` currently gets no display face at all — +self-host a subset **OFL Arabic face** (candidate: Readex Pro, chosen for its +extended-legibility design brief; final pick + subset size recorded in the +asset plan) wired into `--font-display` under `:lang(ar)`. +(2) The full modular scale (`--step--1..7`) becomes the ONLY sanctioned type +sizes in islands (today islands use ad-hoc rems). + +**Spacing / radius / elevation / motion — already exist** (8px grid, radius +scale "never 0", `--dur-1/2`, `--ease-*`). New rule, enforced in review: island +stylesheets may reference **tokens only** — no `--sl-*` vars, no raw hex, no +off-scale rem values. One new elevation token `--shadow-1` replaces the lone +raw box-shadow (`CatalogExplorer.svelte:536`). + +## 3. Starlight strategy (sanctioned points only, upgrade path preserved) + +- Keep: `customCss` cascade (tokens → theme → components), the five existing + component overrides, Starlight i18n/locales, Pagefind. Implementation note + (verified against current Starlight docs): Starlight ships its styles in a + `starlight` cascade layer — declare `@layer` ordering once so our overrides + win without specificity wars; the four sanctioned surfaces (customCss, + component overrides, `head`, `expressiveCode.themes`) cover everything this + plan needs, no internals forked. +- Add ONE override: **`Footer`** — the only untouched default chrome. Ship a + designed footer (project promise line, policy/security/contribute links, + AGPL + "fork me" as identity, locale-aware). +- Tool pages (`/build/`, `/catalog/`, `/check/`, `/build/cost/`, + `/build/models/`) stay INSIDE the docs shell (sidebar is honest wayfinding + for non-devs) but gain a shared `.workbench` page class via frontmatter — + wider content column, tighter lead, tool-first landmark order. No shell fork. +- `/console/` stays standalone (CSP constraint is load-bearing) but **joins the + token layer**: import tokens.css, replace all raw hex, adopt `.btn`/`.field`. +- Landing: replace the stock ``/`` sections + (`index.mdx:37-99`) with a bespoke token-native component (working name + `WorkOrderGrid`) matching the Build Plate's spec-sheet language — Starlight + content components remain fine for interior docs. + +## 4. Audience register (copy is design material) + +Rules: plain verbs, sentence case, name what the person controls, one term of +art per sentence max, define jargon on first use — then it may be used. +Applies to: landing tool cards, `/build` `/check` `/build/models` `/start` +intros, the checker verdict copy, and empty/error states in every island. +Examples of the shift (final copy lands in implementation, es/ar in the same +commit as en): +- "run the exclusion policy in your browser with live license lookups" → "we + check your list against the blocklist, right here on your device — nothing + is uploaded." +- Checker verdict jargon (`npx tsx enforcement/cli.ts…` in the island) moves + behind a "for developers" disclosure; the verdict itself speaks + organizer-first ("2 of 14 tools are owned by companies this project blocks — + here's the chain of ownership"). +- `TODO: confirm` (user-visible in Cost/Compass) becomes a designed + provenance chip — "unverified — awaiting human check" — same zero-fabrication + semantics, no placeholder register. (`todoConfirm`, `model-compass/ui/i18n.ts:73`) + +## 5. Per-surface intent (current → target) + +| Surface | Current (evidence) | Target | +|---|---|---| +| Landing | Build Plate hero ✓, then stock CardGrid/icon cards (`index.mdx:37-99`) | Bespoke WorkOrderGrid in the Plate's spec language; jargon-free card copy | +| Catalog explorer | 1-column flat cards ×60/page (20,562px tall at desktop), native checkboxes in a scroll box, off-token styles | Responsive 1→2→3-col receipt-card grid; designed facet groups w/ counts + `accent-color`; verification status as the receipt language; designed empty/zero-result/error states at full 1,359-entry dataset; locale-aware CTA (`:358`) | +| Checker `/check/` | Verdict = text line + default table; CLI jargon inline; island ignores `lang` (P0) | **The thesis surface.** Receipt-styled verdict band: CLEAN (stamped ok), BLOCKED (customs stamp + ownership chain rendered as a chain), per-row receipts; localized (es/ar) via the house i18n pattern; dev detail behind a disclosure | +| Build Studio | Flow + gate exist ✓; mixed token dialects (`:1355` vs `:1360`); stray English beside its own i18n table (`:1074` etc.) | One dialect (tokens only); policy-gate + receipts inherit the receipt component; every string through `STR`; the .zip moment gets a completion receipt ("what you're taking with you") | +| Cost estimator | All `--sl-*`, raw radii; `TODO: confirm` register | Tokenized; provenance chips; numbers stay honest | +| Model Compass | Hardcoded hex ramp (`:144-152`); English data prose in localized UI; whole registry shipped as eager JS | Tokenized status ramp; caution prose via i18n keys; `client:visible` + consider static-render of the table (island only for filters) | +| Docs + PIE reading | Already designed (ticks, asides, tables) ✓ | No change beyond copy-register pass on first-contact pages | +| Console `/console/` | Raw hex, outside the system (`:470-523`; page `:33-44`) | **Token-compliance of the EXISTING auth screens only** — no feature-room design here (that waits for the owner's PANEL-RESPEC-v2 spec). Stays English (operator surface — documented exception), stays standalone-CSP. `/admin/` (Sveltia CMS) is vendored third-party and explicitly out of design scope | +| Footer (all pages) | Stock Starlight | New Footer override (see §3) | +| Islands, sitewide | 16× `client:load` | `client:visible` where below-the-fold (catalog explorer is above-fold: stays load); measure before/after | + +## 6. Kill-list mapping (every audit hit → its replacement) + +| Hit (audit ref) | Replacement | +|---|---| +| Stock CardGrid landing sections | WorkOrderGrid (§3) | +| Untouched Footer chrome | Footer override (§3) | +| Dev-jargon first-contact copy (5 pages + checker) | Register pass (§4), es/ar in same commits | +| Catalog single-column card wall + native facets | Receipt-card grid + designed facets (§5) | +| Default verdict table in checker | Receipt verdict band (§5) | +| ModelCompass/CostEstimator/Studio off-token styles | Token-only island rule (§2) + `--shadow-1`, verdict washes | +| `TODO: confirm` as UI copy | Provenance chip (§4) | +| PolicyChecker ignores `lang` (P0) | House i18n pattern applied; es/ar strings shipped | +| BuildStudio/AccountWidget stray strings; catalog CTA locale bug | Moved into `STR` tables; `buildBase` used at `:358` | +| Arabic gets no display face | Self-hosted subset Arabic face (§2, asset plan) | +| All-eager hydration | `client:visible` pass (§5) | +| Plausible third-party origin (opt-in, off) | Unchanged — documented as the only sanctioned, opt-in third party; still zero requests by default | + +## 7. Asset plan (all self-hosted) + +| Item | Source | License | Size budget | +|---|---|---|---| +| Bricolage Grotesque latin/latin-ext (existing) | in repo (`public/fonts/`) | OFL (in repo) | 60 KB total ✓ | +| Arabic display face, subset (Readex Pro candidate) | Google Fonts source repo → subset locally via fonttools | OFL | ≤ 45 KB woff2 | +| Receipt/verdict iconography | hand-drawn inline SVG (house style, like BottomNav's set) | project AGPL | inline, no files | +| No stock illustration, no photography | — | — | — | + +## 8. Performance (budgets are a floor) + +Existing CI budgets hold: axe 0 serious/critical; Lighthouse perf ≥ 90, a11y +≥ 95; LCP ≤ 2.5 s; CLS ≤ 0.1 — throttled mobile is the reference profile. +New page-weight ceilings (enforced in verification, measured via the audit +scripts): docs/reading page ≤ 250 KB transferred; tool page ≤ 450 KB +(explorer's `/catalog.json` fetch excluded but measured + reported); landing +≤ 350 KB. Any surface over ceiling gets fixed, never waived. The hydration +pass (§5) must not regress LCP on `/catalog/` (island is the LCP element). + +## 9. Non-goals + +No IA changes (routes, sidebar order, URL scheme all stay). No new features +beyond the approved BACKLOG.md. No body-font purchase/addition for Latin. No +dark/light re-theming (both themes already tokenized). No console i18n. No +touching hand-authored catalog entries (shakespeare, catalog overview) or any +entry's verification data. `operational_advisory`, the enforcement engine, and +POLICIES.md untouched throughout. + +## 10. Self-critique (required before submitting) + +- *"Is this just the default for a docs site?"* The docs shell IS Starlight — + deliberately: reading surfaces benefit from boring. What is NOT default after + this plan: every trust moment shares one bespoke receipt language, the + catalog reads like a supply ledger instead of a card wall, the landing's + feature sections stop being the Starlight template, and the last stock + chrome (footer) is replaced. If the receipt system were deleted, the site + would fall back to a themed docs site — that dependency is the point. +- *Revised while writing:* an earlier draft proposed pulling tool pages out of + the docs shell entirely (app-like, no sidebar). Rejected: the sidebar is how + non-developers recover when lost, and leaving the shell would fork Starlight + behaviors we would then maintain alone (search, i18n picker, skip links). + The `.workbench` class keeps the shell and widens the bench instead. +- *Palette honesty:* the current scheme sits nearest the "dark + accent" + AI-default family. It escapes the kill list on its own terms — green-tinted + paper (not neutral black), a two-hue structure/signal system (not a single + acid accent), documented anti-cream and anti-broadsheet decisions in the + tokens file — and it is already the shipped identity of this project's OG + cards, seal, and hero. Replacing it would be redesign theater; finishing it + is the elevation. diff --git a/audit/AUDIT.md b/audit/AUDIT.md new file mode 100644 index 0000000..1284085 --- /dev/null +++ b/audit/AUDIT.md @@ -0,0 +1,93 @@ +# AUDIT.md — SHIP Phase-1 findings (2026-07-11) + +Read-only research pass for the backlog-ship + design-elevation effort. +Placed at `audit/AUDIT.md` because root `AUDIT.md` is the (historical, +completed) Goose-pivot audit — promotion/renaming is a gate decision. +Companion docs: `BACKLOG.md` (consolidated ledger), `DESIGN.md` (direction), +`audit/screens/` (39 screenshots, 13 flows × 3 widths). + +## Baselines (executed this session) + +- `npm run verify:all` — **green**: astro check 0 errors/0 warnings, both + typechecks clean, **352/352 tests**, enforce + enforce:skills green, + production build 6,653 pages. +- `npx tsx enforcement/cli.ts all --tree .` — exits 1, but **every hit is + self-referential**: the engine's own signal-definition YAMLs and its + negative-test fixtures. **Zero hits in `src/`, `worker/`, `scripts/`, + `public/`.** Refinement (SHIP-GATE-R1 2c): CI **already dogfoods Layers 1–2 + full-tree** (`verify.yml:42-43`, green); only Layer 3 full-tree is + unsatisfiable by construction. Exact exempt paths, enumerated: + `enforcement/excluded-organizations.yaml`, + `enforcement/excluded-provider-signals.yaml`, `enforcement/tests/**`. + DoD-prose fix suffices — no engine change needed — gate item G3. +- `npm audit --omit=dev` — 4 low (esbuild via astro; Windows-dev-server-only, + prod unaffected). Full audit: 14 (7 low/6 moderate/1 high) — the high is + `tmp` via `@lhci/cli`, CI-tooling only. +- Enforcement full-tree + verify baselines contradict nothing in CI (10 + workflows, none disabled; the only two `continue-on-error` are + documented-deliberate: `security-pr.yml:42`, `security-cron.yml:58`). + +## P0 — security / broken / binding-doc conflicts + +| # | Finding | Evidence | +|---|---|---| +| P0-1 | **CLAUDE.md documents an i18n freshness/security-gate layer that does not exist** — incl. a 2-review gate for `security_sensitive` pages. One page carries the flag today (`guides/encrypted-group-messaging-marmot.mdx:10`) and gets **no extra protection**; the schema doesn't even define the field. CLAUDE.md is binding session instructions, so this is the highest-risk doc/reality conflict. | No `scripts/i18n-freshness.mjs`, `scripts/i18n-security-gate.mjs`, `i18n:stamp` script, `TranslationStatus.astro`, `src/config/i18n.mjs`, `TRANSLATING.md`, `.github/CODEOWNERS`, or i18n workflows (verified absent) | +| P0-2 | **PolicyChecker island ignores its `lang` prop** — the verdict surface (the product's thesis) renders English-only inside `/es/check/` and `/ar/check/`. | `src/components/PolicyChecker.svelte` (no `$props()`); pages pass `lang="es|ar"` (`es/check.mdx:18`, `ar/check.mdx:17`); hardcoded strings `:83-127` | + +(P0-3 in the first revision — CLI jargon in the checker verdict — is +**reclassified to P1-15** per the prompt's severity ladder: P0 is +security/broken/policy; copy register is UX. Reclassification requested and +ruled at SHIP-GATE-R1 2b.) + +## P1 — correctness / UX / design debt + +Kill-list hits (each with file:line; full table in the design-audit record): + +| # | Finding | Evidence | +|---|---|---| +| P1-1 | Landing feature sections are stock Starlight `CardGrid`/`Card icon=…` + 3 `LinkCard` grids | `src/content/docs/index.mdx:37-99` | +| P1-2 | Footer chrome is untouched Starlight default (only un-overridden chrome left) | `astro.config.mjs:146-153` | +| P1-3 | Catalog explorer: 1,359 entries as a single-column card wall, 60/page (desktop page renders 20,562px tall); native checkboxes in a scroll box; no `accent-color` anywhere in src | `CatalogExplorer.svelte:513,505-510,298`; screenshot `catalog-explorer-desktop.png` | +| P1-4 | Checker verdict = text line + default-styled table | `PolicyChecker.svelte:101-123` | +| P1-5 | Island style dialects split: token-native vs raw `--sl-*`/hex/off-scale rems. Worst: ModelCompass hex ramp (`#d33 #e80 …`), CostEstimator all-`--sl-*`, BuildStudio mixes both in one stylesheet, lone raw box-shadow | `ModelCompass.svelte:144-152`; `CostEstimator.svelte:240-276`; `BuildStudio.svelte:1355` vs `:1360-1364`; `CatalogExplorer.svelte:536` | +| P1-6 | `TODO: confirm` is user-visible product copy (zero-fabrication semantics, placeholder register) | `model-compass/ui/i18n.ts:73`; rendered `CostEstimator.svelte:220`, `ModelCompass.svelte:48,53,62`; advertised `build/cost.mdx:13`, `build/models.mdx:13` | +| P1-7 | Dev-jargon first-contact copy on organizer surfaces (landing, /build, /check, /build/models, /start) | `index.mdx:87,89`; `build.mdx:13-16`; `check.mdx:12-14`; `models.mdx:10-12`; `start/index.mdx:11-13` | +| P1-8 | i18n strays: BuildStudio hardcoded English beside its own STR table (`:1074,1027,1036,1084,1265,1272,1314-1322`); catalog CTA hardlinks English `/build/` from es/ar (`CatalogExplorer.svelte:358` — locale-aware `buildBase` exists at `:287` unused); AccountWidget ar placeholder (`:79`); English data-layer prose rendered in localized UIs (compass `models.ts:57`, estimator `providers.ts:64-68`, `CatalogList.astro:55-62`) | as cited | +| P1-9 | All 16 island mounts `client:load`; zero `client:visible/idle`. ModelCompass ships its whole registry as eager JS with no no-JS render | grep `client:`; `build/models.mdx:16` | +| P1-10 | `/console/` styled with raw hex outside the token system (own dark-mode media query) | `AdminConsole.svelte:470-523`; `console/index.astro:33-44` | +| P1-11 | Arabic gets no display face (identity absent for RTL readers) | `tokens.css:12-13` | +| P1-12 | CSP still Report-Only (mechanism complete; operator flip pending) | `astro.config.mjs:63`; `security-headers.ts:112-180` | +| P1-13 | Cost Estimator: 12× `unitPrice: null` + 3× `lastVerified: null`; tier bands/scale placeholders. Model Compass: 7 models `score: null` (runbook undercounts as 3) | `providers.ts:62-97`; `tiers.ts:26-30`; `models.ts:37-182` | +| P1-14 | Admin panel phases 4–8 unbuilt (per spec + wrangler binding comments); staging follow-ups (publish→PR, enforcement wiring, expiry job) logged | `docs/admin-panel-spec.md:73-136`; `wrangler.jsonc:48-62` | +| P1-15 | CLI jargon inside the checker verdict aimed at non-developers: "run `npx tsx enforcement/cli.ts all --tree .` on a real project." (reclassified from P0 at SHIP-GATE-R1 2b) | `PolicyChecker.svelte:126-127` | + +## P2 — polish / docs / deferred + +- Doc drift set: ROADMAP Done stops at #35 (repo at #55); admin-spec header + says "not yet built"; PLAN.md footer says "no feature code written"; README + describes Pages-dashboard deploy vs actual Workers Builds topology + (`README.md:127-148`); `TRANSLATING.md` referenced but file is + `TRANSLATIONS.md`; catalog-count disagreement (CLAUDE.md 1,355 / TRANSLATIONS + ~2,182 / disk 1,360); runbook §B2 undercount; MOBILE_FIXES header cites a + merged branch; `operational_advisory` name→workflow mapping lives only in + guard.py. +- `/build/models/` color-contrast: 89 axe nodes (en/es) from opacity + hex + fallbacks (a11y score still ≥ budget) — folds into P1-5's tokenization. +- Catalog prose translation run (pipeline built, needs `ANTHROPIC_API_KEY` + + waves); native-speaker review of ~25 machine-translated pages. +- Dependabot remainder (5 dev-only alerts); the **Astro 7 coupled upgrade** + (astro 7.0.7 + starlight 0.41.3 + @astrojs/svelte 9 — 0.40 is the newest + Starlight compatible with Astro 6; breaking-change list verified, none of + the removed APIs used in-repo) — a decision, not an obligation. +- NIP-07 one-line note in public docs; Plausible remains the only sanctioned + (opt-in, off-by-default) third-party origin. +- Stack otherwise current: wrangler `ratelimits` GA (not beta), + `run_worker_first` current, no deprecated Astro/nostr-tools API usage found. + +## What is NOT broken (verified, for the record) + +Intentional identity system exists and is substantial (tokens/theme/components ++ 5 overrides + Build Plate + self-hosted subset fonts). No purple gradients, +no glassmorphism, no lorem, no stock illustration, no font CDNs. Path A holds +(broker retired; CI-gated). Mobile fixes 1–8 merged. `/api/health`, +`/api/license`, auth, admin auth+staging all live and tested (352 tests). diff --git a/audit/screens/build-studio-desktop.png b/audit/screens/build-studio-desktop.png new file mode 100644 index 0000000..2870ead Binary files /dev/null and b/audit/screens/build-studio-desktop.png differ diff --git a/audit/screens/build-studio-mobile.png b/audit/screens/build-studio-mobile.png new file mode 100644 index 0000000..11444da Binary files /dev/null and b/audit/screens/build-studio-mobile.png differ diff --git a/audit/screens/build-studio-tablet.png b/audit/screens/build-studio-tablet.png new file mode 100644 index 0000000..ce03a74 Binary files /dev/null and b/audit/screens/build-studio-tablet.png differ diff --git a/audit/screens/catalog-entry-desktop.png b/audit/screens/catalog-entry-desktop.png new file mode 100644 index 0000000..5b51c3b Binary files /dev/null and b/audit/screens/catalog-entry-desktop.png differ diff --git a/audit/screens/catalog-entry-mobile.png b/audit/screens/catalog-entry-mobile.png new file mode 100644 index 0000000..23ecab4 Binary files /dev/null and b/audit/screens/catalog-entry-mobile.png differ diff --git a/audit/screens/catalog-entry-tablet.png b/audit/screens/catalog-entry-tablet.png new file mode 100644 index 0000000..0d686d5 Binary files /dev/null and b/audit/screens/catalog-entry-tablet.png differ diff --git a/audit/screens/catalog-explorer-desktop.png b/audit/screens/catalog-explorer-desktop.png new file mode 100644 index 0000000..b4b649a Binary files /dev/null and b/audit/screens/catalog-explorer-desktop.png differ diff --git a/audit/screens/catalog-explorer-mobile.png b/audit/screens/catalog-explorer-mobile.png new file mode 100644 index 0000000..6c6e1c7 Binary files /dev/null and b/audit/screens/catalog-explorer-mobile.png differ diff --git a/audit/screens/catalog-explorer-tablet.png b/audit/screens/catalog-explorer-tablet.png new file mode 100644 index 0000000..b2d18c0 Binary files /dev/null and b/audit/screens/catalog-explorer-tablet.png differ diff --git a/audit/screens/checker-desktop.png b/audit/screens/checker-desktop.png new file mode 100644 index 0000000..0b0158b Binary files /dev/null and b/audit/screens/checker-desktop.png differ diff --git a/audit/screens/checker-mobile.png b/audit/screens/checker-mobile.png new file mode 100644 index 0000000..578d509 Binary files /dev/null and b/audit/screens/checker-mobile.png differ diff --git a/audit/screens/checker-tablet.png b/audit/screens/checker-tablet.png new file mode 100644 index 0000000..461204c Binary files /dev/null and b/audit/screens/checker-tablet.png differ diff --git a/audit/screens/console-desktop.png b/audit/screens/console-desktop.png new file mode 100644 index 0000000..87e0a0a Binary files /dev/null and b/audit/screens/console-desktop.png differ diff --git a/audit/screens/console-mobile.png b/audit/screens/console-mobile.png new file mode 100644 index 0000000..c8c91b1 Binary files /dev/null and b/audit/screens/console-mobile.png differ diff --git a/audit/screens/console-tablet.png b/audit/screens/console-tablet.png new file mode 100644 index 0000000..9a483a0 Binary files /dev/null and b/audit/screens/console-tablet.png differ diff --git a/audit/screens/cost-estimator-desktop.png b/audit/screens/cost-estimator-desktop.png new file mode 100644 index 0000000..9f091f1 Binary files /dev/null and b/audit/screens/cost-estimator-desktop.png differ diff --git a/audit/screens/cost-estimator-mobile.png b/audit/screens/cost-estimator-mobile.png new file mode 100644 index 0000000..c72f5ea Binary files /dev/null and b/audit/screens/cost-estimator-mobile.png differ diff --git a/audit/screens/cost-estimator-tablet.png b/audit/screens/cost-estimator-tablet.png new file mode 100644 index 0000000..0d9c719 Binary files /dev/null and b/audit/screens/cost-estimator-tablet.png differ diff --git a/audit/screens/guides-desktop.png b/audit/screens/guides-desktop.png new file mode 100644 index 0000000..6001ceb Binary files /dev/null and b/audit/screens/guides-desktop.png differ diff --git a/audit/screens/guides-mobile.png b/audit/screens/guides-mobile.png new file mode 100644 index 0000000..a707f54 Binary files /dev/null and b/audit/screens/guides-mobile.png differ diff --git a/audit/screens/guides-tablet.png b/audit/screens/guides-tablet.png new file mode 100644 index 0000000..256dd55 Binary files /dev/null and b/audit/screens/guides-tablet.png differ diff --git a/audit/screens/landing-ar-desktop.png b/audit/screens/landing-ar-desktop.png new file mode 100644 index 0000000..03576c0 Binary files /dev/null and b/audit/screens/landing-ar-desktop.png differ diff --git a/audit/screens/landing-ar-mobile.png b/audit/screens/landing-ar-mobile.png new file mode 100644 index 0000000..d581a88 Binary files /dev/null and b/audit/screens/landing-ar-mobile.png differ diff --git a/audit/screens/landing-ar-tablet.png b/audit/screens/landing-ar-tablet.png new file mode 100644 index 0000000..d4b8cbb Binary files /dev/null and b/audit/screens/landing-ar-tablet.png differ diff --git a/audit/screens/landing-desktop.png b/audit/screens/landing-desktop.png new file mode 100644 index 0000000..1649a11 Binary files /dev/null and b/audit/screens/landing-desktop.png differ diff --git a/audit/screens/landing-es-desktop.png b/audit/screens/landing-es-desktop.png new file mode 100644 index 0000000..2002b63 Binary files /dev/null and b/audit/screens/landing-es-desktop.png differ diff --git a/audit/screens/landing-es-mobile.png b/audit/screens/landing-es-mobile.png new file mode 100644 index 0000000..a4f21d9 Binary files /dev/null and b/audit/screens/landing-es-mobile.png differ diff --git a/audit/screens/landing-es-tablet.png b/audit/screens/landing-es-tablet.png new file mode 100644 index 0000000..5111144 Binary files /dev/null and b/audit/screens/landing-es-tablet.png differ diff --git a/audit/screens/landing-mobile.png b/audit/screens/landing-mobile.png new file mode 100644 index 0000000..33f33f4 Binary files /dev/null and b/audit/screens/landing-mobile.png differ diff --git a/audit/screens/landing-tablet.png b/audit/screens/landing-tablet.png new file mode 100644 index 0000000..0e09d09 Binary files /dev/null and b/audit/screens/landing-tablet.png differ diff --git a/audit/screens/method-desktop.png b/audit/screens/method-desktop.png new file mode 100644 index 0000000..13ceecb Binary files /dev/null and b/audit/screens/method-desktop.png differ diff --git a/audit/screens/method-mobile.png b/audit/screens/method-mobile.png new file mode 100644 index 0000000..7581fea Binary files /dev/null and b/audit/screens/method-mobile.png differ diff --git a/audit/screens/method-tablet.png b/audit/screens/method-tablet.png new file mode 100644 index 0000000..ca79cd1 Binary files /dev/null and b/audit/screens/method-tablet.png differ diff --git a/audit/screens/model-compass-desktop.png b/audit/screens/model-compass-desktop.png new file mode 100644 index 0000000..d6138e1 Binary files /dev/null and b/audit/screens/model-compass-desktop.png differ diff --git a/audit/screens/model-compass-mobile.png b/audit/screens/model-compass-mobile.png new file mode 100644 index 0000000..b94c27b Binary files /dev/null and b/audit/screens/model-compass-mobile.png differ diff --git a/audit/screens/model-compass-tablet.png b/audit/screens/model-compass-tablet.png new file mode 100644 index 0000000..15fadd5 Binary files /dev/null and b/audit/screens/model-compass-tablet.png differ diff --git a/audit/screens/pie-flow-desktop.png b/audit/screens/pie-flow-desktop.png new file mode 100644 index 0000000..e5f7c4e Binary files /dev/null and b/audit/screens/pie-flow-desktop.png differ diff --git a/audit/screens/pie-flow-mobile.png b/audit/screens/pie-flow-mobile.png new file mode 100644 index 0000000..e6042a6 Binary files /dev/null and b/audit/screens/pie-flow-mobile.png differ diff --git a/audit/screens/pie-flow-tablet.png b/audit/screens/pie-flow-tablet.png new file mode 100644 index 0000000..f3cac8d Binary files /dev/null and b/audit/screens/pie-flow-tablet.png differ diff --git a/audit/shoot.mjs b/audit/shoot.mjs new file mode 100644 index 0000000..fb767e9 --- /dev/null +++ b/audit/shoot.mjs @@ -0,0 +1,49 @@ +// audit/shoot.mjs — Phase-1 design-audit screenshots (3 widths per flow). +// Serves nothing itself: expects `npx astro preview` (or any static server) on +// PORT (default 4321). Output: audit/screens/-.png +import { chromium } from 'playwright'; +import { mkdirSync } from 'node:fs'; + +const BASE = process.env.SHOOT_BASE ?? 'http://localhost:4321'; +const OUT = new URL('./screens/', import.meta.url).pathname; +mkdirSync(OUT, { recursive: true }); + +const ROUTES = [ + ['landing', '/'], + ['build-studio', '/build/'], + ['cost-estimator', '/build/cost/'], + ['model-compass', '/build/models/'], + ['catalog-explorer', '/catalog/'], + ['catalog-entry', '/catalog/nostr-tools/'], + ['checker', '/check/'], + ['method', '/method/'], + ['pie-flow', '/pie/baking-pie/'], + ['guides', '/guides/get-started-with-goose/'], + ['console', '/console/'], + ['landing-es', '/es/'], + ['landing-ar', '/ar/'], +]; +const WIDTHS = [[375, 812, 'mobile'], [768, 1024, 'tablet'], [1366, 900, 'desktop']]; + +// This environment pre-installs Chromium at a fixed path; never download browsers. +const browser = await chromium.launch({ executablePath: process.env.SHOOT_CHROMIUM ?? '/opt/pw-browsers/chromium' }); +let shot = 0; +const failures = []; +for (const [slug, path] of ROUTES) { + for (const [w, h, label] of WIDTHS) { + const page = await browser.newPage({ viewport: { width: w, height: h } }); + try { + const res = await page.goto(`${BASE}${path}`, { waitUntil: 'networkidle', timeout: 30000 }); + if (!res || res.status() >= 400) { failures.push(`${path} ${label}: HTTP ${res?.status()}`); await page.close(); continue; } + await page.waitForTimeout(600); // islands settle + await page.screenshot({ path: `${OUT}${slug}-${label}.png`, fullPage: true }); + shot += 1; + } catch (e) { + failures.push(`${path} ${label}: ${e.message.split('\n')[0]}`); + } + await page.close(); + } +} +await browser.close(); +console.log(`shots: ${shot}`); +if (failures.length) { console.log('failures:'); for (const f of failures) console.log(' -', f); }