Commit Graph

1020 Commits

Author SHA1 Message Date
0748f9e5ed docs(architecture): refresh explainers and spec to the shipped system
Some checks failed
CI / typecheck + lint + boundaries + test + build (push) Has been cancelled
CI / Playwright e2e (push) Has been cancelled
CI / Storybook smoke tests + visual regression (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Coverage snapshot / snapshot (push) Has been cancelled
Release Please / release-please (push) Has been cancelled
Sentry PII guard (R31) / pii-guard (push) Has been cancelled
Mutation testing (nightly) / mutate (push) Has been cancelled
Library trace revalidation (weekly) / revalidate (push) Has been cancelled
Bring docs/architecture/ in line with the current repo:

- feature-conformance-explainer.html: drop the "proposed / not yet
  implemented" framing — the system is shipped. Four enforcement points
  become five (adds `pnpm fallow` as the whole-codebase audit). Manifest
  playground shows `coverage`, `analyticsEvents`, `rateLimit`,
  `requiresConsent`. Milestone / anchor / open-question sections kept
  but marked historical.
- agent-first-workflow-and-conformance.md: four → five enforcement
  layers; layer table gains the Fallow row.
- di-explainer.html: bind-production sample rewritten to show
  wireUseCase() + assertFeatureConformance() + the full wrapper stack
  (span → capture → audit? → analytics? → consent? → rateLimit?).
- data-flow-explainer.html: same bind-production refresh for the
  data-flow narrative.
- audit-and-compliance-explainer.html: AuditAction enum 6 → 10 values
  (CONSENT_GRANT / WITHDRAW / RESTRICT / UNRESTRICT);
  BindProductionContext example gains analytics, consentFactory,
  rateLimit.
- vertical-feature-spec.md: §5 layout lists the 8 optional cores plus
  core-testing; §9.5 hedges the turbo.json snippet against the live
  file; §10.4 drops the dated "360 tests" metric for the ADR-020
  coverage architecture; §11 gains a historical lead-in pointing at
  docs/decisions/ as the canonical 25-ADR set.
2026-05-23 14:06:03 +02:00
b455ae8018 docs(architecture): correct package lists and feature-to-feature boundary
Align the architecture docs with the current repo:

- Boundary matrix: feature may depend on core, feature, tooling — a
  feature may import another feature's public exports. overview.md,
  dependency-flow.md, and vertical-feature-spec.md all said the stale
  `feature -> core, tooling`.
- Optional-core lists completed with core-analytics, core-consent,
  core-dsr; tooling list completed with core-testing.
- Package count corrected to the accurate 19-package breakdown.
- BindContext table gained analytics, consentFactory, rateLimit.

Deeper drift (the HTML explainers, vertical-feature-spec §5/§9.5/§11)
is tracked in the local .tmp/ working note, not yet addressed.
2026-05-22 09:53:36 +02:00
80835e4fea chore: ignore the .tmp/ scratch directory
Local working notes and scratch files live in .tmp/ — never committed.
2026-05-22 09:53:22 +02:00
c33e0b2669 feat(skills): add /work-decompose and /work-dispatch orchestration skills
In-session skill counterparts to `pnpm work decompose --execute` and
`pnpm work dispatch --execute`. They dispatch sub-agents — a decomposer
for /work-decompose; separate worktree-isolated implementer and
read-only reviewer sub-agents for /work-dispatch's implement-review loop.

Single source of truth: the skills read `.sandcastle/*.prompt.md` at
dispatch time and never copy them — the prompt files stay authoritative
for both Sandcastle and the skills, so the role definitions cannot
drift. `.sandcastle/` and `pnpm work` are untouched; this is additive.
2026-05-22 09:17:46 +02:00
f3182537c1 fix(boundaries): allow feature-to-feature imports of public exports
turbo.json's boundary config already allows `feature -> feature`, and
the cross-feature event system depends on it — a consumer must import
the publisher's event contract from `@repo/<publisher>`. But the ESLint
boundaries config, ADR-010, and AGENTS.md still declared
`feature -> [core, tooling]`, contradicting turbo.json and the shipped
code (marketing-pages imports @repo/auth).

Align all three to turbo.json: a feature may import another feature's
published public exports. Internals stay sealed by the `exports` map,
and cross-feature behaviour still flows through IEventBus.
2026-05-21 14:30:37 +02:00
f69b53d73e test(generators): add feature generator composition e2e test
The core-package e2e tests cover each generator in isolation. None
exercised generators running in sequence — so the feature template
shipping without an integrations/cms/index.ts barrel went unnoticed
until `gen job` failed against a scaffolded feature.

This test scaffolds a feature, then runs `gen job` against it. `gen job`
throws at its `<gen:job-tasks>` anchor assertion if the barrel is
missing, so the composition gap now fails loudly.
2026-05-21 11:49:56 +02:00
d3944f40db feat(core-eslint): add entity-must-have-test and no-relative-parent-import rules
Two CLAUDE.md conventions had no mechanical gate, so both drifted:
entity models shipped without sibling tests, and feature test files
imported src modules via `../` instead of the `@/` alias.

- `entity-must-have-test` — every entities/models/<x>.ts needs a sibling
  <x>.test.ts (errors and barrels excluded).
- `no-relative-parent-import-in-tests` — feature test files must import
  src via `@/`, not `../`. Scoped to feature packages; core packages are
  governed by their own generator templates.

Both register at warn level, bringing the conformance rule count to 15.
2026-05-21 11:49:45 +02:00
c099d7182b fix(core-eslint): resolve event descriptors in no-undeclared-event-publish
The rule only matched bus.publish("string-literal", ...), but the
canonical pattern that `gen event` prescribes is
bus.publish(eventDescriptor, payload) — an imported identifier, never a
literal. The rule therefore never fired on real code, which is how the
auth signUp publish drifted from its manifest undetected.

Add `_event-ast.js`: resolves a `bus.publish(<identifier>)` argument by
following the import to the event-contract file and extracting the name
from either `defineEvent("...", schema)` or an inline `{ name }` object.
Unresolvable arguments are skipped, so the rule never false-positives.
2026-05-21 11:49:36 +02:00
f2d633c02a test(auth): use @/ alias in bind-production types test
bind-production.types.test.ts imported feature.manifest and the sign-in
use case with `../` parent paths. Test files import src modules via the
`@/` alias per the repo convention — this clears the way for the new
no-relative-parent-import-in-tests rule.
2026-05-21 11:49:26 +02:00
a043445179 test(generators): raise e2e hook timeout to stop cleanup flake
Each core-package e2e test's `onTestFinished` cleanup `rmSync`-es a temp
clone containing a full installed `node_modules`. That delete can exceed
vitest's 10s default hook timeout under load, failing 2-8 of the 8 e2e
tests per run. Set `hookTimeout: 120_000` to match the per-test timeout.
2026-05-21 11:26:04 +02:00
291e128489 fix(generators): emit cms barrel in feature template
The feature generator emitted no `src/integrations/cms/index.ts`, but
`gen job` and `gen event consume` both `assertAnchors` on that file's
`<gen:job-tasks>` anchor — so they failed on any freshly-scaffolded
feature. Add a `cms/index.ts.hbs` template carrying the anchor, wire the
add action into the generator, and export `./cms` from the package.json
template to match all five existing features.
2026-05-21 11:25:55 +02:00
ae4062b607 test(blog): use @/ alias in container test imports
container.test.ts imported cross-directory modules with `../...` paths.
Test files use the `@/` alias for src imports per the repo convention;
this was the outlier vs auth/media/navigation.
2026-05-21 11:25:48 +02:00
affcb28c24 test(auth): add cookie schema test
cookie.ts was the only entity model in the feature packages without a
sibling test. Covers minimal/full attributes, invalid sameSite, and a
missing required field.
2026-05-21 11:25:42 +02:00
714209e63e fix(auth): declare userSignedUpEvent in signUp manifest publishes
The signUp use case calls `bus.publish(userSignedUpEvent, ...)` but its
manifest entry declared `publishes: []`. The conformance event graph was
blind to the entire auth -> marketing-pages welcome-email flow as a
result. Declaring "auth.user.signed-up" makes `pnpm conformance` surface
the event and its publisher.
2026-05-21 11:25:35 +02:00
9cf73e6171 chore(storybook): discover core-ui stories
Storybook's `stories` glob was emptied when core-ui became an optional
scaffoldable package. With core-ui present in the repo, wire the glob
back so its six stories are discovered — the manual post-scaffold step
the `gen core-package ui` generator already documents.
2026-05-20 17:02:59 +02:00
17bacd84d8 test(generators): add e2e snapshots for analytics, consent, dsr
The analytics, consent, and dsr optional-core generators lacked the
byte-identical reconstruction e2e tests the other five optional cores
already have. Add the three tests and their snapshots.

Adding three more tests exposed a latent flaw in the suite: each test
does a full `pnpm install` in a temp clone, and running all eight in
parallel saturated vitest's workers (RPC timeout) while the never-cleaned
temp dirs filled the disk. Run the suite sequentially via
`fileParallelism: false` and remove each temp clone with `onTestFinished`;
also exclude the local `.pnpm-store` from the clone.
2026-05-20 17:02:50 +02:00
14762d4ba0 docs(library-decisions): backfill socketRisk in 2026-05-14 traces
The Socket supply-chain filter (ADR-023) was added after the initial
library-trace backfill, leaving the 36 traces dated 2026-05-14 without
the socketRisk filter-results field the trace schema now expects.
Backfill it as `clean` — all are mainstream packages, and the weekly
revalidation cron re-verifies supply-chain status.
2026-05-20 17:02:13 +02:00
81c6154993 chore(deps): pin @ai-hero/sandcastle to ^0.5.10
The root devDependency carried a "*" wildcard, and ADR-019 documented a
nonexistent ^2.73.0 pin — the package's installed version is 0.5.10.
Pin to ^0.5.10 and correct ADR-019 to match.
2026-05-20 17:02:01 +02:00
708a85ee6d docs(adr): align ADR-024 IAnalytics with shipped interface
The implemented IAnalytics refined the signature ADR-024 sketched:
events are attributed to the user set by identify() (the standard SDK
model) rather than passed per track() call, the attribute parameter is
named `attributes` consistently, and AnalyticsUser stays id-only with
traits riding identify()'s second argument. The code is the more
idiomatic contract and docs/guides/analytics.md already matched it —
update the ADR's interface block, manifest example, and PII-boundary
section to describe what shipped.
2026-05-20 17:01:23 +02:00
a6e75dc94e chore(work): mark 01-land-operator-checklist done 2026-05-20 14:57:03 +02:00
b5b1179a53 chore(work): finish epic compliance-docs-scaffolds 2026-05-20 14:52:53 +02:00
0792a6788a docs(core): add compliance-overview.md to CLAUDE.md Read First 2026-05-20 12:52:02 +00:00
1c7b61f9bf chore(work): tick task in 06-doc-wiring 2026-05-20 14:51:26 +02:00
a74b7360e1 docs(compliance): document policy template convention in README and glossary
Add a "Policy templates" section to docs/compliance/README.md explaining
the docs/compliance/templates/ directory, the copy-to-compliance/ workflow,
the [FILL IN:] placeholder convention, and the verification one-liner.

Add four glossary entries: fill-in template, [FILL IN:] marker,
pre-launch compliance checklist, and compliance overview.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:49:50 +00:00
ef20345b76 chore(work): finish 05-compliance-overview 2026-05-20 14:47:05 +02:00
622ef8ba70 docs(compliance): add compliance-overview hub
Maps all 22 DPA/GDPR playbook sections to their covering ADR, guide,
template, or epic in this template. Restates ADR-025's four explicit
deferrals (RBAC, MFA, breach-detection, GDPR Art. 22) and documents
consumer/infra-scope items (EU region, TLS, MDM, legal instruments).
Includes a full reference index linking every compliance guide, ADR,
template, and epic.
2026-05-20 12:45:20 +00:00
deca89eed0 chore(work): finish 04-pre-launch-compliance-checklist 2026-05-20 14:37:24 +02:00
0c880730df docs(compliance): add pre-launch compliance checklist
Fix broken forward reference to compliance-overview.md (story 05 output)
by replacing the hyperlink with plain text annotated as forthcoming.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:35:32 +00:00
bad9543228 docs(compliance): add pre-launch compliance checklist
Two-column table covering all 22 playbook sections organised by 13
groups (Infrastructure, Data, Application, Secrets, Sub-Processors,
Logging, Breach, DSR, Backup, SDLC, Workforce, Legal, Documentation).

Every "Shipped by template" row names a runnable verification command.
Consumer and infra responsibilities are explicitly labelled. ADR-025
deferrals (GDPR Art. 22, MFA/lockout, breach-detection patterns) are
called out inline. Links outward to compliance-overview.md, all seven
policy templates, audit-and-compliance.md, operator-checklist.md, and
ADR-025.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:31:07 +00:00
91d1f2a6b1 chore(work): finish 03-policy-templates 2026-05-20 14:21:52 +02:00
d32464c94b docs(compliance): add skeleton policy templates (backup, password, device, onboarding, offboarding)
Five skeleton templates for docs/compliance/templates/. Each has YAML
frontmatter (status: template, playbook-section), a "not code-enforced"
banner, and [FILL IN:] markers throughout. password-policy banner cites
ADR-025 §Deferred items by number (MFA + password policy + lockout
deferral). Cross-template relative links all resolve.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:20:35 +00:00
7d831f1d9b chore(work): tick task in 03-policy-templates 2026-05-20 14:16:36 +02:00
e349089b02 docs(compliance): add anchored policy templates (incident-runbook, dsr-procedure)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:14:39 +00:00
1387b0dca0 chore(work): finish 02-refresh-operator-checklist 2026-05-20 14:08:19 +02:00
b23b1d0b89 docs(compliance): refresh operator-checklist with ADR-024 and ADR-025 actions
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>
2026-05-20 12:06:42 +00:00
aa138348a7 chore(docs): track operator-checklist.md verbatim
Land the existing untracked file as a baseline commit so the
"what existed" diff is legible and distinct from story 02's refresh.
2026-05-20 11:59:59 +00:00
96a8554871 chore(work): decompose compliance-docs-scaffolds epic
Generated 1 epic + 6 stories under docs/work/epics/ from the approved
PRD docs/work/prds/compliance-docs-scaffolds.prd.md. Stories cover
landing + refreshing operator-checklist.md, the seven fill-in policy
templates, the pre-launch compliance checklist, the compliance-overview
hub, and doc wiring (CLAUDE.md, README, glossary). Final epic of
ADR-025. Ready for `pnpm work dispatch --execute`.
2026-05-20 13:58:24 +02:00
ee4af9bb96 chore(work): finish epic security-headers-rate-limit-sbom 2026-05-20 13:49:44 +02:00
3bde1fcae8 docs(core-shared): add glossary entries + update conformance refs for rate-limit and security headers
- Add IRateLimit, RateLimited, withRateLimit, SBOM, SecurityHeadersConfig,
  buildSecurityHeaders, nonce (CSP) entries to docs/glossary.md
- Bump conformance rule count 12 → 13 in CLAUDE.md; add rateLimit manifest
  field to description; add no-undeclared-rate-limit to rule list
- Add rateLimit manifest field row + no-undeclared-rate-limit (13th rule)
  to docs/guides/conformance-quickref.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:47:00 +00:00
6c4d0cdf6f chore(work): tick task in 11-documentation 2026-05-20 13:41:52 +02:00
1fa11fec83 docs(security): add security-headers and rate-limiting cookbooks
Adds two new consumer-facing guides:
- docs/guides/security-headers.md: per-framework middleware wiring
  (Next.js, TanStack Start, Payload CMS), nonce threading for inline
  scripts, CSP allowlist customisation, Sentry nonce integration, and
  securityheaders.com verification workflow.
- docs/guides/rate-limiting.md: manifest rateLimit field declaration,
  canonical key-naming convention (<feature>:<scope>:<key>),
  multi-budget patterns, InMemoryRateLimit / NoopRateLimit for dev/test,
  and production backend wiring via BindContext.rateLimit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:39:56 +00:00
b87a406c96 chore(work): finish 10-sbom-ci-workflow 2026-05-20 13:33:07 +02:00
08bc19293a ci(release): attach CycloneDX SBOM to every GitHub release
Amends release-please.yml with conditional steps that run only when
release-please cuts a release:
- checkout + pnpm install to give @cyclonedx/cyclonedx-npm the full
  resolved workspace graph
- pnpm dlx @cyclonedx/cyclonedx-npm generates a CycloneDX 1.6 JSON SBOM
  named sbom-<tag>.cdx.json; --ignore-npm-errors is required because
  npm ls exits non-zero for dev-deps-of-dev-deps pnpm correctly elides
- softprops/action-gh-release@<SHA> (v3.0.0, Renovate-managed) attaches
  the file to the GitHub release as a downloadable asset

Adds ADR-023 §9 amendment documenting the step shape, rationale for
pnpm dlx (avoids lockfile per ADR-022), --ignore-npm-errors behaviour,
SHA pinning per ADR-023 §1, and the extended failure-mode table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:31:08 +00:00
224a5d78c8 chore(work): finish 09-app-wiring-web-tanstack-and-cms 2026-05-20 13:22:08 +02:00
082d4d2369 feat(cms): wire security headers middleware
Add Next.js middleware.ts to apps/cms that applies all six security
headers on every response using the framework-agnostic
buildSecurityHeaders builder. No nonce is generated or forwarded —
the CMS is server-side only so CSP nonces are not required.

Includes a test suite mirroring the web-next pattern that asserts all
six headers are set, no x-nonce is emitted, and CSP mode switches
correctly between dev (unsafe-inline) and prod (strict-dynamic).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:18:30 +00:00
f0968b7a3c chore(work): tick task in 09-app-wiring-web-tanstack-and-cms 2026-05-20 13:11:05 +02:00
5fd483af39 feat(web-tanstack): register security middleware and wire nonce to __root
- Add @tanstack/start + vinxi to deps so defineConfig is available
- Uncomment defineConfig registration in app.config.ts — middleware
  is now actually wired into the Nitro server hook, not just defined
- Update __root.tsx loader to call getNonce(getEvent().node.req)
  from @repo/core-shared/security/tanstack so the per-request nonce
  is read server-side and injected via <meta name="csp-nonce">
- Update __root.test.tsx: mock provides useLoaderData and asserts
  the nonce meta tag is rendered with the correct content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:06:26 +00:00
8d35fabaa5 fix(scripts): add app and sentry-init exclusions to coverage diff gate
Apps (web-next, web-tanstack, cms) do not configure @vitest/coverage-v8
so their source files never appear in the merged lcov; gate them out of
the cover-the-diff check.

core-shared/src/instrumentation/sentry/** is explicitly excluded from
per-package vitest coverage (see core-shared/vitest.config.ts) — exclude
from the diff gate to mirror that decision.

Adds tests for both new exclusion patterns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 10:36:55 +00:00
a540f3afb1 feat(web-tanstack): wire security headers middleware and nonce threading
Register core-shared/security/tanstack server middleware in app.config.ts
as a Nitro/H3 hook that emits the six security headers and forwards the
per-request nonce. Update instrumentation-client to read the nonce from
<meta name="csp-nonce"> and pass it to initSentryClientReact.

Add nonce support to initSentryClientReact (feedbackIntegration receives
styleNonce/scriptNonce), mirroring the initSentryClient pattern already
in place for web-next.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 10:33:09 +00:00
dc718fd9c8 chore(work): finish 08-app-wiring-web-next 2026-05-20 12:14:20 +02:00