Commit Graph

1076 Commits

Author SHA1 Message Date
1883883911 test(scripts): wire scripts/**/*.test.mjs under a vitest runner
241 tests across 16 script test files were wired to NO runner (the
generators vitest config never included them and several used node:test
imports that vitest silently skips). New root vitest.scripts.config.mjs
+ pnpm test:scripts + a CI validate step run them all; node:test imports
converted to vitest keeping node:assert. Split out of 8c88a9a where a
concurrent agent's staged files were swept into the marketing-pages
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:43 +02:00
bab8fb634f fix(marketing-pages): throw PageNotFoundError for missing slugs
getPageBySlug returned undefined while the feature already mapped
PageNotFoundError to NOT_FOUND and blog throws for missing slugs (B11).
Unify on the throwing contract: use case throws, controller narrows to
a non-optional presenter, the server component catches the domain error
to render its not-found state, and the router now surfaces NOT_FOUND.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:41 +02:00
dec24feaa0 fix(web-next): enforce manifest rate limits on the production path
bindAllProduction injected NoopRateLimit, so the sign-in budgets the
auth manifest declares were never enforced in production (audit finding
A4/B3). The production ctx now binds InMemoryRateLimit seeded from the
manifest's rateLimit budgets (manifest stays the source of truth);
dev-seed intentionally keeps Noop so local iteration never throttles.
A regression test drives sign-in through the REAL auth production
binder + app router and asserts the 6th failed attempt returns
TOO_MANY_REQUESTS while other IPs stay unaffected. In-memory counters
are per-process; multi-instance deployments need a shared IRateLimit
backend.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:30:24 +02:00
88ac2649b7 fix(media): return the exact window for non-aligned offsets
Same B9 defect as blog: Payload paginates by page, so a non-aligned
offset returned the wrong window. Fetch the straddled pages and slice
the intra-page remainder. Identical window tests now run against the
mock and the fake-payload stub.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:29:16 +02:00
50c30e9c1b fix(blog): return the exact window for non-aligned offsets
Payload paginates by page, so floor(offset/limit)+1 alone returned the
wrong window whenever offset % limit != 0 (B9). Fetch the straddled
pages and slice the intra-page remainder. The test stub now honours
page like real Payload (it silently ignored it before), and the
contract pins aligned + non-aligned windows on mock and stub alike.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:29:06 +02:00
b66759a1ab fix(auth): derive clientIp server-side, drop it from sign-in input
clientIp was part of the public signInInputSchema, so any client could
spoof its own rate-limit bucket or dodge IP throttling entirely (audit
finding B2). The schema no longer carries it (strict parsing rejects it
with BAD_REQUEST); instead the web-next tRPC fetch adapter derives it in
createTrpcContext from x-forwarded-for (first hop) / x-real-ip — trust
caveat documented — and the router threads ctx.clientIp to the
controller as a second, server-only argument typed outside the input
schema (SignInRequestContext).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:28:56 +02:00
bae2686832 ci: drop duplicate chromium install in storybook job
The job already runs 'playwright install --with-deps chromium' right
after pnpm install; the second install step before the visual step
re-downloaded the same browser for nothing. (S10)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:26:51 +02:00
21860784be chore: prune dead vars from turbo.json globalEnv
SENTRY_PROJECT_WEB_TANSTACK and VERCEL_ENV have zero consumers in the
tree (web-tanstack has no sentry upload config; nothing reads
VERCEL_ENV), so they only widened the global cache key. The other
Sentry/Vercel vars stay — next.config.mjs and init paths read them. (S9)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:26:34 +02:00
4d6734448a test(auth): cover signToken/verifyToken/validateSession crypto paths
The session methods had zero coverage because they call getPayload()
(audit finding B8). Stub the payload module (secret + findByID only) and
exercise the real crypto paths: round-trip, tampered signature, swapped
payload, expired exp, malformed segments, wrong secret, jti-less token
(fail closed), post-revocation rejection, and the per-instance denylist
limit. No running Payload needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:26:01 +02:00
cd1c0334af fix: resolve the root playwright config in pnpm test:visual
The visual-regression leg ran 'pnpm exec playwright test' from the
storybook package cwd, where the root playwright.config.ts is never
picked up — playwright fell back to a configless run with no chromium
project and an undefined baseURL for the story-index fetch. Pass the
root config explicitly. Verified via 'playwright test --list' from
apps/storybook: the [chromium] project and the visual spec resolve. (S8)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:25:57 +02:00
db2afde0dc feat(auth): revoke sessions server-side via a jti denylist
invalidateSession previously ignored its argument, so a stateless JWT
stayed valid until exp after logout (audit finding B5). createSession
now embeds the minted session.id in the token as jti; invalidateSession
records that jti in an in-memory denylist with expiry-based pruning, and
validateSession rejects denylisted (and jti-less, failing closed)
tokens. The denylist is per-process — the single-process limitation and
the shared-store upgrade path are documented in session-denylist.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:25:51 +02:00
c7d1dd8055 fix(storybook): glob all package stories + declare core-ui dep
The stories glob covered only packages/core-ui, so any feature story a
component-must-have-story fix produces would be invisible in Storybook
and skipped by pnpm test:stories. Glob every package's src tree and
declare the workspace dep on @repo/core-ui (the only storied package
today) so turbo's build cache invalidates when its stories change.
Verified: storybook build succeeds, 18 stories in index.json. (S7)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:25:09 +02:00
cd61b31e65 fix(cms): thread a per-request nonce through the admin CSP
The prod CSP emitted script-src 'strict-dynamic' with no nonce seed,
blocking every Payload admin script (A8). Reuse the shared nonce-based
withSecurityHeaders: Payload admin pages are always dynamically
rendered, so Next propagates the nonce read from the forwarded
request's CSP header onto the admin's scripts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:25:04 +02:00
32163312e3 chore(core-typescript): put its TS sources under the typecheck gate
The package's vitest base configs were outside every gate — no tsconfig
and no typecheck script meant tsc never saw them. Add a tsconfig with
allowImportingTsExtensions (vitest.base.jsdom imports ./vitest.base.node.ts
by extension) and an explicit include of the base configs + vitest
config, plus the typescript/@types/node devDeps to run it. (S6)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:24:09 +02:00
00fcc9d9a1 fix(core-shared): set CSP on forwarded request headers for nonce
Next.js only injects the nonce into its own scripts when it can read it
from the request's Content-Security-Policy header. Setting the CSP only
on the response left hydration scripts un-nonced in production (A9).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:24:01 +02:00
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