- 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 packages/core-consent with ConsentCategory, ConsentState,
UserConsentState types and IConsent interface
- Add withConsent wrapper attaching __consentChecked brand at bind time;
unit tests assert brand attachment and factory passthrough
- Add ConsentChecked<F> type to core-shared/conformance/brands.ts and
isConsentChecked helper to brand-runtime.ts
- Extend FeatureManifest with requiresConsent?: readonly string[] field
- Extend assertFeatureConformance to require __consentChecked brand when
requiresConsent.length > 0; synthetic fixture tests cover pass/fail cases
- Propagate __consentChecked in withSpan PROPAGATED_BRANDS so the outermost
binding carries the brand
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds CONSENT_GRANT, CONSENT_WITHDRAW, RESTRICT, UNRESTRICT to the
AuditAction closed enum per GDPR Art. 7 and Art. 18 requirements.
core-consent and core-dsr optional cores (Epic B Stories 03/06) emit
these action types via core-audit's IAuditLog channel; the values must
exist in core-shared's enum before either optional core can be built.
No change to IAuditLog's interface surface — new values flow through
AuditEntry.action automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces SubjectLinkKind, SubjectLink, and CollectionSubject types to
packages/core-shared/src/payload/subject-linkage-types.ts, establishes the
ambient CollectionCustom.subject declaration (parallel to custom.pii / custom.retention
from Epic A), and extends PAYLOAD_AUTH_PII_DEFAULTS with processingRestrictedAt and
consentState as DSR-managed excluded fields. Applies the first canonical usage of
custom.subject = { kind: "self", field: "id" } on the auth users collection.
Adds retention-purge.job.ts to packages/core-shared/src/payload/retention-purge/.
Walks every Payload collection's custom.retention.purgeSchedule, registers one
scheduled job per collection via IJobQueue, queries rows past their activeRetention
period (by createdAt for from-creation, updatedAt for from-last-access), and applies
pseudonymize (null PII fields) or hard-delete per postDeletion.action. Emits one
IAuditLog.record entry per processed row; gracefully skips when auditLog is absent.
Unit tests cover: schedule registration, trigger-type routing, hard-delete branch,
pseudonymize branch, audit emission, graceful auditLog skip, no-activeRetention
short-circuit, and input validation error cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ambient declaration files have no runtime code so v8 coverage
never generates DA records for them. Without an allowlist entry,
coverage:diff reports no-coverage-data for every .d.ts in the
diff. Add /\.d\.ts$/ to ALLOWED_GLOBS with a companion test.
Also configure @vitest/coverage-v8 for core-shared and add
targeted vitest exclusions for infrastructure files that are not
unit-testable (DI symbols, interface files, tRPC context, Sentry
SDK init) — bringing core-shared into the L2 aggregate and making
the L1 diff gate enforce coverage on new executable code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces PiiCategory, DataProcessingPurpose, RetentionTrigger,
RetentionAction, FieldPii, FieldRetention, PAYLOAD_AUTH_PII_DEFAULTS,
PurgeSchedule, and CollectionRetention in core-shared/payload/.
Augments payload's FieldCustom and CollectionCustom interfaces via
ambient declaration so downstream collection configs gain typed
custom.pii and custom.retention / custom.authPii fields.
Credential fields (password, salt, hash, resetPasswordToken,
resetPasswordExpiration, loginAttempts, lockUntil, apiKey, apiKeyIndex)
are null in PAYLOAD_AUTH_PII_DEFAULTS to exclude security material
from DPA mapping. Adds @vitest/coverage-v8 and coverage exclusions
for boilerplate infrastructure files so coverage:diff is gated on
new executable code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds analytics?: Analytics generic field to both BindContext and
BindProductionContext in bind-context.ts, mirroring the pattern used by
IEventBus, IAuditLog, and IJobQueue. AnalyticsProtocol already existed
in bind-protocols.ts and is re-exported from the @repo/core-shared/di
barrel via the existing wildcard export.
Also adds a type-level test for AnalyticsProtocol in bind-protocols.test.ts
to match test coverage for the other protocol types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors the existing Audited check: when a use case declares
analyticsEvents.length > 0 and the bound function lacks the __analyzed
brand, assertFeatureConformance throws ConformanceError at boot time.
Adds three synthetic conformance tests: passes when brand present +
events declared, throws naming Analyzed when events declared + brand
missing, passes when events empty + brand absent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AnalyticsProtocol to bind-protocols, extend WireUseCaseOptions with
optional analytics field, and compose the __analyzed brand inline in
wireUseCase (innermost, before withAudit) when analytics is provided.
Propagate __analyzed through withCapture and withSpan PROPAGATED_BRANDS
so the outermost container binding carries the brand for boot-time
assertion checks (Story 05).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add optional `analyticsEvents?: readonly string[]` to `UseCaseManifest`
in `define-feature.ts` so manifests can declare which analytics events a
use case emits. Field defaults to absent (treated as []) — all existing
manifests remain valid without changes.
Update the feature generator template to emit `analyticsEvents: []` so
newly scaffolded features are analytics-declaration-ready from day one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the `Analyzed<F>` phantom-type brand to brands.ts and the
`isAnalyzed` type-guard to brand-runtime.ts, mirroring the existing
Instrumented/Captured/Audited pattern. Exports both from the
conformance index so Story 04 (wireUseCase) can key off the brand
without a circular dep.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
First implementation milestone of the agent-first coverage architecture
(ADR-020, PRD 2026-05-13). Lands the keystone — coverage bands as a
typed declaration in feature.manifest.ts plus a helper that derives
vitest threshold shapes from them.
New file packages/core-shared/src/conformance/coverage.ts (self-
contained, no relative imports — loadable at vitest config time):
- CoverageBand / CoverageBands / CoverageManifest / VitestThresholds
types
- DEFAULT_COVERAGE_BANDS (baseline 80/75/80/80; entities 100/100/100/
100; use-cases + controllers 100/95/100/100) — matches ADR-011
- DEFAULT_MUTATION_SCORE (80) + DEFAULT_MUTATION_TARGETS (entities +
use-cases)
- getCoverageBands / getMutationConfig — manifest -> resolved bands,
with default fallback for missing layers
- vitestThresholdsFromBands / vitestThresholdsFromManifest — convert
to vitest's coverage.thresholds shape with the layer-to-glob
mapping
define-feature.ts gains the optional coverage field on FeatureManifest
(imports its type from coverage.ts to avoid a relative-import cycle
at config-load time).
Exposed via two subpaths: @repo/core-shared/conformance (re-exports
for source/test code) and @repo/core-shared/conformance/coverage
(direct subpath safe to load from vitest configs, bypasses the index
re-export chain that Node ESM doesn't auto-extension-resolve).
Auth wired as proof-of-concept:
- packages/auth/src/feature.manifest.ts declares its coverage section
- packages/auth/vitest.config.ts imports the helper +
DEFAULT_COVERAGE_BANDS and emits thresholds via
vitestThresholdsFromBands(DEFAULT_COVERAGE_BANDS) — no more
hand-maintained per-glob thresholds block.
Verified: 175/175 tests pass; 14/14 typechecks clean; auth coverage
green (21 tests, 93.77% overall, all per-layer 100% bands hold).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously .claude/settings.local.json was only excluded by the user's
global ~/.config/git/ignore — fresh clones of this template would have
accidentally committed per-user Claude Code settings. Add an explicit
project-level rule (*.local, *.local.*, **/settings.local.json) so the
template ships with the protection baked in.
Also strips two missed "phase" residuals from
core-shared/instrumentation/otel/init-server-node.ts (the word leaked
through the earlier sweep — generic future-work qualifier, not a
setup-history reference, but cleaner without it).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Rename docs/decisions/adr-012-lazar-conformance.md → adr-012-feature-conventions.md
- Strip "Lazar", "Plan 8/9/10/11", "refactor-logs" refs from all ADRs,
architecture docs, HTML explainers, and feature/core AGENTS.md files
- Update all incoming links in docs/, packages/*/AGENTS.md, HTML explainers
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds boot-time check that every manifest-declared use case is bound
through withSpan (__instrumented) + withCapture (__captured), with
withAudit (__audited) enforced when audits[] is non-empty. Propagates
inner brands through withSpan so the outermost container-resolved
binding carries all brand markers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds explicit ProductionUseCase<SignInInput, SignInOutput, AuthManifest["useCases"]["signIn"]>
type annotation to wrappedSignIn in bind-production.ts. To satisfy the Instrumented & Captured
intersection requirement, withSpan gains a brand-preserving overload so composing
withSpan ∘ withCapture returns Instrumented<F> & Captured<F>.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- 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>
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>
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>
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>