Add socketRisk (9th filter result) and lastRevalidated (nullable ISO date)
to the library-decision trace schema. Downstream enforcement layers
(evaluate-library skill, check.mjs major-bump mode, revalidate.mjs cron)
all depend on these fields being validated at the schema layer first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Decomposer produced 9 stories under docs/work/2026-05-14-ci-
security-and-supply-chain/, ordered to land the schema foundation
first and the cross-referencing content (reviewer prompt, guide)
last:
01 - trace schema extensions (socketRisk + lastRevalidated)
02 - Socket integration (skill + CI)
03 - Renovate adoption
04 - major-bump re-evaluation flow
05 - trace revalidation workflow
06 - CodeQL + audit signatures
07 - gitleaks pre-commit
08 - reviewer prompt update
09 - CI security guide + docs
Also fixes a one-char status typo in the PRD frontmatter
(\`appoved\` -> \`approved\`) that landed with the decompose run.
Anchored by ADR-023 + the approved PRD at
docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md.
Sequencing: depends on stories 01/02/04/06 of the in-flight
library-evaluation epic landing first.
- ADR-023 codifies the four-pillar enforcement stack: Renovate for
bumps + Action SHA pinning via pinGitHubActionDigests, Socket.dev
as a 9th hard filter in evaluate-library (free App + self-hosted
socket-cli + reviewer-prompt enforcement), weekly trace
revalidation cron with two-tier divergence action (rolling
dashboard issue + per-dep re-evaluation issues), and the baseline
GitHub-native gates (CodeQL, pnpm audit signatures, gitleaks
pre-commit + native push protection). Failure-mode hierarchy is
the single source of truth referenced by the sandcastle reviewer.
- Section 6 amends ADR-022 in place: major-bump re-evaluation
trigger (minor/patch bumps skip), last-revalidated frontmatter
field (preserves original date for adoption provenance), and
Socket as the 9th hard filter. ADR-022 stays unedited; both ADRs
read as a composed policy.
- PRD at docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md
seeds the implementation epic; explicit sequencing -- depends on
the in-flight library-evaluation epic's stories 01/02/04/06
landing first.
- Glossary gains "Trace revalidation" + "Major-bump re-evaluation"
entries referenced by both ADRs.
Catalyst: 2026-05-14 audit confirmed zero security tooling in the
repo + GitHub Actions pinned to major-version tags (the tj-actions/
changed-files attack class). ADR-022 closes the adoption-time gate;
ADR-023 closes the post-adoption drift gate.
New runtime dependencies require a library trace produced by the
/evaluate-library skill — without this bullet the policy is invisible
to agents starting a fresh session (ADR-022).
Add approved trace files for payload, @trpc/server, @trpc/client, zod,
superjson, @payloadcms/db-postgres, @payloadcms/richtext-lexical, globals,
react, react-dom, vitest, @tanstack/react-query, and all @testing-library/*
packages. All traces dated 2026-05-14, decision: approved, adr: null.
Establishes the baseline so the pre-commit library-decisions gate is
additive (new deps require traces) rather than disruptive (old deps fail
immediately). All 34 trace files pass validateTrace() from schema.mjs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds approved trace files dated 2026-05-14 for all 13 OpenTelemetry
packages in packages/core-shared and packages/core-audit:
@opentelemetry/api, api-logs, context-async-hooks,
instrumentation, instrumentation-http, instrumentation-pg,
instrumentation-undici, resources, sdk-logs, sdk-metrics,
sdk-node, sdk-trace-base, semantic-conventions
All files pass validateTrace() from schema.mjs. The @sentry/opentelemetry
bridge was already covered in the ADR-014 cluster commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Approved traces for @sentry/nextjs, @sentry/opentelemetry,
@sentry/node, and @sentry/react — all at ^10.51.0 in
packages/core-shared. Without these files the pre-commit
library-trace hook would fire retroactively on any future
package.json touch; backfill establishes the baseline.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sandcastle rejects `resumeSession` when `maxIterations > 1` with
"Resume applies to iteration 1 only; multi-iteration resume
semantics are not supported." Since a TDD slice needs the full
30-iteration budget, the session-resume path we shipped in d5c0120
is dead infrastructure that breaks dispatch mid-run.
Rip it out cleanly:
- runOneSlice drops the resumeSession param + the
context-exhaustion safety net + sessionId/usage return fields
- executeDispatch drops the currentStory/currentSession bookkeeping
and the token-reset threshold
- helpers totalInputTokens + isContextExhaustedError go (only used
by the resume path)
- SANDCASTLE_SESSION_TOKEN_RESET removed from .env.example
Net: -153 lines. Each slice is again an independent sandcastle
session; token cost per slice goes up (each implementer
re-discovers context) but the multi-iteration TDD shape works.
A different cross-slice context-passing mechanism (e.g. a
story-level context summary injected into each task spec) is left
as future work.
Add approved trace files for inversify ^6.2.0 and reflect-metadata
^0.2.2, both mandated by ADR-002 (InversifyJS for DI) and used across
all five feature packages. Both pass validateTrace() from schema.mjs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each of the five optional core package generators (events, realtime, audit,
trpc, ui) now copies pre-written decision: approved trace files into
docs/library-decisions/ at scaffold time, covering every direct runtime
dependency of that core package.
This prevents a pre-commit gate failure the first time a developer runs
pnpm turbo gen core-package <name> — the generator is the policy-compliant
path, so the traces land by construction.
- Added docs/library-decisions/*.md.hbs trace files under each of the five
core-package template directories (15 files total)
- Updated generator config to emit traces into workspace docs/library-decisions/
via a second emitTemplateTree call per core package
- Updated all five __snapshots__/core-package/*.snapshot.json to include the
new trace file entries
- Added verify-doc-shas.test.ts to pin SHA256 hashes of all 15 trace templates
so snapshot and file content cannot drift independently
ADR refs: events→ADR-015, realtime→ADR-016, audit→ADR-018;
trpc and ui cite closest ADR or null where no specific ADR exists.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `--staged-against <base>` CLI flag to `check.mjs` so the reviewer
agent can compare `git diff <base>...HEAD` instead of the git index.
This gives the sandcastle reviewer a CI-compatible code path that works
in its clean sandbox where `git diff --cached` may be empty.
Appends a "Library-trace check" section to `.sandcastle/reviewer.prompt.md`
instructing the reviewer to run the command before issuing a verdict.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Translates ADR-022 from decision-record density into an onboarding
narrative: why the policy exists, the tier trigger, the four enforcement
layers, a step-by-step walkthrough, and worked approved/rejected examples
(clsx pass, trpc-to-openapi named-consumer fail).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the /evaluate-library skill runbook at .claude/skills/evaluate-library/
with SKILL.md (8-filter + 3-prompt protocol, collect-cheap-skip-expensive
ordering, trace-write step, skip sentinel), POLICY.md (ADR-022 summary
≤2 pages), TRACE-TEMPLATE.md (complete YAML frontmatter + 11 headings in
order), and EXAMPLES/ with one approved (clsx) and one rejected
(trpc-to-openapi, named-consumer: fail) worked trace.
Updates session-start.sh to surface the skill in session pointers.
The skill is auto-registered by the harness on SKILL.md creation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Registers .claude/hooks/library-policy-nudge.sh under PreToolUse/Bash
and PostToolUse/Edit|Write|MultiEdit. The hook emits a non-blocking
system-reminder pointing at /evaluate-library before runtime deps are
added via pnpm add or via direct package.json edits, so policy
evaluation happens before the pre-commit gate fires.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds scripts/library-decisions/check.mjs that walks staged package.json
diffs, derives tier from path, and fails the commit when a new runtime
dependency in a feature- or core-tier package has no sibling approved
trace staged in docs/library-decisions/.
App-tier additions and devDependency / peerDependency additions are
silently allowed. Wired into .husky/pre-commit as step 4.
check.test.mjs covers all 7 Done-when cases using temp git repo
fixtures (node:test + node:assert, same pattern as schema.test.mjs).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates the shared schema module for library evaluation traces
(ADR-022 §4): Zod-validated frontmatter with all 8 filter fields and
enum constraints, plus parseTrace/validateTrace exports and a custom
YAML frontmatter parser for the nested trace format.
Also adds docs/library-decisions/_template.md with all 11 required
headings (8 Filter + 3 Prompt) in machine-checkable ADR-022 order.
Adds zod as a root devDependency so the script is runnable directly
from the workspace root without a package context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ADR-022 codifies the tiered library-evaluation policy: 8 hard
auto-reject filters (license, types, maintenance, boundary-fit,
shadow-check, EU residency, CVE scan, named consumer), 3
discussion prompts, per-decision trace artifact at
docs/library-decisions/, and a 4-layer enforcement stack
(Claude PreToolUse/PostToolUse hook -> evaluate-library skill ->
pre-commit hook -> sandcastle reviewer prompt). Mirrors the
conformance-system latency pattern from ADR-012.
- PRD at docs/work/prds/2026-05-14-library-evaluation-policy.prd.md
seeds the implementation epic; status: approved, ready for
\`pnpm work decompose\`.
- Glossary gains "Library trace" + "Pre-shipped trace" entries
referenced by both artifacts.
Catalyst: the 2026-05-14 grill session nearly adopted
trpc-to-openapi + zod-to-json-schema before someone asked who the
HTTP consumer was. Honest answer: none -- all callers are TS via
createCaller. This policy makes that question structurally
unavoidable for any future feature- or core-tier dep.
Replace inline withSpan(withCapture(factory(deps))) form in the binder
templates with wireUseCase({...}) calls so newly scaffolded features are
consistent with the migrated production features.
Also add assertFeatureConformance to bind-dev-seed.ts.hbs (aligns with
the migrated auth/navigation pattern) and fix bind-dev-seed.test.ts.hbs
to call binders with the ctx object form (BindContext) instead of the old
two-argument (tracer, logger) form.
Verified by running turbo gen feature testfeature and confirming:
- Generated binders use wireUseCase for use cases
- All 5 conformance gates pass on the scaffold
- Scaffold cleaned up post-verification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.env.example and siblings are config/template files with no executable
code. The coverage:diff script now matches them via the same dotfile
pattern used for .gitignore and .npmrc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wires sandcastle's native `resumeSession` into the dispatch loop so
the implementer walks into task N already knowing what task N-1
discovered — repo layout, helper signatures, gate output, prior diff.
No scratchpad / no hand-curated context file; the agent's own Claude
Code conversation log is the carrier.
Three guardrails keep it bounded:
- Story boundary reset. `currentSession` is dropped whenever
findNextTask returns a different story id. New domain ≈ new
context — keeps story 03 from inheriting story 02's residue.
- Token-threshold reset. After each approved slice, sum the
implementer's last-iteration usage (inputTokens +
cacheCreationInputTokens + cacheReadInputTokens — caching saves
dollars but doesn't free window space). If above
SANDCASTLE_SESSION_TOKEN_RESET (default 140000 ≈ 70% of Sonnet
4.6's 200k), drop the session before the next task. Configurable
via env.
- Context-exhausted safety net. If the model rejects with
"prompt is too long" / "context_length_exceeded" / similar, the
retry loop drops the session and re-runs the attempt fresh
exactly once. Doesn't count against SANDCASTLE_MAX_ATTEMPTS
(different failure mode).
Reviewer always runs fresh — each approve/reject decision should be
independent of prior tasks to keep the gate honest. Within a single
slice's reject-fixup retries, the implementer also carries forward
across attempts (so attempt 2 sees attempt 1's reasoning + the
reviewer notes), but that's per-slice cumulative, not cross-slice.
runOneSlice now returns { sessionId, usage } so executeDispatch can
make the carry-or-reset decision per slice.
Replace inline withSpan + withCapture blocks for getArticles,
getArticleBySlug, and createArticle in both bind-production and
bind-dev-seed with wireUseCase calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace inline withSpan + withCapture blocks for signIn, signUp, and
signOut use cases in both bind-production.ts and bind-dev-seed.ts with
wireUseCase calls. Removes 27 lines of boilerplate per binder file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Catches up the story's checkbox + frontmatter status to reflect that
the slice shipped in commit 1bbe866. The V1 orchestrator landed the
implementer's slice commit on main but had no state-mutation step,
so the bullet stayed unticked and `pnpm work next` would have
redundantly re-dispatched the implementer for a no-op slice.
Bookkeeping-only — no code changes.
Previously the orchestrator ran exactly one implementer + reviewer pair,
printed "(Automatic state mutation by the orchestrator is v2.)", and
exited — the human had to tick the bullet, flip story status, rebuild
state, and re-invoke for every slice. V2 closes the loop:
- Parses the JSON the implementer + reviewer prompts ask the agents to
emit (`parseAgentJson` — tolerates both ```json fenced and bare
trailing { ... } shapes). The reviewer's `decision` and the
implementer's `status` are the orchestrator's discriminators.
- On approve: ticks the bullet in `_story.md` and writes it back. If
the story now has zero unchecked bullets, flips its frontmatter
`status: in-progress → done`; if all sibling stories are also done,
flips the epic's frontmatter the same way. Commits the mutation on
the host as a separate `chore(work): tick/finish ...` commit so the
implementer's slice commit stays clean. `_state.json` regenerates
via the existing pre-commit `rebuild-state` hook.
- On reject: re-dispatches the implementer with the reviewer's notes
appended to TASK_FILE_CONTENT, bounded by SANDCASTLE_MAX_ATTEMPTS
(default 3). On the (max+1)th reject the loop exits 1 with the last
notes printed.
- After every approved slice, calls findNextTask again and dispatches
the next ready bullet — including across story boundaries (the
state-builder treats any non-done story with satisfied deps as
ready, so flipping story 01 to done unblocks story 02 automatically).
- Flags: `--once` (legacy single-slice behavior) and `--max-tasks N`
bound the loop. Default is unlimited — matches the
continuous-execution preference.
Auth/sandbox setup is now pulled out of the per-iteration path so the
loop reuses one sandbox across slices.
Encapsulates withSpan(withCapture(withAudit?(factory(deps)))) composition
and container binding into a single helper, eliminating the structural
boilerplate clone groups repeated across every feature binder pair.
Callers pass { container, symbol, factory, deps, feature, layer, name,
tracer, logger, auditLog? } and get back a fully brand-stacked, container-
bound wired value. Idempotent: unbinds an existing symbol before rebinding.
withAudit lives in core-audit which core-shared cannot import (dependency
inversion: core-audit depends on core-shared). The audit path here replicates
the same semantics — forwarding wrapper + __audited brand — without the
circular dependency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-decomposes the approved binder-wrap-helper PRD after the previous
8-story attempt (dropped in 71c04f5) sliced horizontally — its
"write test" / "write impl" splits would have left intermediate
commits red. The new shape obeys slice = task = PR = commit:
- 01-wire-use-case-helper — 1 task: ship the wireUseCase helper
(factory composition + container bind + brand stack) with its
unit tests and barrel export in one green commit. Blocks 02 + 03.
- 02-migrate-feature-binders — 5 tasks, one per feature
(auth, blog, media, marketing-pages, navigation). Each task
migrates that feature's bind-production.ts + bind-dev-seed.ts
together and keeps all gates green.
- 03-update-generator-templates — 1 task: update the feature
generator's bind-*.hbs so future scaffolds emit wireUseCase
directly instead of inline withSpan + withCapture.
Output of the now-fixed sandcastle decompose dispatch — depends on
the completionSignal + maxIterations wiring (eadbb7e, 26aa97f).
Sandcastle re-invokes agents up to maxIterations even when the work is
already done — the decomposer was looping 4x re-writing the same epic
on every dispatch. Two halves to the fix:
- Pass completionSignal: "<promise>COMPLETE</promise>" explicitly on
all three run() calls (decompose, implementer, reviewer). Makes the
contract visible alongside maxIterations instead of relying on
sandcastle's default.
- Append a "Signal completion (required)" section to each prompt
telling the agent to emit the literal marker as its final line when
the work is genuinely done, plus a "do NOT emit if..." list to
discourage premature signaling.
The three SANDCASTLE_*_ITERATIONS overrides landed inline in
decompose.mjs and dispatch.mjs (commit 26aa97f) but weren't
surfaced in .env.example. Adds them with the same tuning guidance
the inline comments carry, so users discover the knobs from the
canonical env reference instead of having to grep the code.