chore(template): clean-slate template snapshot from bb4a0c7

Curated, product-agnostic snapshot of the post-story-04 tree: demo
content deleted, auth-only reference feature, web-next shell, all gates
green. Product-specific docs, ADRs 027-029, PRDs/epics/archive, editor
library traces, and product naming are curated out; generic template
repairs (coverage provider devDeps, root test:coverage script, live
lint fixes, root-only release-please) are kept. See TEMPLATE.md for
provenance, curation list, and usage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
2026-07-12 20:40:54 +02:00
commit f77e6ea881
1062 changed files with 105156 additions and 0 deletions

52
.github/renovate.json vendored Normal file
View File

@@ -0,0 +1,52 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base",
"helpers:pinGitHubActionDigests",
":separateMajorReleases",
":automergeMinor",
":automergePatch"
],
"dependencyDashboard": true,
"dependencyDashboardLabels": ["renovate/dashboard"],
"commitMessagePrefix": "chore(deps):",
"major": {
"commitMessagePrefix": "chore(deps-major):"
},
"packageRules": [
{
"groupName": "Sentry packages",
"matchPackagePatterns": ["^@sentry/"],
"schedule": ["on monday"],
"automerge": false
},
{
"groupName": "OpenTelemetry packages",
"matchPackagePatterns": ["^@opentelemetry/"],
"schedule": ["on monday"],
"automerge": false
},
{
"groupName": "tRPC packages",
"matchPackagePatterns": ["^@trpc/"],
"schedule": ["on monday"],
"automerge": false
},
{
"groupName": "Payload packages",
"matchPackagePatterns": ["^payload"],
"schedule": ["on monday"],
"automerge": false
},
{
"groupName": "Inversify packages",
"matchPackagePatterns": ["^inversify"],
"schedule": ["on monday"],
"automerge": false
}
],
"dockerfile": {
"enabled": true,
"fileMatch": ["^\\.sandcastle/Dockerfile$"]
}
}

162
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,162 @@
# CI workflow — runs on every push to main and every pull request.
#
# TURBO_TOKEN / TURBO_TEAM: set these in your repository secrets/variables
# to enable Turborepo remote caching. Without them the workflow still works,
# just without the remote-cache speedup.
#
# PAYLOAD_SECRET: the value used here is a throwaway test secret. Do NOT
# reuse it in production. Set a real secret for your deployed environments.
name: CI
on:
push:
branches: [main]
pull_request:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
CI: true
jobs:
validate:
name: typecheck + lint + boundaries + test + build
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: cms_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
with:
# Full history so coverage:diff can resolve `origin/<base-ref>...HEAD`
# against the PR's base branch (ADR-020 L1).
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Audit package signatures
run: pnpm audit signatures --audit-level=high
- name: Socket supply-chain scan
if: github.event_name == 'pull_request'
run: |
if git diff --name-only origin/${{ github.base_ref }}...HEAD \
| grep -qE '(^|/)package\.json$|(^|/)pnpm-lock\.yaml$'; then
npx --yes socket-cli@latest scan .
else
echo "No package.json or pnpm-lock.yaml changes — skipping Socket scan."
fi
- run: pnpm typecheck
- run: pnpm lint
- run: pnpm conformance
- name: Compliance manifest drift check
run: |
pnpm compliance:emit-all --check || {
echo ""
echo "Compliance artifacts are out of date."
echo "Run \`pnpm compliance:emit-all\` locally and commit the updated files."
exit 1
}
- name: Fallow whole-codebase analysis
run: pnpm fallow --format annotations
- run: pnpm turbo boundaries
- name: Test with coverage
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test
PAYLOAD_SECRET: test-secret-do-not-use-in-prod
run: pnpm test:coverage
# L2 — merge per-package lcovs to coverage/lcov.info + emit
# coverage/summary.json (ADR-020). Runs even on test failure so the
# artifact still captures what was produced.
- name: Coverage — aggregate (L2)
if: always()
run: pnpm coverage:aggregate
# L1 — cover-the-diff gate. Only meaningful on PRs (push-to-main has
# no base ref to diff against). Compares against the PR's base branch.
- name: Coverage — diff (L1)
if: github.event_name == 'pull_request'
run: pnpm coverage:diff -- --base origin/${{ github.base_ref }}
- run: pnpm build
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage
path: |
coverage/lcov.info
coverage/summary.json
**/coverage/lcov.info
retention-days: 7
e2e:
name: Playwright e2e
needs: validate
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: cms_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- name: Run e2e
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test
PAYLOAD_SECRET: test-secret-do-not-use-in-prod
run: pnpm test:e2e
storybook:
name: Storybook smoke tests + visual regression
needs: validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- name: Build Storybook
run: pnpm --filter @repo/storybook build:storybook
- run: pnpm test:stories
- name: Install Playwright browsers
run: pnpm exec playwright install chromium --with-deps
- name: Visual regression
run: pnpm test:visual

44
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
# CodeQL static analysis — javascript-typescript.
#
# Runs on every push to main, every pull request, and weekly on Wednesday
# at 02:00 UTC (staggered from the trace-revalidation cron on Monday 06:30).
#
# NOTE (consumers): CodeQL is free for public repositories and GitHub Free
# plans. For *private* repositories it requires GitHub Advanced Security
# (available on GitHub Enterprise Cloud/Server or as an add-on). If you are
# using this template with a private repo and do not have Advanced Security
# enabled, remove or disable this workflow — it will fail at the "Initialize
# CodeQL" step with a licensing error.
name: CodeQL
on:
push:
branches: [main]
pull_request:
schedule:
# 02:00 UTC every Wednesday
- cron: "0 2 * * 3"
permissions:
contents: read
security-events: write
jobs:
analyze:
name: Analyze (javascript-typescript)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
# Uses the default query suite (security-and-quality). To restrict
# to security-only queries, set:
# queries: security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

72
.github/workflows/coverage-snapshot.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
# Coverage snapshot — commits coverage/summary.json back to main after each
# merge so the trend history accumulates in `git log -- coverage/summary.json`
# (ADR-020 L2). This is the only workflow that needs `contents: write`.
#
# Skipped if summary.json hasn't changed since the previous commit.
name: Coverage snapshot
on:
push:
branches: [main]
# Avoid two snapshot runs racing each other on rapid-fire merges.
concurrency:
group: coverage-snapshot
cancel-in-progress: false
permissions:
contents: write
jobs:
snapshot:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: cms_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
with:
# Token with write scope so we can push the snapshot back.
token: ${{ secrets.GITHUB_TOKEN }}
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Test with coverage
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test
PAYLOAD_SECRET: test-secret-do-not-use-in-prod
run: pnpm test:coverage
- name: Aggregate
run: pnpm coverage:aggregate
- name: Commit summary.json if changed
run: |
if git diff --quiet --exit-code coverage/summary.json; then
echo "No summary.json change; skipping commit."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add coverage/summary.json
git commit -m "chore(coverage): snapshot ${GITHUB_SHA::7}
Auto-generated by .github/workflows/coverage-snapshot.yml.
[skip ci]"
git push origin HEAD:main

110
.github/workflows/mutation-nightly.yml vendored Normal file
View File

@@ -0,0 +1,110 @@
# Mutation testing (L3) — nightly run + on-demand. ADR-020.
#
# Stryker is slow (~minutes per feature) so it's NOT part of the default
# CI loop. This workflow runs nightly (and on manual dispatch) across every
# feature with a stryker.config.json, then uploads the HTML + JSON
# mutation reports as artifacts.
#
# On a meaningful score drop (>5%) it opens a tracking issue.
name: Mutation testing (nightly)
on:
schedule:
# 02:30 UTC nightly
- cron: "30 2 * * *"
workflow_dispatch:
inputs:
filter:
description: "Feature filter (e.g. @repo/auth). Empty = all features."
required: false
default: ""
permissions:
contents: read
issues: write
jobs:
mutate:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: cms_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Run mutation testing
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/cms_test
PAYLOAD_SECRET: test-secret-do-not-use-in-prod
run: |
if [ -n "${{ inputs.filter }}" ]; then
pnpm mutate -- --filter "${{ inputs.filter }}"
else
pnpm mutate
fi
continue-on-error: true
- name: Upload mutation reports
if: always()
uses: actions/upload-artifact@v4
with:
name: mutation-reports
path: packages/*/reports/mutation/
retention-days: 30
- name: Open tracking issue on >5% score drop
if: failure()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const reports = [];
const pkgsDir = path.join(process.cwd(), 'packages');
if (fs.existsSync(pkgsDir)) {
for (const pkg of fs.readdirSync(pkgsDir)) {
const json = path.join(pkgsDir, pkg, 'reports', 'mutation', 'mutation.json');
if (fs.existsSync(json)) {
try {
const data = JSON.parse(fs.readFileSync(json, 'utf8'));
const score = data.thresholds?.high && data.systemUnderTestMetrics?.metrics?.mutationScore;
if (typeof score === 'number') {
reports.push({ pkg, score: score.toFixed(2) });
}
} catch { /* skip */ }
}
}
}
if (reports.length === 0) return;
const body = [
'Nightly mutation testing run flagged failures. Latest scores:',
'',
...reports.map(r => `- **${r.pkg}**: ${r.score}%`),
'',
`Run: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
].join('\n');
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `Mutation score drop — ${new Date().toISOString().slice(0, 10)}`,
body,
labels: ['mutation-testing', 'automated'],
});

83
.github/workflows/release-please.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
# Release Please — automated changelog + version bumps on merge to main.
#
# How it works:
# 1. On every push to main, release-please scans conventional commits since
# the last release tag.
# 2. It opens (or updates) a single rolling "release PR" containing:
# - the root package.json version bump
# - new CHANGELOG.md entries grouped by section (Features / Bug Fixes
# / Performance / Refactoring / Documentation / Reverts)
# - updated .release-please-manifest.json
# 3. Merging that PR triggers tag creation (`vN.N.N`) and GitHub release
# notes.
#
# Single root version (template default): only the root package is tracked
# and tags are plain `v*` (`include-component-in-tag: false`). Hybrid
# per-feature versioning is a documented alternative — see ADR-021.
#
# Tracked package, manifest baseline, and changelog sections live in
# `release-please-config.json` + `.release-please-manifest.json`.
name: Release Please
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
# A second push to main while a release PR is open shouldn't fight with the
# first invocation — release-please-action already updates the rolling PR
# idempotently, but concurrency keeps the audit trail clean.
concurrency:
group: release-please
cancel-in-progress: false
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
token: ${{ secrets.GITHUB_TOKEN }}
# The steps below run only when release-please actually cut a release.
# pnpm dlx avoids adding @cyclonedx/cyclonedx-npm to the lockfile (CI-only
# tool per ADR-022); SHA-pinned action follows ADR-023 §1 Renovate pattern.
- uses: actions/checkout@v4
if: ${{ steps.release.outputs.releases_created == 'true' }}
- uses: pnpm/action-setup@v4
if: ${{ steps.release.outputs.releases_created == 'true' }}
with:
version: 9
- uses: actions/setup-node@v4
if: ${{ steps.release.outputs.releases_created == 'true' }}
with:
node-version: 22
cache: pnpm
- name: Install dependencies
if: ${{ steps.release.outputs.releases_created == 'true' }}
run: pnpm install --frozen-lockfile
- name: Generate CycloneDX SBOM
if: ${{ steps.release.outputs.releases_created == 'true' }}
run: >
pnpm dlx @cyclonedx/cyclonedx-npm
--output-file sbom-${{ steps.release.outputs.tag_name }}.cdx.json
--output-format json
--ignore-npm-errors
- name: Attach SBOM to GitHub release
if: ${{ steps.release.outputs.releases_created == 'true' }}
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ steps.release.outputs.tag_name }}
files: sbom-${{ steps.release.outputs.tag_name }}.cdx.json

28
.github/workflows/sentry-pii-guard.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
# R31 — block sendDefaultPii: true from ever landing.
#
# This is a defense-in-depth gate: the privacy posture is also enforced by
# the centralized init helpers in core-shared/instrumentation/sentry/, but
# this grep makes any drift impossible to merge.
name: Sentry PII guard (R31)
on:
pull_request:
push:
branches: [main]
jobs:
pii-guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify sendDefaultPii is never true
run: |
if grep -RIn --include='*.ts' --include='*.tsx' --include='*.mjs' --include='*.cjs' --include='*.js' \
--exclude-dir=node_modules --exclude-dir=.next --exclude-dir=dist --exclude-dir=.turbo \
-E 'sendDefaultPii\s*:\s*true' \
packages/ apps/; then
echo "::error::R31 violation — sendDefaultPii: true is forbidden anywhere in the repo."
exit 1
fi
echo "OK — no sendDefaultPii: true detected."

View File

@@ -0,0 +1,38 @@
# Library trace revalidation — weekly run + on-demand. ADR-022.
#
# Walks every approved + pre-shipped trace in docs/library-decisions/,
# re-runs each trace's verification-commands, classifies divergence as
# soft (minor drift → rolling dashboard issue) or hard (re-evaluation
# warranted → per-dep issue), and opens/updates/closes GitHub issues
# accordingly. Runs in parallel to main — does NOT gate deployments.
name: Library trace revalidation (weekly)
on:
schedule:
# 06:30 UTC every Monday
- cron: "30 6 * * 1"
workflow_dispatch:
permissions:
contents: read
issues: write
jobs:
revalidate:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Revalidate library traces
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node scripts/library-decisions/revalidate.mjs