From 7c3152d8a7e1e555ed20a3d250a71412ea7e9d7c Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 20:59:54 -0500 Subject: [PATCH 1/6] chore(mosaic): load layer guides when agents touch matching files --- .changeset/mosaic-path-scoped-guides.md | 2 ++ packages/mosaic/.claude/rules/controllers.md | 1 + packages/mosaic/.claude/rules/models.md | 1 + packages/mosaic/.claude/rules/stylex.md | 1 + packages/mosaic/.claude/rules/testing.md | 1 + packages/mosaic/.claude/rules/views.md | 1 + packages/mosaic/docs/controllers.md | 6 ++++++ packages/mosaic/docs/models.md | 6 ++++++ packages/mosaic/docs/stylex.md | 5 +++++ packages/mosaic/docs/testing.md | 7 +++++++ packages/mosaic/docs/views.md | 5 +++++ packages/mosaic/lint-staged.config.mjs | 11 +++++++++++ 12 files changed, 47 insertions(+) create mode 100644 .changeset/mosaic-path-scoped-guides.md create mode 120000 packages/mosaic/.claude/rules/controllers.md create mode 120000 packages/mosaic/.claude/rules/models.md create mode 120000 packages/mosaic/.claude/rules/stylex.md create mode 120000 packages/mosaic/.claude/rules/testing.md create mode 120000 packages/mosaic/.claude/rules/views.md create mode 100644 packages/mosaic/lint-staged.config.mjs diff --git a/.changeset/mosaic-path-scoped-guides.md b/.changeset/mosaic-path-scoped-guides.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/mosaic-path-scoped-guides.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/mosaic/.claude/rules/controllers.md b/packages/mosaic/.claude/rules/controllers.md new file mode 120000 index 00000000000..c11030e5150 --- /dev/null +++ b/packages/mosaic/.claude/rules/controllers.md @@ -0,0 +1 @@ +../../docs/controllers.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/models.md b/packages/mosaic/.claude/rules/models.md new file mode 120000 index 00000000000..8d3af9058ab --- /dev/null +++ b/packages/mosaic/.claude/rules/models.md @@ -0,0 +1 @@ +../../docs/models.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/stylex.md b/packages/mosaic/.claude/rules/stylex.md new file mode 120000 index 00000000000..fad77d7ae2c --- /dev/null +++ b/packages/mosaic/.claude/rules/stylex.md @@ -0,0 +1 @@ +../../docs/stylex.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/testing.md b/packages/mosaic/.claude/rules/testing.md new file mode 120000 index 00000000000..71646ae8c93 --- /dev/null +++ b/packages/mosaic/.claude/rules/testing.md @@ -0,0 +1 @@ +../../docs/testing.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/views.md b/packages/mosaic/.claude/rules/views.md new file mode 120000 index 00000000000..f71b6a3daf0 --- /dev/null +++ b/packages/mosaic/.claude/rules/views.md @@ -0,0 +1 @@ +../../docs/views.md \ No newline at end of file diff --git a/packages/mosaic/docs/controllers.md b/packages/mosaic/docs/controllers.md index 94f05e37503..b6210971500 100644 --- a/packages/mosaic/docs/controllers.md +++ b/packages/mosaic/docs/controllers.md @@ -1,3 +1,9 @@ +--- +paths: + - 'src/**/*.controller.ts' + - 'src/**/*.controller.tsx' +--- + # Controllers The controller sits between the view and the outside world. It holds the **local diff --git a/packages/mosaic/docs/models.md b/packages/mosaic/docs/models.md index 545dfb49553..9ef5a9c2fb7 100644 --- a/packages/mosaic/docs/models.md +++ b/packages/mosaic/docs/models.md @@ -1,3 +1,9 @@ +--- +paths: + - 'src/**/*.model.ts' + - 'src/**/*.model.tsx' +--- + # Models The model is the adapter from Clerk into plain data. It is the **only layer in a diff --git a/packages/mosaic/docs/stylex.md b/packages/mosaic/docs/stylex.md index cefcc90ffb0..c23cf21fa91 100644 --- a/packages/mosaic/docs/stylex.md +++ b/packages/mosaic/docs/stylex.md @@ -1,3 +1,8 @@ +--- +paths: + - 'src/**/*.styles.ts' +--- + # Styling a component with StyleX Mosaic is styled with **StyleX**: compile-time atomic CSS, where the style objects diff --git a/packages/mosaic/docs/testing.md b/packages/mosaic/docs/testing.md index e2af154b51d..6c90e0af122 100644 --- a/packages/mosaic/docs/testing.md +++ b/packages/mosaic/docs/testing.md @@ -1,3 +1,10 @@ +--- +paths: + - 'src/**/__tests__/**' + - 'src/**/*.test.ts' + - 'src/**/*.test.tsx' +--- + # Testing a Mosaic flow We follow [Write tests. Not too many. Mostly integration.](https://kentcdodds.com/blog/write-tests): diff --git a/packages/mosaic/docs/views.md b/packages/mosaic/docs/views.md index bc5c08a00d9..4d23e09c500 100644 --- a/packages/mosaic/docs/views.md +++ b/packages/mosaic/docs/views.md @@ -1,3 +1,8 @@ +--- +paths: + - 'src/**/*.view.tsx' +--- + # Views The view renders plain props and calls plain callbacks. Nothing else. diff --git a/packages/mosaic/lint-staged.config.mjs b/packages/mosaic/lint-staged.config.mjs new file mode 100644 index 00000000000..e6e1463dca0 --- /dev/null +++ b/packages/mosaic/lint-staged.config.mjs @@ -0,0 +1,11 @@ +import { lstatSync } from 'node:fs'; + +export default { + '*.{mjs,js,jsx,ts,tsx,vue}': prettier, + '*.{json,md,mdx}': prettier, +}; + +function prettier(files) { + const realFiles = files.filter(file => !lstatSync(file).isSymbolicLink()); + return realFiles.length ? [`pnpm prettier --write ${realFiles.map(file => JSON.stringify(file)).join(' ')}`] : []; +} From 814891fa34f1e247948f6039f6db1b040974a3a7 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 21:10:08 -0500 Subject: [PATCH 2/6] chore(mosaic): point path rules at the guides instead of symlinking them --- packages/mosaic/.claude/rules/controllers.md | 8 +++++++- packages/mosaic/.claude/rules/models.md | 8 +++++++- packages/mosaic/.claude/rules/stylex.md | 7 ++++++- packages/mosaic/.claude/rules/testing.md | 9 ++++++++- packages/mosaic/.claude/rules/views.md | 7 ++++++- packages/mosaic/docs/controllers.md | 6 ------ packages/mosaic/docs/models.md | 6 ------ packages/mosaic/docs/stylex.md | 5 ----- packages/mosaic/docs/testing.md | 7 ------- packages/mosaic/docs/views.md | 5 ----- packages/mosaic/lint-staged.config.mjs | 11 ----------- 11 files changed, 34 insertions(+), 45 deletions(-) mode change 120000 => 100644 packages/mosaic/.claude/rules/controllers.md mode change 120000 => 100644 packages/mosaic/.claude/rules/models.md mode change 120000 => 100644 packages/mosaic/.claude/rules/stylex.md mode change 120000 => 100644 packages/mosaic/.claude/rules/testing.md mode change 120000 => 100644 packages/mosaic/.claude/rules/views.md delete mode 100644 packages/mosaic/lint-staged.config.mjs diff --git a/packages/mosaic/.claude/rules/controllers.md b/packages/mosaic/.claude/rules/controllers.md deleted file mode 120000 index c11030e5150..00000000000 --- a/packages/mosaic/.claude/rules/controllers.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/controllers.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/controllers.md b/packages/mosaic/.claude/rules/controllers.md new file mode 100644 index 00000000000..7ae0dc1e30d --- /dev/null +++ b/packages/mosaic/.claude/rules/controllers.md @@ -0,0 +1,7 @@ +--- +paths: + - 'src/**/*.controller.ts' + - 'src/**/*.controller.tsx' +--- + +Read `packages/mosaic/docs/controllers.md` before changing this file. diff --git a/packages/mosaic/.claude/rules/models.md b/packages/mosaic/.claude/rules/models.md deleted file mode 120000 index 8d3af9058ab..00000000000 --- a/packages/mosaic/.claude/rules/models.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/models.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/models.md b/packages/mosaic/.claude/rules/models.md new file mode 100644 index 00000000000..51df9ef1a98 --- /dev/null +++ b/packages/mosaic/.claude/rules/models.md @@ -0,0 +1,7 @@ +--- +paths: + - 'src/**/*.model.ts' + - 'src/**/*.model.tsx' +--- + +Read `packages/mosaic/docs/models.md` before changing this file. diff --git a/packages/mosaic/.claude/rules/stylex.md b/packages/mosaic/.claude/rules/stylex.md deleted file mode 120000 index fad77d7ae2c..00000000000 --- a/packages/mosaic/.claude/rules/stylex.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/stylex.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/stylex.md b/packages/mosaic/.claude/rules/stylex.md new file mode 100644 index 00000000000..ebed96b1b1b --- /dev/null +++ b/packages/mosaic/.claude/rules/stylex.md @@ -0,0 +1,6 @@ +--- +paths: + - 'src/**/*.styles.ts' +--- + +Read `packages/mosaic/docs/stylex.md` before changing this file. diff --git a/packages/mosaic/.claude/rules/testing.md b/packages/mosaic/.claude/rules/testing.md deleted file mode 120000 index 71646ae8c93..00000000000 --- a/packages/mosaic/.claude/rules/testing.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/testing.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/testing.md b/packages/mosaic/.claude/rules/testing.md new file mode 100644 index 00000000000..12faedb7859 --- /dev/null +++ b/packages/mosaic/.claude/rules/testing.md @@ -0,0 +1,8 @@ +--- +paths: + - 'src/**/__tests__/**' + - 'src/**/*.test.ts' + - 'src/**/*.test.tsx' +--- + +Read `packages/mosaic/docs/testing.md` before changing this file. diff --git a/packages/mosaic/.claude/rules/views.md b/packages/mosaic/.claude/rules/views.md deleted file mode 120000 index f71b6a3daf0..00000000000 --- a/packages/mosaic/.claude/rules/views.md +++ /dev/null @@ -1 +0,0 @@ -../../docs/views.md \ No newline at end of file diff --git a/packages/mosaic/.claude/rules/views.md b/packages/mosaic/.claude/rules/views.md new file mode 100644 index 00000000000..e9a09a6a029 --- /dev/null +++ b/packages/mosaic/.claude/rules/views.md @@ -0,0 +1,6 @@ +--- +paths: + - 'src/**/*.view.tsx' +--- + +Read `packages/mosaic/docs/views.md` before changing this file. diff --git a/packages/mosaic/docs/controllers.md b/packages/mosaic/docs/controllers.md index b6210971500..94f05e37503 100644 --- a/packages/mosaic/docs/controllers.md +++ b/packages/mosaic/docs/controllers.md @@ -1,9 +1,3 @@ ---- -paths: - - 'src/**/*.controller.ts' - - 'src/**/*.controller.tsx' ---- - # Controllers The controller sits between the view and the outside world. It holds the **local diff --git a/packages/mosaic/docs/models.md b/packages/mosaic/docs/models.md index 9ef5a9c2fb7..545dfb49553 100644 --- a/packages/mosaic/docs/models.md +++ b/packages/mosaic/docs/models.md @@ -1,9 +1,3 @@ ---- -paths: - - 'src/**/*.model.ts' - - 'src/**/*.model.tsx' ---- - # Models The model is the adapter from Clerk into plain data. It is the **only layer in a diff --git a/packages/mosaic/docs/stylex.md b/packages/mosaic/docs/stylex.md index c23cf21fa91..cefcc90ffb0 100644 --- a/packages/mosaic/docs/stylex.md +++ b/packages/mosaic/docs/stylex.md @@ -1,8 +1,3 @@ ---- -paths: - - 'src/**/*.styles.ts' ---- - # Styling a component with StyleX Mosaic is styled with **StyleX**: compile-time atomic CSS, where the style objects diff --git a/packages/mosaic/docs/testing.md b/packages/mosaic/docs/testing.md index 6c90e0af122..e2af154b51d 100644 --- a/packages/mosaic/docs/testing.md +++ b/packages/mosaic/docs/testing.md @@ -1,10 +1,3 @@ ---- -paths: - - 'src/**/__tests__/**' - - 'src/**/*.test.ts' - - 'src/**/*.test.tsx' ---- - # Testing a Mosaic flow We follow [Write tests. Not too many. Mostly integration.](https://kentcdodds.com/blog/write-tests): diff --git a/packages/mosaic/docs/views.md b/packages/mosaic/docs/views.md index 4d23e09c500..bc5c08a00d9 100644 --- a/packages/mosaic/docs/views.md +++ b/packages/mosaic/docs/views.md @@ -1,8 +1,3 @@ ---- -paths: - - 'src/**/*.view.tsx' ---- - # Views The view renders plain props and calls plain callbacks. Nothing else. diff --git a/packages/mosaic/lint-staged.config.mjs b/packages/mosaic/lint-staged.config.mjs deleted file mode 100644 index e6e1463dca0..00000000000 --- a/packages/mosaic/lint-staged.config.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import { lstatSync } from 'node:fs'; - -export default { - '*.{mjs,js,jsx,ts,tsx,vue}': prettier, - '*.{json,md,mdx}': prettier, -}; - -function prettier(files) { - const realFiles = files.filter(file => !lstatSync(file).isSymbolicLink()); - return realFiles.length ? [`pnpm prettier --write ${realFiles.map(file => JSON.stringify(file)).join(' ')}`] : []; -} From 6db946e907a6073806f176eca24b82ce82904d93 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 22:00:39 -0500 Subject: [PATCH 3/6] chore(mosaic): point type tests at the testing guide --- packages/mosaic/.claude/rules/testing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mosaic/.claude/rules/testing.md b/packages/mosaic/.claude/rules/testing.md index 12faedb7859..3f9d400450e 100644 --- a/packages/mosaic/.claude/rules/testing.md +++ b/packages/mosaic/.claude/rules/testing.md @@ -3,6 +3,7 @@ paths: - 'src/**/__tests__/**' - 'src/**/*.test.ts' - 'src/**/*.test.tsx' + - 'src/**/*.test-d.ts' --- Read `packages/mosaic/docs/testing.md` before changing this file. From 59941205c7cee7e782cbb441d6e18faa610288a4 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 22:07:26 -0500 Subject: [PATCH 4/6] chore(mosaic): route Codex to the layer guides through AGENTS.md --- AGENTS.md | 1 + packages/mosaic/AGENTS.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 447a0ccb8b6..4a4913ddbeb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,7 @@ Clerk's JavaScript SDK and library monorepo. - Every PR needs a changeset. Agents write `.changeset/.md` directly, because `pnpm changeset` prompts interactively. Empty changesets, for tooling/repo-only changes, are two `---` delimiters with no body. A changeset is a changelog entry for users upgrading the package, not a summary of the work done in the PR. Describe the user-facing change (what changed for someone consuming the library and how it affects them) rather than the implementation details of the diff. If a change has no user-facing impact, use an empty changeset. - Reuse existing patterns before writing new ones. Before adding a helper, hook, or a local way of handling errors, pending state, focus or similar cross-cutting concerns, search the package for one that already solves it. If none fits, stop and ask the user before introducing a new pattern: say what the problem is, what you checked, and what you propose. - PR descriptions follow `.github/PULL_REQUEST_TEMPLATE.md` and add no sections of their own. Never add a "Testing" (or "Test plan" / "How to test") section summarizing the tests written or the checks run; the Checklist covers that and reviewers read the diff. Describe the change, not the work done on it. +- Before editing any file under `packages/mosaic`, read `packages/mosaic/AGENTS.md`. It names the guide to read for each kind of file. ## Verifying changes diff --git a/packages/mosaic/AGENTS.md b/packages/mosaic/AGENTS.md index 863d0637ecd..3e0724b4ab0 100644 --- a/packages/mosaic/AGENTS.md +++ b/packages/mosaic/AGENTS.md @@ -25,6 +25,18 @@ Read the guide for the task you are doing. Paths below are relative to this pack | Add a public prop that shows or hides UI | [Public visibility props](docs/public-props.md) | | Prove a change in a browser | [verify-mosaic](.claude/skills/verify-mosaic/SKILL.md) | +## Before editing a file + +Before you change a file that matches a pattern below, read its guide, even for a one-line change. + +| File | Read | +| ---------------------------------------------------- | ---------------------------------- | +| `src/**/*.styles.ts` | [StyleX](docs/stylex.md) | +| `src/**/*.model.ts(x)` | [Models](docs/models.md) | +| `src/**/*.controller.ts(x)` | [Controllers](docs/controllers.md) | +| `src/**/*.view.tsx` | [Views](docs/views.md) | +| `src/**/__tests__/**`, `*.test.ts(x)`, `*.test-d.ts` | [Testing](docs/testing.md) | + ## Layers in one paragraph A flow lives in `src/features//`. The **model** (`*.model.ts(x)`) is the only file that reads Clerk hooks or calls Clerk resources, and answers with plain data, plain callbacks and a `status`. The **controller** (`*.controller.ts(x)`) holds local state and wraps the model's callbacks for pending, errors and closing. The **view** (`*.view.tsx`) renders plain props. No Clerk resource reaches the controller and no machine snapshot reaches the view. From 04086eaf409d15f3cd4898ad7ac4fdabdabad1d8 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 22:42:54 -0500 Subject: [PATCH 5/6] chore(mosaic): scope the testing patterns in the edit table to src --- packages/mosaic/AGENTS.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/mosaic/AGENTS.md b/packages/mosaic/AGENTS.md index 3e0724b4ab0..64aca90eeb2 100644 --- a/packages/mosaic/AGENTS.md +++ b/packages/mosaic/AGENTS.md @@ -29,13 +29,13 @@ Read the guide for the task you are doing. Paths below are relative to this pack Before you change a file that matches a pattern below, read its guide, even for a one-line change. -| File | Read | -| ---------------------------------------------------- | ---------------------------------- | -| `src/**/*.styles.ts` | [StyleX](docs/stylex.md) | -| `src/**/*.model.ts(x)` | [Models](docs/models.md) | -| `src/**/*.controller.ts(x)` | [Controllers](docs/controllers.md) | -| `src/**/*.view.tsx` | [Views](docs/views.md) | -| `src/**/__tests__/**`, `*.test.ts(x)`, `*.test-d.ts` | [Testing](docs/testing.md) | +| File | Read | +| ------------------------------------------------------------------ | ---------------------------------- | +| `src/**/*.styles.ts` | [StyleX](docs/stylex.md) | +| `src/**/*.model.ts(x)` | [Models](docs/models.md) | +| `src/**/*.controller.ts(x)` | [Controllers](docs/controllers.md) | +| `src/**/*.view.tsx` | [Views](docs/views.md) | +| `src/**/__tests__/**`, `src/**/*.test.ts(x)`, `src/**/*.test-d.ts` | [Testing](docs/testing.md) | ## Layers in one paragraph From 71269edaf51a5e361e27e676f681ad1f14bee801 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Wed, 4 Nov 2026 08:39:09 -0500 Subject: [PATCH 6/6] chore(mosaic): point .stylex.ts edits at the StyleX guide --- packages/mosaic/.claude/rules/stylex.md | 1 + packages/mosaic/AGENTS.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/mosaic/.claude/rules/stylex.md b/packages/mosaic/.claude/rules/stylex.md index ebed96b1b1b..9f6d7ced415 100644 --- a/packages/mosaic/.claude/rules/stylex.md +++ b/packages/mosaic/.claude/rules/stylex.md @@ -1,6 +1,7 @@ --- paths: - 'src/**/*.styles.ts' + - 'src/**/*.stylex.ts' --- Read `packages/mosaic/docs/stylex.md` before changing this file. diff --git a/packages/mosaic/AGENTS.md b/packages/mosaic/AGENTS.md index 64aca90eeb2..1ec5b68606a 100644 --- a/packages/mosaic/AGENTS.md +++ b/packages/mosaic/AGENTS.md @@ -31,7 +31,7 @@ Before you change a file that matches a pattern below, read its guide, even for | File | Read | | ------------------------------------------------------------------ | ---------------------------------- | -| `src/**/*.styles.ts` | [StyleX](docs/stylex.md) | +| `src/**/*.styles.ts`, `src/**/*.stylex.ts` | [StyleX](docs/stylex.md) | | `src/**/*.model.ts(x)` | [Models](docs/models.md) | | `src/**/*.controller.ts(x)` | [Controllers](docs/controllers.md) | | `src/**/*.view.tsx` | [Views](docs/views.md) |