Commit Graph

1061 Commits

Author SHA1 Message Date
dcbf782e21 test(generators): run release-please-utils tests under vitest
The file was doubly dead: the vitest include lacked lib/*.test.mjs and
the file imported node:test, so no runner ever executed it. The include
now picks up lib .mjs tests and the imports move to vitest (node:assert
keeps the original assertion style) — all 5 assertions live. (S5)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:23:06 +02:00
2747feab46 fix(auth): stop leaking usernames via emailDomain span attribute
getUserByUsername emitted the FULL username as the emailDomain span
attribute whenever the username contained no "@" (audit finding B6),
violating the PII-free telemetry rule (ADR-017 §7). Emit only a boolean
hasAtSign in both the production repository and its mock; regression
tests pin that no username-derived string reaches span attributes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:22:00 +02:00
27787193c0 fix(core-testing): align RecordingEventBus with real bus semantics
Handlers and the published record now receive the zod-PARSED payload
(defaults/coercions applied) instead of the raw input, and fan-out uses
Promise.allSettled with errors swallowed by default plus an opt-in
failFast — matching the InMemoryEventBus the events core-package
generator scaffolds. The previous sequential fail-fast divergence was
undocumented, so it is aligned rather than kept. Regression tests pin
parsed-payload delivery, non-short-circuiting fan-out, and failFast
rethrow. (S4; the generator bus templates already carry the fix.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:21:52 +02:00
c9db7c8cd7 fix(auth): add username + passwordHash fields to users collection
The production UsersRepository reads and writes username + passwordHash
via the Payload local API, but the users collection never declared them,
so production sign-up/sign-in was broken (audit finding B1). passwordHash
uses access.read: () => false so credential material never serializes
through any Payload API surface; the repository still reads it with
overrideAccess: true. A contract-shaped test pins every repo-used field
(USERS_REPOSITORY_FIELDS) against the collection config so drift fails
at test time without a database.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:21:16 +02:00
bb2751eede docs: correct stale no-op comments in node test setup
setup/node.ts installs the no-instrumentation guard (vi.mock against
real Sentry/OTel SDK init) via its import — it was never a no-op. Fix
the same lie in core-typescript's jsdom base-config test comments and
test title. (S2)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:20:27 +02:00
3cf2572c85 chore(fallow): make the whole-codebase gate pass without hiding signal
pnpm fallow failed on 14 dead-code issues, 200 clone groups (4.3% >
3.0% threshold) and one cognitive-complexity breach. Changes:
- duplicates.ignore covers convention-mandated boilerplate only (test
  files, stories, fixtures/factories/seeds, binders, symbols, feature
  manifests, vitest configs, mock/repo twins, sentry init twins,
  compliance emitters, rule-meta boilerplate) at threshold 3.0
- usedClassMembers: validateSession (interface-implemented, not yet
  called); ignoreExports: Next's generateMetadata convention export +
  the __getInstrumentationForTests test knob
- duplicate-exports off: client/server RSC twins export the same
  component name by design
- @trpc/client + @trpc/react-query added to ignoreDependencies (peer
  resolution for feature ./ui hooks); *.test.mjs marked dynamically
  loaded (node:test files fallow saw as unreachable)
- delete packages/auth/src/ui/query.ts (empty export{} placeholder
  shadowed by ui/index.ts, genuinely dead)
- extract checkDepTrace/decisionError from checkLibraryDecisions
  (cognitive 32 > 30) — behavior unchanged, tests pass

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:53:16 +02:00
eccd8b0cc1 fix(library-decisions): traceSchema accepts the committed trace shape
The strict schema rejected 38 of 39 approved traces in
docs/library-decisions: lastRevalidated was required (most traces omit
it), and the compliance frontmatter fields the _template.md documents
(is-sub-processor, processes-pii, plus the sub-processor block:
data-sent, region, dpa-signed, sccs-required, contact) were
unrecognized keys. Make lastRevalidated optional, add the compliance
fields (booleanish coercion for YAML string scalars), and require
data-sent when is-sub-processor is true. New loop test asserts every
committed trace parses so the schema can never drift away from the
repo's own corpus again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:47:18 +02:00
5f38a3efce build: bump vitest past GHSA-5xrq-8626-4rwp; coverage-v8 everywhere
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>
2026-07-10 16:44:48 +02:00
c0dbadf1c2 ci: honest audit gate, real Socket CLI, reachable mutation issue step
- 'pnpm audit signatures' is an npm-only feature; pnpm ignored the
  word and ran a plain full audit. Replace with an explicit
  'pnpm audit --prod --audit-level=critical' (documented rationale)
- socket-cli is a 0.0.1 stub on npm; use the real 'socket' CLI and
  fail loudly instead of silently passing
- mutation-nightly's issue-opening step was gated on if: failure(),
  unreachable under continue-on-error — gate on steps.mutate.outcome
- wire the ADR-023 renovate major-bump gate
  (scripts/library-decisions/check.mjs --renovate-pr) on PRs; verified
  it no-ops on non-renovate branches

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:43:32 +02:00
a17b984675 fix(generators): run UI tests, guard optional bus, harden e2e clones
Three generator fixes:
- templates/feature/vitest.config.ts.hbs lacked an include for
  .test.{ts,tsx}; the node base only includes .test.ts, so scaffolded
  UI component tests never executed
- gen event consume emitted an unguarded bus.subscribe although
  ctx.bus is optional in BindContext — now wrapped in if (bus) {}
- e2e repo clones now exclude /dist and /.next build outputs, and
  every dep-stripping e2e strips the scaffolded package from EVERY
  workspace package.json via globSync instead of a hardcoded dependent
  list that drifts as packages gain or drop the dependency

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:42:46 +02:00
1e3220aef5 fix(core-ui): stop excluding Storybook stories from typecheck
*.stories.tsx / *.stories.ts were excluded from tsconfig, so story
files never typechecked and could drift against component props
silently. Include them (they already typecheck cleanly); build is
tsc --noEmit so nothing new is emitted. The Select scrollIntoView port
from the downstream fork does not apply — this tree has no Select atom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:33:41 +02:00
16310c5d62 test(core-shared): assert PII scrub runs before Sentry processors
The previous init-server-node tests only checked that an SDK handle
came back — the ADR-017 §7 invariant (PiiScrubSpanProcessor /
PiiScrubLogRecordProcessor registered BEFORE any exporter-facing
processor) was untested. Capture the NodeSDK constructor config via a
local vi.mock override and assert processor ordering for both the
empty-DSN and DSN-set paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:32:47 +02:00
0234e18425 fix(core-testing): handle batched paths + transformed errors in mock tRPC
httpBatchLink joins same-tick calls into one request whose path is a
comma-separated list of procedure paths; the stub matched the joined
string against a single mock key, so any batched pair failed with 'No
mock for a.one,a.two'. It now answers one element per procedure, in
order. Error bodies are also superjson-serialized — raw error JSON made
the client throw 'Unable to transform response' instead of surfacing
the intended error. Regression tests added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:32:03 +02:00
8476712620 fix(generators): event buses deliver the zod-parsed payload
InMemoryEventBus.publish and PayloadJobsEventBus.publish called
descriptor.schema.parse(payload) but discarded the result, so handlers
and enqueued jobs received the raw input — zod defaults, catches,
transforms and strips never applied. Both bus templates now fan out the
parsed value; regression tests added to both test templates and the
events snapshot hashes regenerated (template-tree sha over
hbs-stripped paths — only the four touched files differ).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:30:53 +02:00
6fd746d3bd fix(core-eslint): activate React rules-of-hooks for all TSX surfaces
next.js and react-internal.js are plain re-exports of base, so no
rules-of-hooks checking was active anywhere despite three React apps
and core-ui. Wire eslint-plugin-react-hooks in base.js scoped to
**/*.tsx (non-React packages untouched). Fixes the one violation it
surfaced: web-tanstack's root route called Route.useLoaderData inside
an anonymous component callback — extracted to a named RootComponent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:27:16 +02:00
9b04fae975 fix(core-shared): derive Analyzed + RateLimited brands in binding slot
ProductionUseCase<I, O, M> only demanded Instrumented + Captured (+
Audited for mutating-with-audits). The boot assertion additionally
requires __analyzed for non-empty analyticsEvents and __rateLimited for
non-empty rateLimit, so the type-level gate under-promised what boot
enforces. The slot now derives both from the manifest entry; the
feature-scoped requiresConsent brand stays boot-only (documented).
Also make IAnalytics extend AnalyticsProtocol from
core-shared/di/bind-protocols so narrowing the ctx protocol fails
typecheck in core-analytics instead of drifting silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:25:09 +02:00
bf04ad70b2 chore: repair pre-existing lint failures blocking the lint gate
- turbo/generators/config.ts used require() inside the reader generator
  action (no-require-imports); use the top-level node:fs imports
- auth authentication.service.ts used a literal self-assertion
  ("users" as "users"); prefer-as-const
- apps/cms lacked web-next's next-env.d.ts triple-slash-reference
  override, and the committed next-env.d.ts now references
  .next/types/routes.d.ts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:21:26 +02:00
498f1fb57a fix(core-eslint): enforce ADR-017 OTel SDK import restriction
The base config documented (and carried allowlist off-blocks for) the
OTel-SDK import restriction but the no-restricted-imports pattern group
only covered @sentry/*. Add @opentelemetry/sdk-*, exporter-*,
instrumentation-*, resources and semantic-conventions to the restricted
group, and extend the off-block for core-audit's trace-id enrichment
test, which legitimately builds an in-memory sdk-trace-base tracer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:21:25 +02:00
90fc48db81 fix(conformance): fail the CI gate on unparseable manifests
parseManifestUseCases returning null for a manifest that exists made
the cross-feature gate silently skip that feature. findUnparseableManifests
now runs before the empty-graph early exit (an unparseable manifest
contributes zero events and would otherwise pass as nothing-to-check)
and any hit fails the run. Reader-closure from the downstream fork is
not ported: this tree has no reads: manifests or ./reader exports yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:14:13 +02:00
0c1df7f5d4 fix(core-eslint): fail loudly on unparseable manifest in wiring gate
usecase-must-be-wired returned {} when the manifest parsed to null,
silently disabling the error-level gate when the manifest existed but
could not be read. It now reports unparseableManifest on Program in
that case; a genuinely missing manifest stays a no-op (that is
feature-must-have-manifest's job).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:11:59 +02:00
3bf0c652e7 fix(core-eslint): parse satisfies-shaped manifests; unify manifest parser
A manifest written as `{...} satisfies FeatureManifest` (or the combined
`as const satisfies` idiom) parsed to null, silently no-oping the
error-level conformance rules. unwrapAsConst now strips TSAsExpression
and TSSatisfiesExpression in a loop. The file also carried a verbatim
second copy of its own parser for parseManifestFully; both public entry
points now share one implementation. The template's field set (audits,
rateLimit, requiresConsent) is preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:10:35 +02:00
d8a3250c12 test(generators): strip core-trpc dep from feature packages in e2e
Some checks failed
CI / typecheck + lint + boundaries + test + build (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
CI / Playwright e2e (push) Has been cancelled
CI / Storybook smoke tests + visual regression (push) Has been cancelled
Mutation testing (nightly) / mutate (push) Has been cancelled
Library trace revalidation (weekly) / revalidate (push) Has been cancelled
The core-package trpc removal e2e was only stripping @repo/core-trpc
from the two app package.json files. blog, marketing-pages, and
navigation also depend on core-trpc for their ./ui hooks, so pnpm
install in the simulated post-removal state failed to resolve the
workspace dep. Strip the dep from every package that references it.
2026-06-03 13:27:53 +02:00
danijel-lf
0a34b45bb7 feat(auth): implement session methods with Payload-backed JWT
Some checks failed
CI / typecheck + lint + boundaries + test + build (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
CI / Playwright e2e (push) Has been cancelled
CI / Storybook smoke tests + visual regression (push) Has been cancelled
Mutation testing (nightly) / mutate (push) Has been cancelled
Library trace revalidation (weekly) / revalidate (push) Has been cancelled
Replace NotImplementedError stubs in AuthenticationService with working
implementations: createSession signs a HS256 JWT using Payload's instance
secret, validateSession verifies and decodes the token then looks up the
user, invalidateSession returns a blank cookie with maxAge 0. No external
JWT dependency — uses Node crypto HMAC directly.

Also clarify withAudit/withAnalytics comments: the wrappers intentionally
delegate recording to the use case body (only it knows which fields to
extract), so the TODO was misleading.
2026-05-28 22:41:30 +02:00
danijel-lf
0fbb880c82 fix(web-next): correct idempotency test to use bindAll not bindAllProduction
bindAllProduction has no idempotency guard — the promise cache lives in
bindAll. Test was calling the wrong function, causing the spy to fire
twice.
2026-05-28 22:41:10 +02:00
danijel-lf
5b74939a51 feat(conformance): implement ReadOnly brand and reader generator
- Add ReadOnly<F> phantom brand to core-shared/conformance (compile-time
  enforcement that readers only wrap non-mutating use cases)
- Add isReadOnly runtime predicate for boot-time assertReaderPurity
- Scaffold pnpm turbo gen reader: creates integrations/readers/ with
  interface, implementation, test, barrel, and adds ./reader export
  subpath to package.json
2026-05-28 22:01:51 +02:00
danijel-lf
b97e6105d3 feat(conformance): wire cross-feature reader pattern into docs and schema
Add reads field to UseCaseManifest, update CLAUDE.md with Q0-Q3 rules,
add ./reader subpath to AGENTS.md exports table, and cascade reader
conventions through conformance quickref, adding-a-feature guide, and
scaffolding guide. Moves gen reader from deferred to planned.
2026-05-28 20:55:34 +02:00
danijel-lf
d4ce68d738 docs(architecture): add ADR-026 cross-feature synchronous readers
Introduce readers as a fourth cross-feature mechanism alongside events,
jobs, and realtime. Readers solve synchronous domain queries across
verticals (e.g. permission checks) where Payload relationTo gives raw
data but the answer requires business-rule evaluation by the owning
feature.

- Define rules Q0-Q3 (query-only, contract public, read-only, no cycles)
- Reader wraps existing use cases via ReadOnly<F> brand enforcement
- Lives under integrations/readers/ with ./reader export subpath
- Manifest reads: ["auth"] field for conformance gate visibility
- Update glossary with Reader, reads, ReadOnly<F> terms
2026-05-28 20:32:56 +02:00
danijel-lf
6a5d602b3b docs: add building-feature-ui guide and update agent instructions
Some checks failed
CI / typecheck + lint + boundaries + test + build (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
CI / Playwright e2e (push) Has been cancelled
CI / Storybook smoke tests + visual regression (push) Has been cancelled
Mutation testing (nightly) / mutate (push) Has been cancelled
- New guide covers server/client component pattern, hooks, DI prefetch,
  HydrationBoundary hydration, core-ui atomic design reuse, Tailwind
  wiring, seed data, and cross-feature boundaries
- Update AGENTS.md with feature UI folder structure and naming rules
- Update CLAUDE.md with feature UI data fetching convention
2026-05-26 15:59:22 +02:00
danijel-lf
8bc32095c1 fix(web-next): cache bindAll promise for concurrent caller safety
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.
2026-05-26 15:59:12 +02:00
danijel-lf
ee45bfe932 refactor(web-next): thin app pages that just render feature components
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.
2026-05-26 15:59:01 +02:00
danijel-lf
7ef0411ffa refactor: split feature UI into .server/.client component pairs
Server components (.server.tsx) resolve controllers from DI, prefetch
data, and wrap client components in HydrationBoundary. Client components
(.client.tsx) use hooks for hydration + background refetch. Barrel
exports server components under clean names — consumers never see the
server/client split.
2026-05-26 15:57:38 +02:00
danijel-lf
9cfa54d382 fix(web-next): add CSS module type declaration 2026-05-26 14:19:23 +02:00
danijel-lf
15d603b00c refactor(web-next): move bindAll to root layout
Centralizes DI initialization in the root layout so individual pages
and the tRPC route handler no longer need to import or call it.
2026-05-26 14:19:00 +02:00
danijel-lf
d71e30bb3a fix(web-next): support USE_DEV_SEED=false and load root .env globally
- 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
2026-05-26 14:13:31 +02:00
danijel-lf
e734530ffe feat(web-next): wire Tailwind CSS v4 via PostCSS
- Add @tailwindcss/postcss + postcss.config.mjs
- Create app.css with @source directives for monorepo packages
- Import app.css in root layout
2026-05-26 14:12:57 +02:00
danijel-lf
a28de6884c feat: add feature UI components with useQuery hooks
- navigation: SiteHeader component + useHeader hook
- blog: ArticleCard, ArticleList, ArticleDetail + useArticleList,
  useArticleBySlug hooks
- marketing-pages: PageHero, PageContent + usePageBySlug,
  useSiteSettings hooks
- App pages now thin server wrappers that prefetch + hydrate;
  feature components own their data fetching via useSuspenseQuery
2026-05-26 14:12:29 +02:00
danijel-lf
3ce71447b3 feat(core-trpc): scaffold tRPC client with React Query providers
- createTRPCContext + useTRPC hook for typed client access
- NextTrpcProvider with SSR-safe absolute URL resolution
- TanstackTrpcProvider for TanStack Start apps
- /api/trpc catch-all route handler in web-next
- Wire NextTrpcProvider into app providers
- Add @repo/core-trpc to transpilePackages
2026-05-26 14:11:27 +02:00
danijel-lf
bca04f4cef fix(storybook): wire Tailwind v4 theme into Storybook preview
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
- Split globals.css into globals.css + theme.css so Storybook can
  import design tokens without the @import "tailwindcss" directive
  that breaks Vite's preview module loading
- Prepend @tailwindcss/vite plugin for correct processing order
- Add @source to scan core-ui components for utility class generation
2026-05-26 13:22:17 +02:00
danijel-lf
29eaefe47e fix: drop .js extensions from remaining relative imports
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
Extends 8111639 to cover __factories__, __seeds__, __contracts__, and
core-testing barrels.
2026-05-26 12:04:44 +02:00
danijel-lf
3eaf50151f chore(core-cms): regenerate Payload types with consentState field
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
2026-05-26 11:46:39 +02:00
danijel-lf
8111639660 fix(di): drop .js extensions from bind-dev-seed imports
Webpack (Next.js transpilePackages) resolves from .ts source directly
and cannot find .js files. The bind-production binders already used
extensionless imports; align bind-dev-seed to match. Also fixes the
turbo gen feature template so new features are consistent.
2026-05-26 11:45:37 +02:00
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