enhance-arch-boundaries — Architecture as a fitness function
Degree of freedom: MIXED — T1 is the priority. Recovering the model
[HIGH freedom]; do-not-invent, shrink-only baseline, and deliberate-violation
probes [LOW freedom — run exactly].
Codify the repo's intended structure as rules that block merge. Import
spaghetti is how agents degrade architecture: each import looks locally
reasonable, no single diff is wrong, and after forty sessions the
layering is gone. A rule in AGENTS.md is advisory. A dependency-cruiser
rule in the aggregator gate is physics.
This skill vs neighbors
| Skill |
Owns |
| enhance-arch-boundaries (this) |
Mechanical boundary rules + shrink-only baseline |
audit-backend-architecture |
Advises which pattern to adopt — does not enforce |
housekeep-gates |
Wires this check into the aggregator |
docs-adr |
Records why the model was chosen |
enhance-agent-guardrails |
Broader guard install; this owns the import graph |
How to reason
- Observe — folder layout, existing conventions, any architecture audit
- Interpret — what layering does the repo already mean to have?
- Classify — recoverable model / no intended structure (stop) / inventing (forbidden)
- Severity — client → server-only / service-role is the worst edge
Worked example
Observe: app/dashboard/page.tsx imports @/lib/supabase/admin
(service-role). No boundary rule.
Interpret: the intended split exists in folder names but is not physics.
Classify: forbidden edge — client → server-only.
Fix: dependency-cruiser / eslint-boundaries rule; grandfather other
violations into a shrink-only baseline; probe a new forbidden import fails CI.
Phase 0 — Recover the intended architecture (do not invent one) [LOW freedom — stop if none]
The rules must encode the architecture the repo means to have.
- Read folder structure and existing conventions (
features/*, app/,
lib/, server/, components/, monorepo packages).
- Read
audit-backend-architecture output if it exists — that is the
drift to stop.
- Confirm with the user in one pass: layers and allowed direction (e.g.
ui → application → domain → infrastructure, never reverse), feature
units that must stay isolated, special zones (server-only, secrets,
generated).
State the model in ten lines before writing any rule. If the repo
genuinely has no intended structure, stop and say so — enforcing a
structure nobody chose creates fights, not quality. Propose a minimal
layering first.
Phase 1 — Install the rule set [HIGH freedom; mapped to the confirmed model]
Tool by stack: dependency-cruiser (framework-agnostic JS/TS),
eslint-plugin-boundaries (when living inside ESLint is preferable),
Nx module-boundary tags, import-linter (Python).
Core rules, each mapped to the confirmed model:
- Layer direction — lower layers cannot import upward; skipping
layers flagged where the model says so.
- Feature isolation — features import each other only via a public
surface (
features/x/index.ts), never deep paths. Shared code goes
through shared/, not sideways.
- No circular dependencies — anywhere. Cycles are where "change one
thing" stops being possible.
- Forbidden edges — client importing server-only / service-role
modules (cross-ref
plan-rls-audit / plan-secrets-audit), UI
importing the DB layer directly, production importing test utilities,
anything importing generated internals.
- No orphans — modules nothing reaches (feeds
workflow-housekeep).
- Dependency hygiene — no
devDependencies from shipped code;
deprecated internals marked un-importable so migration ratchets.
Phase 2 — Grandfather, then ratchet [LOW freedom — shrink-only baseline]
Do not weaken the rules to fit existing violations, and do not block the
repo on fixing them all.
- Capture existing violations into the tool's known-violations
baseline so CI is green today.
- The baseline is a shrink-only ratchet: new violations fail;
existing ones are tracked debt; the baseline file can only get smaller
without review — same reset policy as
housekeep-gates.
- File the grandfathered list as a burndown, worst edges first (cycles
and client→server-secrets before cosmetic layer skips).
Phase 3 — Wire and make agent-legible [HIGH freedom]
- Add the check to CI wired into the aggregator gate (
housekeep-gates)
and mirror it in local hooks (same command, two callers).
- Generate the dependency graph visualization into docs; regenerate
it on the scheduled run so it cannot go stale.
- Write the ten-line model + "how to fix a boundary violation" into
agent rules referencing the mechanical check — the rule text
explains, the gate enforces.
- Record the architecture decision as an ADR (
docs-adr) so a future
session knows the layering was chosen, by whom, and why the rejected
alternative lost.
Definition of Done
Self-critique before claiming done [LOW freedom — do not skip]
- Model confirmed — or you stopped; you did not invent a layering
- Baseline shrink-only — new violations fail; no silent growth
- Probe per rule class — a test import failed CI
- Right owner — pattern advice →
audit-backend-architecture; why → docs-adr
- Aggregator wired —
housekeep-gates
Output format
- Architecture model — ten-line statement of layers, directions, features, special zones
- Rule set — rule | what it forbids | why (mapped to the model)
- Grandfathered baseline — count by rule | worst-first burndown
- Wiring — gate job, hook parity, graph location, agent-rule text added
- Probe evidence — violation per rule class | CI result
Applied directly (enhance-family). Pause for approval only on the model
confirmation and before committing the baseline.
1---2name: enhance-arch-boundaries3description: Install mechanically-enforced architecture boundaries (dependency-cruiser / eslint-boundaries) so layer direction, feature isolation, and forbidden imports fail CI. Use when "enforce module boundaries" or "stop spaghetti imports". Advisory audit → audit-backend-architecture. Rule content → audit-doctrine.4license: MIT5---67# enhance-arch-boundaries — Architecture as a fitness function89**Degree of freedom: MIXED — T1 is the priority.** Recovering the model10`[HIGH freedom]`; do-not-invent, shrink-only baseline, and deliberate-violation11probes `[LOW freedom — run exactly]`.1213Codify the repo's intended structure as rules that block merge. **Import14spaghetti is how agents degrade architecture: each import looks locally15reasonable, no single diff is wrong, and after forty sessions the16layering is gone.** A rule in AGENTS.md is advisory. A dependency-cruiser17rule in the aggregator gate is physics.1819## This skill vs neighbors2021| Skill | Owns |22|---|---|23| **enhance-arch-boundaries** (this) | Mechanical boundary rules + shrink-only baseline |24| `audit-backend-architecture` | Advises which pattern to adopt — does not enforce |25| `housekeep-gates` | Wires this check into the aggregator |26| `docs-adr` | Records *why* the model was chosen |27| `enhance-agent-guardrails` | Broader guard install; this owns the import graph |2829## How to reason30311. **Observe** — folder layout, existing conventions, any architecture audit322. **Interpret** — what layering does the repo already mean to have?333. **Classify** — recoverable model / no intended structure (stop) / inventing (forbidden)344. **Severity** — client → server-only / service-role is the worst edge3536## Worked example3738> **Observe:** `app/dashboard/page.tsx` imports `@/lib/supabase/admin`39> (service-role). No boundary rule.40> **Interpret:** the intended split exists in folder names but is not physics.41> **Classify:** forbidden edge — client → server-only.42> **Fix:** dependency-cruiser / eslint-boundaries rule; grandfather other43> violations into a shrink-only baseline; probe a new forbidden import fails CI.4445---4647## Phase 0 — Recover the intended architecture (do not invent one) [LOW freedom — stop if none]4849The rules must encode the architecture the repo *means* to have.5051- Read folder structure and existing conventions (`features/*`, `app/`,52 `lib/`, `server/`, `components/`, monorepo packages).53- Read `audit-backend-architecture` output if it exists — that is the54 drift to stop.55- Confirm with the user in one pass: layers and allowed direction (e.g.56 `ui → application → domain → infrastructure`, never reverse), feature57 units that must stay isolated, special zones (server-only, secrets,58 generated).5960State the model in ten lines before writing any rule. If the repo61genuinely has no intended structure, **stop and say so** — enforcing a62structure nobody chose creates fights, not quality. Propose a minimal63layering first.6465---6667## Phase 1 — Install the rule set [HIGH freedom; mapped to the confirmed model]6869Tool by stack: **dependency-cruiser** (framework-agnostic JS/TS),70**eslint-plugin-boundaries** (when living inside ESLint is preferable),71Nx module-boundary tags, import-linter (Python).7273Core rules, each mapped to the confirmed model:7475- **Layer direction** — lower layers cannot import upward; skipping76 layers flagged where the model says so.77- **Feature isolation** — features import each other only via a public78 surface (`features/x/index.ts`), never deep paths. Shared code goes79 through `shared/`, not sideways.80- **No circular dependencies** — anywhere. Cycles are where "change one81 thing" stops being possible.82- **Forbidden edges** — client importing server-only / service-role83 modules (cross-ref `plan-rls-audit` / `plan-secrets-audit`), UI84 importing the DB layer directly, production importing test utilities,85 anything importing generated internals.86- **No orphans** — modules nothing reaches (feeds `workflow-housekeep`).87- **Dependency hygiene** — no `devDependencies` from shipped code;88 deprecated internals marked un-importable so migration ratchets.8990---9192## Phase 2 — Grandfather, then ratchet [LOW freedom — shrink-only baseline]9394Do not weaken the rules to fit existing violations, and do not block the95repo on fixing them all.9697- Capture existing violations into the tool's **known-violations98 baseline** so CI is green today.99- The baseline is a **shrink-only ratchet**: new violations fail;100 existing ones are tracked debt; the baseline file can only get smaller101 without review — same reset policy as `housekeep-gates`.102- File the grandfathered list as a burndown, worst edges first (cycles103 and client→server-secrets before cosmetic layer skips).104105---106107## Phase 3 — Wire and make agent-legible [HIGH freedom]108109- Add the check to CI wired into the aggregator gate (`housekeep-gates`)110 and mirror it in local hooks (same command, two callers).111- Generate the dependency **graph visualization** into docs; regenerate112 it on the scheduled run so it cannot go stale.113- Write the ten-line model + "how to fix a boundary violation" into114 agent rules *referencing the mechanical check* — the rule text115 explains, the gate enforces.116- Record the architecture decision as an ADR (`docs-adr`) so a future117 session knows the layering was chosen, by whom, and why the rejected118 alternative lost.119120---121122## Definition of Done123124- [ ] Intended model recovered from the repo + confirmed with the user (or the no-structure finding raised)125- [ ] Rules installed: layer direction, feature isolation via public surfaces, no cycles, forbidden edges (incl. server-only → client), no orphans, dep hygiene126- [ ] Existing violations grandfathered into a shrink-only baseline; burndown filed worst-first127- [ ] Check in the aggregator gate + local hooks with one shared command128- [ ] Graph visualization generated into docs with a regeneration schedule129- [ ] Agent rules updated to state the model and point at the gate; decision recorded via `docs-adr`130- [ ] Deliberate-violation probe: a test import that breaks each rule class fails CI131132## Self-critique before claiming done [LOW freedom — do not skip]1331341. **Model confirmed** — or you stopped; you did not invent a layering1352. **Baseline shrink-only** — new violations fail; no silent growth1363. **Probe per rule class** — a test import failed CI1374. **Right owner** — pattern advice → `audit-backend-architecture`; why → `docs-adr`1385. **Aggregator wired** — `housekeep-gates`139140## Output format1411421. **Architecture model** — ten-line statement of layers, directions, features, special zones1432. **Rule set** — rule | what it forbids | why (mapped to the model)1443. **Grandfathered baseline** — count by rule | worst-first burndown1454. **Wiring** — gate job, hook parity, graph location, agent-rule text added1465. **Probe evidence** — violation per rule class | CI result147148Applied directly (enhance-family). Pause for approval only on the model149confirmation and before committing the baseline.