Commit Graph

611 Commits

Author SHA1 Message Date
4976352354 docs(work): story 01 — defineFeature helper + brands 2026-05-12 21:25:28 +02:00
b2dfd91367 docs(work): scaffold docs/work and conformance-system-v1 epic 2026-05-12 21:24:03 +02:00
31906b9e3e plan(conformance): milestone i — defineFeature + brands + auth.signIn proof
13-task implementation plan for the type-level seam of the conformance system.
Ships brand types (Instrumented/Captured/Audited), defineFeature helper,
ProductionUseCase branded slot, withAudit wrapper, authManifest, and rebinds
auth.signIn through the branded slot — with a negative type test asserting
unwrapped factories are rejected at the slot.

Each task is a single red→green TDD cycle with one commit. Companion
docs/work/conformance-system-v1/ epic + story records are created early in
the plan so subsequent agents have full context when picking up later
stories.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:18:23 +02:00
9576987637 docs(architecture): agent-first workflow design + work-shape guides
Captures the design from the brainstorm session on agent-first development
in this template. Architecture doc covers the four interlocking pillars:

- Conformance engine (manifest + brands + ESLint + boot + CI gate)
- Agent workflow (PRD -> Epic -> Story -> Task; manifest-first ordering;
  TDD per slice; in/out scope at every level)
- Local task system at docs/work/ (filesystem markdown, derived committed
  _state.json, single-writer orchestrator rule)
- Sandcastle orchestrator (implementer + reviewer loop, DAG-respecting,
  configurable retry cap)

Work-shape guides extend the architecture doc with operational detail for
frontend work (atomic design, Storybook-as-spec, component + Playwright
screenshot test gates, Storybook MCP reviewer integration) and
infrastructure work (ADR-first flow, dedicated ADR elicitation skill,
optional core packages vs. new infrastructure layers).

Phasing is conformance-first: build the enforcement system manually, then
build the dispatch substrate, then migrate remaining features through it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:08:44 +02:00
63d54dcddc docs(architecture): feature-conformance interactive explainer + extensions
Adds the interactive HTML explainer for the feature-conformance enforcement
design, covering the four layers (TS brands, ESLint, boot assertion, CI gate),
manifest playground, mistake catalog, layer composition, build-order milestones,
anchor points in the repo, and an "extensions" section on sharper AST checks
and code-conventions enforcement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:08:34 +02:00
c5c1b1b845 docs(architecture): fix ADR slug refs in audit explainer footer
The agent that produced the explainer guessed three ADR filenames
(adr-014-instrumentation-interfaces.md, adr-017-otel-migration.md,
adr-015-events-jobs.md). The actual files use different slugs.
2026-05-11 17:16:24 +02:00
b76483c4f2 docs(architecture): audit-and-compliance HTML explainer + sibling crosslinks 2026-05-11 17:15:12 +02:00
7c915cb447 docs(architecture): surface core-audit + DPA across architecture docs
Touches the deeper architecture surfaces the Phase 6 sweep skipped:

- overview.md: split must-have (core-shared, core-cms, core-api) from
  optional (core-trpc, core-ui, core-realtime, core-events, core-audit);
  add core-audit to the Five tags optional list
- dependency-flow.md: extend the bindAll diagram with resolveAudit;
  add auditLog row to the BindContext table; rename the
  TRACER/LOGGER/METRICS heading to include AUDIT (ADR-018); note the
  R52-style boundary rule for @repo/core-audit (consume via protocol)
- vertical-feature-spec.md: target-state section now states 3 must-have
  + 5 optional cores; tag matrix includes the optional cores; bind-
  production signature destructure includes auditLog
- di-explainer.html: §08 instrumentation gains an IAuditLog block + the
  Wiring path tree shows resolveAudit + auditLog in ctx
- testing-strategy.md: RecordingAuditLog reference + reset() guidance
2026-05-11 17:06:58 +02:00
451a3cdbc3 chore(lockfile): resolve core-audit OTel devDeps after merge
pnpm install on the merged tree pulled in @opentelemetry/api,
api-logs, context-async-hooks, and sdk-trace-base under
packages/core-audit's devDependencies. The merge commit didn't fully
include the resolved lockfile entries; this commit aligns them so CI
installs cleanly.
2026-05-11 16:54:08 +02:00
56dcf48ff2 Merge branch 'worktree-audit-and-compliance': audit logging & DPA compliance (ADR-018)
Adds @repo/core-audit as the 5th optional core package (gen core-package audit), providing DPA-compliant audit logging:

- AuditEntry shape (closed enum, no payload/body), IP truncation, salted pseudonymization
- Two integration patterns: use-case calls + Payload hooks
- Sinks: NoopAuditLog, StdoutJsonAuditLog, PayloadAuditLog (append-only), MultiSinkAuditLog (settle-all + stderr fallback)
- TraceIdEnrichingAuditLog decorator bridges to OTel correlationId
- Privileged eraseSubject for GDPR via overrideAccess
- RecordingAuditLog test double in core-testing
- Generator template captured + byte-identical e2e reconstruction
- ADR-018, docs/guides/audit-and-compliance.md, 6 doc surfaces updated

28 commits, base ac8dfcca1b54ad.
2026-05-11 16:53:12 +02:00
a1b54ad833 docs: surface core-audit as 5th optional package across discovery points 2026-05-11 16:41:20 +02:00
0b68d23d58 feat(generators): wire audit entry + e2e byte-identical reconstruction test 2026-05-11 16:40:18 +02:00
3fe95694c5 feat(generators): capture core-audit as verbatim template files 2026-05-11 16:39:00 +02:00
ecb8dd65f4 docs(guide): audit-and-compliance how-to guide 2026-05-11 16:38:23 +02:00
2b0c54c013 docs(adr): ADR-018 audit logging & DPA compliance 2026-05-11 16:36:34 +02:00
c06f47b81e feat(core-audit): createAuditAfterReadHook factory for opt-in VIEW capture
Adds the afterRead hook factory for per-collection opt-in automatic VIEW
audit entry emission. Fire-and-forget design ensures failing sinks never
propagate to the user-facing read. Includes sentinel IP/UA fallbacks,
truncateIp /24 integration, shouldSkip predicate, and system actor
resolution. All 6 TDD test cases pass; both barrels updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:33:59 +02:00
55993a2c93 feat(core-audit): wrap bound auditLog with TraceIdEnrichingAuditLog
bindAudit now wraps the inner sink/fan-out with TraceIdEnrichingAuditLog so
all sinks receive AuditEntry.correlationId auto-populated from the active
OTel span. bind-audit.test.ts assertions updated to check instanceof
TraceIdEnrichingAuditLog first, then .inner for the concrete sink type.
TraceIdEnrichingAuditLog exported from the package barrel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:31:19 +02:00
07548c4620 feat(core-audit): TraceIdEnrichingAuditLog decorator for OTel correlation
Decorator implementing IAuditLog that auto-populates AuditEntry.correlationId
from the active OTel span via currentTraceId(). Caller-supplied correlationId
always wins (explicit > implicit). eraseSubject passes through unmodified.
Adds @opentelemetry/{api,sdk-trace-base,context-async-hooks} as devDeps for
test infrastructure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:29:15 +02:00
098fcd0d51 feat(core-shared): currentTraceId helper for OTel-audit correlation bridge
Reads the active OTel span context via trace.getActiveSpan(); returns the
32-char hex traceId or undefined when no span is active or traceId is the
all-zeros invalid value. Re-exported from both instrumentation/otel/index
and instrumentation/index barrels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:27:36 +02:00
131efd5d2f feat(core-audit): admin tRPC procedure for eraseSubject
Adds auditProcedure (adminOnly middleware + defineErrorMiddleware([])) in
core-audit/src/integrations/api/procedures.ts. Adds createAuditRouter that
captures an IAuditLog and exposes a single eraseSubject mutation with zod
input validation. Non-admins receive FORBIDDEN. Barrel re-exports
pseudonymize, createAuditErasureHook, createAuditRouter, auditRouter,
AuditRouter, auditProcedure, AdminTrpcUser. Adds AUDIT_PSEUDONYM_SALT to
turbo.json globalEnv to clear lint warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:25:09 +02:00
270897c550 feat(core-audit): createAuditErasureHook Payload afterDelete factory
Adds createAuditErasureHook in core-audit/src/hooks/. The factory returns
a CollectionAfterDeleteHook that calls auditLog.eraseSubject() when a
document is deleted. Defaults to "pseudonymize" mode; coerces numeric ids
to string; skips undefined/null/object ids. Barrel at hooks/index.ts.
6 unit tests cover all guard branches.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:23:08 +02:00
18fddcc45f feat(core-audit): PayloadAuditLog.eraseSubject (pseudonymize + delete via overrideAccess)
Replaces the Phase-2 stub with a real impl. Mode "delete" issues a bulk
payload.delete with overrideAccess:true to bypass the append-only rule.
Mode "pseudonymize" fetches up to 10_000 matching docs and patches each
actorId to the token produced by pseudonymize(). Adds 3 eraseSubject unit
tests to the existing payload-audit-log test file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:22:35 +02:00
846b4c2511 feat(core-audit): pseudonymize helper (sha256 + AUDIT_PSEUDONYM_SALT)
Adds `pseudonymize(actorId)` in core-audit — SHA-256 of salt+":"+actorId
truncated to 16 hex chars, prefixed "erased-". Salt from
AUDIT_PSEUDONYM_SALT env (fallback dev label). 6 unit tests: deterministic,
salt-change-differs, fallback-no-throw.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:22:30 +02:00
2e5097c457 fix(core-testing): RecordingAuditLog uses inline type alias (boundary: tooling cannot depend on core)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:19:15 +02:00
cbde74b7ee feat(core-audit): package barrel exports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:16:44 +02:00
cb285cbd1c feat(core-testing): RecordingAuditLog test double
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:15:42 +02:00
683dab691f feat(core-audit): bindAudit binder with sink selection + prod salt validation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:14:47 +02:00
7f75f3d56a feat(core-audit): MultiSinkAuditLog fan-out with settle-all + stderr fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:13:48 +02:00
fc4e4a1392 feat(core-audit): PayloadAuditLog.record impl (eraseSubject lands in Phase 3)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:13:18 +02:00
04c99346c6 feat(core-audit): append-only auditLogs Payload collection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:12:48 +02:00
03e3ef39cd feat(core-audit): StdoutJsonAuditLog impl with audit + audit-erasure markers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:12:18 +02:00
17996e9347 feat(core-audit): NoopAuditLog impl
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:11:52 +02:00
12a8391944 feat(core-audit): IAuditLog interface + AUDIT_SYMBOLS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:11:30 +02:00
0c5ad08dcd feat(core-audit): scaffold optional package (no impls yet)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:10:58 +02:00
a3b7100d10 feat(core-shared): BindContext.auditLog? field (5th generic)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:04:58 +02:00
a7e383593a feat(core-shared): AuditLogProtocol + ./audit subpath export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:04:29 +02:00
cc4de8eb75 feat(core-shared): truncateIp helper (/24 IPv4, /48 IPv6) per DPA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:03:45 +02:00
1247e1804a feat(core-shared): AuditEntry type with closed action enum + required tenant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:03:19 +02:00
ac8dfcc5d4 docs(plan): audit logging & DPA compliance (6 phases, ~30 commits)
Implementation plan for the spec at docs/superpowers/specs/2026-05-11-
audit-and-compliance-design.md. Six phases:

- Phase 1: AuditLogProtocol + AuditEntry type + truncateIp helper in
  core-shared; BindContext.auditLog? (5th generic).
- Phase 2: @repo/core-audit optional package — NoopAuditLog,
  StdoutJsonAuditLog, append-only audit-logs Payload collection,
  PayloadAuditLog (record), MultiSinkAuditLog fan-out, bindAudit
  binder with prod salt validation, RecordingAuditLog in core-testing.
- Phase 3: GDPR erasure plumbing — pseudonymize helper (sha256 +
  AUDIT_PSEUDONYM_SALT), PayloadAuditLog.eraseSubject via
  overrideAccess, createAuditErasureHook Payload afterDelete factory,
  admin tRPC procedure (audit.eraseSubject).
- Phase 4: OTel correlation bridge — currentTraceId() in core-shared,
  TraceIdEnrichingAuditLog decorator wraps inner sinks at bindAudit
  time. Explicit caller correlationId wins.
- Phase 5: createAuditAfterReadHook factory for opt-in per-collection
  VIEW capture; fire-and-forget semantics with stderr fallback.
- Phase 6: ADR-018 + audit-and-compliance.md guide + generator template
  at turbo/generators/templates/core-package/audit/ + byte-identical
  snapshot + e2e test + 6 doc surface refreshes (template-tiers,
  scaffolding-doc, CLAUDE.md, AGENTS.md, data-flow-explainer, README).

Total: ~30 commits expected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:01:11 +02:00
40b5b1a94f docs(spec): audit logging & DPA compliance design
Six-phase design for @repo/core-audit (5th optional package).

Phase 1: AuditLogProtocol + AuditEntry type + truncateIp helper in
  core-shared; BindContext.auditLog? optional field.
Phase 2: @repo/core-audit package with NoopAuditLog + PayloadAuditLog
  (append-only collection) + StdoutJsonAuditLog (structured JSON) +
  MultiSinkAuditLog fan-out wrapper + RecordingAuditLog in core-testing.
Phase 3: GDPR erasure plumbing — eraseSubject impls, pseudonymize helper
  (sha256-with-salt), admin tRPC procedure, createAuditErasureHook
  Payload afterDelete hook factory.
Phase 4: OTel correlation bridge — currentTraceId() helper,
  TraceIdEnrichingAuditLog decorator wraps inner sinks at bind time so
  AuditEntry.correlationId auto-populates from active OTel span.
Phase 5: createAuditAfterReadHook factory for opt-in per-collection
  automatic VIEW capture; reference wiring documented (printed by
  generator as a diff, NOT auto-installed in auth).
Phase 6: ADR-018, audit-and-compliance.md guide, generator template
  + byte-identical snapshot + e2e test, doc refreshes (CLAUDE.md,
  AGENTS.md, template-tiers, data-flow-explainer, README,
  scaffolding-doc).

Compliance grounded in DPA "Logging & Monitoring" requirements:
closed action enum (VIEW/CREATE/UPDATE/DELETE/EXPORT/PERMISSION_CHANGE),
required tenant field, type-enforced "what NOT to log" (no payload/
body/oldValue/newValue fields), IP /24 v4 + /48 v6 truncation, sha256-
salted pseudonymization, append-only Payload collection with privileged
overrideAccess erasure path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:46:33 +02:00
726934bdeb Merge branch 'worktree-opentelemetry-migration': OpenTelemetry migration (ADR-017) 2026-05-11 12:59:20 +02:00
82d80014bf fix(otel): address lint and test regressions from C1/C2 fixes
- Prefix unused opts param with _opts in bindOtelInstrumentation (ESLint)
- Extend IPv6 regex to cover prefix::suffix form (e.g. 2001:0db8::1) so
  the IPv6 scrub test passes correctly
- Delete orphaned sentry-pii-scrubber.test.ts files from apps/cms and
  apps/web-tanstack that imported the deleted sentry/scrub module (broke
  typecheck; the OTel-layer replacement tests are in pii-scrub-processor.test.ts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 12:42:38 +02:00
9acf16f399 chore: address Phase 5 review polish (test descriptions, version conflict comment, mock cleanup)
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>
2026-05-11 12:40:11 +02:00
7c74a1c9e1 fix(otel): scrub IP addresses in attribute values + log bodies (R32 compliance)
PII_KEY_SUBSTRINGS extended with all OTel HTTP semconv IP attribute keys
(http.client_ip, client.address, net.peer.ip, etc.) so they are key-redacted.
scrubValue() added to pii-scrub-processor.ts applies IPV4_REGEX / IPV6_REGEX
replacement for attribute values whose keys are not PII-keyed, and for log
record bodies that pass the substring check. Closes the gap left by deletion
of the old beforeSend value-level IP scrubbers. 5 new tests added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 12:39:22 +02:00
4ea9a5c38e fix(otel): consolidate to single OTel SDK init at instrumentation.register hook
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>
2026-05-11 12:38:17 +02:00
05524dfcea chore(web-next): remove legacy Sentry scrubber test (covered by OTel processor tests)
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>
2026-05-11 12:22:42 +02:00
e43e03cdee docs: refresh architecture references for OTel migration 2026-05-11 12:18:44 +02:00
81f2477537 docs(adr): ADR-017 OpenTelemetry migration 2026-05-11 12:15:01 +02:00
8bedb649ca refactor(core-testing): no-sentry → no-instrumentation (mocks OTel too) 2026-05-11 12:14:18 +02:00
bd95315a44 refactor(core-eslint): finalize OTel + Sentry import allowlist (R40 + R52) 2026-05-11 12:12:57 +02:00