# INTERFACE-DESIGN — Boundary-shape lens

This lens asks: **are the boundaries between modules well-shaped?** Are
there feature-envy patterns? Hidden coupling? Async leakage? Boundary
violations the reactive `apt:review` Pass 4 audit would catch if a diff
crossed them, but which currently exist statically and nobody is
auditing?

## Red flags this lens surfaces

### Feature envy
A method in module A spends most of its time reading fields from
module B's types. The behavior wants to live in B but the import
direction made it land in A. Symptom: lots of `b.x`, `b.y`, `b.z`
accesses in A's methods.

### Hidden coupling
Module A imports module B by accident — not because B's exported
surface is being used, but because B re-exports something A actually
wanted from a third place. Symptom: `import { foo } from './b'` where
`foo` is defined in C and B is just a passthrough.

### Async leakage
A function in A returns a Promise, but the work it kicks off settles
asynchronously somewhere else (timer, event handler, background job).
The caller can `await` the Promise and think they're done, but the
real work is still in flight. Symptom: tests pass synchronously but
real-world behavior is racy.

### Wide-then-narrow interfaces
Module A exports 30 functions, but only 3 are called externally. The
other 27 are accidental surface. Symptom: `index.ts` exports `*`.

### Pass-through importers
Module A exports `foo` which it imports from B and re-exports
unchanged. Adds a hop with no value. Symptom: A's body contains
`export { foo } from './b'` and nothing else for that symbol.

### Dependency direction violations
Per Aperant's import-direction convention (`packages/` never imports
from `cloud/`), this lens flags any backward import. The static lint
catches some of these; this lens catches the rest (e.g., implicit type
re-exports that leak `cloud/` types into `packages/`).

## Apply in Phase 1

When the explore step scans the named area:

1. For each module boundary (file-level or directory-level), identify
   the explicit imports + exports.
2. Look for the 6 red flags above.
3. Note in the inventory: `{Module A} ↔ {Module B} — {red-flag name}
   ({one-line evidence})`.

## Distinguishing this from DEEPENING and LANGUAGE

| Lens | Question |
|---|---|
| **DEEPENING** | Does each abstraction earn its keep at its current depth? |
| **INTERFACE-DESIGN** | Are the gaps between abstractions well-shaped? |
| **LANGUAGE** | Does the codebase use the same word for the same thing? |

A red flag in one lens often surfaces in another, but the lens is the
diagnostic angle. A god-object problem is DEEPENING (the abstraction
is too deep AND too wide); a feature-envy problem is INTERFACE-DESIGN
(the boundary is in the wrong place).

## Deletion test (Phase 2 prep)

For each candidate this lens flags, the Phase-2 deletion test asks:

> If I moved this code across the boundary it currently sits behind
> (or removed the boundary entirely), what would break?

If the answer is "nothing important — the boundary was an accident",
the deletion test passes and the candidate is high-priority for
refactor (move the code; the boundary disappears).

If the answer is "we'd lose the ability to test / mock / version this
slice independently", the deletion test fails — the boundary was
earning its keep, even if the shape is awkward. Re-scope to "reshape"
rather than "remove".

## What this lens does NOT cover

- Whether the abstractions on either side of the boundary are deep
  enough → see `DEEPENING.md`
- Whether the boundary's types and method names use the codebase's
  domain language → see `LANGUAGE.md`
