Skip to content

Commit ddec531

Browse files
committed
docs(agents): drop the hand-kept rule-to-check table from CLAUDE.md
Name the enforcing check on the rule's own bullet instead, and tighten the Comments bullet.
1 parent c307193 commit ddec531

1 file changed

Lines changed: 3 additions & 28 deletions

File tree

‎CLAUDE.md‎

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ This file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.c
66

77
- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.
88
- **Logging**: `createLogger` from `@sim/logger`; `logger.info` / `logger.warn` / `logger.error`, never `console.log`. Inside `withRouteHandler` the logger already carries the request ID — no manual `withMetadata({ requestId })`.
9-
- **Comments**: name things so the code explains itself. TSDoc documents exported APIs and non-obvious modules. An inline `//` is only for a terse, non-obvious *why*, or for a script-enforced `// <tag>: <reason>` annotation (`boundary-raw-fetch`, `double-cast-allowed`, `boundary-raw-json`, `untyped-response`, `rq-lint-allow`, `client-boundary-allow`, `utils-lint-allow`, …). A comment never narrates what the next line does, restates a name or type, or records change history ("moved from X", "previously", "now uses Y") — history belongs in the commit message. No `====` separators. The `/you-might-not-need-a-comment` skill applies this to a diff.
9+
- **Comments**: name things so the code explains itself. TSDoc documents exported APIs and non-obvious modules. An inline `//` is only for a terse, non-obvious *why*, or for a script-enforced `// <tag>: <reason>` annotation (`boundary-raw-fetch`, `double-cast-allowed`, `boundary-raw-json`, `untyped-response`, `rq-lint-allow`, `client-boundary-allow`, `utils-lint-allow`, …). History belongs in the commit message. No `====` separators or commented-out code (`check:comment-hygiene` enforces this). The `/you-might-not-need-a-comment` skill applies this to a diff.
1010
- **ID generation**: `generateId()` (UUID v4, the default) or `generateShortId(size?)` (URL-safe, 21 chars by default) from `@sim/utils/id` — never `crypto.randomUUID()`, `nanoid`, or `uuid`. Both use `crypto.getRandomValues()`, so they also work in non-secure (HTTP) browsers.
11-
- **Common utilities**: use the shared helpers from `@sim/utils` instead of inline implementations:
11+
- **Common utilities**: use the shared helpers from `@sim/utils` instead of inline implementations (`check:utils` flags the inline forms):
1212
- `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`
1313
- `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`
1414
- `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`
@@ -86,7 +86,7 @@ The `'use client'` server boundary, the app/worker runtime env split, and featur
8686
- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). A folder with 3+ exports gets an `index.ts` barrel; never re-export from a non-barrel file. `import type` for type-only imports. Order and lazy-loading through barrels: `.claude/rules/sim-imports.md`.
8787
- **TypeScript**: no `any` and no non-null `!` (use precise types or `unknown` with guards; `check:explicit-any` ratchets both); no export nothing imports (`check:unused-exports`); a props interface for every component; `as const` for constant objects/arrays; explicit ref types (`useRef<HTMLDivElement>(null)`).
8888
- **Unused bindings** fail lint (biome `noUnusedVariables`, `noUnusedFunctionParameters`): delete the dead variable, import, or parameter and update callers; write `catch {}` when the error is unused. Prefix `_` only for a parameter that must hold its position because a later one is used. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.
89-
- **Components**: `'use client'` only for hooks or browser APIs. Structure order, extraction thresholds, and list-render rules: `.claude/rules/sim-components.md`. Render-performance idioms (lazy-init refs, hoisting, `Map` pre-indexing, `[...arr].sort()` never `toSorted()` on client paths): `.claude/rules/sim-react-performance.md`. For effect/state/memo/callback anti-patterns use the `/you-might-not-need-*` skills and verify against the running UI.
89+
- **Components**: `'use client'` only for hooks or browser APIs (`check:client-boundary` guards the server boundary). Structure order, extraction thresholds, and list-render rules: `.claude/rules/sim-components.md`. Render-performance idioms (lazy-init refs, hoisting, `Map` pre-indexing, `[...arr].sort()` never `toSorted()` on client paths): `.claude/rules/sim-react-performance.md`. For effect/state/memo/callback anti-patterns use the `/you-might-not-need-*` skills and verify against the running UI.
9090
- **State ownership**: React Query owns server data — never `useState` + `fetch`; shareable client view-state (tabs, filters, search, pagination, selected id) lives in the URL via `nuqs`; Zustand owns global client state; `useState` owns UI-only state. Hooks: `.claude/rules/sim-hooks.md`. Stores (`devtools`, `persist` only with an explicit `partialize` whitelist, workflow value invariants): `.claude/rules/sim-stores.md`. URL state: `.claude/rules/sim-url-state.md`.
9191
- **Utils**: inline a helper with one consumer; create `utils.ts` when 2+ files share it — in `lib/` (app-wide) or `feature/utils/` (feature-scoped). Check `lib/` before writing a new one.
9292
- **Lists and menus** mirror the order the user already reads elsewhere (toolbar, settings nav), encoded in one exported order constant (resource menus share `RESOURCE_MENU_ORDER`, a product order that does not mirror the sidebar); a separator marks only a change in what the action acts on (typically one, before the destructive action): `.claude/rules/sim-list-ordering.md`.
@@ -149,28 +149,3 @@ bun run apps/sim/scripts/check-block-registry.ts origin/staging
149149
```
150150

151151
CI also runs `bun run check:migrations <base>` (it needs a base ref, so it is not in `check:audits`; run it with `origin/staging` when you touch `packages/db/migrations/**`), checks that `drizzle-kit generate` in `packages/db` produces no new migration, and runs a non-blocking `bun audit`. When an audit fails, its output and its script's header say what the rule protects; fix the code, never the check. Ratchet baselines (`scripts/*baseline.json`) only shrink: regenerate one with the update flag its failure output names (`--update` or `--update-baseline`) after removing violations, never to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded when growth is deliberate (see its skill).
152-
153-
| Written rule | Enforced by |
154-
| --- | --- |
155-
| Formatting, lint, no `nanoid`/`uuid` imports, no unused variables or parameters | `bun run lint` (biome) |
156-
| `@sim/utils` over inline idioms (`Math.random`, `crypto.randomUUID`, `JSON` clone, `instanceof Error` message, `setTimeout` sleep) | `check:utils` |
157-
| `apps → packages` only; realtime import bans | `check:boundaries`, `check:realtime-prune` |
158-
| Route contracts, no `zod` in routes or clients, `requestJson`, boundary annotations | `check:api-validation:strict`, `check:api-contract-routes`, `check:route-verbs` |
159-
| Application authorization funnel stays light; principal and capability policy | `check:application-graph`, `check:principal-kind-parity`, `check:capability-subject`, `check:actorless-executor-operations`, `check:permission-group-enforcement` |
160-
| `'use client'` server boundary | `check:client-boundary` |
161-
| React Query keys, `staleTime`, `signal` | `check:react-query` |
162-
| Zustand v5 selector stability | `check:zustand-v5` |
163-
| Tool registry out of client and prefetch graphs | `check:tool-registry-boundary` |
164-
| Outbound HTTP through the egress guard; tool request boundary | `check:egress-boundary`, `check:tool-request-boundary` |
165-
| Imports resolve under Turbopack; no `@/triggers` → `@/blocks` cycle | `check:import-specifiers`, `check:trigger-block-cycle` |
166-
| Canvas sentences, BYOK wiring, fork-dependent subblocks, reachable tool params | `check:canvas-sentences`, `check:byok-providers`, `check:fork-dependent-coverage`, `check:tool-param-reachability` |
167-
| Central mocks, colocated tests, script tests collected | `check:test-patterns`, `check:script-tests` |
168-
| Zero-downtime migrations | `check:migrations <base>` |
169-
| Unused files, exports, types, dependencies (exports and types ratcheted) | `check:unused-exports` (knip) |
170-
| No `any` or non-null `!` (ratcheted), no suppressions of either | `check:explicit-any` |
171-
| kebab-case file names; no file repeating its folder's name | `check:file-names` |
172-
| No banner separators or commented-out code | `check:comment-hygiene` |
173-
| Skills and rules projections in sync; guidance references resolve | `check:skills`, `check:guidance-refs` |
174-
| Generated artifacts fresh (tool metadata, deployment config, docs, catalog, agent stream docs; CLI/MCP/OpenAPI surfaces) | the five `*:check` entries in `check:audits`; `check:cli-api`, `check:mcp-operations`, `check:openapi` |
175-
176-
Rules not in this table (logging, the rest of comment style and naming, imports, styling, state ownership, caching) are enforced by review only; follow them as written.

0 commit comments

Comments
 (0)