feat(skills): improve-codebase-architecture skill adapted for template-vertical

Adapts mattpocock/skills/engineering/improve-codebase-architecture to
this repo. Four files at .claude/skills/improve-codebase-architecture/:

  SKILL.md (104 lines):
    - Explore -> Present candidates -> Grilling loop process
    - "Hard constraints (do not propose violations)" section
      enumerating ADRs 006/008/010/012/013/014/015/017/020/021 that
      bound the design space
    - Repointed at docs/glossary.md (not CONTEXT.md) and
      docs/decisions/ (not docs/adr/)
    - Exploration shortcuts specific to this repo: pnpm fallow,
      pnpm coverage:diff, feature.manifest.ts, pnpm turbo boundaries
    - Grilling loop side-effects target the right glossary section
      and the next available ADR number (currently 022)

  DEEPENING.md (93 lines):
    - 4 dependency categories mapped to this repo's reality:
        Cat 1 (in-process) -> entities/use-cases/presenters
        Cat 2 (local-substitutable) -> our existing real + mock
          adapter pattern (every port has both; mocks ARE stand-ins)
        Cat 3 (remote but owned) -> cross-feature events via
          IEventBus (E0/E1 rules)
        Cat 4 (true external) -> Payload, Sentry/OTel, socket.io
          (each constrained to its vendor-isolation seam by ADR)
    - Seam discipline section recognises DI symbols + manifest entries
      as concrete seams alongside .interface.ts files
    - Testing strategy: replace not layer (matches ADR-020 L0 + L1)
    - Conformance check command list at the end (typecheck, lint,
      test --coverage, conformance, fallow:audit, coverage:diff)

  INTERFACE-DESIGN.md (66 lines):
    - Parallel sub-agent "Design It Twice" pattern preserved
    - Every sub-agent brief MUST include glossary terms + ADR
      constraints + manifest awareness
    - Output items extended with "Manifest + binder impact" and
      "ADR conflicts (if any)"
    - Comparison axes include conformance impact + coverage delta
    - Cross-feature moves flag release-please version-bump
      implications (per ADR-021 commit-path targeting)

  LANGUAGE.md (79 lines):
    - Matt's 7 abstract terms preserved (module, interface,
      implementation, depth, seam, adapter, leverage, locality)
    - New "Mapping to this repo's identifiers" table — abstract
      term -> concrete file shape (e.g. seam -> *.interface.ts +
      DI symbol + manifest entry + <gen:*> anchor)
    - Rejected framings extended with our reserved meanings
      ("boundary" stays the ESLint workspace-tag term; "service"
      stays the DI port term)

Per user follow-up: vocabulary anchored so that "module" defaults
to "feature" in this repo (since features are our primary unit of
organisation). Abstract refactor sense survives only when the cross-
scale abstraction is the point. Glossary.md updated:
  - "Feature" entry adds the "module = feature in refactor sense"
    cross-link
  - New "Architecture refactor vocabulary" section with 9 terms
    (Module, Interface (refactor sense), Implementation, Depth,
    Seam, Adapter, Leverage, Locality, Deletion test, Deepening)
    — all framed so feature is the primary instance
  - Flagged ambiguities entry for "module" rewritten to capture the
    three coexisting senses (workspace package / Node ESM / refactor
    vocabulary defaulting to feature); new entries for "seam" and
    "adapter" to prevent drift with the existing "boundary" / "service"
    / "scope" reservations

Hooks updated:
  - session-start.sh skills line lists the new skill
  - prompt-context.sh adds a 10th keyword group firing on
    refactor / deepening / shallow / architecture / seam / adapter /
    interface design / design it twice — inject points at SKILL.md
    + summarises the vocabulary and hard constraints

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 17:30:59 +02:00
parent 769548c186
commit c85f96c62e
7 changed files with 391 additions and 4 deletions

View File

@@ -0,0 +1,78 @@
# Language
Shared vocabulary for every suggestion this skill makes. Use these terms exactly — don't substitute "component," "service" (we use that narrowly for DI ports), "API," or "boundary" (overloaded with our workspace-tag enforcement). Consistent language is the whole point.
This vocabulary is foundational for the skill's reasoning. The project's domain vocabulary lives in [`docs/glossary.md`](../../../docs/glossary.md) — terms like _use case_, _manifest_, _slice_, _binder_, _brand_, _conformance band_, _coverage layer_. Both vocabularies are in scope when proposing deepenings; see the "Mapping to this repo's identifiers" section below for how the abstract terms here land on concrete file shapes.
## Terms
**Module****in this repo, "module" defaults to "feature"** (`packages/<name>/`). The abstract definition (anything with an interface + implementation) still applies at narrower scales — a use case, controller, repository/service port, or binder can also be a module — but **whenever the refactor scope is "the whole thing", say feature**. Reach for "module" only when the abstraction across scales actually matters (e.g., comparing how a use case's depth differs from its containing feature's depth).
_Avoid_: unit, component, service (we use "service" for DI ports specifically).
**Interface**
Everything a caller must know to use the module correctly. Includes the type signature, but also invariants, ordering constraints, error modes, required configuration, performance characteristics, **manifest declarations**, and **DI symbol contract**.
_Avoid_: API, signature (too narrow — those refer only to the type-level surface).
**Implementation**
What's inside a module — its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Payload-backed repository) or a large adapter with a small implementation (an in-memory mock). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
**Depth**
Leverage at the interface — the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation.
**Seam** _(from Michael Feathers)_
A place where you can alter behaviour without editing in that place. The _location_ at which a module's interface lives. Choosing where to put the seam is its own design decision, distinct from what goes behind it.
_Avoid_: boundary (this repo uses "boundary" specifically for ESLint workspace-tag rules — keep it for that meaning).
**Adapter**
A concrete thing that satisfies an interface at a seam. Describes _role_ (what slot it fills), not substance (what's inside). In this repo every port typically has at least two adapters (real + mock); some have three (real + mock + recording).
**Leverage**
What callers get from depth. More capability per unit of interface they have to learn. One implementation pays back across N call sites and M tests.
**Locality**
What maintainers get from depth. Change, bugs, knowledge, and verification concentrate at one place rather than spreading across callers. Fix once, fixed everywhere.
## Principles
- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
- **The deletion test.** Imagine deleting the module. If complexity vanishes, the module wasn't hiding anything (it was a pass-through). If complexity reappears across N callers, the module was earning its keep.
- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test _past_ the interface, the module is probably the wrong shape.
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. In this repo, the typical justification is "one real adapter + one mock for tests" — that's two.
- **The manifest is a structural seam.** A feature's `feature.manifest.ts` declares its use cases / events / jobs / channels / required cores / coverage bands. Refactors that move behaviour between features MUST move manifest entries too; the conformance gates enforce this.
## Relationships
- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
- **Depth** is a property of a **Module**, measured against its **Interface**.
- A **Seam** is where a **Module**'s **Interface** lives.
- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
## Mapping to this repo's identifiers
Abstract → concrete translation table. When proposing a deepening, name things using the right column.
| Abstract term | Where it lands in this repo |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Module** | **Primarily a feature** (`packages/<name>/`) — that's the canonical refactor scope. Also: a use case (`*.use-case.ts`), controller (`*.controller.ts`), repository port + adapters (`*.repository.{interface,mock,}.ts`), service port + adapters (`*.service.{interface,mock,}.ts`), binder (`bind-production.ts` / `bind-dev-seed.ts`), manifest (`feature.manifest.ts`), or a core package (`packages/core-<name>/`) — when the refactor operates at those narrower scales. When in doubt, say "feature". |
| **Interface** | The exported types from a module file: `IXUseCase = ReturnType<typeof xUseCase>`, `IXController`, the `<x>.repository.interface.ts` shape, the manifest's declared keys, the Zod input/output schemas, the DI symbol contract. |
| **Implementation** | The factory body, the adapter class body, what the binder wires. |
| **Seam** | `<x>.repository.interface.ts`, `<x>.service.interface.ts`, the DI symbol (`*_SYMBOLS.IXRepository`), the manifest entry, a `// <gen:*>` anchor, the protocol types in `core-shared/di/bind-protocols.ts`. |
| **Adapter** | `<x>.repository.ts` (Payload real) ↔ `<x>.repository.mock.ts` (in-memory). For instrumentation: `Noop*``Otel*``Recording*` (test). For bus: `InMemoryEventBus``PayloadJobsEventBus`. |
| **Test stand-in** | The `.mock.ts` adapter (constructed directly + injected into the factory). No container rebinding (ADR-012). |
## Rejected framings
- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout's original metric): rewards padding the implementation. We use depth-as-leverage instead.
- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know, including manifest entries and DI symbols.
- **"Boundary"** as a synonym for **seam**: this repo uses "boundary" specifically for ESLint workspace-tag rules (`feature` may depend on `core` + `tooling` only). Keep that meaning intact; say **seam** or **interface** when discussing features.
- **"Service" as a generic term**: in this repo, **service** = a DI-injected port for non-collection capabilities (`IAuthenticationService`, `IMailerService`). Not a generic stand-in for "feature" or "the module doing the work."
- **"Module" as the canonical noun**: avoid in everyday discourse — say **feature** (or **use case** / **controller** / **package** when narrower). "Module" is the abstract refactor vocabulary's word for the same thing, useful only when the abstraction across scales is the point.
## Cross-references
- [`docs/glossary.md`](../../../docs/glossary.md) — project domain vocabulary (use case, manifest, slice, brand, coverage band, etc.)
- [`docs/decisions/`](../../../docs/decisions/) — 21 ADRs that constrain the design space
- [SKILL.md](SKILL.md) — the skill's process + hard constraints
- [DEEPENING.md](DEEPENING.md) — dependency categories + seam discipline
- [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md) — parallel sub-agent design exploration