docs(work): re-decompose binder-wrap-helper into vertical-slice stories

Re-decomposes the approved binder-wrap-helper PRD after the previous
8-story attempt (dropped in 71c04f5) sliced horizontally — its
"write test" / "write impl" splits would have left intermediate
commits red. The new shape obeys slice = task = PR = commit:

- 01-wire-use-case-helper — 1 task: ship the wireUseCase helper
  (factory composition + container bind + brand stack) with its
  unit tests and barrel export in one green commit. Blocks 02 + 03.
- 02-migrate-feature-binders — 5 tasks, one per feature
  (auth, blog, media, marketing-pages, navigation). Each task
  migrates that feature's bind-production.ts + bind-dev-seed.ts
  together and keeps all gates green.
- 03-update-generator-templates — 1 task: update the feature
  generator's bind-*.hbs so future scaffolds emit wireUseCase
  directly instead of inline withSpan + withCapture.

Output of the now-fixed sandcastle decompose dispatch — depends on
the completionSignal + maxIterations wiring (eadbb7e, 26aa97f).
This commit is contained in:
2026-05-13 19:17:10 +02:00
parent eadbb7ebd9
commit 317ec518aa
5 changed files with 217 additions and 3 deletions

View File

@@ -0,0 +1,43 @@
---
id: 01-wire-use-case-helper
epic: 2026-05-13-binder-wrap-helper
title: Introduce wireUseCase helper in core-shared
type: technical-story
status: in-progress
feature: core-shared
depends-on: []
blocks: [02-migrate-feature-binders, 03-update-generator-templates]
---
## Goal
Add `wire-use-case.ts` + `wire-use-case.test.ts` to `packages/core-shared/src/conformance/` and export the helper from the `@repo/core-shared/conformance` barrel. The helper encapsulates `withSpan(withCapture(withAudit?(factory(deps))))` composition and performs the container bind step, so callers pass options and get back a brand-stacked wired value.
## Why
Every feature binder currently inlines the same `withSpan + withCapture` wrapping per use case — 3079 lines of mechanical boilerplate per binder pair. A single helper in core-shared eliminates the structural clone groups and makes adjusting the wrapping shape a one-file change.
## Done when
- `packages/core-shared/src/conformance/wire-use-case.ts` exists and is exported from `packages/core-shared/src/conformance/index.ts`.
- `wire-use-case.test.ts` covers: no-audit path (Instrumented + Captured brands present), audit path (Instrumented + Captured + Audited brands present), span-name derivation (`<feature>.<name>`), capture-tag structure (`{ feature, layer, name }`), container binding (symbol resolves to wired value), idempotent re-bind (unbind + bind when symbol already bound).
- `pnpm typecheck && pnpm test && pnpm lint && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass.
- `wire-use-case.ts` reaches 100% statement/branch coverage (file is small; exhaustive test coverage is achievable).
## In scope
- `packages/core-shared/src/conformance/wire-use-case.ts` — the helper and its exported types.
- `packages/core-shared/src/conformance/wire-use-case.test.ts` — unit tests using `RecordingTracer` / `RecordingLogger` / `RecordingAuditLog` (or equivalent mocks from core-shared/core-audit).
- Export line added to `packages/core-shared/src/conformance/index.ts`.
- No changes to `withSpan`, `withCapture`, or `withAudit` internals — the helper composes them as-is.
## Out of scope
- Feature binder changes (Story 02).
- Generator template changes (Story 03).
- `wireController` peer — the `layer` discriminator in `wireUseCase` covers controllers too if the shapes converge; a separate peer is a follow-up if needed.
- Repository or service binding patterns.
## Tasks
- [ ] Add `wire-use-case.ts` + `wire-use-case.test.ts` to `packages/core-shared/src/conformance/` and export from the conformance index — implement the helper (options object: `container`, `symbol`, `factory`, `deps`, `feature`, `layer`, `name`, `tracer`, `logger`, optional `auditLog`) with full unit tests covering both audit and no-audit paths, brand presence assertions via `isInstrumented` / `isCaptured` / `isAudited`, container binding, and idempotent re-bind; all gates pass on this single commit.

View File

@@ -0,0 +1,48 @@
---
id: 02-migrate-feature-binders
epic: 2026-05-13-binder-wrap-helper
title: Migrate all five feature binders to wireUseCase
type: technical-story
status: todo
feature: auth, blog, media, marketing-pages, navigation
depends-on: [01-wire-use-case-helper]
blocks: []
---
## Goal
Replace every inline `withSpan + withCapture (+ withAudit)` block in each feature's `bind-production.ts` and `bind-dev-seed.ts` with a call to `wireUseCase`. Existing binder-level and integration tests must continue to pass unchanged. Each feature migrates in its own commit.
## Why
The five inline wrapping clone groups are the top source of duplication reported by `pnpm fallow`. Moving to `wireUseCase` eliminates all five clone groups and leaves each binder as decision content only (which adapter, which mode, which symbol).
## Done when
- All 10 binder files (`auth ×2`, `blog ×2`, `media ×2`, `marketing-pages ×2`, `navigation ×2`) call `wireUseCase` for every use case binding; no inline `withSpan + withCapture` blocks remain for use cases.
- `assertFeatureConformance` at the tail of each `bind-production.ts` continues to accept all wired use cases (brands present, manifest entries match).
- `pnpm typecheck && pnpm test && pnpm lint && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` pass after each per-feature commit.
- `pnpm fallow dupes` no longer shows the five binder-pair clone groups in its output.
## In scope
- `packages/auth/src/di/bind-production.ts` + `bind-dev-seed.ts` — 3 use cases (signIn, signUp, signOut).
- `packages/blog/src/di/bind-production.ts` + `bind-dev-seed.ts` — 3 use cases (getArticles, getArticleBySlug, createArticle).
- `packages/media/src/di/bind-production.ts` + `bind-dev-seed.ts` — 3 use cases (getMedia, listMedia, deleteMedia).
- `packages/marketing-pages/src/di/bind-production.ts` + `bind-dev-seed.ts` — 2 use cases (getPageBySlug, getSiteSettings).
- `packages/navigation/src/di/bind-production.ts` + `bind-dev-seed.ts` — 1 use case (getHeader).
- If any binder's existing tests assert on internal wrapping shape rather than observable behaviour, refactor those assertions to test through the helper's contract (same commit as the binder change).
## Out of scope
- Controller bindings — covered by the helper per the PRD but deferred to a follow-up if scope is a concern.
- Repository and service bindings — stay as direct `.toConstantValue()` calls.
- Apps' `bindAll()` dispatcher — untouched.
## Tasks
- [ ] Migrate `auth` binders (`bind-production.ts` + `bind-dev-seed.ts`) to `wireUseCase` for all 3 use cases (signIn, signUp, signOut); all gates pass on this commit.
- [ ] Migrate `blog` binders to `wireUseCase` for all 3 use cases (getArticles, getArticleBySlug, createArticle); all gates pass on this commit.
- [ ] Migrate `media` binders to `wireUseCase` for all 3 use cases (getMedia, listMedia, deleteMedia); all gates pass on this commit.
- [ ] Migrate `marketing-pages` binders to `wireUseCase` for both use cases (getPageBySlug, getSiteSettings); all gates pass on this commit.
- [ ] Migrate `navigation` binders to `wireUseCase` for the single use case (getHeader); all gates pass on this commit; verify `pnpm fallow dupes` no longer surfaces the five binder-pair clone groups.

View File

@@ -0,0 +1,39 @@
---
id: 03-update-generator-templates
epic: 2026-05-13-binder-wrap-helper
title: Update feature generator templates to emit wireUseCase call shape
type: technical-story
status: todo
feature: core-shared
depends-on: [01-wire-use-case-helper]
blocks: []
---
## Goal
Update `turbo/generators/templates/feature/src/di/bind-production.ts.hbs` and `bind-dev-seed.ts.hbs` so that `pnpm turbo gen feature` scaffolds new features with `wireUseCase` calls instead of the longhand inline `withSpan + withCapture` form.
## Why
New features scaffolded after the migration would otherwise drift back to the inline form, re-introducing the clone group. The generator is the canonical source for scaffolded binder shape; updating it closes the loop.
## Done when
- `turbo/generators/templates/feature/src/di/bind-production.ts.hbs` emits `wireUseCase(...)` calls (not inline `withSpan + withCapture`) for the placeholder use case.
- `turbo/generators/templates/feature/src/di/bind-dev-seed.ts.hbs` emits `wireUseCase(...)` calls for the placeholder use case.
- Running `pnpm turbo gen feature testfeature` produces binder files that call `wireUseCase` and pass `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` (or the generated feature is cleaned up after verification).
## In scope
- `turbo/generators/templates/feature/src/di/bind-production.ts.hbs`
- `turbo/generators/templates/feature/src/di/bind-dev-seed.ts.hbs`
- Any supporting type imports in the templates that change as a result (e.g. dropping `ProductionUseCase` intermediate type if the helper absorbs it).
## Out of scope
- Generator templates for events, jobs, realtime, or core-packages — those do not produce binder files with use-case wrapping.
- Runtime behaviour changes — the generated output must be semantically equivalent to the old inline form.
## Tasks
- [ ] Update `bind-production.ts.hbs` and `bind-dev-seed.ts.hbs` in `turbo/generators/templates/feature/src/di/` to emit `wireUseCase` calls; verify a test-scaffold of a new feature produces correct binder output and all gates pass on this commit.

View File

@@ -0,0 +1,23 @@
---
id: 2026-05-13-binder-wrap-helper
prd: docs/work/prds/2026-05-13-binder-wrap-helper.prd.md
title: Collapse binder duplication via wireUseCase helper
type: epic
status: in-progress
features: [core-shared, auth, blog, media, marketing-pages, navigation]
created: 2026-05-13
---
## Goal
Introduce a `wireUseCase(...)` helper in `@repo/core-shared/conformance/` that encapsulates the `withSpan + withCapture (+ optional withAudit)` composition. Refactor all five features' binders to call the helper. Update the feature generator templates to emit the new call shape by default.
## Why
Five of `pnpm fallow`'s top-ten clone groups come from binder pairs across features. The inline wrapping runs 3079 duplicated lines per binder pair. The helper becomes the single source of truth for the wrapping shape; per-feature binders shrink to their decision content plus a list of `wireUseCase` calls.
## Stories
- [ ] [01 — Introduce `wireUseCase` helper in core-shared](01-wire-use-case-helper/_story.md)
- [ ] [02 — Migrate all five feature binders to `wireUseCase`](02-migrate-feature-binders/_story.md)
- [ ] [03 — Update generator templates to emit `wireUseCase` call shape](03-update-generator-templates/_story.md)

View File

@@ -1,6 +1,44 @@
{
"updated_at": "2026-05-13T16:41:39.179Z",
"updated_at": "2026-05-13T17:17:12.493Z",
"epics": {
"2026-05-13-binder-wrap-helper": {
"status": "in-progress",
"title": "Collapse binder duplication via wireUseCase helper",
"prd": "docs/work/prds/2026-05-13-binder-wrap-helper.prd.md",
"stories": {
"01-wire-use-case-helper": {
"status": "in-progress",
"title": "Introduce wireUseCase helper in core-shared",
"ac_total": 1,
"ac_completed": 0,
"depends_on": [],
"blocks": [
"02-migrate-feature-binders",
"03-update-generator-templates"
]
},
"02-migrate-feature-binders": {
"status": "todo",
"title": "Migrate all five feature binders to wireUseCase",
"ac_total": 5,
"ac_completed": 0,
"depends_on": [
"01-wire-use-case-helper"
],
"blocks": []
},
"03-update-generator-templates": {
"status": "todo",
"title": "Update feature generator templates to emit wireUseCase call shape",
"ac_total": 1,
"ac_completed": 0,
"depends_on": [
"01-wire-use-case-helper"
],
"blocks": []
}
}
},
"template-reset-v1": {
"status": "done",
"title": "Template reset — strip setup-process noise + archive history",
@@ -17,7 +55,30 @@
}
}
},
"ready": [],
"blocked": [],
"ready": [
{
"epic": "2026-05-13-binder-wrap-helper",
"story": "01-wire-use-case-helper",
"title": "Introduce wireUseCase helper in core-shared"
}
],
"blocked": [
{
"epic": "2026-05-13-binder-wrap-helper",
"story": "02-migrate-feature-binders",
"title": "Migrate all five feature binders to wireUseCase",
"waiting_on": [
"2026-05-13-binder-wrap-helper/01-wire-use-case-helper"
]
},
{
"epic": "2026-05-13-binder-wrap-helper",
"story": "03-update-generator-templates",
"title": "Update feature generator templates to emit wireUseCase call shape",
"waiting_on": [
"2026-05-13-binder-wrap-helper/01-wire-use-case-helper"
]
}
],
"needs_prd_ship": []
}