diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 747bd89..80a2549 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,6 +93,87 @@ jobs: - name: Wrangler config + bundle dry-run run: bun x wrangler deploy --dry-run + # Verifies the sandbox image tags pinned in wrangler.jsonc actually + # exist in the Cloudflare managed registry. Catches the failure + # mode where a contributor bumps the Dockerfile + pin but forgets + # `wrangler containers build -p` (the image push). The dry-run + # above does NOT validate tag existence, so without this guard the + # failure would surface at deploy time rather than PR time. + # + # Pin alignment between env blocks is workflow-aware: + # dev-targeting PRs: pins may diverge (staging-leads-prod + # during the soak-then-promote workflow; + # see RELEASES.md § Sandbox image releases). + # Each pin is verified independently against + # the registry. + # main-targeting PRs: pins MUST be equal (released state). + # The release PR is expected to promote the + # top-level pin to match env.staging before + # merging. + # + # Skipped on forks (secrets are unavailable to PRs from forks by + # default). Same-repo branches always hit it. + - name: Verify pinned sandbox image exists in CF registry + if: github.event.pull_request.head.repo.full_name == github.repository + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} + BASE_REF: ${{ github.base_ref }} + run: | + set -euo pipefail + + prod_img=$(bun x wrangler deploy --dry-run 2>&1 \ + | grep -oE 'registry\.cloudflare\.com/[^ )]+' | head -1 || true) + stg_img=$(bun x wrangler deploy --dry-run --env staging 2>&1 \ + | grep -oE 'registry\.cloudflare\.com/[^ )]+' | head -1 || true) + + if [ -z "$prod_img" ] || [ -z "$stg_img" ]; then + echo "::warning::could not extract image URI from wrangler dry-run; skipping registry probe" + exit 0 + fi + + prod_tag="${prod_img##*:}" + stg_tag="${stg_img##*:}" + + # Always: independent registry-existence check on each pinned + # tag. Allows staging to lead prod during development. + images=$(bun x wrangler containers images list 2>/dev/null || true) + fail=0 + + if echo "$images" | grep -qE "anc-sandbox[[:space:]]+${prod_tag}\b"; then + echo "verified: top-level pin anc-sandbox:${prod_tag} is in the CF managed registry" + else + echo "::error::top-level pin anc-sandbox:${prod_tag} not found in CF managed registry" + echo "did you forget: bun x wrangler containers build -p -t anc-sandbox:${prod_tag} docker/sandbox/" + fail=1 + fi + + if [ "$prod_tag" = "$stg_tag" ]; then + echo "verified: env.staging pin equals top-level pin (lockstep)" + elif echo "$images" | grep -qE "anc-sandbox[[:space:]]+${stg_tag}\b"; then + echo "verified: env.staging pin anc-sandbox:${stg_tag} is in the CF managed registry" + else + echo "::error::env.staging pin anc-sandbox:${stg_tag} not found in CF managed registry" + echo "did you forget: bun x wrangler containers build -p -t anc-sandbox:${stg_tag} docker/sandbox/" + fail=1 + fi + + # Release-time invariant: main-targeting PRs must have aligned + # pins. Dev-targeting PRs may have divergent pins (staging + # leads prod during development). + if [ "${BASE_REF}" = "main" ] && [ "$prod_tag" != "$stg_tag" ]; then + echo "::error::release PR to main has divergent image pins" + echo " top-level: anc-sandbox:${prod_tag}" + echo " env.staging: anc-sandbox:${stg_tag}" + echo "Promote the top-level pin to match env.staging before merging." + echo "See RELEASES.md § Sandbox image releases for the soak-and-promote procedure." + fail=1 + fi + + if [ "$fail" -ne 0 ]; then + exit 1 + fi + annotate-warnings: # Surfaces scorecard/registry orphan counts (U8) as a PR comment so # reviewers see drift without grepping CI logs. Build still passes diff --git a/.gitignore b/.gitignore index 149c616..13c646e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,10 @@ playwright-report/ # the local CLI checkout (not committed); per-run logs land in out/. /docker/score/anc /docker/score/out/ + +# Vale baseline packs — downloaded by `vale sync` from the URLs in .vale.ini's +# Packages directive. The brand and site packs are committed; these are +# fetched per-machine. +/styles/proselint/ +/styles/write-good/ +/styles/.vale-config/ diff --git a/.impeccable.md b/.impeccable.md deleted file mode 100644 index 7c0b145..0000000 --- a/.impeccable.md +++ /dev/null @@ -1,118 +0,0 @@ -# .impeccable.md - -Design context for the agentnative spec site. Consumed by `/impeccable`, `/typeset`, and other impeccable skills. Source -of truth for non-technical design decisions; the full design system lives in [`docs/DESIGN.md`](docs/DESIGN.md) with -reproducible artifacts split across [`src/styles/`](src/styles/) (shipped CSS, generated by -`scripts/design/generate-palette.mjs`) and [`docs/research/design/`](docs/research/design/) (color analysis, OG concept -variants, preview HTML). - -## Design Context - -### Users - -**Two first-class audiences, same URL space:** - -- **Developers** evaluating or adopting the agent-native CLI standard. Most arrive from a Show HN post ("agentnative — - check if your CLI is agent-native") or a Twitter/Bluesky share. Many already maintain CLIs (`gh`, `jq`-adjacent - tooling, internal dev-tools) and want to know, in under 60 seconds, what the standard requires, whether their tool - already conforms, and how to install `agentnative` (the companion Rust linter) to check. They read on laptops and - larger phones, typically during work, in a context where they are deciding whether to take the standard seriously. -- **AI agents** consuming the spec programmatically via `/llms.txt`, `/llms-full.txt`, `.md` URL suffixes, and `Accept: - text/markdown` content negotiation. Their UX is "does the markdown come back clean, are anchors stable, is the - structure predictable across versions." They are not a nice-to-have — the site's entire thesis is that this audience - is first-class. Design decisions that make the HTML prettier at the cost of the markdown channel are regressions. - -**Not primary audiences:** managers deciding budget (use `davies.fyi` later), procurement (never), the general public -(will not find this by accident and we should not chase them). - -The job-to-be-done: "Tell me, authoritatively and in one place, what it means for a CLI to be agent-native — and link me -to the tool that measures it." - -### Brand Personality - -**Three words: opinionated, precise, inviting.** - -- **opinionated** — the standard has a point of view. It does not enumerate tradeoffs and shrug; it says "MUST do X, - here is the failure mode if you don't, here is the canonical fix." That tone shows up in the type hierarchy and the - keyword treatment (§4.7 MUST/SHOULD/MAY colorization), not just the prose. -- **precise** — RFC 2119 language throughout. Anchors are stable and citable. Contrast ratios are measured, not - asserted. Typography is exact (measure capped, scale ratio locked, no arbitrary sizes). Precision is the - authority-source; take it away and the site reads like a blog post. -- **inviting** — this is what Option 1 was missing and what the user flagged as "too boring." The site should make a - reader (or an agent handler) *want* to keep reading. That comes from the details: distinctive typeface, humane - measure, OKLCH-true color that does not feel sterile, a code-block treatment that says "this is a reference I can - trust," a `:target` landing that actually lands well. Inviting is not "friendly" and it is not "marketing." It is - "rewards engagement." - -**Voice anchor (from the Brand-System doc):** xAI cover letter — belief-statement opener, concrete numbers, honest about -gaps, "the code speaks for itself" close. The site speaks as a standard, not a person (so first- person singular is -out), but it inherits the anchor's pattern: concrete before abstract, show-then-tell, no filler adjectives. - -### Aesthetic Direction - -**Theme: both modes, user-toggleable.** `prefers-color-scheme` as the default, explicit `[data-theme]` override, -`system` as a third state. Dark mode is deliberately designed, not inverted (see docs/DESIGN.md §4.2). - -**References (what to borrow from):** - -- `clig.dev` — single-column confidence, quiet accent, type rhythm that makes one long page readable end-to-end. -- `developers.cloudflare.com` — code-block treatment, copy UX, sidebar TOC that tracks scroll, dark-mode intentionality. -- `rust-lang.org/book` — inline `code` and block code share a visual family; generous code padding. -- `simonwillison.net` — plain-HTML-feel can carry real authority; visible author-hand without marketing chrome. -- `anthropic.com/claude/docs` — dense type, code as first-class citizen. - -**Anti-references (what to reject):** - -- Dashboard / admin-panel patterns: card grids of equal-sized tiles, sparkline decorations, side-stripe borders on - callouts (an absolute ban per `reference/color-and-contrast.md`). -- Product-landing patterns: hero sections, gradient backgrounds, big CTA buttons, glassmorphism, animated scroll - reveals, hover-scale transforms. -- The AI-slop palette: cyan/purple gradients on dark backgrounds, gradient text, neon accents, purple-to-blue hero - washes. -- "Serious documentation" templating: generic icons above every section heading, identical cards for principles, a left - sidebar with a monochrome vertical accent bar. -- The "second-favorite font" reflex: Inter, Plex, Fraunces, Lora, DM Sans, Space Grotesk, Instrument Serif, Outfit, Plus - Jakarta Sans — all banned per the font selection procedure. The right answer is further out than the font catalog's - first scroll. - -**Palette posture:** cool-neutral base (hue 250), one accent in the same hue family, three semantic warm accents for -MUST / SHOULD / MAY (ship option 7b — inline keyword color only; block-level variants deferred to live-site iteration -after the 7b-plus side-stripe was rejected per impeccable's ban on `border-left > 1px` on callouts). OKLCH throughout; -sRGB hex fallbacks. Full methodology and contrast verification in `docs/research/design/color-analysis.md`. - -### Design Principles - -1. **The markdown source is the source of truth.** Any design decision that would force the `text/markdown` channel to - carry presentational markup loses to the channel that keeps it clean. The HTML render is derivative. -2. **Authority through precision, engagement through detail.** The site's credibility comes from measured contrast, - stable anchors, and RFC-grade language. The site's stickiness comes from details a reader only notices because - they're reading slowly: the tabular-numeral version/date in the footer, a small-caps RFC-2119 keyword, a code-block - corner-label that does not shout. -3. **Restraint is not austerity.** Option 1 was rejected because it defaulted to "spec = minimal = boring." The correct - move is one distinctive, considered choice per dimension (one typeface family with character, one accent, one motion - idiom) — then stop. -4. **No AI-slop fingerprints.** No side-stripe borders on callouts (impeccable's #1 ban — applies even to semantic - MUST/SHOULD/MAY use). No gradient text. No card grids. No icon-headed section cards. No second-favorite fonts. No - dashboard analogies. -5. **Earn every element.** Each typeface, each animation, each interactive widget must pass "what would the site lose - without this?" If the answer is "nothing," cut it. The total homepage budget is 2 MB — JS is fine. The taste budget - is tight. - ---- - -## Additional context the skills should know - -- **Tech stack (decided):** static site generation; plain HTML from markdown via a ~200-line Node build; Cloudflare - Worker (~80 lines) for routing + `Accept: text/markdown` + `.md` URL suffix + `Link` / `X-Llms-Txt` response headers + - `X-Robots-Tag: noindex` on the markdown variant. See docs/DESIGN.md §3. -- **Scope (decided):** index + 7 principle anchors on one page + `/check` + `/about`. Nine surfaces total. - docs/DESIGN.md §3.1 locks SSG as non-negotiable. -- **Typography status:** settled via `/typeset` on 2026-04-14. Body + display: Pangram Pangram's Uncut Sans (OFL). Code: - GitHub Next's Monaspace Xenon (OFL). Neither appears on the reflex-fonts-to-reject list. Full spec and loading - strategy in docs/DESIGN.md §4.3; preview at `docs/research/design/must-should-may-preview.html`. Any future typography - change should re-run `/typeset` rather than propose fonts from memory. -- **Accessibility:** WCAG 2.1 AA and APCA Lc ≥ 60 body minimum both pass in both modes, verified in - `docs/research/design/color-analysis.md`. Keep it that way on any typography change — smaller sizes or lower-contrast - weights must be re-checked. -- **JS posture:** JS is fine. Total homepage budget is 2 MB. Use a library if it earns its place. Native Clipboard API - is fine, framework runtimes still need justification. diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000..e3ad4d7 --- /dev/null +++ b/.vale.ini @@ -0,0 +1,46 @@ +StylesPath = styles +MinAlertLevel = warning +Vocab = brand, site +Packages = https://github.com/vale-cli/write-good/releases/download/v0.4.1/write-good.zip, https://github.com/vale-cli/proselint/releases/download/v0.3.4/proselint.zip + +[*.md] +BasedOnStyles = Vale, brand, site, write-good, proselint +Vale.Terms = NO +brand.MarketingRegister = error +brand.HedgeWords = error +brand.FillerAdjectives = error +site.BannedFonts = error +site.BannedAesthetics = error +write-good.E-Prime = NO +write-good.Passive = warning +write-good.TooWordy = warning +write-good.ThereIs = warning +proselint.But = NO +proselint.Annotations = NO +proselint.Typography = warning + +[{docs/brainstorms,docs/plans,docs/research}/**] +BasedOnStyles = + +[AGENTS.md] +BasedOnStyles = + +[CHANGELOG.md] +BasedOnStyles = + +# PRODUCT.md is the channel's policy doc (was .impeccable.md before the +# 2026-05-12 migration). It names the banned aesthetic patterns and the +# rejected fonts the rule packs catch. Disable both here only (the rules +# fire on the policy doc that defines them). +[PRODUCT.md] +site.BannedAesthetics = NO +site.BannedFonts = NO + +# DESIGN.md is the rationale doc (was docs/DESIGN.md before the 2026-05-12 +# hoist). It records why specific fonts were rejected (Inter, IBM Plex, +# Fraunces, Space Grotesk, Instrument Serif) and which aesthetic patterns +# are out. Same precedent as PRODUCT.md: the doc that names what was +# rejected legitimately enumerates the rejected names. +[DESIGN.md] +site.BannedFonts = NO +site.BannedAesthetics = NO diff --git a/AGENTS.md b/AGENTS.md index 7c586a8..a9ecb86 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,7 +58,7 @@ anchors, and semantic HTML. Keep this framing in every decision. per-host clone commands) - `/skill.json` — canonical machine-primary skill manifest. Same data, agent-readable. `Content-Type: application/json`, `X-Robots-Tag: noindex`. Both `/skill` and `/skill.json` derive from `src/data/skill.json` at build time; full surface - contract in `docs/DESIGN.md` §3.9 + contract in `DESIGN.md` §3.9 - `content/*.md` — markdown source of truth for every page (principle files, check, about, index) - Cloudflare Worker — routes requests: `.md` suffix OR `Accept: text/markdown` returns raw markdown source; otherwise returns HTML rendered from the same markdown via CommonMark @@ -96,11 +96,11 @@ narrative, attribution rules) is load-bearing. - Mobile-first; a11y baseline (skip-link, semantic landmarks, `>= 4.5:1` contrast, `:focus-visible`, `prefers-reduced-motion`) -The design-survey step produces a `docs/DESIGN.md` in the repo with palette, type stack, spacing scale, code-block -treatment, and dark/light tokens before any HTML is written. +The design-survey step produces a `DESIGN.md` in the repo with palette, type stack, spacing scale, code-block treatment, +and dark/light tokens before any HTML is written. Design context consumed by the `/impeccable` and `/typeset` skills (users, brand personality, aesthetic direction, -design principles): [`.impeccable.md`](.impeccable.md). +design principles): [`PRODUCT.md`](PRODUCT.md). ## Visual fidelity @@ -158,7 +158,7 @@ agent-side browser-verify rule above is the working gate. | `~/obsidian-vault/Projects/brettdavies-agentnative/research/index.md` | Shared research index for both this site and the `agentnative` CLI linter | External signal (blog posts, HN threads, competitor CLIs) extracted into curated quotes + principle mapping. Read before writing principle copy or launch framing that cites third parties. | | `~/obsidian-vault/Projects/brettdavies-agentnative/principles/index.md` | Canonical spec for P1-P7 (one file per principle, pressure-testable) | Source of truth for principle meaning. Site copy in `content/principles/` is written **manually** from these files — no build-time import, no live link. When principle spec changes, propagate to site copy deliberately. | | `~/.gstack/projects/brettdavies-agentnative-site/brett-main-build-plan-20260414-130000.md` | Build & distribution plan | Scaffolding decisions for /ce-plan and /ce-work: target repo tree, build pipeline, deployment. Locked decisions; Cloudflare-specifics verified. | -| `~/.gstack/projects/brettdavies-agentnative-site/brett-main-eng-review-20260414-123800.md` | Eng review | Architecture + code quality + test coverage review for M1. §12 lists all docs/DESIGN.md edits. Decisions resolved; no blockers. | +| `~/.gstack/projects/brettdavies-agentnative-site/brett-main-eng-review-20260414-123800.md` | Eng review | Architecture + code quality + test coverage review for M1. §12 lists all DESIGN.md edits. Decisions resolved; no blockers. | | `docs/plans/2026-04-17-001-feat-registry-leaderboard-scorecard-pages-plan.md` | Registry + leaderboard plan | Plan 1 scope: tool registry, pre-computed scorecards, `/scorecards` leaderboard, `/score/` pages. Plan 2 (live scoring via CF Sandbox) is separate. | ## Related repos @@ -208,6 +208,6 @@ The production domain (candidates: agentnative.dev / .io / .org) is **Brett's pu authoritative spec that the site's `content/principles/` copy is written from (manually, not auto-derived). 3. Run a reference-site survey for visual design (clig.dev, 12factor.net, htmx.org, rust-lang.org book, json-schema.org, fly.io docs, matklad.github.io — pick 3-4 that embody "simple, traditional, modern flair" and extract tokens). - Produce `docs/DESIGN.md` before writing any HTML. + Produce `DESIGN.md` before writing any HTML. 4. Scaffold the markdown sources in `content/` first, then the Worker + HTML renderer, then wire deploy. 5. Check in with Brett before attaching the production domain. diff --git a/BRAND.md b/BRAND.md new file mode 100644 index 0000000..5844d8f --- /dev/null +++ b/BRAND.md @@ -0,0 +1,105 @@ +# BRAND.md: agentnative voice and identity + +Source of truth for the voice and identity of the agentnative standard. Shared across the spec, the website, the linter, +the skill bundle, and any future channel. Each channel inherits from this document and adds channel-specific register +and artifacts in its own `PRODUCT.md`. + +> **Source of truth: `agentnative-spec/BRAND.md`.** This file is vendored into each channel repo (`agentnative-site`, +> `agentnative-cli`, `agentnative-skill`) via `scripts/sync-prose-tooling.sh`. Edits in +> a consumer repo will be overwritten on the next sync. File issues and PRs against this repo. + +## Brand identity + +**Three words: opinionated, precise, inviting.** + +- **Opinionated.** The standard has a point of view. It does not enumerate tradeoffs and shrug; it states "MUST do X, + here is the failure mode if you don't, here is the canonical fix." The point of view is what makes the standard worth + citing. +- **Precise.** RFC 2119 language. Anchors stable and citable. Numbers measured, not asserted. Where a contract has a + canonical realization (a flag spelling, an exit code, a path), it is named explicitly. +- **Inviting.** The reader (or agent handler) keeps reading by design. That comes from details: typography that rewards + a slow read, prose that rewards a fast scan, code blocks that read like reference material a reader can trust. + Inviting is not "friendly" and it is not "marketing." It rewards engagement. + +## Voice anchor + +Concrete before abstract. Show then tell. No filler adjectives. The standard speaks as a standard, not a person ( +first-person singular is out), but every channel inherits the same sequence: state the contract, show the failure mode, +name the canonical fix. + +## Audiences + +Two first-class consumers across all channels: + +- **Humans** evaluating, adopting, implementing, or extending the standard. Spec-channel readers are technically deep + and arrive with skepticism; site-channel readers are time-pressured and decide in 60 seconds whether to take the + standard seriously; linter users invoke at the terminal. Each channel narrows further in its own `PRODUCT.md`. +- **AI agents** consuming the standard programmatically: markdown via `Accept: text/markdown`, requirement IDs via + frontmatter parsing, skill bundles via `SKILL.md`/`AGENTS.md` discovery, linter findings via JSON. Their UX is "do + anchors stay stable, do IDs survive reorganizations, does the channel render cleanly across versions." This is not a + nice-to-have. The agent audience is first-class. Decisions that improve a channel for humans at the cost of agent + legibility are regressions. + +## Universal anti-patterns + +These bans apply across every channel. The narrative below explains *why* each category is banned; the executable +contract for *what* is banned lives in [`styles/brand/README.md`](styles/brand/README.md), generated from the Vale rule +pack at `styles/brand/*.yml`. + +- **No marketing register.** First-person belief and recommendation framings are out. The standard speaks in the third + person about contracts, not in the first person about beliefs. +- **No hedge words.** Probabilistic softeners undercut MUST and SHOULD. The contract is the contract. +- **No filler adjectives.** Marketing modifiers do no work. Concrete before abstract; the noun carries the meaning. +- **No verbatim quotation from any single source.** Where multiple sources converge on a claim, the standard's wording + sounds like triangulation, not citation. Lineage belongs in the README's `Acknowledgements` section, not in the + contract. + +## Voice anchors: concrete examples + +The ✓ column shows the contract voice. The ✗ column names the category of failure rather than reproducing literal banned +phrases. Those live in [`styles/brand/README.md`](styles/brand/README.md). The category labels describe the shape of the +failure each ✓ phrasing replaces. + +| ✓ | ✗ | +| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| "CLI tools MUST run without human input." | First-person belief framing with lowercase RFC keyword and audience speculation. | +| "Authentication failed: token expired (`expires_at: 2026-03-25T00:00:00Z`). Run `tool auth refresh` or set `TOOL_TOKEN`." | Apologetic register with vague remediation and no actionable diagnostic. | +| "Numeric output is locale-independent: `.` decimal, no thousands grouping, regardless of `LC_NUMERIC`." | First-person recommendation with hedge word and unspecific "potential issues" framing. | + +## Channels + +The shared identity above applies to every channel. Each channel adds register and artifacts in its own `PRODUCT.md`: + +- **Spec** (`agentnative-spec/PRODUCT.md`): RFC 2119 register, third-person standards voice, present tense, no + first-person plural, no implementation leakage in MUSTs. +- **Site** (`agentnative-site/PRODUCT.md`): visual system (palette, typography, code-block treatment, OG image), + tech-stack decisions (SSG, Worker, content negotiation), JS budget, dark-mode design. +- **Skill bundle**: instructional voice, second-person imperative is allowed, agent-loadable. +- **Linter (`anc`)**: terse error messages, ≤80-column help text, four-part error rubric (offending value, constraint, + valid example, remediation). + +## Channel artifacts + +Each channel's repo carries its own narrow stack on top of this universal `BRAND.md`. The canonical layout: + +| Channel | `PRODUCT.md` location | Deep tier-3 | Vale rule pack | How `BRAND.md` arrives | +| ------------ | ----------------------------------------------- | ------------------------------------------------------ | -------------- | ------------------------------- | +| Spec | `agentnative-spec/PRODUCT.md` | `principles/`, `docs/architecture/`, `docs/decisions/` | `styles/spec/` | (origin — this repo) | +| Site | `agentnative-site/PRODUCT.md` | `DESIGN.md` (root) | (none yet) | `scripts/sync-prose-tooling.sh` | +| CLI (`anc`) | `agentnative-cli/PRODUCT.md` (when warranted) | `src/` (Rust source IS the artifact) | (planned) | `scripts/sync-prose-tooling.sh` | +| Skill bundle | `agentnative-skill/PRODUCT.md` (when warranted) | `bundle/` | (planned) | `scripts/sync-prose-tooling.sh` | + +A channel earns its `PRODUCT.md` when channel-specific decisions (visual system, error rubric, instructional voice, +etc.) accumulate enough that the universal `BRAND.md` cannot carry them. The spec and site channels have crossed that +threshold today. + +**Convention: deep tier-3 artifacts live at the repo root, not in `docs/`.** The site channel's `DESIGN.md` sits at +`agentnative-site/DESIGN.md` (not `docs/DESIGN.md`) so the `/impeccable` skill loader and human readers find it without +traversal. Future deep companions (e.g., a hypothetical `GOVERNANCE.md`) follow the same pattern. Only research +artifacts and historical plans live under `docs/`. + +## Sync + +This document is the source of truth. The site syncs it via `scripts/sync-spec.sh` alongside `principles/*.md`, +`VERSION`, and `CHANGELOG.md`. The skill bundle and linter sync similarly when they grow brand-aware artifacts. A PR +that changes `BRAND.md` flags whether channel sync is needed; channel repos pick up the change in a follow-on PR. diff --git a/docs/DESIGN.md b/DESIGN.md similarity index 98% rename from docs/DESIGN.md rename to DESIGN.md index d0739d9..91d5649 100644 --- a/docs/DESIGN.md +++ b/DESIGN.md @@ -11,14 +11,13 @@ below are referenced by name; check out `dev` to read them locally. **Shipped — consumed by the site build, present on `main`:** -- [`src/styles/foundation.css`](../src/styles/foundation.css) — generated drop-in stylesheet. Contains palette custom +- [`src/styles/foundation.css`](src/styles/foundation.css) — generated drop-in stylesheet. Contains palette custom properties (light default, dark via `prefers-color-scheme`, explicit `[data-theme]` overrides), typography tokens (`--font-sans`, `--font-mono`, scale), and the shipped 7b inline-keyword rules. `@font-face` declarations are deliberately NOT in this file (see §4.3 — they live in the site build so the stylesheet is safe to load from any origin without phantom 404s). Copied byte-for-byte to `dist/css/foundation.css` by `src/build/assets.mjs`. -- [`scripts/og/og.html`](../scripts/og/og.html) + [`scripts/og/og.css`](../scripts/og/og.css) — production source-of- - truth for the social card. Rendered to `public/og-image.png` by `scripts/og/generate.ts` (Playwright + Sharp, ≤150 KB - PNG). +- [`scripts/og/og.html`](scripts/og/og.html) + [`scripts/og/og.css`](scripts/og/og.css) — production source-of- truth + for the social card. Rendered to `public/og-image.png` by `scripts/og/generate.ts` (Playwright + Sharp, ≤150 KB PNG). **Research / show-your-work — `dev` branch only (referenced by name, no link from `main`):** @@ -34,10 +33,10 @@ below are referenced by name; check out `dev` to read them locally. **Generator — `scripts/design/`** (tooling, not shipped to dist but lives on `main`): -- [`scripts/design/generate-palette.mjs`](../scripts/design/generate-palette.mjs) — the script that emits both - artifacts: writes `src/styles/foundation.css` and (on the `dev` branch) `docs/research/design/color-analysis.md`. Run - via `cd scripts/design && bun install && bun run generate` (or `bun run scripts/design/generate-palette.mjs` from the - repo root). +- [`scripts/design/generate-palette.mjs`](scripts/design/generate-palette.mjs) — the script that emits both artifacts: + writes `src/styles/foundation.css` and (on the `dev` branch) `docs/research/design/color-analysis.md`. Run via `cd + scripts/design && bun install && bun run generate` (or `bun run scripts/design/generate-palette.mjs` from the repo + root). ## 1. Summary @@ -240,8 +239,8 @@ max-age=31536000, immutable`. **Why this wins for this site, specifically.** -- **Nine pages.** Everything a framework gives for free (sidebar nav, search indexing, component library, multi-version - switcher) is either unused or faintly in the way. +- **Nine pages.** Everything a framework provides by default (sidebar nav, search indexing, component library, + multi-version switcher) is either unused or faintly in the way. - **Content-negotiation semantics stay in one file we own.** Even with Starlight's plugin ecosystem, `Accept: text/markdown` on the same URL is not a plugin — it is a Worker concern. If the Worker exists regardless, the framework's incremental value for this site is reduced to "it generates the HTML shell" — which we can do in ~40 lines @@ -323,7 +322,7 @@ the filename — no separate manifest file, no frontmatter ordering key. | `src/client/theme.ts` | `dist/js/theme.js` | Loaded via `