From 3687fb4f381409d4fdd35091be94689d5addcf15 Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Fri, 10 Jul 2026 10:16:03 +0100 Subject: [PATCH] Name lib/ + tests/ as the canonical package subfolders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The depth-based rules already treat any subfolder as private, so the config needs no change. Document lib/ (implementation) and tests/ as the conventional two-folder shape, with entry points at the root — a fixed convention that never needs the config extended for a new folder. Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/in-progress/setup-ts-deep-modules/SKILL.md | 8 ++++---- .../setup-ts-deep-modules/dependency-cruiser.config.cjs | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/skills/in-progress/setup-ts-deep-modules/SKILL.md b/skills/in-progress/setup-ts-deep-modules/SKILL.md index 07f6ada..5963b8a 100644 --- a/skills/in-progress/setup-ts-deep-modules/SKILL.md +++ b/skills/in-progress/setup-ts-deep-modules/SKILL.md @@ -17,11 +17,11 @@ src/packages/ / index.ts ← an entry point (public). Import this from outside. client.ts ← another entry point. Packages may expose SEVERAL. - / ← internals: hidden from outside, free to import each other. + lib/ ← implementation: hidden from outside, free to import each other. tests/ ← co-located tests + fixtures (a subfolder, so private). ``` -The public surface is the package's **root files** — not one designated `index.ts`. Implementation lives in subfolders. +The public surface is the package's **root files** — not one designated `index.ts`. By convention implementation lives in `lib/` and tests in `tests/`, giving every package the same two-folder shape. The rule itself is general, though: *anything* in *any* subfolder is private, so you never extend the config to add a folder. Four rules, all `error`: @@ -88,7 +88,7 @@ This is the completion criterion for the whole skill — a config that doesn't f ### 7. Document the convention -Write a `README.md` **in the packages folder** (`/README.md`) — next to the packages it governs — covering: the `src/packages//` layout, "import only through a package's entry points (its root files)", that implementation lives in subfolders, where tests live, and how to run `lint:boundaries`. **Discourage barrel files** explicitly — expose several small entry points instead of re-exporting a whole subtree through one index. Keep it to the copy-me snippet plus the four rules in one paragraph each. +Write a `README.md` **in the packages folder** (`/README.md`) — next to the packages it governs — covering: the `src/packages//` layout (entry points at the root, `lib/` for implementation, `tests/` for tests), "import only through a package's entry points (its root files)", and how to run `lint:boundaries`. **Discourage barrel files** explicitly — expose several small entry points instead of re-exporting a whole subtree through one index. Keep it to the copy-me snippet plus the four rules in one paragraph each. Then add a **context pointer** to it from the repo's agent-instructions file — `CLAUDE.md` if present, else `AGENTS.md` (create `AGENTS.md` if neither exists). One line is enough, e.g. `Packages are deep modules — see [src/packages/README.md](./src/packages/README.md) before adding or importing one.` This is what makes an agent discover the boundary rule instead of tripping over it. @@ -97,6 +97,6 @@ Then add a **context pointer** to it from the repo's agent-instructions file — ## Notes - The config's `$1` back-references (dependency-cruiser's group matching) are what let a package reach its own internals while outsiders can't — don't flatten them into separate per-package rules. -- Public vs private is decided by **depth**: a package's root files are entry points; anything in a subfolder is private. Adding an entry point is just adding a root file — no barrel, no config change. +- Public vs private is decided by **depth**: a package's root files are entry points; anything in a subfolder is private. The conventional subfolders are `lib/` (implementation) and `tests/`, but the rule doesn't hardcode them — any subfolder is private, so a new folder never needs a config change. Adding an entry point is just adding a root file — no barrel. - Packages are **flat**: one tier of immediate children under the root. A package's internals may nest as deep as you like; a package may not contain another package. - Use `.cjs` (not `.js`) so the config's `module.exports` works even in `"type": "module"` repos. diff --git a/skills/in-progress/setup-ts-deep-modules/dependency-cruiser.config.cjs b/skills/in-progress/setup-ts-deep-modules/dependency-cruiser.config.cjs index 119fb72..94a9dcd 100644 --- a/skills/in-progress/setup-ts-deep-modules/dependency-cruiser.config.cjs +++ b/skills/in-progress/setup-ts-deep-modules/dependency-cruiser.config.cjs @@ -4,8 +4,10 @@ // Each package under the packages root is a DEEP MODULE: a lot of behaviour // behind a small interface. A package's PUBLIC SURFACE is its ENTRY POINTS — // the files at the package root. Implementation lives in SUBFOLDERS and is -// private. A package may expose several small entry points (index.ts, -// client.ts, server.ts, …) — prefer that over one giant barrel index. +// private — by convention `lib/` for implementation and `tests/` for tests, +// though any subfolder is private. A package may expose several small entry +// points (index.ts, client.ts, server.ts, …) — prefer that over one giant +// barrel index. // // The only thing you should ever need to edit here is PACKAGES_ROOT.