The Unified Identity Platform: Trusted Identity for Machines and Humans — Verified, Private, Attested
Static marketing website for the Artagon Identity Platform, built with Astro and deployed to GitHub Pages at artagon.com.
- Quick Start
- Project Structure
- Technology Stack
- Configuration
- Development
- Build & Deployment
- Security & Performance
- Scripts & Utilities
- CI/CD Pipeline
- OpenSpec Workflow
- Contributing
# Install dependencies
npm install
# Start development server (http://localhost:4321)
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
# Format code
npm run formatartagon-site/
├── .agents/ # AI agent configuration and policies
├── .github/workflows/ # GitHub Actions CI/CD pipelines
├── openspec/ # OpenSpec spec-driven development
│ ├── changes/ # Proposed changes and features
│ ├── specs/ # Current capability specifications
│ ├── project.md # Project conventions
│ ├── contributing.md # Contribution guidelines
│ └── config.yaml # OpenSpec CLI project descriptor
├── public/ # Static assets (copied as-is to .build/dist/)
│ ├── .well-known/ # Well-known URIs (security.txt, etc.)
│ ├── assets/ # Images, logos, OG images, theme.css, fonts/
│ │ └── logos/ # Logo variants and documentation
│ ├── icons/ # Favicons and app icons
│ ├── docsearch.json # Algolia DocSearch config
│ ├── robots.txt # Search engine directives
│ └── _redirects # Cloudflare Pages redirect rules
# (sitemap-index.xml is GENERATED to
# .build/dist/ at build time, NOT
# pre-existing in public/)
├── scripts/ # Build, lint, verify, and asset scripts
# (run `ls scripts/*.mjs scripts/*.sh`
# for the current count — frozen counts
# pre-pt380 (~14 .mjs + 8 .sh) drifted
# as new scripts landed; SSoT is the dir)
│ ├── csp.mjs # Content Security Policy injection (postbuild)
│ ├── sri.mjs # Subresource Integrity injection (postbuild)
│ ├── lhci-serve.mjs # LHCI local server with ready signal
│ ├── verify-prerequisites.mjs # USMR Phase 0.5 archive-ordering gate
│ ├── verify-design-prerequisites.mjs # adopt-design-md-format archive gate
│ ├── verify-font-self-hosting.mjs # font-src 'self' postbuild gate
│ ├── lint-skip-link.mjs # Skip-link first-tabbable postbuild gate
│ ├── lint-taglines.mjs # Single-source tagline gate
│ ├── lint-tokens.mjs # Raw color literal gate
│ ├── sync-build-config.mjs # Generates lighthouserc.json + lychee.toml
│ ├── make-og-from-template.sh # OG image generation
│ ├── make-favicon.sh # Favicon generation
│ ├── icons/ # Icon generation utilities
│ ├── lib/ # Internal helpers (e.g. openspec-archive.mjs)
│ └── ... # Other helpers (oklch-to-hex, check-design-drift, etc.)
├── src/
│ ├── components/ # Reusable Astro components
│ # (run `find src/components -maxdepth 2
│ # -name '*.astro' -o -name '*.tsx' | wc -l`
│ # for current count — pre-pt381 (~30) drifted
│ # as the redesign added/retired surfaces;
│ # SSoT is the dir tree)
│ │ ├── Header.astro # Main navigation
│ │ ├── Footer.astro # Site footer
│ │ ├── SeoTags.astro # SEO meta tags
│ │ ├── FaqSearch.astro # FAQ search functionality
│ │ ├── ui/ # UI primitives (Card, SectionHeader, FeatureList)
│ │ └── ...
│ ├── data/ # Typed structured-data registries (~11 modules)
│ │ ├── faq.ts # FAQ entries
│ │ ├── roadmap.ts # Roadmap horizontal-timeline registry
│ │ ├── pillars.ts # Three-pillars taxonomy
│ │ ├── standards.ts # Standards / specs registry
│ │ ├── glossary.ts # Acronym/term glossary (consumed by Standard chips)
│ │ └── ... # Other registries (organization, affiliations, bridge, etc.)
│ ├── lib/ # Pure helpers (charset, markdown, reading-time)
│ ├── scripts/ # Browser-side TS (tweaks-state, etc.)
│ ├── content/ # Astro Content Collections (pages/, writing/, taglines.json)
│ ├── layouts/
│ │ └── BaseLayout.astro # Main layout template
│ └── pages/ # File-based routing
│ ├── index.astro # Homepage
│ ├── platform/ # Platform features
│ ├── roadmap/ # Product roadmap
│ ├── faq/ # FAQ page
│ ├── vision/ # Vision and strategy
│ ├── docs/ # Docs shell
│ ├── console/ # Console shell
│ ├── search/ # Search interface
│ └── ...
├── astro.config.ts # Astro configuration (TypeScript)
├── lighthouserc.json # Lighthouse CI thresholds
├── lychee.toml # Link checker configuration
├── package.json # Dependencies and scripts
└── README.md # This file
- Astro v6.2.1 - Static site generator with zero JS by default (per
package.json"astro": "6.2.1") - Static Output - Pure HTML/CSS/JS, no server required
- File-based Routing - Pages in
src/pages/map to routes
The four @astrojs/* integrations declared in package.json (versions accurate as of package.json lines 80-83):
- @astrojs/mdx 5.0.4 - MDX content rendering for the 3 content collections defined in
src/content.config.ts:pages(src/content/pages/*.mdxfor marketing-route bodies),writing(src/content/writing/*.mdxfor/writing/[slug]long-form posts), andauthors(src/content/authors/*.mdxfor author entries referenced from writing frontmatter). Pre-pt394 the README mentioned onlypages+/writing/[slug], omitting theauthorscollection. - @astrojs/react 5.0.4 - React island runtime for interactive components. Live islands:
TrustChainIsland,TweaksPanel,PillarsIsland,BridgeFlow,UseCasesIsland(runls src/components/*Island.tsx src/components/BridgeFlow.tsx src/components/TweaksPanel.tsxfor the current set — pre-pt393 the README listed onlyTrustChainIsland+TweaksPanel, undercounting the 5 actual islands by 60%; the 3 omitted islands all landed under USMR Phase 5.x). USMR Phase 5.x added the integration. - @astrojs/rss 4.0.18 - RSS 2.0 feed generation for
/writing/feed.xml(auto-discovery wiring landed in pt197) - @astrojs/sitemap 3.7.2 - Automatic
sitemap-index.xmlgeneration- Filters out
/_drafts/pages AND every route in theNOINDEX_ROUTESallow-list (astro.config.tsper pt146) - Canonical domain:
https://artagon.com
- Filters out
- Node.js 22+ (per
.nvmrc22.12 andpackage.jsonengines.node>=22.0.0; CI workflows pinned to 22 per pt193) - npm - Package manager
- Prettier 3.8.3 - Code formatting (per
package.json"prettier": "3.8.3")
- Subresource Integrity (SRI) - Cryptographic hash validation for JS/CSS
- Content Security Policy (CSP) - Script execution controls via meta tags
- Lighthouse CI - Automated performance and accessibility audits
- Lychee - Link checking and validation
File: astro.config.ts
The config is the canonical TypeScript form (the legacy astro.config.mjs
form was migrated to .ts in early USMR Phase 5.x; the .ts extension
lets the config import the typed BUILD paths from build.config.ts).
For the live source see astro.config.ts directly —
the snippet below is a representative excerpt:
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";
import mdx from "@astrojs/mdx";
import react from "@astrojs/react";
import { BUILD } from "./build.config.ts";
export default defineConfig({
site: "https://artagon.com", // canonical domain (matches CNAME)
output: "static",
trailingSlash: "never",
outDir: BUILD.dist,
cacheDir: BUILD.cache.astro,
integrations: [mdx(), react(), sitemap({ filter: /* noindex routes */ })],
});File: lighthouserc.json
Performance thresholds enforced in CI:
- Performance: ≥90% (warning)
- Accessibility: ≥95% (error)
- Best Practices: ≥90% (error)
- SEO: ≥95% (error)
Runs 2 times per URL and uploads to temporary public storage.
Local usage:
npm run build
npx -y @lhci/cli@0.15.x autorun --config=lighthouserc.jsonThe LHCI config starts scripts/lhci-serve.mjs, which prints READY once the
static server responds. Override the port with LHCI_PORT if needed.
File: lychee.toml
verbose = "info"
accept = ["200..299", "403"]
exclude_path = ["dist", "node_modules"]
exclude = [
"http://localhost.*",
"https://twitter.com/.*", # Often blocks bots
"mailto:.*"
]
retry_wait_time = 2
max_retries = 2
timeout = 20The dev and build scripts both run a prebuild/predev chain
that regenerates lighthouserc.json + lychee.toml from
build.config.json SSoT. The build script runs a 10-step
postbuild chain (per the "Build Scripts" subsection below for
the full ordered list — gated structurally by the pt267
lint-readme-postbuild-chain-sync test).
{
"dev": "astro dev", // Start dev server
"predev": "npm run build:prebuild-chain", // Sync configs
"build": "astro build", // Static site build
"prebuild": "npm run build:prebuild-chain", // Sync configs
"preview": "astro preview", // Preview production build
"format": "prettier --write .", // Format all files
"postbuild": "<10-step chain — see Build Scripts below>"
}Build pipeline summary:
prebuild/predevrunnpm run build:prebuild-chain(regenerates the JSON/YAML/TOML configs that derive frombuild.config.json).astro buildcompiles pages, components, and assets to.build/dist/.@astrojs/sitemapemitssitemap-index.xml(filtered to indexable routes perNOINDEX_ROUTESallow-list).postbuildruns the 10-step chain (verify-prereqs → verify-design-prereqs → lint-tokens → font-self-hosting → SRI → CSP → skip-link → taglines → design.md → design-md-uniqueness — the canonical ordered list lives in the "Build Scripts" subsection below).
npm run dev- Runs on
http://localhost:4321 - Hot module reloading enabled
- File watching for instant updates
npm run dev and npm run build work with no env vars set —
no .env file is required for the basic local dev loop. The
following env vars OPT IN to optional behaviors that
contributors may encounter:
# Visual-regression suite (gated locally; CI runs in dedicated job)
VISUAL_REGRESSION=1
# Lighthouse CI local-server overrides
LHCI_PORT=8081 # Port for scripts/lhci-serve.mjs
LHCI_DIR=.build/dist # Dist directory to serve
LHCI_READY_INTERVAL_MS=250 # Poll interval for READY signal
LHCI_READY_REQUEST_TIMEOUT_MS=2000 # Per-request timeout
# CI-only (set automatically by GitHub Actions; do not set locally)
GITHUB_TOKEN=... # gh API auth for spec-validate / pr-spec-compliance
GITHUB_REPOSITORY=owner/repo # Auto-set by Actions
GITHUB_EVENT_PATH=/path/to/event # Auto-set by Actions
GITHUB_SHA=... # Footer build-sha (falls back to git rev-parse locally)
# Test override (do not set unless writing tests against verify-prerequisites.mjs)
VERIFY_PREREQ_ARCHIVE_SHA=<sha>
# Hot-loop opt-out for the sync:build-config drift gate. Used by .husky/
# pre-commit:14 to bypass per-commit; also recognized by
# scripts/sync-build-config.mjs:37 to short-circuit the prebuild/predev
# sync. Per the openspec/specs/build-config/spec.md `Pre-commit safety
# net` Requirement, opt-out applies only when explicitly set per-commit.
SKIP_BUILD_SYNC=1
# pt264 archaeology — `AXE_AUDIT=1` was the pre-Phase-5.1p.8 opt-in
# for the axe-core WCAG audit. The gate flipped to MANDATORY in
# pt5.1p.8 after round-3 violations cleared; the env var no longer
# gates anything (the test runs unconditionally on chromium /
# webkit / Mobile Safari).If a .env file is created locally for any of the above, it is
gitignored per .gitignore. The GLOBAL_DOTENV=1 shell toggle
(per the ~/.config/AGENTS.md XDG contract) loads ~/.env on
startup; default off.
- Create
.astrofile insrc/pages/ - Use
BaseLayoutwrapper:
---
import BaseLayout from "../layouts/BaseLayout.astro";
---
<BaseLayout
title="Page Title"
description="SEO description (150-160 chars)"
path="/page-path"
>
<!-- Page content -->
</BaseLayout>- Update navigation in
src/components/Header.astroif adding top-level route
Location: src/components/
All components are Astro components (.astro files) with:
- Zero JavaScript by default (ships pure HTML/CSS)
- Optional client-side JS via
<script>tags - TypeScript support in frontmatter
Key Components:
Header.astro- Main navigation (six-linkNAV_LINKSper pt87 canonical: Platform · Bridge · Use cases · Standards · Roadmap · Blog). The original header theme toggle was removed in pt87 and the standaloneThemeToggle.astrowas deleted as orphan in pt166; theme switching is now exercised via the dev-onlyTweakspanel.Footer.astro- Site footer (4-column flat grid: Platform / Developers / Company / Legal × 5 links each per pt5.5.3 canonical)SeoTags.astro- Meta tags, Open Graph, Twitter Card, JSON-LDOrganization+WebSiteschema, robots prop, canonical URLsFaqSearch.astro- Client-side FAQ search (renders inside/faqroute alongsideFaqItem.astroper-entry markup)RoadmapTimeline.astro- Roadmap horizontal-timeline cards (consumes thesrc/data/roadmap.tstyped registry)
Location: src/data/
TypeScript files exporting structured content. Each module ships an explicit export type for the row shape and a readonly array constant. The shapes shown below are the live shapes — they may evolve, and the canonical source of truth is the .ts file itself.
faq.ts - FAQ entries (consumed by /faq route + FaqItem.astro per-entry markup):
export type FaqItem = {
id: string;
category: string;
question: string;
answer: string; // Markdown rendered via the Astro Markdown pipeline
};
export const FAQ_CATEGORIES = [
"Platform Overview",
"Authentication & Identity",
// ...
] as const;
export const FAQS: FaqItem[] = [
{
id: "what-is-artagon",
category: "Platform Overview",
question: "...",
answer: "...",
},
];roadmap.ts - Product roadmap horizontal-timeline registry (consumed by RoadmapTimeline.astro at /roadmap per the pt5.7 5-tuple shape that replaced the earlier 5.1c stub):
export type RoadmapStatus = "shipping" | "in-build" | "design" | "planned";
export interface RoadmapPhase {
id: "v1" | "v2" | "v3" | "v4" | "v5"; // anchor id for /roadmap#v3 deep links
version: "V1" | "V2" | "V3" | "V4" | "V5"; // mono prefix in the card header
// ... time band, scope items, ...
}npm run buildBuild Process:
- Prebuild:
npm run sync:build-configregenerateslighthouserc.jsonandlychee.tomlfrombuild.config.json. - Astro compiles pages, components, and assets to
.build/dist/. - Sitemap generated at
.build/dist/sitemap-index.xml(filtered to indexable routes per theNOINDEX_ROUTESallow-list inastro.config.ts). - Postbuild: the 10-step chain runs (per
package.jsonpostbuildscript — see "Build Scripts" subsection below for the full ordered list). Highlights: SRI hashes (sri.mjs), CSP meta-tag injection (csp.mjs), skip-link gate, taglines gate, design.md format gate, font-self-hosting gate, etc.
Output: .build/dist/ directory with static HTML/CSS/JS
All runtime artifacts live under .build/{cache,reports,dist}/ — see docs/build-artifacts.md for the full SSoT contract, generated-file list, and "add a tool" contributor checklist.
npm run clean # remove .build/ entirely
npm run clean:cache # remove .build/cache/ only (keep reports + dist)
npm run clean:reports # remove .build/reports/ onlyAll clean scripts are lock-aware (refuse to delete while a test run holds .build/.run.lock).
npm run previewServes .build/dist/ on http://localhost:4321 to test production build.
Automatic deployment on push to main branch.
Workflow: .github/workflows/deploy.yml
build job:
1. Checkout code (actions/checkout, SHA-pinned — initial pin landed in
security-audit commit `42d54ad` `chore(ci): SHA-pin all actions`;
ongoing version bumps land via Dependabot's `github-actions`
ecosystem entry in `.github/dependabot.yml`. Pre-pt386 cite was
"per pt? CODEOWNERS rule" — stale TODO placeholder; CODEOWNERS
doesn't enforce SHA-pinning, the security-audit + Dependabot pair
does.)
2. Setup Node.js 22 (actions/setup-node)
3. Install deps with `npm ci` (frozen lockfile — NOT `npm install`)
4. `npm run build` (runs prebuild → astro build → postbuild chain)
5. Upload `.build/dist/` Pages artifact
deploy job: 6. Deploy to GitHub Pages (actions/deploy-pages)Required Pages setting: set Source to GitHub Actions so deployments run from .github/workflows/deploy.yml (branch-based Jekyll builds are not supported).
Configuration:
- Custom domain:
artagon.com(set in repo settings) - CNAME:
artagon.com(inpublic/CNAME) - HTTPS: Enforced via GitHub Pages settings
- Permissions:
contents: read, pages: write, id-token: write
Tagged releases (e.g., v0.1.0) automatically:
- Build the site
- Create GitHub release
- Attach
dist.zipto release
Workflow: .github/workflows/release.yml
openspec/specs/* govern behavior; DESIGN.md governs visual presentation; implementation traces to both. On conflict, the spec wins and DESIGN.md is updated in the same change.
DESIGN.md— canonical visual identity contract: color tokens (OKLCH prose + sRGB hex frontmatter), typography, spacing, rounded scale, and component visual contracts.docs/design-md.md— authoring and maintenance guide: precedence chain, how to add a token, how to bump the upstream@google/design.mdversion, OKLCH↔hex hybrid policy, upstream attribution, and thecheck:design-driftallow-list.
The npm run lint:design gate runs in postbuild and on every PR that touches DESIGN.md. See docs/design-md.md for full details.
Script: scripts/sri.mjs
Automatically adds cryptographic hashes to local JS/CSS resources:
<!-- Before -->
<script src="/assets/app.js"></script>
<!-- After -->
<script
src="/assets/app.js"
integrity="sha256-..."
crossorigin="anonymous"
></script>Important: Disable CDN minification to avoid SRI mismatches.
Script: scripts/csp.mjs
Generates strict CSP as <meta> tag with the 9 directives constructed by buildPolicy() in scripts/csp.mjs:
default-src 'self'- Only same-origin resourcesscript-src 'self' 'sha256-...'- Inline scripts hashed; orphan-hash detection incsp.mjsfails the build if any emitted hash is not present in the finalscript-srcdirectivestyle-src 'self' 'unsafe-inline'- Styles allowed (Astro scoped styles)img-src 'self' data:- Images from same origin + data URIsfont-src 'self'- Fonts from same origin only (no third-party CDNs); theverify-font-self-hosting.mjspostbuild gate fails the build if anydist/**/*.{html,css}referencesfonts.googleapis.comor similarconnect-src 'self'- XHR / fetch / WebSocket targets locked to same originobject-src 'none'- No pluginsbase-uri 'none'- Prevent base tag injectionframe-ancestors 'none'- Prevent clickjacking
All inline <script> tags are hashed and added to script-src automatically. DocSearch / Algolia origins are added as extras only when DocSearch is enabled.
- Zero JS by default - Astro ships pure HTML/CSS
- Scoped CSS - Component styles are scoped and bundled
- Asset optimization - Images and icons optimized for web
- Static generation - No server-side rendering overhead
- Lighthouse scores - Enforced ≥90% performance in CI
Recommended headers (configure at CDN/hosting level):
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
All scripts located in scripts/ directory.
./scripts/make-og-from-template.sh \
"Artagon — Identity that can prove it" \
"Passkeys • VCs • Attestation • Policy" \
public/assets/og-image.png \
--logo public/assets/logo-lockup.pngCreates Open Graph images from SVG templates.
./scripts/make-favicon.shGenerates multi-size favicons from source SVG.
./scripts/icons/make-icons.shCreates app icons, touch icons, and PWA assets.
Documentation:
scripts/icons/README.md- Icon generation overviewscripts/icons/VERIFICATION.md- Icon verification checklist
./scripts/svg-to-pngs.sh input.svg output-dir/Batch converts SVG to multiple PNG sizes.
./scripts/convert-logos.shGenerates logo variants from source files.
Documentation:
public/assets/logos/README.md- Logo usage guidelines and variant inventory
./scripts/compose-app-icons.sh source.(svg|png) [out-dir]Generates PWA, iOS (AppIcon.appiconset), and Android (mipmap-*) app icon sets from a single source image.
./scripts/make-og-image.sh "Title text" [out.png]Quick OG image renderer (1200×630, ImageMagick). Distinct from make-og-from-template.sh (full SVG-template + logo overlay path).
./scripts/optimize-svg.sh input.svg [output.svg]svgo-based SVG optimizer (multipass mode). Used before committing new logos / illustrations.
The postbuild pipeline (per package.json postbuild script) runs the following steps in order after astro build:
verify:prerequisites—scripts/verify-prerequisites.mjsasserts therefactor-styling-architecturechange is archived (or its merge SHA is an ancestor of HEAD) before USMR work can land.verify:design-prerequisites—scripts/verify-design-prerequisites.mjsassertsadopt-design-md-formatarchive ordering beforedesign-system-format-dependent work can land (adopt-design-md-formatarchived 2026-05-05; live capability spec isopenspec/specs/design-system-format/spec.md).lint:tokens—scripts/lint-tokens.mjswalksgit ls-filesfor raw color literals outside DESIGN.md frontmatter; fails on hex/rgb/hsl/oklch literals not declared as tokens.verify-font-self-hosting.mjs—scripts/verify-font-self-hosting.mjsasserts no third-party font CDN refs leak into.build/dist/(locksfont-src 'self').sri.mjs— Scans.build/dist/*.html, computes SHA-256 for local JS/CSS, addsintegrity+crossoriginattributes, writes.build/dist/sri-manifest.json.csp.mjs— Extracts inline script SHA-256 hashes, buildsscript-srcdirective, injects<meta http-equiv="Content-Security-Policy">into every.build/dist/*.html. Includes orphan-hash detection (every emitted hash MUST be present in script-src).lint:skip-link—scripts/lint-skip-link.mjsasserts every built page in.build/dist/has a skip-link as the first focusable element with a validhref="#<id>"target.lint:taglines—scripts/lint-taglines.mjsenforces single source for tagline strings viasrc/content/taglines.json.lint:design—design.md lint DESIGN.mdper@google/design.md@0.1.1.lint:design-md-uniqueness—scripts/verify-design-md-uniqueness.mjsguards against duplicate DESIGN.md FILES — exactly one at the repo root, none undersrc/,new-design/, or any other tracked path (two design systems in flight would defeat the precedence chain). Pre-pt383 said "duplicate token names" — comment-as-code drift; the script checks file-path uniqueness, not token-name uniqueness.verify:indexation—scripts/validate-indexation.mjs(USMR Phase 10.7 / pt418) runs three checks against the built output: (A) meta-robots parity — every.build/dist/*.htmlpage MUST have<meta name="robots" content="noindex, nofollow">iff its route is inNOINDEX_ROUTES(persrc/lib/indexation.tsSSoT, pt416); (B) sitemap<lastmod>parity vs MDXupdated/publishedfrontmatter (vacuously satisfied today; becomes load-bearing once Phase 10.1 wires the sitemapserializehook); (C)public/_redirectsdestinations are same-origin (start with/, no://, no//).
The chain is lock-aware: clean.mjs uses .build/.run.lock
to coordinate cleanup across parallel runs (exit code 73 if the
lock is held by another process).
Other build-related scripts not in the postbuild chain:
scripts/sync-build-config.mjs— regenerates JSON/YAML/TOML configs (lighthouserc.json,lychee.toml) frombuild.config.jsonSSoT (run viaprebuildbeforeastro build).scripts/lhci-serve.mjs— Lighthouse CI local server withREADYsignal (consumed bylighthouserc.json).scripts/check-design-drift.mjs— orphan-token detection. Every CSS custom property declared inpublic/assets/theme.cssMUST resolve to a token inDESIGN.mdYAML frontmatter, OR appear on the documented allow-list indocs/design-md.md§6. Wired into.github/workflows/design-md-lint.yml(runs on every push + pull_request, NOT weekly). Pre-pt389 cite said "run bydesign-md-drift.ymlweekly cron" — that's a different workflow:design-md-drift.ymlrunsspec:cache+ upstream-spec drift checks (regeneratesopenspec/.cache/design-md-spec.mdfrom the npm package and verifies upstream repo health), NOT orphan-token detection.scripts/check-oklch-hex-parity.mjs+scripts/oklch-to-hex.mjs— design.md OKLCH↔hex hybrid policy gates (precondition oflint:design).scripts/verify-design-md-telemetry.mjs— telemetry verification for design.md adoption metrics.
Location: .github/workflows/
Trigger: Push to main or manual dispatch
Jobs:
- Build - Install deps, build site, upload artifact
- Deploy - Deploy to GitHub Pages
Node version: 22
Trigger: pull_request (any branch) + push to main + workflow_dispatch (manual). Per lighthouse.yml lines 2-6 — pre-pt390 the README listed only "Push to main", which undercounted the live trigger set.
Process:
- Build site
- Start HTTP server on port 8081 (via
scripts/lhci-serve.mjs) - Run Lighthouse audits
- Assert score thresholds against
lighthouserc.jsonassertMatrix - Upload results
Thresholds: declared in lighthouserc.json assertMatrix — TWO URL-pattern blocks today: one for /brand (performance 80% error, accessibility 95% error, best-practices 90% error, seo off), one default (.* — performance 85% warn, accessibility 90% error, best-practices 90% warn, seo 90% warn, plus LCP ≤ 4000 ms warn). Pre-pt390 the README listed a single threshold block (Performance ≥90% warn, Accessibility ≥95% error, etc.); none of those values matched the live lighthouserc.json shape. Live values shift over time as performance budgets evolve — read lighthouserc.json assertMatrix directly for the canonical contract; this section names the SSoT rather than freezing values that drift.
Trigger: pull_request (any branch) + push to main + workflow_dispatch (manual). Per link-check.yml lines 2-4 — there is NO schedule/cron trigger. Pre-pt392 the README said "Scheduled (weekly) or manual" — phantom-trigger drift; the workflow has never had a cron and runs per-commit, not weekly.
Process:
- Build site
- Run
lycheelink checker on:- Markdown files (
**/*.md) - HTML files (
public/**/*.html) - Astro components (
src/**/*.astro)
- Markdown files (
Configuration: lychee.toml
Trigger: pull_request (any branch) + workflow_dispatch (manual). Per agents-check.yml lines 2-4. Pre-pt391 the README said "Push" — that wasn't a live trigger; the workflow has never run on push.
Process: Asserts 4 specific agent-config files exist (the workflow's only step is a test -f chain): .agents/AGENT_MANIFEST.yaml, .agents/llm-config.json, .agents/policies/guardrails.md, .agents/context/vision-and-roadmap-full.md. Pre-pt391 the README described it as "Validates AI agent configuration" — vague; the gate only file-exists-checks the 4 named paths, not the contents.
Trigger: Manual dispatch or changes to the workflow file
Prepares the repository for GitHub Copilot coding agent sessions. The workflow must define a single copilot-setup-steps job that checks out the repo, installs the latest LTS Node.js version, and runs npm ci.
Trigger: Push of any tag matching v*.*.* OR v*.* OR v* (3 tag patterns per release.yml line 4 — tags: ["v*.*.*", "v*.*", "v*"]). Pre-pt391 the README listed only v*.*.*, undercounting the live tag-pattern set.
Process:
- Build site
- Create zip of
.build/dist/ - Create GitHub release
- Attach
dist.zipas release asset
Trigger: Push to main or feature/*, pull request to main, manual dispatch
Process: Runs the Playwright cross-engine test matrix (5 shards × all projects) plus dedicated visual-regression and accessibility jobs (chromium / webkit / Mobile Safari). Uploads per-shard reports as artifacts (14-day retention). See AGENTS.md §"Testing" for the full project matrix and shard-distribution contract.
Trigger: Push to main or feature/*, pull request to main, manual dispatch
Process: Runs the design.md linter (design.md lint DESIGN.md per @google/design.md@0.1.1) plus the project's ast-grep token-citation gates. Fails the build on token drift, retired-alias use, or design.md format violations.
Trigger: Pull request to main modifying DESIGN.md
Process: Posts a PR-scoped comment showing the design.md diff (added/removed/changed tokens, components, color contracts) so reviewers can see visual-identity changes inline.
Trigger: Weekly cron (Mondays 06:00 LA time) or manual dispatch
Process: Refreshes openspec/.cache/design-md-spec.md from npx @google/design.md spec --format markdown. Catches upstream spec changes that would invalidate our pinned @google/design.md@0.1.1 adoption.
Trigger: Pull request
Process: Validates every PR references a parent spec issue (label spec) AND aligns implementation with the OpenSpec change's acceptance criteria. Tags scope-creep PRs with needs-spec / scope-creep labels per .github/labels.yml.
Trigger: GitHub issue labeled spec or closed
Process: Drives the OpenSpec review cadence — pings reviewers when a spec is opened, closes the loop when a spec is implemented or rejected.
This project uses OpenSpec for spec-driven development.
- Specs (
openspec/specs/) - Current truth, what IS built - Changes (
openspec/changes/) - Proposals, what SHOULD change - Archive (
openspec/changes/archive/) - Completed changes
# List active changes
openspec list
# List all specifications
openspec list --specs
# Show change or spec details
openspec show <change-id>
openspec show <spec-id> --type spec
# Validate changes
openspec validate <change-id> --strict
# Archive after deployment
openspec archive <change-id> --yes-
Create Proposal - New feature or breaking change
openspec/changes/<change-id>/proposal.mdopenspec/changes/<change-id>/tasks.mdopenspec/changes/<change-id>/specs/<spec-id>/spec.md
-
Create Issue - Link or copy the spec into the issue description
-
Implement - Follow tasks.md checklist
- Branch:
feature/site(<spec#>)-<feature-short-name>(spec#is the GitHub issue number) - Commits:
feat(<spec-id>): description - Update agent context docs with a summary of branch changes
- Branch:
-
Archive - After deployment to production
- Branch:
archive/<change-id> - Command:
openspec archive <change-id> --yes - Creates:
openspec/changes/archive/YYYY-MM-DD-<change-id>/
- Branch:
AGENTS.md- Canonical project + OpenSpec agent guide (CLAUDE.md / GEMINI.md symlink to it)openspec/project.md- Project context and conventionsopenspec/contributing.md- Contribution guidelines with examples
See openspec/contributing.md for:
- Branch naming conventions (conventional commits)
- OpenSpec workflow examples
- AI agent best practices
- Archiving and closing issues
- Code formatting and linting
-
Check existing work:
openspec list # Active changes openspec list --specs # Existing capabilities
-
Create issue and include spec content:
# Issue description must link to or include the OpenSpec change gh issue create --title "Spec: <change-id>" --body "..."
-
Create feature branch (spec# = GitHub issue number):
git checkout -b feature/site(<spec#>)-<feature-short-name>
-
Make changes, commit:
git commit -m "feat(<spec-id>): description Implements openspec/changes/<change-id>/ Affects: specs/<spec-id>/spec.md Closes #<spec#>"
-
Run local checks:
npm run format npm run build npm run preview
-
Create PR:
gh pr create --title "feat(<spec-id>): Title" --body "..."
-
After deployment, archive:
git checkout -b archive/<change-id> openspec archive <change-id> --yes openspec validate --strict # Commit, PR, merge
- Formatting: Prettier (run
npm run format) - Commits: Conventional commits format
- Components: Astro components with TypeScript frontmatter
- CSS: Scoped component styles, CSS custom properties
- Branch follows naming convention (
feature/site(spec#)-...for OpenSpec changes;feat/,fix/, etc. otherwise) - Commits reference OpenSpec changes and issues
- Local build succeeds (
npm run build) - Code formatted (
npm run format) - SEO tags updated for new pages
- Navigation updated if adding routes
- Screenshots included for UI changes
- OpenSpec tasks marked complete
[x]
Location: .agents/ and OpenSpec instructions
.agents/policies/guardrails.md- Development guardrails and constraints.agents/policies/release-checklist.md- Pre-release validation stepsAGENTS.md- Main OpenSpec + project agent instructions (single source of truth at the repo root)CLAUDE.md→AGENTS.md(symlink)GEMINI.md→AGENTS.md(symlink)
AI agents should:
- Read
AGENTS.mdfor OpenSpec workflow + project conventions - Read
openspec/project.mdfor project context - Read
openspec/contributing.mdfor contribution guidelines - Use OpenSpec commands to understand existing work
- Follow OpenSpec branch naming (
feature/site(spec#)-...) for spec work; conventional naming for other changes - Update agent context docs with a summary of branch changes
- Create proposals for new features/breaking changes
- Track tasks with TODO lists
- Archive changes after deployment
Living guides (current state authoritative):
docs/AUTOMATED_TESTING.md- Playwright + vitest + node:test runners, project matrix, gating contract; pre-Phase-1 sections retired in pt233/pt235 in favor of pointer-to-tests/README.md.docs/CONTRIBUTING.md- OpenSpec workflow + spec/proposal/implementation cadence; complements the canonicalopenspec/contributing.md.docs/build-artifacts.md- SSoT contract for.build/{cache,reports,dist}/, generated-file inventory, "add a tool" contributor checklist; governed byopenspec/specs/build-config/spec.md.docs/design-md.md- design.md authoring guide (token-add procedure, OKLCH↔hex hybrid, upstream version-bump runbook).docs/decisions/0001-no-tailwind.md- Architecture-decision record on the no-Tailwind contract.docs/guides/styling-guide.md- Component-vs-utility decision tree, theme-variable reference, post-archive distilled record fromrefactor-styling-architecture.docs/guides/new-design-conversion.md- Conversion guide fromnew-design/extracted/mocks to live Astro routes; consumed by USMR Phase 5.x work.
Asset / icon references:
public/assets/logos/README.md- Logo file reference and variants.scripts/icons/README.md- Icon generation guide.scripts/icons/VERIFICATION.md- Icon verification checklist.
Historical snapshots (read-only):
docs/SITE_AUDIT.md- Phase-1-era audit dated 2026-05-04 (marked HISTORICAL SNAPSHOT in pt236; the system has evolved substantially since — see the marker for major drifts).
Private repository - All rights reserved.
For issues or questions:
- Open an issue on GitHub
- Review existing OpenSpec changes and specs
- Check
.agents/policies/for guardrails and checklists