Skip to content

Commit 35adcf7

Browse files
authored
improvement(guidance): make every ratchet shrink-only, scope rules to their files, fix stale guidance (#8594)
* improvement(guidance): make every ratchet shrink-only, scope rules to their files, fix stale guidance - check:test-patterns --update refuses new violations and fails closed on a missing baseline - check:react-query drops its unused, growable baseline: every violation fails, as it already did - check:utils drops the id.ts allowlist entry by rewording the comment that tripped it - constitution, react-performance, and url-state rules load only for the files they govern - guidance: Switch stays the boolean toggle, current chip names, @sim/utils/random, db-migrate covers the schema mock and drizzle sync, apps/sim/AGENTS.md maps common tasks to skills * chore(guidance): widen rule scopes to the changelog route, package hooks, and nuqs navigation helpers * chore(guidance): scope url-state by directory so app hooks and helpers stay covered
1 parent 52878d1 commit 35adcf7

24 files changed

Lines changed: 92 additions & 114 deletions

‎.agents/skills/db-migrate/SKILL.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ The lint flags risky *shapes*; it cannot know whether a given drop is *safe righ
8383
```
8484
The reason must be specific and name the PR/version that removed the dependency. An empty reason fails the lint.
8585
- **Warnings** (`data-backfill`): non-blocking, but confirm the batching/idempotency before merging.
86-
4. Verify locally: `cd packages/db && bun run db:migrate` against a dev DB.
86+
4. Regenerate the test schema mock: `bun run scripts/generate-schema-mock.ts` (`check:schema-mock` in `check:audits` fails after any `schema.ts` change until you do).
87+
5. Re-run `(cd packages/db && bunx drizzle-kit generate)` once your migration is written: it must report no schema changes and write no new file, or CI fails on the schema and migrations disagreeing.
88+
6. Verify locally: `cd packages/db && bun run db:migrate` against a dev DB.
8789

8890
## Hard rule
8991

‎.claude/rules/constitution.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
---
22
description: Sim product language, positioning, and tone guidelines
3+
paths:
4+
- "apps/sim/app/(landing)/**"
5+
- "apps/sim/lib/landing/**"
6+
- "apps/sim/content/**"
7+
- "apps/sim/emails/broadcasts/**"
8+
- "apps/sim/app/layout.tsx"
9+
- "apps/sim/app/manifest.ts"
10+
- "apps/sim/app/llms*.txt/**"
11+
- "apps/sim/app/changelog.xml/**"
12+
- "apps/docs/**"
313
---
414

515
# Sim — Language & Positioning

‎.claude/rules/emcn-components.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ paths:
66

77
# EMCN Components
88

9-
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch`→`ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
9+
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, date field→`ChipDatePicker`). `ChipSwitch` is a segmented choice between options, not a replacement for `Switch`: a boolean on/off toggle stays `Switch`. For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
1010

1111
## Chip chrome — single source of truth
1212

‎.claude/rules/sim-queries.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,4 @@ Hooks import named type aliases from `@/lib/api/contracts/**` and never import `
172172

173173
## Enforcement
174174

175-
`scripts/check-react-query-patterns.ts` (`bun run check:react-query`, run in CI) statically enforces these conventions: every `useQuery`/`useInfiniteQuery`/`useSuspenseQuery` declares an explicit `staleTime`, inline `queryFn`s destructure `signal`, `queryKey`s reference a colocated factory rather than an inline literal, every `*Keys` factory in `hooks/queries/**` exposes an `all` root key, and every identifier the `queryFn` forwards into the fetch also appears in the `queryKey` (`key-fetch-arg-drift`). `hooks/queries/**` is a zero-tolerance zone; the rest of `apps/sim/**` is ratcheted against `scripts/check-react-query-patterns.baseline.json`. For a genuine exception, put `// rq-lint-allow: <reason>` on the line directly above the flagged construct.
175+
`scripts/check-react-query-patterns.ts` (`bun run check:react-query`, run in CI) statically enforces these conventions: every `useQuery`/`useInfiniteQuery`/`useSuspenseQuery` declares an explicit `staleTime`, inline `queryFn`s destructure `signal`, `queryKey`s reference a colocated factory rather than an inline literal, every `*Keys` factory in `hooks/queries/**` exposes an `all` root key, and every identifier the `queryFn` forwards into the fetch also appears in the `queryKey` (`key-fetch-arg-drift`). Any violation under `apps/sim/**` fails. For a genuine exception, put `// rq-lint-allow: <reason>` on the line directly above the flagged construct.

‎.claude/rules/sim-react-performance.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
---
22
description: Behavior-preserving React render-performance idioms
3+
paths:
4+
- "apps/sim/**/*.ts"
5+
- "apps/sim/**/*.tsx"
6+
- "packages/emcn/**"
7+
- "packages/workflow-renderer/**"
38
---
49

510
# React & Render Performance

‎.claude/rules/sim-settings-pages.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,9 @@ and — on activatable rows only — the hover band. Never hand-roll any of it,
224224
divider, body. Also carries `headerAccessory` and `action` slots. Never
225225
re-derive the label/divider chrome; `sim-styling.md` owns those tokens.
226226
- **`SettingsField`** (`…/components/settings-field`) — a read-only label/value
227-
pair in a detail body: muted caption over the value. Pair it with
228-
`SETTINGS_FIELD_VALUE_CLASSES` for the value text.
227+
pair in a detail body: muted caption over the value. Pass the value as text and it
228+
renders the value paragraph itself; pass a node when the value needs its own
229+
presentation (a control, an icon beside the value, status styling).
229230
- **`SettingsEmptyState`** (`…/components/settings-empty-state`) — the canonical
230231
muted status message, for empty lists, "no results", loading gates, **and
231232
failed loads** (`tone='error'`). `variant='fill'` (default) centers in the

‎.claude/rules/sim-styling.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Layout/sizing ONLY: `flex-1`, `w-full`, `w-[Npx]`, `min-w-0`, `max-w-*`, margins
108108
- **Modal body** (`ChipModalBody`): `gap-4` between fields, padding `px-2 pt-4 pb-4.5`.
109109
- **Header/footer**: horizontal gutter `px-4` (header `pt-3`; footer `px-4 pt-2 pb-2`, tinted bar).
110110
- **Every body field MUST be a `ChipModalField`** — NEVER hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). WHY: body `px-2` + field `px-2` = effective `px-4`, exactly matching the `px-4` header/footer. A hand-rolled row skips the field gutter, sits at `px-2`, and is visibly misaligned. Inline errors go through the `error` prop, not a hand-rolled `<p>`.
111-
- **Uncovered controls** (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX) → `ChipModalField type='custom'` with a `title`. It still applies the `px-2` gutter and renders the canonical `Label`, so it stays aligned. Never drop such a control into a raw `<div>`, and never add a body-level wrapper `<div>` with a custom `gap-*` that fights `gap-4`.
111+
- **Uncovered controls** (`ChipCombobox`, `ChipSelect`, `ChipDatePicker`, `ChipTimePicker`, `ChipButtonGroup`, arbitrary JSX) → `ChipModalField type='custom'` with a `title`. It still applies the `px-2` gutter and renders the canonical `Label`, so it stays aligned. Never drop such a control into a raw `<div>`, and never add a body-level wrapper `<div>` with a custom `gap-*` that fights `gap-4`.
112112
- **Page section rhythm** (integrations/skills/settings): muted `text-small` label + `mt-[9px] mb-3 h-px bg-[var(--border)]` divider, sections stacked `gap-7`. Reuse `SettingsSection` (`app/workspace/[workspaceId]/settings/components/settings-section/settings-section.tsx`) rather than re-deriving it.
113113

114114
When a standalone labeled field outside a `ChipModal` needs the same look (e.g. `SkillImport`), match the field rhythm by hand: `flex flex-col gap-[9px]`, muted label, `ChipInput`/`ChipTextarea` control, `text-caption` error below.

‎.claude/rules/sim-url-state.md‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
description: Shareable client view-state lives in the URL via nuqs
33
paths:
44
- "apps/sim/app/**/*.tsx"
5-
- "apps/sim/app/**/*.ts"
6-
- "apps/sim/app/**/search-params.ts"
7-
- "apps/sim/ee/**/*.tsx"
8-
- "apps/sim/ee/**/*.ts"
5+
- "apps/sim/app/workspace/**/*.ts"
6+
- "apps/sim/app/o/**/*.ts"
7+
- "apps/sim/ee/**"
8+
- "apps/sim/hooks/**"
9+
- "apps/sim/stores/**"
10+
- "apps/sim/lib/url-state/**"
11+
- "apps/sim/**/search-params.ts"
12+
- "apps/sim/**/*navigation.ts"
913
---
1014

1115
# URL / Query-Param State (nuqs)

‎.cursor/rules/constitution.mdc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: "Sim product language, positioning, and tone guidelines"
3-
alwaysApply: true
3+
globs: ["apps/sim/app/(landing)/**","apps/sim/lib/landing/**","apps/sim/content/**","apps/sim/emails/broadcasts/**","apps/sim/app/layout.tsx","apps/sim/app/manifest.ts","apps/sim/app/llms*.txt/**","apps/sim/app/changelog.xml/**","apps/docs/**"]
44
---
55

66
<!-- Generated from .claude/rules/constitution.md by `bun run skills:sync`. Edit the source, not this file. -->

‎.cursor/rules/emcn-components.mdc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ globs: ["packages/emcn/**"]
77

88
# EMCN Components
99

10-
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch`→`ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
10+
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, date field→`ChipDatePicker`). `ChipSwitch` is a segmented choice between options, not a replacement for `Switch`: a boolean on/off toggle stays `Switch`. For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover).
1111

1212
## Chip chrome — single source of truth
1313

0 commit comments

Comments
 (0)