ADR-024: analytics backend is consumer-chosen; operator must decide whether to wire a vendor and, if so, run /evaluate-library first (ADR-022 gate applies). Documents IAnalytics wiring, flush() shutdown hook, and optional React provider. ADR-025: compliance directory setup — generate and commit compliance/ YAML files as audit evidence; verify drift gate in pre-commit + CI; schedule retention purge job per custom.retention; hand-author compliance/sub-processors.manual.yml for non-npm vendors. All existing ADR-022/023 content preserved unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
10 KiB
Operator checklist
The decisions and code that make this template's security + supply-chain stack work are landed in code (ADR-022, ADR-023, the library-evaluation epic, the CI-security epic). This doc covers the human-side actions required to put it into operation in a real GitHub-hosted repo. Read top to bottom on first adoption; revisit the "Ongoing" section weekly.
The biggest leverage move is #1 (push to a remote) — until that
happens, the entire .github/workflows/ surface is inert. Everything
else cascades from there.
Right now (unblocks everything else)
1. Push to a GitHub remote. No remote is configured by default on a fresh template clone, which means CI doesn't run anywhere. Either:
gh repo create <owner>/template-vertical --source=. --private --push(or--public)- Or push to an existing remote:
git remote add origin <url> && git push -u origin main
Decision attached: public vs private. Public → CodeQL is free, Socket free tier just works. Private → CodeQL needs GitHub Pro/Team/Enterprise plan (workflow runs unconditionally but GitHub gates execution).
During the dispatch loop (the agent drives this; you watch)
2. Continue the in-flight library-evaluation epic. Run
pnpm work dispatch --execute. The dispatcher picks up the next unticked
bullet and marches through.
Refresh ~/.claude/.credentials.json from the macOS keychain when
sandcastle returns 401 (the keychain one-liner; happens ~every 30 days):
security find-generic-password -s "Claude Code-credentials" -a "$USER" -w \
> ~/.claude/.credentials.json
chmod 600 ~/.claude/.credentials.json
3. After library-evaluation epic completes, the CI-security epic
unblocks. pnpm work dispatch --execute picks up its story 01
automatically.
After all implementation lands (one-time setup, ~30 minutes total)
4. Install GitHub Apps (one click each, free tier):
- Renovate →
https://github.com/apps/renovate→ grant repo access. After install, Renovate opens an onboarding PR — merge it to enable. First real PR is the Action SHA-pin sweep (rewrites@v4→@<sha>across all workflows). Merge that too. - Socket Security →
https://github.com/apps/socket-security→ grant repo access. PR comments start appearing on the nextpackage.jsondiff.
5. Toggle GitHub repo settings (Settings → Code security and analysis):
- ✅ Dependabot alerts (server-side vuln scan) — your passive monitoring surface
- ✅ Dependabot security updates — OFF (Renovate handles bumps; alerts stay on for visibility)
- ✅ Secret scanning + push protection — blocks known token patterns at the GitHub edge
- ✅ CodeQL alerts (auto-enabled by the workflow)
6. Configure branch protection on main (Settings → Branches →
main):
- Require status checks:
validate,socket-security,CodeQL - Require linear history (matches release-please's expectations)
- Do not add
library-policy/re-evaluationas a blocker — ADR-023 explicitly decided against gating main on revalidation issues
7. Add TURBO_TOKEN + TURBO_TEAM as repo secrets/variables for
Turborepo remote caching (already documented in ci.yml's comment
block).
8. Sentry DSNs if you want production observability per ADR-014 /
ADR-017 (WEB_NEXT_SENTRY_DSN, CMS_SENTRY_DSN, etc.) — set as repo
secrets for the apps you actually deploy.
Ongoing (per-decision, weekly cadence)
9. Renovate's weekly PR stream.
- Minor + patch bumps auto-merge if green. Nothing to do.
- Major bumps block until
evaluate-libraryre-runs and refreshes the trace'slast-revalidated. The dispatch loop can pick these up via story-style task — or you walk the skill manually (/evaluate-library <name> --tier <feature|core> --target <path>).
10. Weekly trace-revalidation cron fires every Monday 06:30 UTC.
- Soft divergence → appended to the rolling
library-policy/dashboardissue. Skim weekly; mostly no-action. - Hard divergence → fresh
library-policy/re-evaluationissue per affected dep. Human triage required (ADR-023 §3, no auto-dispatch). Decide: re-walk evaluate-library, accept-with- allowlist, or migrate off the library. Add the issue to the dispatch queue if the re-walk is mechanical.
11. CVE accepted-risk decisions. When pnpm audit flags something
with no patch available, add accepted-cves: [CVE-XXXX-YYYY] to the
relevant trace's frontmatter with a note explaining why the risk is
accepted.
12. License-allowlist requests. ADR-022 names
MIT/Apache-2.0/BSD/ISC/MPL-2.0 as allowlisted. If a real need surfaces
(e.g. a GPL-3.0-with-classpath-exception library), you decide whether
to extend — and the decision becomes an ADR-022 amendment in a new ADR.
Analytics backend (ADR-024)
13. Choose and wire an analytics vendor — or skip. @repo/core-analytics ships
IAnalytics as a vendor-neutral contract with no default backend. The analytics
backend is consumer-chosen: the template deliberately ships no vendor because
the choice requires ADR-022's library evaluation gate (EU residency, license,
Socket.dev) and your product's consent model.
If you want product analytics:
- Evaluate the vendor first. Run
/evaluate-library <name> --tier core --target packages/core-analytics. The resulting trace atdocs/library-decisions/<date>-<name>.mdis the required evidence before adding the package to your lockfile. - Implement
IAnalytics. Write a wrapper around the vendor SDK that satisfies the four-method interface (track,identify,pageView,flush). - Wire at DI bind time. Pass the implementation into
ctx.analyticsinbind-production.ts. Feature binders pick it up and compose it into thewithAnalytics(...)wrapper chain automatically. - Wire
flush()into graceful shutdown. Hookanalytics.flush()into your app'sSIGTERM/beforeExithandler to drain the in-memory batch before the process exits — event loss on container/serverless shutdown is the most common analytics bug. - Client side (optional). Wrap your app root in
<AnalyticsProvider value={...}>from@repo/core-analytics/react; exposeIAnalyticsto components viauseAnalytics().
If you don't want analytics, do nothing — the template boots with NoopAnalytics
by default and no wiring is required.
Compliance directory (ADR-025)
14. Generate and commit the compliance/ YAML files. Three audit-evidence
files live at the repo root under compliance/, generated from Payload schema
declarations and library traces:
pnpm compliance:emit-all
# produces:
# compliance/data-map.yml (from Payload field custom.pii tags)
# compliance/retention-policy.yml (from Payload collection custom.retention)
# compliance/sub-processors.yml (from docs/library-decisions/ traces flagged
# is-sub-processor: true)
git add compliance/
git commit -m "compliance: initial audit-evidence YAML files"
Run once after your Payload schema stabilises. Re-run and commit whenever schema
changes. The CI drift gate (pnpm compliance:emit-all --check) fails if generated
output diverges from source.
15. Verify the compliance drift gate in pre-commit and CI.
- Pre-commit:
.husky/pre-commitshould include thepnpm compliance:emit-all --checkstep. Verify afterpnpm install. - CI:
ci.yml'svalidatejob runs the same check on every PR. Add it to branch-protection required checks (step 6 above) so drift can't land onmain.
16. Schedule the retention purge job. core-shared/jobs/retention-purge.job.ts
reads custom.retention from each Payload collection at boot and enqueues
per-collection purge runs on the declared purgeSchedule. To activate:
- Set
custom.retention: { activeRetention, postDeletion, purgeSchedule, hardDeleteAfter }on each Payload collection you want auto-purged. Seedocs/compliance/retention-policy.example.ymlfor the schema. - Confirm the job runner is live in production — the purge job enqueues via
IJobQueue, which requires@repo/core-eventsor Payload's native jobs queue. - Verify
audit_eventsgains{ action: "DELETE", reason: "retention-policy" }entries after the first purge cycle.
17. Hand-author compliance/sub-processors.manual.yml for non-npm vendors.
The sub-processors generator only surfaces vendors that have an npm package in
docs/library-decisions/. Any vendor you call via a pure REST API — no SDK, no
library trace — needs a manual entry:
# compliance/sub-processors.manual.yml
- name: SendGrid
is-sub-processor: true
processes-pii: true
data-sent: [email-address, display-name]
region: US
dpa-signed: 2026-01-15
sccs-required: true
contact: privacy@sendgrid.com
Commit this file alongside compliance/sub-processors.yml. Both are treated as
audit evidence; unlike the generated files, this one is always hand-maintained.
Read once the docs land
docs/guides/adding-a-library.md(library-evaluation epic story 05) — human reading-room for the 9 filters + 3 promptsdocs/guides/ci-security.md(CI-security epic story 09) — human reading-room for the four pillars + failure-mode hierarchy table
Cleanup notes
- Untracked files in repo root —
check-shas-cjs.js,check-shas.mjs,check-shas2.mjs,check-shas3.mjs. These appeared during SHA-experimentation work and aren't part of any commit. Decide whether to delete or.gitignore. apps/*/tsconfig.tsbuildinfocontinues to churn on every typecheck. Achore: gitignore tsbuildinfocommit would stop that drift.
Related
- ADR-022 — Library evaluation policy
- ADR-023 — CI security + supply-chain enforcement stack
- ADR-024 — Product analytics channel
- ADR-025 — EU compliance baseline (DPA/GDPR scope)
docs/guides/runbook.md— first-time template setup (Postgres, dev servers, sandcastle auth)docs/guides/adding-a-library.md— adding a runtime depdocs/guides/ci-security.md— security stack reference