pnpm audit --prod --audit-level=critical (the new CI gate) failed on
vitest <3.2.6 (critical, arbitrary file read/execute via the UI
server). Bump vitest + @vitest/coverage-v8 in-range across the
workspace and add @vitest/coverage-v8 to every package that runs
vitest but lacked it (core-audit, core-cms, core-eslint, core-testing,
core-trpc, apps/cms, web-next, web-tanstack, turbo/generators) so
'pnpm test -- --coverage' works in every package. No compound test
scripts exist, so the vitest-last pass-through concern does not apply.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the boolean bound flag with a cached promise so concurrent
callers (layout + page server components) await the same binding
operation. Prevents DI resolution before containers are populated.
Pages import feature server components and pass only route-derived
props (slug, id). All data fetching, prefetch, and hydration is owned
by the feature's .server.tsx component.
- Add USE_DEV_SEED=false branch to bindAll dispatcher
- Use dotenv-cli to inject root .env into all Turbo tasks
- Add globalDependencies for .env cache invalidation
- Add apps/web-next/middleware.ts calling withSecurityHeaders() from
core-shared/security/next; exports matcher config excluding static assets
- Update layout.tsx to call getNonce() and render <meta name="csp-nonce">
so client-side JS can read the per-request nonce
- Update instrumentation-client.ts to read nonce from csp-nonce meta tag
and pass it to initSentryClient for feedbackIntegration CSP compliance
- Add nonce option to initSentryClient (InitClientOpts.nonce) and thread
styleNonce + scriptNonce into feedbackIntegration when provided
- Add middleware test asserting all six headers, prod/dev CSP shape, and
x-nonce presence; add feedbackIntegration nonce tests to core-shared
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add withRateLimit(rateLimit, fn) in rate-limit/with-rate-limit.ts,
attaching the RateLimited brand at DI bind time
- Extend wireUseCase to accept optional rateLimit?: IRateLimit and
compose withRateLimit innermost (before analytics/audit); propagate
__rateLimited through analytics + audit inline wrappers
- Extend withSpan and withCapture PROPAGATED_BRANDS to include
__rateLimited so the outermost binding carries the brand
- Extend assertFeatureConformance to require __rateLimited brand when
manifest.useCases[name].rateLimit.length > 0; refactored into
helper functions to stay within complexity thresholds
- Add rateLimit?: IRateLimit to BindContext; default to NoopRateLimit
in web-next bindAllProduction and bindAllDevSeed aggregators
- Unit tests for withRateLimit brand attachment, factory passthrough,
and composition; synthetic fixture tests for conformance errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ConsentFactoryProtocol / ConsentGrantMeta / ConsentProtocol to
core-shared/di/bind-protocols so feature binders can wire per-user
consent without a hard dep on the optional @repo/core-consent package.
BindContext gains an optional consentFactory? field following the same
pattern as bus?, auditLog?, etc.
signUpUseCase gains a 4th optional dep (consentFactory). When present
and the input includes a cookieHeader containing cc_consent=<categories>,
the use case calls consent.grant for each category with
method:"signup-migration" and returns a clearCookie payload (Max-Age:0)
so the anonymous cookie is cleared on the HTTP response.
Tests use RecordingConsent from @repo/core-testing to assert migration
call shape and cookie-clear; no-cookie and no-factory branches are also
covered. All coverage bands hold at 100% for use-cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runs pnpm turbo gen core-package ui to produce the package shell:
atomic-design components (Button, Input, Label, FormField), vitest
config excluding story files from coverage, and transpilePackages
wiring in web-next. Adds @vitest/coverage-v8 devDep and
label.stories.tsx to satisfy lint/coverage gates.
Also fixes scripts/library-decisions/check.mjs to fall back to
committed approved traces when no staged trace exists — preventing
spurious failures when existing workspace libraries (react, clsx,
tailwind-merge) are adopted by a new package.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the Payload-backed IConsent that reads/writes users.consentState
and emits CONSENT_GRANT/CONSENT_WITHDRAW audit entries via injected auditLog.
Adds RecordingConsent test double in core-testing for unit-test injection.
Adds bindProductionConsent/bindDevSeedConsent DI binders and InMemoryConsent
for dev/seed contexts. Contract tests cover grant/withdraw/isGranted round-trip,
audit entry shape, metadata persistence (bannerVersion/policyVersion/method),
and getCategories reflection of state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add consent to CORE_PACKAGE_GENERATORS in turbo/generators/config.ts so
pnpm turbo gen core-package consent is a valid command (not hand-rollable)
- Create turbo/generators/templates/core-package/consent/ mirroring the
analytics template shape (AGENTS.md, package.json, tsconfig, turbo, vitest,
eslint, src/index.ts scaffolds)
- Regenerate packages/core-consent/ from the new template (replaces the
previous hand-rolled attempt that violated the generator-first rule)
- Add __consentChecked to withCapture PROPAGATED_BRANDS so the brand bubbles
through the full withSpan→withCapture wrapper chain to the outermost binding
that assertFeatureConformance reads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add the `pnpm turbo gen core-package analytics` generator template and
run it to scaffold the @repo/core-analytics workspace package. The
package lands in placeholder state (empty barrel export) ready for the
IAnalytics + NoopAnalytics implementation in the next commit.
Includes:
- turbo/generators/templates/core-package/analytics/ templates
- turbo/generators/config.ts analytics generator registration
- packages/core-analytics/ placeholder scaffold
- apps/web-next/next.config.mjs transpilePackages entry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Final sweep for setup-process bookkeeping not caught by template-reset-v1.
ADRs drop Plan-N qualifiers; spec collapses the historical 11-phase
migration table; scaffolding guide drops "Phase added" column; comment
prefixes referencing R-numbers in test describes / eslint inline comments
are normalized. Architecture-level rule IDs (R40, R52, E0, J0, etc.) are
preserved where they serve as stable cross-references in ADRs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
I3: bind-production.test.ts instrumentation orthogonality tests updated to use
bindOtelInstrumentation as primary name (bindSentryInstrumentation alias still
wired in mock setup for deprecation-alias coverage, not in assertions).
I4: as never cast in init-server-node.ts annotated with explanation of the
sdk-trace-base / sdk-node TypeScript version conflict that necessitates it.
I5: SentryLogRecordProcessor removed from @sentry/opentelemetry mock in
no-instrumentation.ts — that class does not exist in v10.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All three apps' instrumentation.ts files now call initOtelServerNode directly
instead of initSentryServer/initSentryServerNode, closing the startup window
where @sentry/nextjs auto-instrumentation could send unscrubbed errors before
bindAll() fires. bindOtelInstrumentation no longer calls initOtelServerNode
(SDK init belongs at app boot, binding at request scope). Orphaned sentry/
init-server*.ts files deleted; their package.json subpath exports removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The sentry-pii-scrubber.test.ts file tested the now-deleted sentry/scrub.ts
module. PII scrubbing is now validated in core-shared/otel/pii-scrub-processor.test.ts
at the correct layer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
apps/web-next/bind-production.ts imports bindOtelInstrumentation instead
of bindSentryInstrumentation. Test mock tracks both names via the same
spy so existing assertions on bindSentryInstrumentation still pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AGENTS.md (root): marks core-ui as optional in the package table and
boundary rules; points per-package docs to the .hbs template
- apps/storybook/AGENTS.md: rewrites around no-core-ui-by-default;
stories glob and globals.css import described as post-scaffold steps
- apps/web-next/AGENTS.md: cross-reference updated to template file
- apps/web-tanstack/AGENTS.md: cross-reference updated to template file
- docs/architecture/data-flow-explainer.html: core-ui bullet notes
optional status + generator command
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removal across all three consumers:
- apps/storybook: strips @repo/core-ui dep, clears stories glob (now []),
removes globals.css import from preview.ts
- apps/web-next: strips @repo/core-ui dep + transpilePackages entry
- apps/web-tanstack: strips @repo/core-ui dep
- packages/core-ui: deleted entirely (28 files)
No app pages needed surgery — neither web-next nor web-tanstack source
files imported @repo/core-ui directly. The storybook app referenced it
only via the stories glob and the globals.css preview import.
Gates: 40/40 tasks successful, all 4 e2e byte-identical reconstructions
pass (realtime, events, trpc, ui).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five fixes surfaced by the branch-wide code review on the realtime layer:
- server.ts: replace dynamic `import("@repo/auth/di/container")` with a
static top-of-file import. The dynamic-import workaround from 6a0ac63 is
no longer needed once the root tsconfig + TSX_TSCONFIG_PATH expose
decorator metadata to tsx; verified by booting `pnpm dev` clean.
- server.ts: correct the inline structural type for `validateSession` to
match the real `IAuthenticationService` contract (non-nullable, throws
on invalid session) and wrap the call in try/catch so unauthenticated
bubbles to a `null` return instead of dead-code `result ? ... : null`.
- bind-production.ts: extract `maybeRegisterRealtimePing()` that wraps the
built-in ping inbound handler in the same `withSpan(withCapture(...))`
sandwich the realtime-handler generator emits (R41–R44), so the
proof-of-life channel models the convention rather than registering raw.
- bind-production.test.ts: add 4 tests for the `REALTIME_PING_DISABLED`
env-gate (registered when unset in both binders, not registered when
"true", treated as enabled when "1").
- docs/guides/realtime.md: correct the integration-test reference at
line 285 — the test does not call `bindAllDevSeed()`; it builds the
Socket.IO server inline and exercises gates 1+2 only (gates 3+4 live in
socket-io-realtime-server.test.ts).
- adr-016: add a "Known follow-ups" section recording 6 lower-priority
refinements deferred from this branch (bridge stub test scaffolding,
registry register/registerChannel precedence, channel-template dot
constraint, server bare catch{}, BindAllDeps Partial widening, AGENTS.md
anchor count phrasing).
CI gates: lint 0 errors / 4 warnings (pre-existing turbo.json warnings),
typecheck clean, 24 web-next tests pass (was 20; 4 new env-gate tests),
boundaries 0 issues across 504 files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- no-direct-socket-io: extend allowlist to cover apps/*/src/**/*.test.ts so
the realtime-ping e2e integration test can import socket.io/socket.io-client
directly; add two valid test cases to keep the rule's own test suite green
- tsconfig.json (root): add root-level tsconfig with experimentalDecorators +
emitDecoratorMetadata and no "include" so tsx 4.21.0's createFilesMatcher
resolves decorator config for all workspace packages, not just web-next's
own source tree
- web-next dev script: pass TSX_TSCONFIG_PATH=../../tsconfig.json so the
custom Node server uses the root tsconfig for all modules it loads
- next.config.mjs: add @repo/core-events and @repo/core-realtime to
transpilePackages so Next.js webpack can resolve their workspace source files
- server.ts: replace static authContainer import with a dynamic import inside
IRealtimeAuthenticator.authenticate so Inversify decorators are applied only
after bindAll() has already populated the container
All CI gates pass: lint (0 errors), typecheck, 20 tests (incl. realtime-ping
e2e), boundaries (0 issues).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends bindAll/bindAllProduction/bindAllDevSeed to accept BindAllDeps
and thread realtime + realtimeRegistry as the 6th and 7th positional args
into each per-feature binder. Adds bindRealtimeBridge stub (empty allowlist
in v1). bindAll accepts optional deps with InMemoryRealtimeBroadcaster
fallback so existing page-level callers stay type-safe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces `next dev` / `next start` with a custom Node http server that
co-hosts Next.js and Socket.IO on the same port, wiring in the
RealtimeHandlerRegistry and session-cookie-based IRealtimeAuthenticator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
End-to-end proof-of-life. bindAllDevSeed wires InMemoryEventBus +
InMemoryJobQueue across all features. signUpController publishes
auth.user.signed-up; marketing-pages' handler enqueues
marketing-pages.send-welcome-email; the in-memory queue dispatches
the wrapped job which records on RecordingMailerService.
Adds ./di/container, ./di/symbols, ./services/mailer, and
./services/recording-mailer to auth + marketing-pages exports so
the e2e test can resolve from the per-feature containers without
deep-importing.
resolveEventsAndJobsProduction wires PayloadJobsEventBus + PayloadJobQueue
against the bootstrapped Payload instance; resolveEventsAndJobsDevSeed
wires InMemoryEventBus + InMemoryJobQueue. Each per-feature binder now
receives (bus, queue) so Phase 7 generators can subscribe handlers and
register job tasks at the <gen:event-handlers>/<gen:jobs> anchors.
TypeScript 6 deprecates `baseUrl` in favor of resolving `paths`
relative to the file that defines them. Earlier commit (bde40e4)
silenced the warning via `ignoreDeprecations`; this commit does
the actual migration so the warning goes away on its merits.
Changes:
1. tsconfig.base.json: drop `baseUrl: "."`. Add an explicit `./`
prefix to every `paths` entry so they resolve relative to the
base config's location (the repo root) — same behavior as
before, but no baseUrl needed. (TypeScript requires path entries
to be relative or absolute when baseUrl is absent; bare paths
like "packages/..." are not allowed.)
2. apps/web-next/tsconfig.json: drop baseUrl. The `@/*` mapping's
target was already "./src/*" — relative-from-tsconfig-file is
the new default, so no other change.
3. apps/cms/tsconfig.json: same — drop baseUrl, paths already use
"./" prefix.
4. apps/storybook/tsconfig.json: same.
5. apps/web-tanstack/tsconfig.json: same.
Generator templates (turbo/generators/templates/feature/tsconfig.json.hbs)
already omit baseUrl, so future-generated features inherit the new
posture.
Reverts the ignoreDeprecations setting added in bde40e4 — no longer
needed.
Verified:
- pnpm typecheck 14/14
- pnpm lint 15/15
- pnpm test 26/26
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/web-next/instrumentation.ts (server) and instrumentation-client.ts
(browser) hooks, wraps next.config.mjs with withSentryConfig (R52), and adds
the R38 per-app PII scrubber smoke test.
Spec deviation: extend PII_KEY_SUBSTRINGS with "ipaddress" so keys like
ipAddress trigger key-level redaction (tighter posture than the spec's
substring list; existing scrub.test.ts still passes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>