Convention shift: epic folders + PRD filenames + frontmatter id
fields are now bare slugs. The created: timestamp (Phase 2) carries
the date; folder names don't repeat it. A future <task-id>-<slug>
shape (e.g. ClickUp) lands cleanly when that integration ships.
Renames (git mv preserves history):
- docs/work/2026-05-13-binder-wrap-helper/
-> docs/work/binder-wrap-helper/
- docs/work/2026-05-14-library-evaluation-policy/
-> docs/work/library-evaluation-policy/
- docs/work/2026-05-14-ci-security-and-supply-chain/
-> docs/work/ci-security-and-supply-chain/
- docs/work/prds/2026-05-13-binder-wrap-helper.prd.md
-> docs/work/prds/binder-wrap-helper.prd.md
- docs/work/prds/2026-05-13-coverage-architecture.prd.md
-> docs/work/prds/coverage-architecture.prd.md
- docs/work/prds/2026-05-14-library-evaluation-policy.prd.md
-> docs/work/prds/library-evaluation-policy.prd.md
- docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md
-> docs/work/prds/ci-security-and-supply-chain.prd.md
Frontmatter updates inside the renamed files: epic id, epic prd,
story epic, PRD id, PRD builds-on all drop date prefixes.
System folder + state file move:
- New docs/work/_system/ holds framework-managed state.
- docs/work/_state.json -> docs/work/_system/_state.json.
- state-builder.mjs adds _system to SKIP_FOLDERS.
- cli.mjs + state-sync-guard.mjs + .husky/pre-commit point at the
new path.
template-reset-v1 epic deleted entirely (one-off cleanup epic from
the pre-date-convention era; status was already done).
Generator-template updates (so new artifacts ship in the right
shape):
- .sandcastle/decomposer.prompt.md emits bare-slug folder names +
ISO created: timestamp.
- .claude/skills/to-prd/SKILL.md template uses bare-slug filename +
bare-slug id field + ISO created: timestamp.
Doc reference updates: glossary, runbook, agent-first-workflow-
and-conformance, reviewer prompt, ADR-020, ADR-022, ADR-023 all
point at the new paths/slugs.
26 KiB
id, title, type, status, author, created, updated, adr, builds-on
| id | title | type | status | author | created | updated | adr | builds-on |
|---|---|---|---|---|---|---|---|---|
| ci-security-and-supply-chain | CI security + supply-chain enforcement stack | prd | approved | danijel | 2026-05-14T00:00:00Z | 2026-05-14T19:16:52.691Z | adr-023 | library-evaluation-policy |
Problem
The repo's current security posture, audited 2026-05-14: zero security tooling.
No Dependabot, no Renovate, no CodeQL, no Snyk, no Trivy, no OSV-Scanner, no
Socket, no gitleaks, no pnpm audit signatures step. GitHub Actions are
pinned to major-version tags (actions/checkout@v4, pnpm/action-setup@v4,
googleapis/release-please-action@v4), which the 2025 tj-actions/changed-files
incident proved unsafe.
ADR-022 + the in-flight library-evaluation epic close the adoption-time gate for new dependencies. They do not close the drift gate. Six post-adoption threats remain uncovered:
- CVE disclosures against pinned versions. The trace's
verification-commandssnapshot goes stale silently when new advisories drop. - Supply-chain behavior compromise —
event-stream,ua-parser-js,tj-actions/changed-files,xz-utils. CVE scanning is a lagging indicator; these shipped malware that no CVE database had seen at the moment of compromise. - Maintainer-account compromise. A trusted upstream maintainer's npm
account gets phished; the next patch publishes a malicious post-install
script; everyone on
^1.2.0inherits it. - GitHub Actions supply chain. Major-tag pinning is documented insecure.
- License drift (e.g. Sentry going BSL on a major; Elasticsearch going SSPL).
- EU-residency drift when a vendor announces US-only changes mid-flight.
ADR-023 codifies the four-pillar enforcement stack that closes these gaps. This PRD implements it.
Goal
A four-pillar CI security stack — Renovate-managed bumps + Action SHA pinning, Socket-based supply-chain-behavior detection, continuous trace revalidation extending ADR-022, and baseline GitHub-native gates — composed via a single failure-mode hierarchy that the sandcastle reviewer prompt enforces machine-readably for agent-driven PRs.
In scope
- Renovate adoption —
.github/renovate.jsonconfiguring per-workspace npm bumps (grouped by ecosystem cluster), Dockerfile bumps for.sandcastle/Dockerfile, GitHub Actions SHA pinning viapinGitHubActionDigests, major/minor split with automerge for green minor+patch PRs. - One-time Action SHA-pin sweep. Renovate's first run rewrites the 6
existing
uses:references in.github/workflows/*.ymlfrom major-version tags to full 40-char SHAs. - Socket.dev integration as the 9th hard filter in
evaluate-library. Trace schema gainssocket-risk: clean | flagged | "<finding-summary>"infilter-results:. Verification-commands gains the Socket scan command..socket.jsonconfigures issue-rules with named severity thresholds (default:critical → error). - Socket CI step in
ci.yml'svalidatejob — runssocket-cli scanagainst the lockfile, fails oncritical. - Socket GitHub App install instructions in the human guide for consumers.
- Trace revalidation workflow at
.github/workflows/trace-revalidation-weekly.yml— weekly cron +workflow_dispatch. Scope: every approved + pre-shipped trace. Two-tier divergence action: soft → rolling dashboard issue (library-policy/dashboardlabel); hard → per-dep issue (library-policy/re-evaluationlabel). No auto-edit of traces, no auto-dispatch, no main-CI gating. - Trace schema extensions in
scripts/library-decisions/schema.mjs—last-revalidated: <ISO date>frontmatter field;socket-riskfilter result;verification-commandsarray gains the Socket entry. - Major-bump re-evaluation flow —
scripts/library-decisions/check.mjsdetects when a Renovate PR bumps a runtime dep across a semver-major boundary in a feature/core package and requires the trace'slast-revalidatedto be refreshed. Minor + patch bumps do NOT require re-evaluation. - CodeQL workflow at
.github/workflows/codeql.ymlforjavascript-typescript; runs on push to main + PRs + weekly schedule. pnpm audit signatures --audit-level=highadded as one step inci.yml'svalidatejob.gitleakspre-commit hook in.husky/pre-commitas a step alongside the existing state-sync guard. Custom-pattern allowlist via.gitleaks.toml.- Sandcastle reviewer prompt update — extend
.sandcastle/reviewer.prompt.mdto read Socket CI output (viagh run view) and reject oncritical, and to read CodeQL findings and reject onerrorseverity. - Failure-mode hierarchy table ships in
docs/guides/ci-security.mdand is referenced from the reviewer prompt. docs/guides/ci-security.mdhuman reading-room — covers each gate, consumer-toggleable settings (GitHub native push protection, Socket App install, branch protection forlibrary-policy/*labels), the failure-mode hierarchy table, and worked examples (a passing Renovate minor-bump PR, a blocked major-bump PR, a hard-divergence revalidation issue).- CLAUDE.md "Key Conventions" gains a one-line bullet pointing to ADR-023 + the guide.
- Glossary already includes Trace revalidation and Major-bump re-evaluation (landed inline during the grill session that produced ADR-023).
Out of scope
- Paid Socket Team plan / server-side PR-block enforcement. The free App + self-hosted CLI achieves equivalent enforcement at $0; paid upgrade is a per-consumer decision.
- Snyk, Trivy, OSV-Scanner. Free GitHub-native (CodeQL + Dependabot alerts + push protection) + Socket + Renovate cover the surface at $0.
- Container scanning for the sandcastle Dockerfile. Renovate handles base-image bumps; the sandbox is short-lived and host-isolated.
- Auto-removal of approved-then-unused deps —
pnpm fallowterritory. - License auto-enforcement at the lockfile layer (license-checker plugins). Defer until the policy has run for some time.
- Anything app-tier. ADR-022 exempts app tier from traces; this PRD inherits that exemption.
- Devdeps in any tier. Only
dependencies(runtime) participate in the policy. - Splitting ADR-023 from ADR-022 amendments into two ADRs. Decided in the grill (Q7d): one ADR, ADR-022 unedited but cited.
- Auto-dispatch on
library-policy/re-evaluationissues. Human triage required; the dispatch loop drains the queue on demand. - CI gating on
library-policy/re-evaluation(block main). Main keeps deploying; trace re-walks happen in parallel.
Constraints
- ADR-023 is the source of truth. This PRD implements but does not extend it.
- ADR-022 stays unedited. The amendments in ADR-023 §6 are what the implementation honors. Both ADRs must be readable as a composed policy.
- ADR-019 — the sandcastle reviewer prompt is one of four enforcement layers. Reviewer-prompt extensions compose with the existing prompt shape and with the library-evaluation epic's story 06 (whose reviewer-prompt updates land first).
- ADR-021 — release-please picks up dep changes from commit history.
Renovate's bump commits must use Conventional Commits (
chore(deps):,chore(deps-major):) so release-please's per-package bump rules apply cleanly. - Template-vs-consumer framing — every artifact ships as a consumer-inheritable default. Plan-gated tools (CodeQL on private repos) include clear error messages when the consumer's GitHub plan doesn't cover them.
- Conformance system parity — the failure-mode hierarchy mirrors ADR-012's latency-tiered shape. Same vocabulary, same agent feedback loop.
- Conventional Commits — every commit produced by the implementation
follows
<type>(<scope>): <subject>. --no-verifyis forbidden — the bash-guard hook enforces this; new pre-commit checks inherit the protection.- Reviewer prompt is the single composable gate for agent PRs — the sandcastle reviewer must be able to derive "approve/reject" from CI outputs (Socket findings, CodeQL severity) without needing a separate judgment surface.
Success criteria
pnpm typecheck && pnpm test && pnpm lint && pnpm conformance && pnpm fallow:auditpass green at the end of the epic.pnpm coverage:diffcovers every changed executable line introduced by the implementation slices.- Renovate's first run (on this repo and on any consumer's downstream
fork) opens a PR that SHA-pins every
uses:reference in.github/workflows/*.yml. After merge, no@v<N>-style tag pin remains in any workflow. - Running
evaluate-libraryagainst any new package now collects and records asocket-riskfilter result and includes thesocket-cliinvocation inverification-commands. Existing backfilled traces (from the library-evaluation epic) getsocket-riskadded via the Renovate-bump flow or via an explicit backfill task. - A Renovate PR that bumps
@sentry/nodefrom7.x → 8.xagainstpackages/marketing-pagesis blocked from auto-merge until the trace'slast-revalidatedis refreshed by running theevaluate-libraryskill. - A Renovate PR that bumps
@sentry/nodefrom7.5.0 → 7.6.0against the same package auto-merges if all gates pass — no trace refresh required. .github/workflows/trace-revalidation-weekly.ymlruns successfully on its first weekly cron, against the existing ~10 backfilled traces, and produces either zero divergence or alibrary-policy/dashboardissue with a comparison diff.- A simulated hard-divergence trigger (manually mutating a trace's
expected
cve-scanvalue vs. whatpnpm auditreturns) opens alibrary-policy/re-evaluationissue with the correct title format and body. - A simulated
criticalSocket finding in CI causes the sandcastle reviewer to reject the slice with notes referencing the Socket finding. - A simulated
error-severity CodeQL finding causes the same reviewer rejection. pnpm audit signaturesruns as a step in CI and fails the job when a deliberately-tampered package signature is staged.gitleakspre-commit hook blocks a commit that adds a known token pattern (Stripe-style test key) to any tracked file.docs/guides/ci-security.mdincludes the failure-mode hierarchy table, the consumer-toggleable settings list, and at least two worked examples (one approved flow, one blocked flow).CLAUDE.mdKey Conventions includes the ADR-023 bullet.- All existing backfilled traces (from the library-evaluation epic)
carry a
last-revalidatedfield after the first weekly cron run.
User stories
- As a developer running
pnpm addagainst a feature package, I want Socket's risk score to be one of the filter results theevaluate-libraryskill collects and records, so I get a single composed answer instead of having to remember to check Socket separately. - As an agent dispatched against a slice that bumps a runtime dep,
I want Renovate's bump PR to either auto-merge (minor/patch) or
require me to walk
evaluate-library(major) so the policy gate is automatic, not remembered. - As a reviewer (human or agent) of a Renovate major-bump PR, I
want the trace's
last-revalidatedfield refreshed by theevaluate-libraryre-run so I can see at a glance "this dep was re-validated today" before approving. - As an agent reviewing a slice in sandcastle, I want the reviewer
prompt to read Socket CI output + CodeQL findings + library-trace
presence in one composed check and reject on any
critical/error, so I have a single composable gate. - As a maintainer who hasn't touched the repo for a week, I want the weekly trace revalidation cron to produce at most one rolling dashboard issue (soft divergence) and zero per-dep issues (hard divergence) unless something actually drifted, so my notification surface stays clean.
- As a future agent considering a previously-approved library that's
now Socket-flagged, I want the trace revalidation cron to open a
library-policy/re-evaluationissue with the trace path + the Socket finding + a clean re-walk handoff, so I can drive the re-evaluation without re-discovering the prior context. - As a maintainer reviewing CI output for a PR that touches
package.json, I want Socket's comment + thesocket-cli scanstep's result + the library-trace presence check to all be visible in one place (the PR's checks panel), so the decision is one glance, not three. - As a security-conscious maintainer, I want every
uses:reference in every workflow pinned to a 40-char SHA + a trailing# v<N>comment, so thetj-actions/changed-filesclass of attack is closed and Renovate keeps the SHAs current. - As a maintainer who accidentally pastes a token into a commit,
I want the
gitleakspre-commit hook to refuse the commit + GitHub native push protection to be a second line of defense, so a leaked secret never reaches the remote. - As a code reviewer looking at a PR with a CodeQL
errorfinding, I want the finding to appear in PR checks as a hard-block, so the pattern doesn't merge. - As a maintainer reading the repo for the first time, I want
docs/guides/ci-security.mdto walk me through the four pillars- the failure-mode hierarchy + the consumer-toggleable settings, so I understand what to enable in a downstream repo without spelunking workflows.
Implementation decisions
Module sketch — what lands where, by concern (no specific file paths where prose suffices):
- Renovate config — single
.github/renovate.jsonextending a small set of presets:config:base,helpers:pinGitHubActionDigests,:separateMajorReleases,:automergeMinor,:automergePatch. CustompackageRules:group@sentry/*,@opentelemetry/*,@trpc/*,payload*, andinversify*into per-cluster weekly PRs. Dockerfile manager enabled for.sandcastle/Dockerfile.dependencyDashboard: trueopens a single issue that summarizes open + queued PRs. - Socket integration — schema layer.
scripts/library-decisions/schema.mjsgainssocketRisk: z.union([z.literal("clean"), z.literal("flagged"), z.string()])in thefilter-resultsZod schema. Theverification-commandsarray gains the Socket entry. The trace template (_template.md) mirrors the new field. - Socket integration — skill layer.
.claude/skills/evaluate-library/SKILL.mdgains a "9 — Supply-chain behavior (Socket)" section. The skill's fail-fast logic (collect-cheap-skip-expensive) treats Socket as expensive (network call) and runs it after the cheap structural filters.socket-cliis the verification command; output parsing follows Socket's JSON schema. - Socket integration — CI layer. One step in
ci.yml'svalidatejob:socket-cli scan --json | jq <severity-filter>. Fail oncritical..socket.jsonlives at repo root:{ "issueRules": { "critical": "error", "high": "warn", "medium": "ignore", "low": "ignore" } }. - Trace revalidation workflow. New file
.github/workflows/trace-revalidation-weekly.yml. Triggers:schedule: - cron: "30 6 * * 1"(Monday 06:30 UTC, avoiding the Sunday→Monday CI peak), plusworkflow_dispatch. Job: checkout, install, run a new scriptscripts/library-decisions/revalidate.mjsthat walks every approved + pre-shipped trace, re-runs each trace'sverification-commands, classifies divergence, opens or updates issues viaghCLI. Permissions:issues: write,contents: read(NOcontents: write— no auto-edit). - Major-bump re-evaluation flow.
scripts/library-decisions/check.mjsgains a new mode: when invoked on a Renovate-generated PR (detected via branch prefixrenovate/), it parses the lockfile diff to extract bumped deps + their from/to versions, classifies each as major / minor / patch, and for any feature/core-tier major bump checks that the corresponding trace'slast-revalidatedfield is fresh (set today). If not fresh, exit non-zero with a pointer to theevaluate-libraryskill. - CodeQL workflow. Standard GitHub-issued template:
language: javascript-typescript, triggerspush: branches: [main],pull_request, and weeklyschedule. Default queries. - Pre-commit
gitleaks..husky/pre-commitgains step:gitleaks protect --staged --redact..gitleaks.tomlships with the repo's allowlist patterns (e.g. test fixtures in__seeds__/that look like tokens but aren't). - Reviewer-prompt update.
.sandcastle/reviewer.prompt.mdgains a "CI security checks" section after the existing library-trace check (from the library-evaluation epic's story 06). The reviewer readsgh run viewoutput for the PR's check suite, looks for Socket findings of severitycriticaland CodeQL findings of severityerror, and rejects the slice if either is present with notes referencing the specific finding. - Human guide —
docs/guides/ci-security.mdfollows the same shape asdocs/guides/coverage.md: overview, per-pillar section, failure-mode hierarchy table, consumer settings list, two worked examples. - CLAUDE.md update — one bullet in Key Conventions:
"CI security + supply-chain enforcement: Renovate for bumps + Action
SHA pinning, Socket for supply-chain behavior, weekly trace
revalidation, CodeQL + audit signatures + gitleaks. See ADR-023 +
docs/guides/ci-security.md."
Trace schema extension (Zod, lifted from ADR-023 §6.3):
filterResults: z.object({
// ... existing 8 fields ...
socketRisk: z.union([
z.literal("clean"),
z.literal("flagged"),
z.string(), // human-readable finding summary
]),
});
lastRevalidated: z.string().nullable(); // ISO date or null on a fresh adoption
The date field stays mandatory (adoption-provenance); last-revalidated
is set on major-bump re-eval (Q3) and on a successful trace revalidation
run (J).
Failure-mode hierarchy (lifted from ADR-023 §5): the table is the
source of truth referenced by both the reviewer prompt and
docs/guides/ci-security.md. Changes to the hierarchy require an ADR
amendment.
Sequencing — depends on the library-evaluation epic. This PRD's implementation depends on the in-flight library-evaluation epic:
- Story 01 of library-evaluation (trace schema foundation) must land first — this PRD extends that schema.
- Story 02 of library-evaluation (pre-commit check script) must land first — this PRD extends that script with the major-bump-detection mode.
- Story 04 of library-evaluation (evaluate-library skill) must land first — this PRD adds the Socket filter to that skill.
- Story 06 of library-evaluation (reviewer-prompt update) must land first — this PRD extends the reviewer prompt added there.
Sandcastle dispatch should order this PRD's epic after the library-evaluation epic completes.
Conformance system composition — no new use cases, controllers, manifest entries, audits, events, jobs, or realtime channels. This PRD is workflow/policy implementation, not feature-domain change. The conformance gates apply only to the new TypeScript/JS modules (Zod schema extensions, the revalidate.mjs script, the check.mjs major-bump mode) — they get standard vitest coverage.
Testing decisions
scripts/library-decisions/schema.mjsextensions — unit tests covering:socketRiskfield round-trips for all three variants (clean/flagged/<string>);lastRevalidatedaccepts ISO dates andnull; missingsocketRiskon a trace fails validation;lastRevalidated: nullis the default for fresh traces.scripts/library-decisions/check.mjsmajor-bump mode — integration tests covering: minor bump on a feature-tier dep → pass without trace refresh; major bump on a feature-tier dep with freshlast-revalidated→ pass; major bump on a feature-tier dep with stalelast-revalidated→ fail with a clear pointer; major bump on an app-tier dep → pass (app tier exempt); patch bump in a Renovate branch → pass; non-Renovate branch with a major bump → pass (the rule is Renovate-PR-scoped).scripts/library-decisions/revalidate.mjs— integration tests using a fixture trace directory: trace with no drift → no issue opened; trace with soft drift → dashboard issue created/updated; trace with hard drift → per-dep issue opened with correct labels + title format; trace already covered by an open per-dep issue → no duplicate issue; rejection trace → skipped entirely. UseghCLI mocks or a fake GitHub API surface for the issue-write side.- Renovate config — no automated test; verified by Renovate Dependency Dashboard preview run + manual review of the first PR (the SHA-pin sweep).
- Socket CI step — smoke test by adding a known-flagged package
fixture to a test branch and asserting CI fails. Captures the
socket-clioutput format we depend on for parsing. - CodeQL workflow — no test; the workflow file IS the test (GitHub validates the YAML; CodeQL action either runs or no-ops per consumer plan).
pnpm audit signaturesstep — verified by the existence of the step inci.yml+ a smoke test where a deliberately-corrupt signature fails CI.gitleakspre-commit hook — bash smoke test that pipes a staged commit containing a known token pattern through the hook and asserts exit code non-zero. Use a Stripe-style test key as the fixture.- Reviewer-prompt extension — no automated test in the conformance
sense (it's a prose runbook for an agent). Success criterion is
manual: dispatch an agent against a PR with a simulated Socket
criticalfinding, verify the agent rejects with the expected notes. - Prior art — mirror the test patterns from the library-evaluation epic's stories 01–02 (trace schema + check script). The fixture and assertion shape carries over directly.
- Coverage bands — new scripts under
scripts/library-decisions/aren't feature packages, so no per-layer thresholds. Default expectation: 100% statement coverage on the new branches (the scripts are small).
Open questions
- Q1: Should the Renovate config use a
branchPrefixother than the defaultrenovate/to make the check.mjs Renovate-PR-detection more robust against future Renovate refactors? — Recommended: no — Renovate'srenovate/prefix has been stable for years; use the default and detect via that prefix. Future-proofing here is premature. - Q2: Should
socket-cli scanrun on every CI PR or only on PRs that touchpackage.json/pnpm-lock.yaml? — Recommended: only on PRs that touch those files. Use apaths:filter on the step. Cheaper CI; same coverage (Socket can't catch behavior changes in a PR that doesn't change deps). - Q3: Should the major-bump re-evaluation rule apply when Renovate
groups multiple deps in one PR? — Recommended: the rule applies
per-dep, not per-PR. If a grouped PR contains 3 minor bumps + 1 major
bump, the trace for the major-bump dep needs
last-revalidatedrefreshed; the 3 minor bumps don't trigger. The check.mjs script walks the lockfile diff and validates each bumped dep independently. - Q4: Should
library-policy/re-evaluationissues auto-close when the trace'slast-revalidatedis refreshed in a subsequent commit? — Recommended: yes, the trace revalidation workflow checks for open issues whose dep names appear in newly-refreshed traces and closes them with a comment citing the refresh commit. - Q5: Where do the gitleaks allowlist patterns live? — Recommended:
ship a minimal
.gitleaks.tomlat repo root with one explicit allowlist for__seeds__/**test fixtures. Document that consumers extend it for their own custom patterns. - Q6: Should the
dependencyDashboardissue Renovate opens be labeled identically to the trace revalidationlibrary-policy/dashboardissue? — Recommended: no, keep them separate. Renovate's dependency dashboard is about pending bumps; the trace revalidation dashboard is about post-adoption drift. Different queues, different labels (renovate/dashboardvslibrary-policy/dashboard).
Out of scope (deferred)
- Branch protection rules. Configuring GitHub branch protection to
require Socket + CodeQL + audit-signatures + library-trace-check
checks before merge is a per-repo settings change, not a tracked
file. Document the recommended ruleset in
docs/guides/ci-security.mdand leave application to consumers. - Auto-dispatch on
library-policy/re-evaluationissues. Decided out of scope in the grill (Q7c); revisit if human triage becomes a bottleneck in practice. - Renovate Dependency Dashboard → docs/work/ task integration.
Surfacing pending bumps as
pnpm worktasks would let agents pick them up via dispatch. Interesting but separate. - OSSF Scorecard integration. Complementary to Socket but duplicates several signals; defer until the four-pillar stack has matured.
- StepSecurity Harden Runner. Adds runtime egress detection on top of Action SHA pinning. Defer; the SHA pins close the primary attack surface.
- Socket Team plan upgrade. Free tier is documented as adequate for this template; consumers upgrade per their own threat model.
- License-checker lockfile-layer enforcement. Defer until the policy has run for some time.
Further notes
- Anchored by ADR-023 — CI security + supply-chain enforcement stack. Read that first.
- Builds on ADR-022 — Library evaluation policy. ADR-022 stays
unedited; ADR-023 §6 amends it with major-bump trigger,
last-revalidatedfield, and Socket as the 9th hard filter. - Builds on PRD
library-evaluation-policy— many of this PRD's modules extend artifacts being built by that PRD's epic. The sequencing constraint in §Implementation Decisions is load-bearing. - Glossary entries for Trace revalidation and Major-bump re-evaluation landed during the 2026-05-14 grill session that produced ADR-023.
- Conversation provenance — the 2026-05-14 grill-with-docs session
that produced this PRD is captured in the session transcript;
ADR-023 cites the audit of zero security tooling + the
tj-actions/changed-filesincident as concrete catalysts.