Commit Graph

423 Commits

Author SHA1 Message Date
379772a000 feat(navigation): add retention metadata to header global
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 19:19:54 +00:00
3b2d618cfc feat(media): add retention metadata to media collection
Adds custom.retention (monthly purge, 90-day post-deletion hard-delete)
to the media Payload collection. No uploadedBy field exists in the
collection so no custom.pii annotation is needed.
2026-05-18 19:16:23 +00:00
cc000e1ad4 feat(marketing-pages): add retention metadata to pages and site-settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 19:12:17 +00:00
a6fc874f11 feat(blog): add retention metadata to articles collection
Monthly purge schedule with 90-day post-deletion hard-delete window.
Part of compliance backfill (Story 05).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 19:08:04 +00:00
a32ff12a69 feat(auth): add PII and retention metadata to users collection
Tag displayName as identification-username PII and declare daily purge
with 30-day post-deletion hard-delete retention. PAYLOAD_AUTH_PII_DEFAULTS
covers email/credentials automatically — no authPii override needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 19:03:58 +00:00
7ea38bc5b9 feat(core-shared): add retention-purge background job
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>
2026-05-18 18:56:14 +00:00
1eb32ab23b feat(core-eslint): add pii-declaration-must-be-complete rule
Adds the `conformance/pii-declaration-must-be-complete` ESLint rule at
warn severity. The rule detects `custom: { pii: { ... } }` blocks in
Payload config files and warns when any of the four required sub-fields
(`category`, `purpose`, `exportable`, `restrictable`) is missing.

Incomplete PII declarations can produce incorrect audit reports —
sub-second editor feedback catches the gap before it reaches
compliance/data-map.yml.

- Rule + 7 RuleTester fixtures (complete passes, each missing field
  warns, non-pii custom block is no-op, malformed custom.pii is no-op)
- Registered in plugin.js + base.js at "warn"
- Conformance rule count bumped 7 → 8 in CLAUDE.md +
  conformance-quickref.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:33:48 +00:00
0d4be0a4f4 fix(coverage): exempt .d.ts files from diff coverage gate
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>
2026-05-18 18:26:50 +00:00
a94e8032b5 feat(core-shared): add PII and retention type primitives
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>
2026-05-18 18:23:24 +00:00
a7e0bf290d feat(core-analytics): add React provider and useAnalytics hook
Adds a ./react subpath export to @repo/core-analytics containing
<AnalyticsProvider value={IAnalytics}> and useAnalytics(): IAnalytics.
useAnalytics() throws AnalyticsContextError when called outside a provider.
React Testing Library test verifies track() flows through context using
RecordingAnalytics. Switches vitest config to pick up .tsx test files
via environmentMatchGlobs and extends tsconfig to react-library.json
for JSX support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:54:30 +00:00
395143466c feat(core-eslint): add no-undeclared-analytics-event ESLint rule
Adds the conformance/no-undeclared-analytics-event rule at warn severity,
mirroring no-undeclared-audit and no-undeclared-event-publish. The rule
cross-checks analytics.track("X", ...) literal slugs in *.use-case.ts
files against manifest.useCases[name].analyticsEvents, providing
sub-second editor feedback before boot-time conformance fires.

- Extends _manifest-ast.js to parse analyticsEvents arrays in both
  extractUseCaseEntry helpers
- Registers rule in plugin.js and base.js at ["warn", { repoRoot }]
- RuleTester fixtures: declared pass, undeclared warn, non-literal no-op,
  non-use-case file no-op, missing manifest entry no-op

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:43:37 +00:00
a832a7dfaa feat(core-shared): add AnalyticsProtocol to BindContext and BindProductionContext
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>
2026-05-18 15:38:48 +00:00
92b17f7d64 feat(core-shared): add Analyzed brand check to assertFeatureConformance
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>
2026-05-18 15:19:29 +00:00
32018e8a7b feat(core-shared): extend wireUseCase with analytics arg and Analyzed brand propagation
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>
2026-05-18 15:12:57 +00:00
0ee0355f5e feat(core-shared): add analyticsEvents field to UseCaseManifest
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>
2026-05-18 11:57:49 +00:00
c7bdf7cf3b feat(core-analytics): add withAnalytics wrapper and Analyzed brand export
Adds `withAnalytics(analytics, factory)` to packages/core-analytics —
mirrors the `withAudit` pattern: thin forwarding closure that attaches
the `__analyzed` brand via `attachBrand` from `@repo/core-shared/conformance`
without mutating the original factory.  Exports `Analyzed<F>` type and
`withAnalytics` from the `@repo/core-analytics` root barrel.

Adds `with-analytics.test.ts` asserting brand is present after wrapping,
absent on the original fn, output passes through unchanged, and errors
propagate.  Adds `@repo/core-shared` as a production dependency.

Also fixes `scripts/library-decisions/check.mjs` to exempt workspace-protocol
entries (`workspace:*`) from the library trace requirement — internal monorepo
packages are not third-party libraries and were incorrectly gated.  Adds a
regression test in `check.test.mjs` covering the exemption.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:52:44 +00:00
d128106fd2 feat(core-shared): add Analyzed brand and isAnalyzed type guard
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>
2026-05-18 11:41:48 +00:00
67879ffad2 feat(core-testing): add RecordingAnalytics test double
Implements IAnalytics with recorded arrays (tracked, identified,
pageViewed) and flush() that drains the buffer. Mirrors the pattern
established by RecordingAuditLog; inline type aliases avoid a
build-graph cycle with @repo/core-analytics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:35:32 +00:00
563eab06a6 feat(core-analytics): add IAnalytics interface, types, and NoopAnalytics
Replaces generator placeholder with IAnalytics interface (track, identify,
pageView, flush), AnalyticsAttributeValue + AnalyticsUser types, and
NoopAnalytics implementation. Adds sibling tests covering all four methods
with 100% coverage. All conformance + coverage gates pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:31:58 +00:00
ea384c67c4 feat(core-analytics): scaffold @repo/core-analytics via generator
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>
2026-05-18 11:26:24 +00:00
8cb531e0cd feat(core-eslint): add usecase-must-be-wired conformance rule
Catches manifest use cases that aren't wired through wireUseCase(...) in
bind-production.ts / bind-dev-seed.ts. wireUseCase is the canonical helper
that attaches __instrumented / __captured / __audited brands — skipping
it produces an unbranded binding that assertFeatureConformance would
reject at boot. This rule shifts that detection from ~3s (boot) to <1s
(lint), keeping the layered conformance pattern: TS brands (compile),
ESLint (lint), boot assertion (dev), smoke tests (CI).

CLAUDE.md + conformance-quickref.md updated for the new rule (5 → 6).
2026-05-18 11:03:17 +02:00
bd770cad5d test(features): exercise bind-production conformance assertion in CI
Each feature's bind-production.ts already ends with
assertFeatureConformance(), but that check only fired on `pnpm dev`.
Adds a smoke test per feature that invokes the binder with a Noop ctx,
so missing __instrumented / __captured / __audited brands now fail
under `pnpm test` (and therefore in CI), closing the gap between the
TypeScript brand layer and the boot-time runtime assertion.
2026-05-18 10:57:20 +02:00
81a791c5fd refactor(navigation): migrate binders to wireUseCase for getHeader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:08:42 +00:00
dcc7edcd18 refactor(marketing-pages): migrate binders to wireUseCase for both use cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:04:34 +00:00
b93ce25b2a refactor(media): migrate binders to wireUseCase for all 3 use cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:59:59 +00:00
43d88be4bd refactor(blog): migrate binders to wireUseCase for all 3 use cases
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>
2026-05-13 17:55:43 +00:00
88b41798d6 refactor(auth): migrate use-case binders to wireUseCase
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>
2026-05-13 17:51:16 +00:00
1bbe866a5c feat(core-shared): add wireUseCase helper to conformance barrel
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>
2026-05-13 17:28:57 +00:00
b96cce5d74 feat: hybrid versioning + automated CHANGELOG via release-please
Closes the user's ask: versioning + a changelog generated on merging
to main, building on the just-mandated Conventional Commits substrate
(CLAUDE.md Key Conventions).

Architecture: ADR-021. Cookbook: docs/guides/releasing.md.

Initial state — six tracked packages at v0.1.0:
  - .                          -> template-vertical  (tag: template-v...)
  - packages/auth              -> @repo/auth         (tag: auth-v...)
  - packages/blog              -> @repo/blog         (tag: blog-v...)
  - packages/media             -> @repo/media        (tag: media-v...)
  - packages/marketing-pages   -> @repo/marketing-pages (tag: marketing-pages-v...)
  - packages/navigation        -> @repo/navigation   (tag: navigation-v...)

Core packages, tooling, and apps are NOT independently versioned
(ADR-021 rationale: core bumps cascade; apps aren't consumables;
surfacing them would create noise without information).

Configuration:
  - release-please-config.json   - 6 tracked packages, hybrid scope,
                                   pre-1.0 conservative bump policy
                                   (feat: -> patch, feat!: -> minor),
                                   conventional-commit type mapping
  - .release-please-manifest.json - baseline 0.1.0 for all 6 packages
  - .github/workflows/release-please.yml - googleapis/release-please-
                                   action@v4 on push to main,
                                   concurrency-gated, write
                                   permissions for the rolling PR

Workflow: on every push to main, release-please scans commits since
the last release tag PER PACKAGE (using commit-path, not the
conventional-commit scope), updates a single rolling release PR with
version bumps + per-package CHANGELOG entries. Merging that PR cuts
per-package tags + GitHub releases.

CHANGELOG files seeded at v0.1.0 baseline:
  - CHANGELOG.md (root)
  - packages/<feature>/CHANGELOG.md (5 features)
Subsequent versions are appended by release-please from commit
history. Do not edit manually.

Visibility surfaces updated (every agent entry point):
  - CLAUDE.md Read First + new "Versioning is hybrid" Key Conventions
    bullet (with bump policy summary)
  - AGENTS.md preamble - new "Releases:" callout alongside Commits
  - docs/glossary.md - new Releasing section with 8 terms (Conventional
    Commits, release-please, Hybrid versioning, Tag prefix, Rolling
    release PR, Bump targeting, Pre-1.0 bump policy, Release-As trailer,
    CHANGELOG.md)
  - docs/README.md - guides tree updated with releasing.md
  - .claude/hooks/session-start.sh - one-line release reminder
  - .claude/hooks/prompt-context.sh - new keyword group for
    release/version/bump/semver/tag prompts

Package.json version bumps:
  - root: name "template" -> "template-vertical", version "0.1.0"
  - packages/auth, blog, media, marketing-pages, navigation: "0.0.0" -> "0.1.0"

Root rename rationale: release-please tags use the package-name + the
component prefix; "template-vertical" matches the repo identity (and
the user's question preview).

First release-please PR after this lands will sweep all subsequent
post-baseline commits into 0.1.1 / 0.2.0 bumps as appropriate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:17:16 +02:00
bf0b049583 feat(coverage): L0 unification — close test gaps in nav, media, mp
Closes the per-layer threshold gaps surfaced by the 2026-05-13 PRD
audit. After this commit all five features pass their declared
100%/100%/95%/100% bands on entities + use-cases + controllers.

media (was: missing @vitest/coverage-v8 + missing vitest config block +
  one controller at 86.66% lines / 75% branches)
  - Added @vitest/coverage-v8 dev dep
  - Applied the standard helper-driven vitest config
  - Declared the coverage section in feature.manifest.ts
  - Added 2 tests to list-media.controller.test.ts covering the
    InputParseError branch (unknown fields + invalid limit)
  - Now: 16 files / 80 tests / 97.12% / controllers 100%

marketing-pages (was: get-site-settings.controller at 93.54% lines /
  90.9% branches)
  - Added 1 test to get-site-settings.controller.test.ts covering the
    InputParseError branch on unknown fields
  - Now: 22 files / 68 tests / 95.66% / controllers 100%

navigation (was: entities/errors/common.ts at 50% function hits +
  get-header.controller at 86.66% lines / 80% branches)
  - Root cause: InputParseError class never instantiated in any test
  - Added 2 tests to get-header.controller.test.ts covering the
    InputParseError branch + verifying the Zod cause is preserved.
    One test exercises both gap files at once (controller throws,
    InputParseError class is constructed).
  - Wired navigation/vitest.config.ts through the shared helper
  - Declared the coverage section in feature.manifest.ts
  - Now: 11 files / 45 tests / 98.04% / entities + controllers 100%

All 5 features now drive thresholds from the manifest via the helper.
The duplication problem the keystone eliminates is gone.

Repo-wide via `pnpm coverage:aggregate`:
  - statements 95.87% (lh 2994 / lf 3123)
  - branches   88.91% (brh 433 / brf 487)
  - functions  100%   (fnh 142 / fnf 142)
  - lines      95.87%

`pnpm coverage:diff -- --base HEAD~1` reports status: pass.

coverage/summary.json refreshed in the same commit so the trend
captures the post-unification state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:39:08 +02:00
6428f10b82 feat(coverage): pnpm mutate (Stryker) + L3 implementation
Lands L3 of the agent-first coverage architecture (ADR-020) — the
mutation-testing layer. Stryker on entities + use-cases (the pure
business-logic surface) catches the third dimension of test quality:
tests that exist + execute the code but assert nothing.

Deps (root devDependencies):
  - @stryker-mutator/core ^8.7.0
  - @stryker-mutator/vitest-runner ^8.7.0

Shared base: packages/core-testing/stryker.base.json
  - testRunner: vitest (uses each feature's vitest.config.ts)
  - mutate: src/entities/** + src/application/use-cases/** (excludes
    tests, factories, contracts)
  - thresholds: high 90 / low 80 / break 80
  - reporters: progress + html + json (reports/mutation/{index.html,
    mutation.json})
  - incremental mode enabled, concurrency 4, timeout 10s
  - exposed via @repo/core-testing/stryker.base.json subpath export

Per-feature config: packages/auth/stryker.config.json
  - 4-line file that extends the shared base
  - Proof-of-concept; other features get a config when L0 unification
    closes their existing test gaps

Driver: scripts/coverage/mutate.mjs (zero-dep Node ESM)
  - discoverStrykerConfigs: walks packages/* and apps/* for
    stryker.config.json
  - Supports --filter <name>, --since <ref> (incremental), --json
  - Runs Stryker per-feature via node_modules/.bin/stryker run
  - Surfaces per-package pass/fail summary; exits 1 on any failure
  - Tests: scripts/coverage/mutate.test.mjs (3 tests, all green)

CI: .github/workflows/mutation-nightly.yml
  - Cron at 02:30 UTC + workflow_dispatch with filter input
  - Uploads reports/mutation/** as artifact (30-day retention)
  - On failure, opens a tracking issue labelled mutation-testing
  - permissions: contents: read, issues: write
  - 60-min timeout (Stryker is slow by design)

Generator: turbo gen feature now scaffolds stryker.config.json from
turbo/generators/templates/feature/stryker.config.json.hbs — new
features ship mutation-ready out of the box.

Guide: docs/guides/coverage.md L3 section fleshed out with run
syntax, config shape, base config inventory, CI behavior, and a
"what you're looking for" primer on mutation scores.

Lockfile churn: pnpm regenerated the lockfile for the new deps;
~5K-line net reduction is collateral (pnpm version drift) but
mechanical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:31:30 +02:00
15db9c48cb refactor(blog,marketing-pages): wire coverage helper + declare manifest bands
Migrates blog and marketing-pages to the same pattern auth landed in
f7baa8b: vitest config consumes vitestThresholdsFromBands(
DEFAULT_COVERAGE_BANDS) instead of the duplicated hand-written
thresholds block, and each manifest declares its coverage section
explicitly.

Verified semantically identical to the previous hand-written
thresholds — the same numbers come out the other end of the helper.
No new regressions:
  - blog: 89 tests, 96.33% overall, all bands green
  - marketing-pages: 67 tests, 95.28% overall — controllers/ shows a
    real 93.54% lines / 90.9% branches gap that has been there since
    before this refactor (the previous hand-written threshold was the
    same 100%/95%). This is one of the L0 unification work items
    listed in the PRD's findings; capturing here as the third feature
    with real test gaps (navigation, media, marketing-pages).

Three of five features now drive their vitest thresholds from the
manifest helper: auth ✓, blog ✓, marketing-pages ✓. Navigation and
media stay on the legacy config until L0 unification closes their
test gaps (touching them would expose the same failures and add
nothing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:13:08 +02:00
f7baa8bfd1 feat(core-shared/conformance): manifest coverage schema + vitest helper
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>
2026-05-13 13:51:13 +02:00
5d61343068 chore: gitignore *.local files at project level + strip phase residuals
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>
2026-05-13 13:10:53 +02:00
2edc76002a refactor(docs): strip residual Phase/Plan setup-history references
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>
2026-05-13 10:28:31 +02:00
318dc05b6e refactor: strip residual R-number test descriptions + Lazar URL from ADR-013 2026-05-13 10:22:17 +02:00
2ceaa08944 chore(template-reset): update snapshots, fix lint, rebuild state
- Regenerate audit + realtime core-package e2e snapshots (template
  Phase-label changes altered file hashes)
- Fix pre-existing lint error in auth authentication.service.ts:
  rename unused params to _user / _sessionId, drop stale eslint-disable
  comments that were on wrong lines
- Mark story tasks 1-9 done; rebuild _state.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:17:50 +02:00
841655573b docs(adr): rename ADR-012 — drop Lazar; update title + content + cross-refs
- 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>
2026-05-13 10:07:37 +02:00
17ae157365 refactor: strip Phase/Plan/R-number references from source comments 2026-05-13 09:51:45 +02:00
69d84a598d feat(core-eslint): wire frontend conformance rules into plugin + base
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:41:24 +02:00
159db9e542 feat(core-eslint): atomic-tier-import-direction rule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:41:01 +02:00
f933ca74ff feat(core-eslint): component-must-have-test rule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:40:40 +02:00
9ef545b714 feat(core-eslint): component-must-have-story rule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 07:40:19 +02:00
f06f7dbadf feat(features): extend assertFeatureConformance to bind-dev-seed paths 2026-05-13 07:35:40 +02:00
171ed20527 refactor(core-eslint): readManifestSource delegates to AST parser 2026-05-13 07:34:00 +02:00
d83d97755e feat(core-eslint): parseManifestFully — AST-based full manifest extraction 2026-05-13 07:33:41 +02:00
79f96a94a1 feat(core-eslint): flip feature-must-have-manifest from warn to error 2026-05-13 00:10:48 +02:00
5fa7de48d8 feat(marketing-pages): conformance manifest + self-asserting bind-production 2026-05-13 00:10:29 +02:00
7953a44203 feat(navigation): conformance manifest + self-asserting bind-production 2026-05-13 00:10:03 +02:00
dc7aafb97f feat(media): conformance manifest + self-asserting bind-production 2026-05-13 00:09:36 +02:00