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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Inserts a new §06 "Tracing & error capture" between the existing
"Tradeoffs by part" (§05) and the verdict (which moves to §07).
Includes the trace tree (HTTP → tRPC → controller → use-case → repo
→ Payload), capture-rules table (where captureException fires per
layer + the no-capture rule for defineErrorMiddleware), the
__sentryReported double-report guard explanation, and the R31–R38
PII rule list. Adds matching CSS (.trace-tree / .capture-rules /
.pii-rules) tuned to the existing dark-paper palette and Fraunces
heading font. Updates the contents nav from 6 → 7 columns.
di-explainer.html gets a new §08 "Instrumentation symbols" with cards
for TRACER and LOGGER, the bindAll() Rule-0 wiring path, and a note
on why feature containers also bind the symbols. Two-column grid
collapses to one on narrow viewports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds "Asserting spans and captures" section to tdd-workflow.md with
RecordingTracer/Logger usage and inline withSpan wrapping pattern for
direct-injection tests. Adds R49/R50 section to testing-strategy.md
covering the no-sentry guard, contract suite span assertions, and
RecordingTracer/Logger field reference. Adds "TRACER / LOGGER (Plan 10)"
subsection to dependency-flow.md showing the bindAll → feature-container
wiring path. Adds an "src/instrumentation/" section to core-shared/AGENTS.md
documenting the two interfaces, three impl pairs, withSpan helper, scrubbers,
both Next.js + Vite/React init helpers, and the subpath exports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the seven Plan 10 conventions to CLAUDE.md (interfaces in core-shared,
spans at bind time, throw-site capture, PII rules, three Sentry projects,
orthogonal binding). Adds an "Instrumentation conventions" section to
AGENTS.md with repo constructor/method patterns, capture-rules table,
boundary allowlist, and test rules. Appends §16 "Instrumentation & error
capture" to vertical-feature-spec.md (the spec already has 15 sections,
so appending rather than slotting in as §10 to avoid renumbering).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Adds initSentryServerNode + initSentryClientReact to core-shared
(Vite/non-Next variants of the existing init helpers — same R31/R32/R33
posture, R34/R35/R37 replay defaults). Extends no-sentry.ts to mock
@sentry/node + @sentry/react. Wires the web-tanstack server/client
instrumentation entry hooks and adds the R38 PII test.
Spec deviation: web-tanstack has no vite.config.ts yet (placeholder app
per its package.json). The @sentry/vite-plugin dep is added but unused
until the TanStack Start build is wired in a later plan. A minimal
src/vite-env.d.ts shims ImportMetaEnv for the client entry until the
full Vite types land.
@sentry/node and @sentry/react are added to core-shared as optional
peerDependencies so feature packages don't transitively pull them in;
they're also devDependencies of core-shared for typecheck/test runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/cms/instrumentation.ts (server-only — Payload admin client DSN
is out-of-scope per spec §8). Wraps the Payload-wrapped next config with
withSentryConfig. Adds the R38 PII scrubber test. Required adding
@repo/core-shared as a direct dep of cms (was only transitive before).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/web-next/instrumentation.ts (server) and instrumentation-client.ts
(browser) hooks, wraps next.config.mjs with withSentryConfig (R52), and adds
the R38 per-app PII scrubber smoke test.
Spec deviation: extend PII_KEY_SUBSTRINGS with "ipaddress" so keys like
ipAddress trigger key-level redaction (tighter posture than the spec's
substring list; existing scrub.test.ts still passes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends bind-production.test.ts with a vi.mock for @repo/core-shared/instrumentation
that wraps bindSentryInstrumentation and bindNoopInstrumentation in vi.fn (so call
counts are observable while real implementations still run). Adds 4 orthogonality
tests covering: DSN absent + production env, DSN set + dev env, Sentry + dev seed,
Noop + production — verifying instrumentation choice is independent of repo mode.
Total: 6 original + 4 new = 10 passing tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces resolveInstrumentation() in apps/web-next/src/server/bind-production.ts:
reads WEB_NEXT_SENTRY_DSN; if set, calls bindSentryInstrumentation; else Noop.
Both bindAllProduction and bindAllDevSeed call resolveInstrumentation() at the
top. Exports __resetBindStateForTests() and __getInstrumentationForTests() for
test helpers. Adds inversify + reflect-metadata as direct dependencies of web-next.
Existing 6 tests still pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds DI binder functions for instrumentation. bindNoopInstrumentation binds
NoopTracer + NoopLogger; bindSentryInstrumentation calls initSentryServer then
binds SentryTracer + SentryLogger. Both are idempotent (unbind-first). Adds
inversify + reflect-metadata as devDependencies so container tests compile.
Re-exports from instrumentation barrel. 6 new tests (3+3).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>