You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c947017
Browse filesBrowse the repository at this point in the historyBrowse files
improvement(audits): check rule frontmatter globs in check:guidance-refs
A paths glob that matches nothing silently stops the rule from loading.
sim-api-contracts still targeted the removed apps/sim/hooks/selectors; drop
it, and name check:api-validation:strict as the gate the rule describes.
Copy file name to clipboardExpand all lines: .claude/rules/sim-api-contracts.md
+2-3Lines changed: 2 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,6 @@ paths:
5
5
- "apps/sim/lib/api/**"
6
6
- "apps/sim/lib/**/application/**"
7
7
- "apps/sim/hooks/queries/**"
8
-
- "apps/sim/hooks/selectors/**"
9
8
---
10
9
11
10
# API Contracts and Routes
@@ -19,15 +18,15 @@ Boundary HTTP request and response shapes for all routes under `apps/sim/app/api
19
18
20
19
## Enforcement
21
20
22
-
`bun run check:api-validation`enforces boundary policy and prints ratchet metrics (route Zod imports, route-local schema constructors, route `ZodError` references, client hook Zod imports, and related counters). `bun run check:api-validation:strict` is the CI gate: it additionally fails on annotations with empty reasons. Both must pass on PRs.
21
+
`bun run check:api-validation:strict` is the gate (it runs in `check:audits`): it enforces boundary policy, prints ratchet metrics (route Zod imports, route-local schema constructors, route `ZodError` references, client hook Zod imports), and fails on annotations with empty reasons. `check:api-validation` is the same audit without the strict reason check.
23
22
24
23
Whole-file allowlists for routes that legitimately import Zod for non-boundary reasons go through `INDIRECT_ZOD_ROUTES` in `scripts/check-api-validation-contracts.ts`, not per-line annotations.
25
24
26
25
## Boundary annotations
27
26
28
27
A small number of legitimate exceptions are tolerated when annotated. The audit recognizes four forms:
29
28
30
-
-`// boundary-raw-fetch: <reason>` — directly above a raw `fetch(` inside `apps/sim/hooks/queries/**`, `apps/sim/hooks/selectors/**`, or any other source under `apps/sim/**` (outside an API route handler) that targets a same-origin `/api/...` URL. Only for streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests.
29
+
-`// boundary-raw-fetch: <reason>` — directly above a raw `fetch(` inside `apps/sim/hooks/queries/**` or any other source under `apps/sim/**` (outside an API route handler) that targets a same-origin `/api/...` URL. Only for streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests.
31
30
-`// double-cast-allowed: <reason>` — directly above an `as unknown as X` cast outside test files.
32
31
-`// boundary-raw-json: <reason>` — directly above a raw `await request.json()` / `await req.json()` read (or the multi-line `await request.clone().json()` shim variant) in a route handler. Only when the body is a JSON-RPC envelope, a tolerant `.catch(() => ({}))` parse, or otherwise cannot go through `parseRequest`.
33
32
-`// untyped-response: <reason>` — directly above a `schema: z.unknown()` / `schema: z.object({}).passthrough()` / `schema: z.record(z.string(), z.unknown())` response declaration (or a simple alias to one of those) in a contract file. Only when the response body is genuinely opaque (user-supplied data, third-party passthrough).
Copy file name to clipboardExpand all lines: .cursor/rules/emcn-components.mdc
+3-3Lines changed: 3 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -22,7 +22,7 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items
22
22
23
23
## Component catalogue
24
24
25
-
- **`Chip` / `ChipLink`** — the pill button (`<button>` / Next `<Link>`). Variants: `primary`, `destructive`, `border-shadow`, `border`, `outline` (a true `--border` border, no shadow or hover fill); the bare chip is implicit (omit `variant`). `filled` is deliberately NOT a `Chip` variant — it is reserved for chip fields/triggers. For a selected/toggle chip use the `active` prop, never a variant. `leftIcon`/`rightIcon`, `active`, `fullWidth`. Chips carry **no outer margin** — space between them is the parent's `gap`. The old `mx-0.5` default and its `flush` opt-out are gone; do not reintroduce either, and never add a margin to a chip through `className`.
25
+
- **`Chip` / `ChipLink`** — the pill button (`<button>` / Next `<Link>`). Variants: `primary`, `destructive`, `border-shadow`, `border`, `outline` (a true `--border` border, no shadow or hover fill); the bare chip is implicit (omit `variant`). `filled` is deliberately NOT a `Chip` variant — it is reserved for chip fields/triggers. For a selected/toggle chip use the `active` prop, never a variant. `leftIcon`/`rightIcon`, `active`, `fullWidth`. Chips carry **no outer margin** — space between them is the parent's `gap`, so never give a chip a margin, by default or through `className`.
26
26
- **`ChipInput`** — single-line text field. `icon`, `endAdornment`, `error`, `inputClassName` (inner `<input>`); `className` styles the chrome wrapper.
27
27
- **`ChipCopyInput`** — the canonical view-only field: a read-only `ChipInput` at full opacity with a trailing copy-to-clipboard button. View-only is a display mode, not a disabled state — reach for it (or `ChipModalField type='copy'`) over a `disabled` (greyed) input for values the user cannot edit.
28
28
- **`ChipTextarea`** — multi-line sibling. `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
@@ -53,8 +53,8 @@ Declare keyboard intent on the action-owning primitive; never add document-level
53
53
- **Props over `className` overrides.** When a consumer needs to change chrome, expose a prop (`error`, `icon`, `endAdornment`, `inputClassName`); reaching for `className` to restyle chrome is the smell.
54
54
- **`cn()` for a single state toggle, CVA for genuine multiple variants.** A lone `error` boolean is `cn()`, not a CVA variant.
55
55
- **Discriminated-union props for modes** (e.g. `multiple`, the modal field `type`) instead of near-duplicate components.
56
-
- **Delete legacy variants after migration** — don't leave dead paths (this paradigm removed `Input variant='chip'` and `ChipMultiSelect`).
57
-
- **Verify CSS vars exist.** An undefined var resolves to `currentColor` (caused a real black-border bug). Align to the canonical tokens: normal weight, `--text-body`, `--text-icon`.
56
+
- **Delete legacy variants after migration** — leave no dead paths.
57
+
- **Verify CSS vars exist.** An undefined var resolves to `currentColor`, which draws black borders. Align to the canonical tokens: normal weight, `--text-body`, `--text-icon`.
58
58
- Use Radix UI primitives for accessibility. Export the component and its `variants` (when using CVA). Document with TSDoc + a usage example.
59
59
60
60
Color tokens and icon-size conventions are canonical in `.claude/rules/sim-styling.md` — follow it rather than restating.
<!-- Generated from .claude/rules/sim-api-contracts.md by `bun run skills:sync`. Edit the source, not this file. -->
@@ -16,15 +16,15 @@ Boundary HTTP request and response shapes for all routes under `apps/sim/app/api
16
16
17
17
## Enforcement
18
18
19
-
`bun run check:api-validation` enforces boundary policy and prints ratchet metrics (route Zod imports, route-local schema constructors, route `ZodError` references, client hook Zod imports, and related counters). `bun run check:api-validation:strict` is the CI gate: it additionally fails on annotations with empty reasons. Both must pass on PRs.
19
+
`bun run check:api-validation:strict` is the gate (it runs in `check:audits`): it enforces boundary policy, prints ratchet metrics (route Zod imports, route-local schema constructors, route `ZodError` references, client hook Zod imports), and fails on annotations with empty reasons. `check:api-validation` is the same audit without the strict reason check.
20
20
21
21
Whole-file allowlists for routes that legitimately import Zod for non-boundary reasons go through `INDIRECT_ZOD_ROUTES` in `scripts/check-api-validation-contracts.ts`, not per-line annotations.
22
22
23
23
## Boundary annotations
24
24
25
25
A small number of legitimate exceptions are tolerated when annotated. The audit recognizes four forms:
26
26
27
-
- `// boundary-raw-fetch: <reason>` — directly above a raw `fetch(` inside `apps/sim/hooks/queries/**`, `apps/sim/hooks/selectors/**`, or any other source under `apps/sim/**` (outside an API route handler) that targets a same-origin `/api/...` URL. Only for streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests.
27
+
- `// boundary-raw-fetch: <reason>` — directly above a raw `fetch(` inside `apps/sim/hooks/queries/**` or any other source under `apps/sim/**` (outside an API route handler) that targets a same-origin `/api/...` URL. Only for streaming responses, binary downloads, multipart uploads, signed-URL flows, OAuth redirects, and external-origin requests.
28
28
- `// double-cast-allowed: <reason>` — directly above an `as unknown as X` cast outside test files.
29
29
- `// boundary-raw-json: <reason>` — directly above a raw `await request.json()` / `await req.json()` read (or the multi-line `await request.clone().json()` shim variant) in a route handler. Only when the body is a JSON-RPC envelope, a tolerant `.catch(() => ({}))` parse, or otherwise cannot go through `parseRequest`.
30
30
- `// untyped-response: <reason>` — directly above a `schema: z.unknown()` / `schema: z.object({}).passthrough()` / `schema: z.record(z.string(), z.unknown())` response declaration (or a simple alias to one of those) in a contract file. Only when the response body is genuinely opaque (user-supplied data, third-party passthrough).
Every export of a `'use client'` module becomes a *client reference* on the server — server-evaluated code (RSC pages/layouts, `prefetch.ts`, route handlers, block definitions, triggers) can only *render* it as a component or pass it as a prop, never *call* it (doing so throws at runtime, e.g. `tableKeys.list is not a function`; `next build` does not catch it). Keep server-importable query primitives (key factories, fetchers, mappers, constants) in non-`'use client'` modules — see `.claude/rules/sim-queries.md`. Enforced by `scripts/check-client-boundary-imports.ts`.
14
+
Server-evaluated code (RSC pages/layouts, `prefetch.ts`, route handlers, block definitions, triggers) can render an export of a `'use client'` module but never call it; the call throws at runtime and `next build` does not catch it. Keep anything a server module calls (key factories, fetchers, mappers, constants) in a non-`'use client'` module. Detail and the escape hatch: `.claude/rules/sim-queries.md`. Enforced by `bun run check:client-boundary`.
When rendering or sorting a list of rows against a lookup collection (members, folders, tags), keep the per-row work O(1):
35
35
36
-
- **Precompute a lookup `Map` once**, never `array.find(...)` per row. Build `const byId = useMemo(() => { const m = new Map<string, T>(); for (const x of items ?? []) m.set(x.id, x); return m }, [items])` and read `byId.get(id)` in the sort comparator, `.map(...)`, and cell builders. A `.find` inside a sort comparator is O(n²·log n) — the worst offender. Depend memos on the derived `Map`, not the raw array.
37
-
- **Sort a copy with `[...array].sort(cmp)`**, never `toSorted` in client code — see `sim-react-performance.md` → "Never mutate a shared array in place".
36
+
- **Precompute a lookup `Map` once** (`useMemo` over the collection) and read `byId.get(id)` in the sort comparator, `.map(...)`, and cell builders. A `.find` inside a sort comparator is O(n²·log n). Depend downstream memos on the derived `Map`, not the raw array. Map-building and copy-then-sort rules: `sim-react-performance.md`.
38
37
- **Partition in a single pass** — when splitting one collection into several (`fileIds`/`folderIds`), do one `for…of` pushing into each bucket and return `{ a, b }` from a single `useMemo`, not two memos that each `map→filter→map` the same source twice.
39
38
40
39
## react-doctor (`bunx react-doctor`) — apply the wins, skip the false positives
**A list orders itself the way the user already reads the same things somewhere else.** Dropdowns, context menus, tab strips, command palettes, and settings navs are all *second* presentations of a set the user has already seen — in a toolbar, in the settings nav, in a column-header row. When the second presentation reorders that set, the user re-reads it from scratch every time.
11
11
12
-
This is not a style preference. Order is the cheapest affordance a list has, and the only one that costs nothing to get right.
13
-
14
12
## The rule
15
13
16
14
Before writing a list of items, find where the user sees those same items *first*. That surface owns the order; your list mirrors it.
0 commit comments