Commit Graph

505 Commits

Author SHA1 Message Date
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
c1b8a7e434 feat(auth): signUp publishes userSignedUpEvent
signUpUseCase now takes an IEventBus and publishes userSignedUpEvent
after creating the user (synthesizing email from username since auth
is username-based). Use case mocks-default in module.ts get a fresh
InMemoryEventBus per resolution; bind-production / bind-dev-seed wire
the shared bus passed by bindAll. Tests updated to inject
RecordingEventBus, including a new test that asserts publish on
success and silence on failure.
2026-05-08 16:28:21 +02:00
7f63adf740 feat(auth): userSignedUpEvent contract 2026-05-08 16:24:12 +02:00
1c13b757ed feat(marketing-pages): RecordingMailerService 2026-05-08 16:23:32 +02:00
73f6db54b3 feat(marketing-pages): IMailerService interface (proof-of-life) 2026-05-08 16:23:00 +02:00
d5f230e07f feat(turbo-gen): job generator
Adds `pnpm turbo gen job <feature> <slug> <void|typed>` which scaffolds
the factory + test + Payload TaskConfig, then modifies the feature's
job-symbol, job-bind, and cms job-task anchors to wire the wrapped
job into the per-feature container. Also registers a custom Handlebars
`eq` helper used by the void/typed branch in the job templates.
2026-05-08 16:20:33 +02:00
8e081c2e97 feat(turbo-gen): templates for gen job 2026-05-08 16:18:50 +02:00
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