--- name: improve-codebase-architecture description: Find deepening opportunities in this repo, informed by docs/glossary.md and the ADRs in docs/decisions/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make the codebase more testable and AI-navigable. Respects the conformance system, boundary rules, and the 21 ADRs that govern shape. --- # Improve Codebase Architecture Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability, scoped to what this template's existing rules permit. ## Glossary Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service" (we use that for DI ports specifically), or "boundary" (we use that for ESLint workspace-tag rules). Full definitions in [LANGUAGE.md](LANGUAGE.md). - **Module** — **in this repo defaults to "feature"** (`packages//`). The abstract definition (anything with interface + implementation) still applies at narrower scales — a use case, controller, repository/service port, binder, or core package can also be a module — but say "feature" whenever that's the scope. Reach for "module" only when comparing depth/leverage across scales. - **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, schemas, DI shape. Not just the TypeScript type signature. - **Implementation** — the code inside. - **Depth** — leverage at the interface: a lot of behaviour behind a small interface. **Deep** = high leverage. **Shallow** = interface nearly as complex as the implementation. - **Seam** — where an interface lives; a place behaviour can be altered without editing in place. In this repo seams take a concrete shape: `*.interface.ts` files, DI symbols, manifest declarations, and `` anchors. - **Adapter** — a concrete thing satisfying an interface at a seam. In this repo: `.repository.ts` (Payload real impl) vs `.repository.mock.ts` vs `Recording*` test doubles. - **Leverage** — what callers get from depth. - **Locality** — what maintainers get from depth: change, bugs, knowledge concentrated in one place. Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list): - **Deletion test**: imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. - **The interface is the test surface.** - **One adapter = hypothetical seam. Two adapters = real seam.** This skill is **informed by** the project's domain model and architecture decisions. Read [`docs/glossary.md`](../../../docs/glossary.md) for project vocabulary and the relevant ADR(s) in [`docs/decisions/`](../../../docs/decisions/) before proposing anything in their territory. ## Hard constraints (do not propose violations) These are settled decisions — propose deepening WITHIN them, never against them: - **Factory-function use cases & controllers** (ADR-012, ADR-013) — every use case is `(deps) => async (input) => output`; every controller is one verb-noun pair per file with a co-located `presenter`. - **Schemas in the use-case file** (ADR-013) — `xInputSchema`/`xOutputSchema` colocate with the factory; don't propose moving them to a separate module. - **Per-feature DI containers** (ADR-008) — don't propose a single global container. - **Five boundary tags + the dependency-direction matrix** (ADR-006, ADR-010) — features may depend only on `core` + `tooling`; cross-feature reactions go through `IEventBus` (ADR-015). - **Manifest-first ordering** (ADR-012, ADR-020) — new use cases land manifest → contracts → tests → impl; don't propose collapsing the steps. - **Brand-based conformance** — `Instrumented` / `Captured` / `Audited` are attached at DI bind time via `withSpan` / `withCapture` / `withAudit`; don't propose moving the wrapping elsewhere. - **Generator-first** — `pnpm turbo gen ` is the entry point for new features/events/jobs/realtime channels. Don't propose hand-rolled scaffolding. - **Conventional Commits** (CLAUDE.md Key Conventions) — any refactor lands as conventional-commit messages. - **Hybrid versioning** (ADR-021) — refactors that move code between feature packages have version + CHANGELOG implications. If a proposed deepening **would** violate an ADR, surface it explicitly with the ADR number and a "worth reopening because…" justification — but only if the friction is real enough. Most should be silently scoped out. ## Process ### 1. Explore Read [`docs/glossary.md`](../../../docs/glossary.md) and any ADRs in the area you're touching first. Then walk the codebase noting friction. The primary unit of attention is the **feature** (`packages//`); narrower units (use cases, controllers, repositories) get attention when the friction lives at that scale. - Where does understanding one concept require bouncing between many small files across `entities/`, `application/`, `infrastructure/`, `interface-adapters/` inside a single feature? - Where is a feature **shallow** — its public surface (the `.` + `./ui` + `./api` exports) nearly as complex as its internal implementation? Or where inside a feature is a smaller unit shallow: - A `service.interface.ts` with one method that wraps a single repository call. - A `presenter` that's just `(x) => x`. - A controller body that's just `useCase(parsed.data)` with no transformation. - A repository wrapping another repository. - A use case wrapping another use case. - Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)? - Where do tightly-coupled features leak across their seams? (e.g. a feature reaches into another feature's internals via deep import — though ESLint should catch this.) - Which parts are untested, or hard to test through their current interface? `pnpm coverage:diff` and `pnpm fallow` surface candidates. Useful exploration shortcuts in this repo: - `pnpm fallow` — dead exports, dupes, complexity hotspots, circular deps - `pnpm fallow:audit` — the AI-change audit; surfaces drift across recent edits - `git log --oneline --follow -- ` — change frequency is a depth signal - `cat packages//src/feature.manifest.ts` — declared surface of a feature - `pnpm turbo boundaries` — workspace dependency graph Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. ### 2. Present candidates Present a numbered list of deepening opportunities. For each candidate: - **Files** — which files / features / smaller units are involved (give exact paths) - **Problem** — why the current architecture is causing friction - **Solution** — plain English description of what would change - **Benefits** — explained in terms of **locality** and **leverage**, plus how tests would improve - **ADR impact** — any ADR this touches. If the proposed change conflicts with a current ADR, mark it explicitly: _"contradicts ADR-NNN — worth reopening because…"_ (only when the friction warrants it). - **Manifest impact** — if the change moves use cases / events / jobs / channels across features, the `feature.manifest.ts` of each feature involved will need an update; flag this so the user knows the conformance gates will require manifest edits before code edits (manifest-first ordering). **Use [`docs/glossary.md`](../../../docs/glossary.md) vocabulary for the domain (use case, manifest, slice, feature, etc.) and [LANGUAGE.md](LANGUAGE.md) vocabulary for the architecture (module, seam, adapter, depth, leverage, locality).** Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?" ### 3. Grilling loop Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. Side effects happen inline as decisions crystallize: - **Naming a deepened module after a concept not in [`docs/glossary.md`](../../../docs/glossary.md)?** Add the term to the glossary right there — same discipline as the `grill-with-docs` skill. Pick the appropriate section (Packages / Architecture layers / Feature building blocks / Conformance / Cross-feature / Instrumentation / Workflow / Releasing). - **Sharpening a fuzzy term during the conversation?** Update the glossary inline. - **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as ADR-NNN so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future agent to avoid re-suggesting the same thing. The next ADR number is `001 + max(existing)` (currently `ADR-022`). Our ADR shape: `Context → Decision → Alternatives considered → Consequences → Related`. - **Refactor will move code between feature packages?** Flag the release-please impact: both affected packages will bump versions on the next release PR. - **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md). ## Related skills - `grill-with-docs` (`.claude/skills/grill-with-docs/`) — stress-tests plans against ADRs + glossary + manifests; share the same glossary-update discipline. - `to-prd` — if the deepening is large enough to merit a multi-task epic, materialize it as a PRD.