Commit Graph

1048 Commits

Author SHA1 Message Date
31f825a5d7 feat(turbo-gen): wire gen event consume actions
Adds consumeActions for the event generator: scaffolds the handler file
+ test, the Payload event-task that completes the production-bus loop,
modifies the consumer feature's symbols (// <gen:event-handler-symbols>),
both binders (// <gen:event-handlers>), and the cms re-export
(// <gen:job-tasks>). The bind block wraps the handler in span+capture,
binds it into the per-feature container, and subscribes it on the bus.
2026-05-08 16:18:12 +02:00
d53869f712 feat(turbo-gen): templates for gen event consume (handler + Payload task) 2026-05-08 16:16:35 +02:00
5cc6099015 feat(turbo-gen): event generator (publish mode)
Wires `pnpm turbo gen event` with publish/consume mode prompt; publish
branch generates the contract file + test and threads it through the
feature's // <gen:events> anchor. Consume branch is a stub that throws
until Task 40 lands the handler-side templates and modify-blocks.
2026-05-08 16:16:04 +02:00
0801469e89 feat(turbo-gen): templates for gen event publish 2026-05-08 16:13:07 +02:00
cf71547223 feat(turbo-gen): assertAnchors helper 2026-05-08 16:12:52 +02:00
d800d98574 feat(web-next): bindAll resolves IEventBus + IJobQueue per env
resolveEventsAndJobsProduction wires PayloadJobsEventBus + PayloadJobQueue
against the bootstrapped Payload instance; resolveEventsAndJobsDevSeed
wires InMemoryEventBus + InMemoryJobQueue. Each per-feature binder now
receives (bus, queue) so Phase 7 generators can subscribe handlers and
register job tasks at the <gen:event-handlers>/<gen:jobs> anchors.
2026-05-08 16:09:57 +02:00
2b4e576b0d feat(navigation): bind binders accept (bus, queue) params 2026-05-08 16:07:05 +02:00
4e77134ff6 feat(marketing-pages): bind binders accept (bus, queue) params 2026-05-08 16:05:49 +02:00
a9100de052 feat(media): bind binders accept (bus, queue) params 2026-05-08 16:04:37 +02:00
d6fe37450a feat(blog): bind binders accept (bus, queue) params 2026-05-08 15:17:40 +02:00
b0bd4c0bb3 feat(auth): bind binders accept (bus, queue) params 2026-05-08 15:16:16 +02:00
721d373856 chore(generators): add // <gen:*> anchor comments for event/job generators 2026-05-08 14:09:05 +02:00
8b3b118bb0 test(core-eslint): anchor-presence CI guard for // <gen:*> comments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:49:35 +02:00
bd1610a888 chore(navigation): add // <gen:*> anchor comments for event/job generators
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:48:24 +02:00
3c0eb34f5e chore(marketing-pages): add // <gen:*> anchor comments for event/job generators
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:47:40 +02:00
b672506c30 chore(media): add // <gen:*> anchor comments for event/job generators
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:47:04 +02:00
2506c2676b chore(blog): add // <gen:*> anchor comments for event/job generators
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:46:31 +02:00
73bfb22fe2 chore(auth): add // <gen:*> anchor comments for event/job generators
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:23:52 +02:00
5b766242be feat(core-eslint): rules E1 (no handler re-exports) and J (no direct payload.jobs)
Implements spec § 7 via no-restricted-syntax built-in selectors — mirrors
the R40 no-restricted-imports precedent; no custom plugin required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:18:14 +02:00
1357e45f55 feat(core-testing): RecordingEventBus
Adds RecordingEventBus implementing IEventBus for use in unit tests.
Validates payloads via the descriptor schema, records all publish calls,
and delivers events to subscribed handlers synchronously in subscription order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:05:34 +02:00
590b92f190 feat(core-testing): RecordingJobQueue
Adds RecordingJobQueue implementing IJobQueue for use in unit tests.
Records all enqueue calls with taskSlug/input/options and returns
synthetic incrementing jobIds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:04:22 +02:00
05f0b02856 docs(core-events): clarify subscribe consumerFeature + failFast drop
Code-quality review nits: IEventBus.subscribe docstring said "debug
tag" but InMemoryEventBus actually ignores the param entirely. Also
add a comment to InMemoryEventBus.publish noting that under failFast
only the first rejection rethrows; other failures are intentionally
dropped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 12:02:45 +02:00
430c831743 feat(core-events): public barrel
Export EventDescriptor, defineEvent, IEventBus, EventHandler,
CORE_EVENTS_SYMBOLS, InMemoryEventBus, InMemoryEventBusOptions, and
PayloadJobsEventBus from src/index.ts. All typecheck, test (11 tests),
and lint checks pass. turbo boundaries: 454 files, no issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:58:07 +02:00
ef451de437 feat(core-events): PayloadJobsEventBus (fan-out via IJobQueue)
TDD: red (test-only), then green. PayloadJobsEventBus validates before
enqueueing, names tasks __events.<event>.<consumer> deterministically, and
enqueues one task per subscriber via Promise.all. 3 new tests (11 total).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:57:48 +02:00
cb785b600a feat(core-events): InMemoryEventBus with failFast option
TDD: red (test-only), then green. InMemoryEventBus validates payloads via
descriptor.schema before fanout, delivers to all handlers via Promise.allSettled,
swallows handler errors by default, and rethrows first error when failFast:true.
5 new tests (8 total passing).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:57:19 +02:00
3da93efb89 feat(core-events): IEventBus interface + symbol registry
IEventBus defines publish<T> and subscribe<T> with consumerFeature parameter.
CORE_EVENTS_SYMBOLS.IEventBus uses Symbol.for for cross-module identity.
Typecheck passes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:56:52 +02:00
ba3b6c9af6 feat(core-events): EventDescriptor + defineEvent
TDD: red (test-only), then green. defineEvent(name, schema) returns a typed
EventDescriptor<TName, TSchema>. Also added @repo/core-testing to devDeps
(required for nodeVitestConfig's setupFiles). 3 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:56:37 +02:00
f99a307f72 chore(core-eslint,turbo): tag @repo/core-events as core
The existing turbo.json boundaries use tag names (not package names), and
the core-eslint boundaries config uses the packages/core-* glob. Both already
cover @repo/core-events through its turbo.json "tags": ["core"] declaration.
pnpm turbo boundaries confirms: 445 files in 18 packages, no issues found.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:55:44 +02:00
f94128943a chore(core-events): scaffold package
Create @repo/core-events package skeleton with package.json, tsconfig.json,
vitest.config.ts (using mergeConfig with nodeVitestConfig), eslint.config.js,
turbo.json (tagged core), and AGENTS.md. Register workspace via pnpm install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:55:28 +02:00
2f52a11db8 feat(core-shared/jobs): public barrel via @repo/core-shared/jobs subpath 2026-05-08 11:36:29 +02:00
23a1faa002 feat(core-shared/jobs): PayloadJobQueue 2026-05-08 11:36:12 +02:00
b96cff34ba feat(core-shared/jobs): InMemoryJobQueue with register() 2026-05-08 11:35:50 +02:00
a2a83e1cf2 feat(core-shared/jobs): symbol registry 2026-05-08 11:35:25 +02:00
cf32175684 feat(core-shared/jobs): IJobQueue interface 2026-05-08 11:35:15 +02:00
e85c89144b feat(core-shared): add ./jobs subpath export 2026-05-08 11:33:05 +02:00
892f924603 docs: surface turbo gen feature in AGENTS.md and CLAUDE.md
Wires the existing turbo gen feature generator into AGENTS.md (Adding
a Feature section, Key Commands, Specification & Guides) and
CLAUDE.md (Quick Start, Read First). Adds a fast-path callout at the
top of the manual walkthrough in docs/guides/adding-a-feature.md
pointing at scaffolding-a-feature.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:30:10 +02:00
c2a32363a8 docs(plan): bring production-bus task auto-registration into scope
gen event consume now emits a second file: an __events-<publisher>-
<event>.task.ts Payload task that delegates to the consumer's
container-resolved wrapped handler. The bind block also binds the
wrapped handler into the per-feature container by symbol so the
task can resolve it. With this, PayloadJobsEventBus is fully wired
end-to-end with no manual per-event task hand-write required.
Removes the corresponding Known Follow-up item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:28:25 +02:00
42fb3996d3 docs(plan): events and jobs implementation plan
56 bite-sized tasks across 9 phases implementing the spec at
docs/superpowers/specs/2026-05-08-events-and-jobs-design.md:
core-shared/jobs (IJobQueue + 2 impls), core-events package
(IEventBus + 2 impls), recording test helpers, two ESLint rules,
existing-feature anchor retrofit (5 features × 6 anchors), bindAll
bus/queue swap, three Plop generators with anchor-comment protocol,
end-to-end proof-of-life (sign-up → welcome email), and the full
documentation set (ADR-015 + new guide + AGENTS/CLAUDE/scaffolding
updates). Self-review caught and fixed one type-inconsistency
(3-arg subscribe) and explicitly deferred the production task-
registration gap. TDD-ordered with one commit per task.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:20:19 +02:00
cf41d11eff docs(spec): cross-feature events and background jobs design
Long-form design for a future ADR-015. Defines IEventBus (new
@repo/core-events) and IJobQueue (@repo/core-shared/jobs); the bind/swap
rules; the events/, events/handlers/, jobs/, integrations/cms/jobs/
folder layout; the on-<publisher>-<event-kebab>.handler.ts naming rule;
the span+capture sandwich extension with op: "event-handler" and
op: "job"; three new ESLint rules; RecordingEventBus and RecordingJobQueue
for tests; pnpm turbo gen event {publish|consume} and gen job generators
plus the // <gen:*> anchor-comment protocol; and the existing-feature
retrofit. Status: draft pending user review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:43:53 +02:00
72d5513916 chore(tsconfig): migrate off deprecated baseUrl
TypeScript 6 deprecates `baseUrl` in favor of resolving `paths`
relative to the file that defines them. Earlier commit (bde40e4)
silenced the warning via `ignoreDeprecations`; this commit does
the actual migration so the warning goes away on its merits.

Changes:

1. tsconfig.base.json: drop `baseUrl: "."`. Add an explicit `./`
   prefix to every `paths` entry so they resolve relative to the
   base config's location (the repo root) — same behavior as
   before, but no baseUrl needed. (TypeScript requires path entries
   to be relative or absolute when baseUrl is absent; bare paths
   like "packages/..." are not allowed.)
2. apps/web-next/tsconfig.json: drop baseUrl. The `@/*` mapping's
   target was already "./src/*" — relative-from-tsconfig-file is
   the new default, so no other change.
3. apps/cms/tsconfig.json: same — drop baseUrl, paths already use
   "./" prefix.
4. apps/storybook/tsconfig.json: same.
5. apps/web-tanstack/tsconfig.json: same.

Generator templates (turbo/generators/templates/feature/tsconfig.json.hbs)
already omit baseUrl, so future-generated features inherit the new
posture.

Reverts the ignoreDeprecations setting added in bde40e4 — no longer
needed.

Verified:
- pnpm typecheck 14/14
- pnpm lint 15/15
- pnpm test 26/26

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:44:00 +02:00
bde40e4ade chore(tsconfig): silence baseUrl-deprecation warning via ignoreDeprecations
TypeScript 6 deprecates `baseUrl` in favor of resolving `paths`
relative to the tsconfig file's directory. The repo's path mappings
in tsconfig.base.json currently rely on baseUrl: "." to resolve
"packages/<feature>/src/index.ts" entries against the repo root,
and four app tsconfigs (web-next, cms, web-tanstack, storybook) use
baseUrl: "." for their own "@/*" → "./src/*" mappings.

Migrating away from baseUrl is a separate cleanup — for now, set
"ignoreDeprecations": "6.0" in the base config so the warning
clears across every config that extends it (extends merges
compilerOptions). This is exactly what TypeScript's own docs
recommend for projects that intend to keep the option until 7.0.

Verified: pnpm typecheck 14/14, pnpm lint 15/15.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:41:20 +02:00
302b0b860c fix(turbo-gen): add local eslint config + package.json for generators dir
The IDE was reporting eslint errors on turbo/generators/config.ts and
on the generated package because there's no eslint config at the repo
root — when eslint walks up from turbo/generators/, it never finds
one. The CLI didn't surface this (eslint silently exited fine on
files it couldn't config), but the IDE eslint daemon did.

- turbo/generators/eslint.config.js extends @repo/core-eslint/base
  and ignores templates/** (the .hbs partials contain {{...}}
  placeholders that aren't valid TS — never lint them).
- turbo/generators/package.json declares "type": "module" so Node
  doesn't warn about CJS-vs-ESM ambiguity when loading the eslint
  config.

Verified end-to-end:
- `npx eslint . -p tsconfig.json` clean in turbo/generators/.
- `pnpm turbo gen feature --args widgets Widget widgets` →
  pnpm install → @repo/widgets passes lint, typecheck, and 25/25
  tests across 9 files.
- packages/widgets cleaned up before commit; not checked in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:39:54 +02:00
bec10fb171 fix(turbo-gen): annotate answers param + add tsconfig for generators dir
Two diagnostics from the IDE on turbo/generators/config.ts:

1. `Cannot find module '@turbo/gen'` — there was no tsconfig in
   turbo/generators/, so the IDE was opening config.ts in loose mode
   without proper module resolution context. Added a small
   tsconfig.json that extends tsconfig.base, sets NodeNext module
   resolution, and includes only ./**/*.ts (templates excluded).

2. `Parameter 'answers' implicitly has an 'any' type` — annotated
   the printNextSteps custom-action function param as
   `Record<string, unknown>` (Plop's runtime answers shape). The
   inner cast to the typed Answers shape was already in place.

`npx tsc --noEmit -p turbo/generators/tsconfig.json` is now clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 01:11:40 +02:00
019d4866a0 feat(turbo): turbo gen feature generator (Phase 1, single-entity)
Adds `pnpm turbo gen feature` to scaffold a Lazar-conformant feature
package matching the navigation reference shape: entity + Zod schema,
single use case (`get<Entity>`), controller, mock + Payload-stub real
repository (with span + capture), DI module/container/symbols, and tRPC
router with full BAD_REQUEST/NOT_FOUND error mapping. The generated
`bind-production.ts` and `bind-dev-seed.ts` compose the post-R44
`withSpan(tracer, opts, withCapture(logger, tags, factory(deps)))`
sandwich at bind time.

Verified by generating a sample `packages/example/` feature and running
`pnpm --filter @repo/example lint typecheck test` — all three pass
(9 test files, 25 tests). Cleaned up after verification so no example
package is committed.

Phase-1 limitations (documented in `docs/guides/scaffolding-a-feature.md`
and printed by the generator on success): no Payload CMS templates, no
React Query helpers, faker-driven factories left as stubs, single
entity / single use case, and aggregator wiring (core-api/root,
apps/web-next bindAll) is left as a manual checklist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 01:09:22 +02:00
b4ec48f058 docs(agents): bring agent-facing docs in line with the R44 fix
Six docs lagged after the post-merge R44 fix added withCapture +
reported-flag.ts. They mentioned withSpan only and described the
capture story as if it were inline in use-case / controller bodies.
This commit aligns them with what shipped.

CLAUDE.md (Key Conventions):
- "Spans applied at DI bind time" → "Spans + capture composed at DI
  bind time" with the withSpan(withCapture(factory)) sandwich and the
  outermost-span ordering note.
- "Capture at throw sites only" expanded to mention the
  __sentryReported flag, the three flag-checking sites (withCapture,
  SentryLogger, RecordingLogger), and where the helper lives.

AGENTS.md (Instrumentation conventions):
- Use case + controller wrapping example shows the full sandwich.
- Capture-rules table now explicitly says "via withCapture" for use
  cases and controllers, and "flag set, withCapture bails" for the
  bubbled cases.

packages/core-shared/AGENTS.md:
- "with-span.ts" entry split into a paired with-span + with-capture
  block, including the actual sandwich code.
- New entry for reported-flag.ts explaining the helper and why
  RecordingLogger inlines the check (boundary rule).
- Barrel re-export list updated.

docs/architecture/vertical-feature-spec.md (§16):
- The bind-production line now describes the withSpan(withCapture(...))
  sandwich, the outermost-span rationale, and the bubbled-error bail.

docs/architecture/dependency-flow.md (TRACER/LOGGER subsection):
- bindAll diagram updated: real repo line annotates inline calls; use
  case + controller lines show withSpan(withCapture(...)).

docs/guides/tdd-workflow.md (Asserting spans and captures):
- Direct-injection example shows the binder sandwich.
- Capture-assertion example explains the flag-bail behaviour and
  links to the new tests/r44-no-double-capture.test.ts e2e example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:54:23 +02:00
281c7ac941 docs(html): align trace-tree code background with existing code-bg palette
The .trace-tree blocks I added in §06 / §08 used var(--ink-2) (#5b4a3c —
medium brown), which clashed with the page's dedicated --code-bg
(#16110d, deep dark). Switched to --code-bg + --code-paper for text,
plus the same border + inset/drop shadow as pre.code, so the new blocks
read consistently with every other code block on the page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:48:15 +02:00
01707e801b docs(html): update §06 + §08 with verified layer-by-layer instrumentation usage
After the user pushed back on intent-vs-shipped state, I greped each layer
and updated both explainers to reflect actual code, not documented intent.

data-flow-explainer.html §06 — "Tracing & error capture":
- New "Where instrumentation actually lives" table — per-layer breakdown
  of inline span / inline capture / composed-via-wrapper, with a verifiable
  grep showing only repos have inline calls.
- New "The wrapper sandwich" code block showing the actual repo body
  shape next to the bind-time withSpan(withCapture(...)) composition.
- Capture-rules table refined to reflect the R44 fix that just landed:
  use cases capture business-rule errors and output-schema failures
  (not bubbled-from-repo); controllers capture safeParse failures
  (not bubbled-from-use-case); the __sentryReported flag is what makes
  this safe.
- Double-report-guard paragraph now mentions withCapture, SentryLogger,
  and RecordingLogger all check the flag (not just SentryLogger).

di-explainer.html §08 — "Instrumentation symbols":
- Wiring path updated from "withSpan(tracer, ...)" to
  "withSpan(withCapture(...))" to reflect the post-merge wiring.
- New "Two wrappers, applied as a sandwich" table comparing what each
  wrapper does and where it fires; closing note that repos can't use
  the wrapper because they own per-call attributes.

Also bundled: a 1-line aesthetic SVG noise tweak in di-explainer.html
(opacity='0.25', baseFrequency 0.85→0.95) that was sitting in the
working tree before this session — preserved across the Plan 10 merge
via stash/pop and now committed alongside the doc update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:43:42 +02:00
f0775d6ecc feat(instrumentation): close R44 gap — throw-site capture for use cases + controllers
Plan 10 documented R44 (capture at originating-throw layer) but only the
R43 repo leg was wired. captureException had zero call sites in any
controller or use-case body. This commit closes the gap.

Mechanism:
- Extract __sentryReported flag helpers into core-shared/instrumentation/
  reported-flag.ts. SentryLogger switches to importing them; RecordingLogger
  carries an inlined copy (tooling → core boundary disallows the import).
- Add withCapture(logger, tags, fn) higher-order wrapper paralleling
  withSpan. On throw: capture-with-tags, mark, re-throw. Bail if the flag
  was already set — covers the bubbled-from-repo case so each error
  surfaces in the logger exactly once with the inner-most layer's tags.
- Apply withSpan(withCapture(factory)) in every feature's bind-production
  and bind-dev-seed: auth (3 use cases × 3 controllers), blog (3×3),
  marketing-pages (2×2), navigation (1×1), media (3×3). Span is outermost
  so the errored span timing reflects the capture-and-rethrow.
- RecordingLogger.captureException now also honours the flag — test
  capture counts stay honest when both repo and outer layer wrap.

Tests:
- packages/core-shared/src/instrumentation/with-capture.test.ts —
  4 cases covering success, capture-on-throw, mark-on-capture, no-double
  via the flag.
- packages/blog/tests/r44-no-double-capture.test.ts — 3 cases: repo throw
  → 1 capture with repo tags; controller parse fail → 1 capture with
  controller tags; success → 0 captures.

Verification: pnpm test 26/26, pnpm lint 15/15, pnpm typecheck 14/14.

Docs: ADR-014 and the refactor log gain a "Post-merge follow-up" section
recording the gap, the fix, and the underlying lesson (don't describe
intent as shipped state — grep first).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:28:22 +02:00
1771be3034 Merge branch 'worktree-feature+instrumentation-sentry'
Some checks failed
CI / typecheck + lint + boundaries + test + build (push) Has been cancelled
CI / Playwright e2e (push) Has been cancelled
Sentry PII guard (R31) / pii-guard (push) Has been cancelled
CI / Storybook smoke tests (push) Has been cancelled
2026-05-07 20:45:03 +02:00
60967a630d docs(plan-10): finalize ADR-014 + tick refactor log
Replaces the ADR-014 stub with the full Accepted-status decision record:
context, the 7 numbered decisions (vendor-neutral interfaces, full-depth
instrumentation, throw-site capture, PII rules, three Sentry projects,
ESLint boundary, Recording* test pair), alternatives considered,
positive/negative consequences, and an "execution notes" section
covering the spec deviations and surprises encountered.

Updates the refactor log: ticks all 33 tasks, populates "Decisions
deviated from spec" (PII key list extended with ipaddress; spec
section appended as §16 not §10; vite.config skipped; HTML section
numbering kept conservative) and "Notable surprises" (apps needed
direct core-shared deps; deep subpath exports for dynamic import;
peerDependenciesMeta.optional for @sentry/node and @sentry/react;
pre-existing lint debt; allowlist patterns needed **/-prefix for
flat-config glob matching from sub-package cwd).

Final verification (run before commit):
- pnpm test  → 26/26 tasks pass
- pnpm lint  → 15/15 tasks pass (warnings-only)
- pnpm typecheck → 14/14 tasks pass
- R31 grep gate (sendDefaultPii: true) → clean, no matches

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:41:54 +02:00