Commit Graph

19 Commits

Author SHA1 Message Date
ee069574ba fix: address final-review polish (docs + comments + test coverage)
- AGENTS.md bind-production code block: shows the slim default state
  (no @repo/core-events / @repo/core-realtime imports, BindProductionContext
  with no generic args) with a comment pointing to the scaffold workflow.
  The previous block showed a fully-wired post-scaffold state without
  signaling that none of those packages exist in main.
- bind-protocols.test.ts: top-of-file comment clarifies what these tests
  actually verify (protocol shapes have required methods) vs what the
  spec text might suggest (full assignability of optional packages'
  interfaces — that's verified by the e2e reconstruction tests, not here).
- core-package-generator.md: drops two stale "Until Phases 3-6 land"
  parentheticals — the phases shipped.
- config.test.ts: extends the choices assertion to cover all 4 names
  (realtime, events, trpc, ui).
- marketing-pages bind-* comments: reverse the inverted optional/required
  language. queue (IJobQueue) is from core-shared and always present;
  bus is the optional one (from @repo/core-events when scaffolded).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:01:40 +02:00
57b2ff5191 refactor: remove core-realtime from main (scaffoldable via gen core-package realtime) 2026-05-09 13:45:52 +02:00
ca2e7d8c10 fix: address Phase 1 spec review findings
- di-explainer.html: update stale JS data strings from positional-arg
  form (bindAllProduction(config), bindProductionBlog(config,tracer,...))
  to ctx-arg form (bindAllProduction(deps), bindProductionBlog(ctx)) with
  a note on ctx shape.
- auth/sign-up.use-case: change bus param from IEventBus to
  EventBusProtocol|undefined; guard bus.publish with if(bus) so the use
  case is safe when core-events is absent (Phase 3+).
- auth/bind-production + bind-dev-seed: drop as IEventBus cast and unused
  IEventBus import; ctx.bus is now passed directly (typed as
  EventBusProtocol|undefined).
- marketing-pages/bind-production + bind-dev-seed: drop as IJobQueue cast
  and unused IJobQueue import; wrap event-handler DI block in if(queue)
  guard so the handler is only bound when core-jobs is wired.
- core-shared/src/index.ts: add `export * from "./di"` as the plan
  specified (subpath export alone is no longer the only access path).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 13:02:36 +02:00
fe6c41b69d refactor(marketing-pages): binders take BindContext arg 2026-05-09 12:42:29 +02:00
579c5911fd feat(marketing-pages): bind binders accept (realtime, realtimeRegistry) params
Extend bindProductionMarketingPages and bindDevSeedMarketingPages to 7-arg signatures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 22:41:13 +02:00
2939b94302 chore(marketing-pages): add // <gen:realtime-*> anchor comments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 22:06:45 +02:00
7e844c646d feat(marketing-pages): sendWelcomeEmail job + handler enqueues it
sendWelcomeEmailJob takes IMailerService, validates the typed input
(userId + email), and delegates to mailer.sendWelcome. The
onAuthUserSignedUpHandler now takes IJobQueue and enqueues
"marketing-pages.send-welcome-email" with the event payload.

Both binders bind a RecordingMailerService at the IMailerService
symbol (production placeholder until a real adapter ships) and pass
mailer / queue into the wrapped factories. Dev-seed additionally
queue.register()s the slug so the in-memory queue dispatches via
the wrapped job; production relies on the generated Payload event-task
to resolve the wrapped handler from the container.
2026-05-08 16:35:45 +02:00
6b57a34c0c feat(marketing-pages): subscribe to auth.user.signed-up
Generated handler + Payload event-task via gen event consume,
threaded through symbols / both binders / cms re-export at the
configured anchors. bus.subscribe wires the in-memory delivery in
dev-seed; the __events.auth.user.signed-up.marketing-pages Payload
task closes the production-bus loop.

Also fixes two generator-level issues found during Phase 8:
- Drop publisher prompt's `when` clause so --args can supply the
  4th positional argument (Plop limitation: --args cannot bypass
  conditional prompts). Validate runs only in consume mode.
- Switch event-task.ts.hbs from TaskConfig<"slug-string"> to
  TaskConfig<{ input: ...; output: object }> since runtime-generated
  event slugs are not keys of TypedJobs['tasks'].
2026-05-08 16:33:14 +02:00
73f6db54b3 feat(marketing-pages): IMailerService interface (proof-of-life) 2026-05-08 16:23:00 +02:00
4e77134ff6 feat(marketing-pages): bind binders accept (bus, queue) params 2026-05-08 16:05:49 +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
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
955a763c66 feat(eslint+ci): R40 boundary rule for @sentry/* + R31 sendDefaultPii grep gate
Adds two flat-config blocks to core-eslint/base.js: (1) repo-wide
no-restricted-imports for @sentry/* with the R40 message, (2) an
allowlist override for the only paths permitted to import the Sentry
SDK directly — core-shared/instrumentation/sentry/**, the bind-sentry
DI files, the no-sentry test guards, and apps' instrumentation* /
next.config / vite.config / sentry.*.config files. Patterns use
**/-prefix so they match whether ESLint runs from the repo root or
from inside a sub-package.

Also adds the standard `argsIgnorePattern: "^_"` config (used
throughout the repo) and a Node-globals override for *.mjs/*.cjs/*.js
and *.config.{ts,tsx} so withSentryConfig in next.config.mjs lints
clean. Required adding `globals` as a core-eslint dep.

Adds .github/workflows/sentry-pii-guard.yml — a lightweight CI step
that fails any PR introducing `sendDefaultPii: true` (R31). Excludes
node_modules / dist / .next / .turbo from the grep so vendored SDK
JSDoc examples don't false-positive.

Pre-existing lint nits cleared as part of getting `pnpm lint` green:
- core-testing define-contract-suite.test.ts: void the unused
  receivedTracer (mirrors the next test's pattern)
- marketing-pages bind-dev-seed.ts: drop unused MockSiteSettingsRepository
  import
- marketing-pages get-site-settings.use-case.ts: drop the now-redundant
  eslint-disable for `_input`

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:31:15 +02:00
404cb07e79 feat(marketing-pages): wire instrumentation — site-settings + pages spans + use-case/controller withSpan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 00:26:45 +02:00
10479c4d55 feat(features): add bind-dev-seed binders for auth/marketing-pages/navigation/media
Mirrors the canonical blog pattern landed earlier on this branch.

Per feature:
- src/__seeds__/dev.ts — lazy buildDev<Entities>() function using the
  feature's existing factory for sensible defaults
- src/di/bind-dev-seed.ts — bindDevSeed<Feature>() async function that
  rebinds the repo symbol(s) to a populated MockXRepository via
  .toConstantValue
- src/di/bind-dev-seed.test.ts — 3+ tests per feature (populates,
  reachable by id/slug, idempotent)
- package.json — adds ./di/bind-dev-seed subpath export

Tests + use cases continue to construct mocks directly; the seed never
runs from a *.test.ts path. App boot wiring (USE_DEV_SEED env branch)
follows in a separate commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:04:33 +02:00
353a41b244 refactor(marketing-pages): factory-style use cases + per-use-case controllers
- Use cases (get-page-by-slug, get-site-settings) → factory functions with I*UseCase aliases
- Controllers split: pages.controller.ts → 2 single-responsibility files
- DI module wires factories with .toDynamicValue()
- tRPC router resolves controllers via container

Refactor log: §2, §3, §4.1, §4.2, §5.1
Spec: §6.3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 00:14:27 +02:00
aa325f91cc refactor(features): rename mock/payload/interface files per Lazar pattern
Convention now: <name>.repository.{ts,mock.ts,interface.ts}.
Renames .mock prefix to .mock suffix; drops .payload prefix from real
impls (canonical name = real impl); dot-separates the .repository
qualifier in interface filenames. Class names follow suit:
PayloadXRepository → XRepository; Mock* unchanged.

Refactor log: §1, §3
Spec: §9.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 23:50:01 +02:00
02aee1ec23 feat(marketing-pages): add bindProductionMarketingPages(config) DI helper 2026-05-05 08:50:30 +02:00
875bae3fde feat(marketing-pages): add per-feature InversifyJS container 2026-05-05 08:28:50 +02:00