{"owner":"simstudioai","repo":"sim","hasSkills":true,"totalSkillsCount":6,"totalTokensCount":12526,"categories":["root-instruction","claude-rule","mcp-config","marketplace","plugin-manifest","cursor-rule"],"hasMcp":true,"mcpConfig":{"mcpServers":{"sim":{"command":"npx","args":["-y","@modelcontextprotocol/server-sim"]}}},"found":["AGENTS.md","CLAUDE.md",".cursor/mcp.json",".claude-plugin/marketplace.json",".claude-plugin/plugin.json",".cursorrules (ascii-simulation-game-cursorrules-prompt-file.mdc)"],"skills":{"AGENTS.md":"# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n\n<!-- BEGIN:turborepo-agent-rules -->\n\n# This is NOT the Turborepo you know\n\nTurborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p \"require.resolve('turbo/package.json')\"` from a workspace that depends on `turbo`.\n\nRead `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.\n\nThis block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `\"agentGuidance\": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.\n<!-- END:turborepo-agent-rules -->\n","CLAUDE.md":"# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/plugin.json)",".cursorrules (ascii-simulation-game-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for ASCII simulation game development.\"\nglobs: **/*\nalwaysApply: false\n---\nyou are an expert game designer and game programmer, you will choose the best game design and coding practices for all decisions in this project.\n\nThe game is based on a 10x10 grid, each square has a 10x10 grid inside of it. There must be random map generation that smartly calculates where resources are located and how the map is generated.\n\nThe player does not control anything in the game the player is simply an observer, therefore there should be logs for almost everything in the game and it should be turn based.\n\nAll nations should operate the same, their capabilities should be balanced. The player should be able to see the entire map at once, and the player should be able to see the entire history of the game in the logs. There should be a way to zoom in on a specific square to see more detail.\n\nNations should be able to trade resources with each other. Nations should be able to go to war with each other. Nations should be able to make peace with each other.\n\nThe time period of the game is constant and there is no technological tree. It takes place in ancient times.\n\nnations should spawn a minimum distance away from eachother\n\nthe entire game should be colored ASCII based in terms of graphics\n\nThere should be neutral land that can be claimed by any nation. Neutral land should be randomly generated each game.\n\nThere should be a way to view the current owner of a square. There should be a way to view the current resources of a square.\n\nvalue of resources should be based on their rarity throughout the entire map. nations can use gold to either buy resources or armies.\n\narmies are the primary way that nations can expand their territory.\n\nthere should be no talent tree or technology tree, nations should be balanced without the need for such a tree\n\npopulation should collect in towns and cities\n\nroads should connect towns and cities\n\nresources are spread throughout nations through roads\n\nnations attempt to spread their resources evenly over their territory\n\ngold is not omni present and must be transported using roads to the location where it is spent to build armies or develop land\n\noceans should be randomly generated to separate continents\n\nrivers should be randomly generated to connect oceans and flow across the map vertically or horizontally\n\nrivers are a food source for the land and farms can be built on them\n\nmountains should be randomly generated throughout the map\n\nmountains should be impassable by armies\n\nmines in mountains provide metal at 20% efficiency\n\nNations should expand towards resources that they have a low amount of of and away from resources that they have a high amount of\n\narmies should spawn at the town or city that issued the order\n\ntowns can only spawn a max level 3 army\n\ntowns have a 3 square radius for gathering resources\n\nas towns grow their radius grows, there are 3 levels of towns and cities\n\na Nation's largest city is its capital\n\npopulation can only live in towns and cities\n\nresources should be spread throughout the map in a way that encourages nations to expand into new squares\n\narmies can travel across oceans at .25x speed\n\narmies can travel on rivers to move across the map at 3x speed\n\nthere is a \"battle list\" that shows all the battles that have happened and stats about them\n\narmies go from level 1 to level 10 based on their funding\n\ninner squares can be developed into farms, forests, mines\n\narmies require wood, food, and metal to be created.\n\nnations must pay upkeep depending on the amount of armies and developed land they have\n\nbattles are resolved by the difference in army level and a RISK esque dice roll mechanic that is effected by army level\n\narmies can build castles that are good defensively and allow for funding of armies\n\narmies can be used to conquer squares from other nations\n\narmies can be used to defend squares from other nations\n\narmies can be used to attack other nations\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\nnations should start with the same amount of gold and land\n\nthe map should be color coded to show the owner of the square\n\nthere should be effects over the screen that mimic a CRT monitor\n\nthe game should aim to be similar to Conway's Game of Life where the nations are the living organisms.\n\nlike conway's game of life, nations should be able to \"see\" eachother and react to eachother\n\nlike conway's game of life, the nations should be able to \"see\" the resources and react to them\n\nthere should be a chart page that tracks just about everything that can be tracked in the game\n"},"files":{"AGENTS.md":"# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n\n<!-- BEGIN:turborepo-agent-rules -->\n\n# This is NOT the Turborepo you know\n\nTurborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p \"require.resolve('turbo/package.json')\"` from a workspace that depends on `turbo`.\n\nRead `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.\n\nThis block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `\"agentGuidance\": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.\n<!-- END:turborepo-agent-rules -->\n","CLAUDE.md":"# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/plugin.json)",".cursorrules (ascii-simulation-game-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for ASCII simulation game development.\"\nglobs: **/*\nalwaysApply: false\n---\nyou are an expert game designer and game programmer, you will choose the best game design and coding practices for all decisions in this project.\n\nThe game is based on a 10x10 grid, each square has a 10x10 grid inside of it. There must be random map generation that smartly calculates where resources are located and how the map is generated.\n\nThe player does not control anything in the game the player is simply an observer, therefore there should be logs for almost everything in the game and it should be turn based.\n\nAll nations should operate the same, their capabilities should be balanced. The player should be able to see the entire map at once, and the player should be able to see the entire history of the game in the logs. There should be a way to zoom in on a specific square to see more detail.\n\nNations should be able to trade resources with each other. Nations should be able to go to war with each other. Nations should be able to make peace with each other.\n\nThe time period of the game is constant and there is no technological tree. It takes place in ancient times.\n\nnations should spawn a minimum distance away from eachother\n\nthe entire game should be colored ASCII based in terms of graphics\n\nThere should be neutral land that can be claimed by any nation. Neutral land should be randomly generated each game.\n\nThere should be a way to view the current owner of a square. There should be a way to view the current resources of a square.\n\nvalue of resources should be based on their rarity throughout the entire map. nations can use gold to either buy resources or armies.\n\narmies are the primary way that nations can expand their territory.\n\nthere should be no talent tree or technology tree, nations should be balanced without the need for such a tree\n\npopulation should collect in towns and cities\n\nroads should connect towns and cities\n\nresources are spread throughout nations through roads\n\nnations attempt to spread their resources evenly over their territory\n\ngold is not omni present and must be transported using roads to the location where it is spent to build armies or develop land\n\noceans should be randomly generated to separate continents\n\nrivers should be randomly generated to connect oceans and flow across the map vertically or horizontally\n\nrivers are a food source for the land and farms can be built on them\n\nmountains should be randomly generated throughout the map\n\nmountains should be impassable by armies\n\nmines in mountains provide metal at 20% efficiency\n\nNations should expand towards resources that they have a low amount of of and away from resources that they have a high amount of\n\narmies should spawn at the town or city that issued the order\n\ntowns can only spawn a max level 3 army\n\ntowns have a 3 square radius for gathering resources\n\nas towns grow their radius grows, there are 3 levels of towns and cities\n\na Nation's largest city is its capital\n\npopulation can only live in towns and cities\n\nresources should be spread throughout the map in a way that encourages nations to expand into new squares\n\narmies can travel across oceans at .25x speed\n\narmies can travel on rivers to move across the map at 3x speed\n\nthere is a \"battle list\" that shows all the battles that have happened and stats about them\n\narmies go from level 1 to level 10 based on their funding\n\ninner squares can be developed into farms, forests, mines\n\narmies require wood, food, and metal to be created.\n\nnations must pay upkeep depending on the amount of armies and developed land they have\n\nbattles are resolved by the difference in army level and a RISK esque dice roll mechanic that is effected by army level\n\narmies can build castles that are good defensively and allow for funding of armies\n\narmies can be used to conquer squares from other nations\n\narmies can be used to defend squares from other nations\n\narmies can be used to attack other nations\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\nnations should start with the same amount of gold and land\n\nthe map should be color coded to show the owner of the square\n\nthere should be effects over the screen that mimic a CRT monitor\n\nthe game should aim to be similar to Conway's Game of Life where the nations are the living organisms.\n\nlike conway's game of life, nations should be able to \"see\" eachother and react to eachother\n\nlike conway's game of life, the nations should be able to \"see\" the resources and react to them\n\nthere should be a chart page that tracks just about everything that can be tracked in the game\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/simstudioai/sim/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n\n<!-- BEGIN:turborepo-agent-rules -->\n\n# This is NOT the Turborepo you know\n\nTurborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p \"require.resolve('turbo/package.json')\"` from a workspace that depends on `turbo`.\n\nRead `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.\n\nThis block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `\"agentGuidance\": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.\n<!-- END:turborepo-agent-rules -->\n","isInternal":false,"tokens":5461,"sizeBytes":23170},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/simstudioai/sim/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n","isInternal":false,"tokens":5163,"sizeBytes":21828},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/marketplace.json","title":"Claude Plugin Marketplace Catalog","category":"marketplace","format":"json","content":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/marketplace.json)","tokens":300,"sizeBytes":194},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":180},{"name":"mcp.json","path":".cursor/mcp.json","rawUrl":"https://raw.githubusercontent.com/simstudioai/sim/HEAD/.cursor/mcp.json","title":"Model Context Protocol (MCP) Configuration","category":"mcp-config","format":"json","content":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.cursor/mcp.json)","tokens":300,"sizeBytes":173},{"name":".cursorrules","path":".cursorrules (ascii-simulation-game-cursorrules-prompt-file.mdc)","rawUrl":"https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/main/rules/ascii-simulation-game-cursorrules-prompt-file.mdc","title":"Awesome Cursorrules: ascii simulation game","category":"cursor-rule","format":"markdown","content":"---\ndescription: \"Cursor rules for ASCII simulation game development.\"\nglobs: **/*\nalwaysApply: false\n---\nyou are an expert game designer and game programmer, you will choose the best game design and coding practices for all decisions in this project.\n\nThe game is based on a 10x10 grid, each square has a 10x10 grid inside of it. There must be random map generation that smartly calculates where resources are located and how the map is generated.\n\nThe player does not control anything in the game the player is simply an observer, therefore there should be logs for almost everything in the game and it should be turn based.\n\nAll nations should operate the same, their capabilities should be balanced. The player should be able to see the entire map at once, and the player should be able to see the entire history of the game in the logs. There should be a way to zoom in on a specific square to see more detail.\n\nNations should be able to trade resources with each other. Nations should be able to go to war with each other. Nations should be able to make peace with each other.\n\nThe time period of the game is constant and there is no technological tree. It takes place in ancient times.\n\nnations should spawn a minimum distance away from eachother\n\nthe entire game should be colored ASCII based in terms of graphics\n\nThere should be neutral land that can be claimed by any nation. Neutral land should be randomly generated each game.\n\nThere should be a way to view the current owner of a square. There should be a way to view the current resources of a square.\n\nvalue of resources should be based on their rarity throughout the entire map. nations can use gold to either buy resources or armies.\n\narmies are the primary way that nations can expand their territory.\n\nthere should be no talent tree or technology tree, nations should be balanced without the need for such a tree\n\npopulation should collect in towns and cities\n\nroads should connect towns and cities\n\nresources are spread throughout nations through roads\n\nnations attempt to spread their resources evenly over their territory\n\ngold is not omni present and must be transported using roads to the location where it is spent to build armies or develop land\n\noceans should be randomly generated to separate continents\n\nrivers should be randomly generated to connect oceans and flow across the map vertically or horizontally\n\nrivers are a food source for the land and farms can be built on them\n\nmountains should be randomly generated throughout the map\n\nmountains should be impassable by armies\n\nmines in mountains provide metal at 20% efficiency\n\nNations should expand towards resources that they have a low amount of of and away from resources that they have a high amount of\n\narmies should spawn at the town or city that issued the order\n\ntowns can only spawn a max level 3 army\n\ntowns have a 3 square radius for gathering resources\n\nas towns grow their radius grows, there are 3 levels of towns and cities\n\na Nation's largest city is its capital\n\npopulation can only live in towns and cities\n\nresources should be spread throughout the map in a way that encourages nations to expand into new squares\n\narmies can travel across oceans at .25x speed\n\narmies can travel on rivers to move across the map at 3x speed\n\nthere is a \"battle list\" that shows all the battles that have happened and stats about them\n\narmies go from level 1 to level 10 based on their funding\n\ninner squares can be developed into farms, forests, mines\n\narmies require wood, food, and metal to be created.\n\nnations must pay upkeep depending on the amount of armies and developed land they have\n\nbattles are resolved by the difference in army level and a RISK esque dice roll mechanic that is effected by army level\n\narmies can build castles that are good defensively and allow for funding of armies\n\narmies can be used to conquer squares from other nations\n\narmies can be used to defend squares from other nations\n\narmies can be used to attack other nations\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\nnations should start with the same amount of gold and land\n\nthe map should be color coded to show the owner of the square\n\nthere should be effects over the screen that mimic a CRT monitor\n\nthe game should aim to be similar to Conway's Game of Life where the nations are the living organisms.\n\nlike conway's game of life, nations should be able to \"see\" eachother and react to eachother\n\nlike conway's game of life, the nations should be able to \"see\" the resources and react to them\n\nthere should be a chart page that tracks just about everything that can be tracked in the game\n","tokens":1002,"sizeBytes":4817}],"systemPromptSnippet":"<yakaai_skills repo=\"simstudioai/sim\">\n<!-- File: AGENTS.md (Tokens: ~5461 | Category: root-instruction) -->\n# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n\n<!-- BEGIN:turborepo-agent-rules -->\n\n# This is NOT the Turborepo you know\n\nTurborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p \"require.resolve('turbo/package.json')\"` from a workspace that depends on `turbo`.\n\nRead `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.\n\nThis block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `\"agentGuidance\": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.\n<!-- END:turborepo-agent-rules -->\n\n<!-- File: CLAUDE.md (Tokens: ~5163 | Category: claude-rule) -->\n# Sim Development Guidelines\n\nThis file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.claude/rules/*.md` (indexed in `apps/sim/AGENTS.md`): Claude loads each one by path, and any other agent reads the file a section points to before editing in that area. Skills live in `.agents/skills/`.\n\n## Global Standards\n\n- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.\n- **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 })`.\n- **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, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.\n- **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. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.\n- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):\n  - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`\n  - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error`; never `e instanceof Error ? e : new Error(String(e))`\n  - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — never `e instanceof Error ? e.message : 'fallback'`\n  - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))`\n  - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — never `Object.fromEntries(Object.entries(...).filter(...))`\n  - `isRecordLike(value)` from `@sim/utils/object` — never redeclare `typeof value === 'object' && value !== null && !Array.isArray(value)`\n  - `toRecord(value)` / `toRecordOrNull(value)` / `toArray(value)` from `@sim/utils/object` — coerce an untyped payload value; never inline `isRecordLike(v) ? v : {}` or `Array.isArray(v) ? v : []`. Where the source is already typed, keep the inline `Array.isArray` check: it narrows, while `toArray` asserts\n  - `toStringOrNull(value)` / `toNumberOrNull(value)` / `toBooleanOrNull(value)` from `@sim/utils/coerce` — read one scalar out of an untyped payload; never declare a local one-liner byte-identical to one of these. Keep a local helper that differs: `undefined` instead of `null` changes the wire shape, and a `Number.isFinite` or string-parse variant is a stricter check these omit\n  - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis\n  - `escapeRegExp(value)` from `@sim/utils/string` — never inline `replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')`\n  - `compareStrings(left, right)` from `@sim/utils/string` — code-unit ordering for hashes, fingerprints, and cross-process comparisons; never `localeCompare` there\n  - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — never reimplement exponential backoff inline\n- **Deployment flags in the browser**: client code inside a workspace, organization, or standalone settings surface reads `hosted`, `billingEnabled`, `chatEnabled`, and the enterprise feature set through `useDeploymentShape()` (components) or `getDeploymentShape()` (block conditions, stores, helpers) from `@/lib/core/config/deployment-shape`, never `isHosted`/`isBillingEnabled`/... from `env-flags`. Those constants freeze at module init from the root layout's `NEXT_PUBLIC_*` transport, which Next's bare 404 shell and `global-error` never emit, so a recovered tab would render Sim Cloud as self-hosted; the reader is seeded from the server-resolved workspace host context, organization layout, or standalone settings layout instead. Server code keeps reading `env-flags`.\n- **Type-checking**: `bun run type-check` at the root checks every workspace (`bun run type-check` inside a workspace checks just that one). `apps/sim`'s tsconfig excludes `*.test.ts(x)`, so `type-check` skips its tests; CI's `test-types` job ratchets their errors per file and TS code instead (`check:test-types`, whose header says how to fix a finding): a test file may not gain one. Never remove the `@typescript/native` alias from the root `devDependencies`: nothing imports it, but without it a bare `tsc` resolves to the ~10x slower TypeScript 6 compiler. `bun run check:native-typecheck` enforces this; its header explains the bin-resolution trap.\n\n## Architecture\n\n### Repository layout\n\n```\napps/\n├── sim/                    # Next.js app: UI, API routes, workflow builder, executor\n│   ├── app/                # App router — pages and API routes (app/api/**)\n│   ├── blocks/             # Block definitions and registry\n│   ├── tools/              # Tool definitions and registry\n│   ├── triggers/           # Trigger definitions and registry\n│   ├── connectors/         # Knowledge base connectors\n│   ├── executor/           # Workflow execution engine\n│   ├── providers/          # LLM provider integrations\n│   ├── components/         # Shared app UI (ui/, icons, …)\n│   ├── hooks/              # Shared hooks (queries/, selectors/)\n│   ├── stores/             # Zustand stores\n│   ├── lib/                # App-wide modules, incl. lib/api/contracts and lib/<domain>/application\n│   └── ee/                 # Enterprise features\n├── realtime/               # Bun Socket.IO server (collaborative workflow builder)\n├── desktop/                # Electron shell around the hosted web app\n├── docs/                   # Documentation site\n└── pii/                    # Python PII detection service (not in the JS/turbo build)\n\npackages/\n├── emcn/                   # @sim/emcn — design system (chip family, tokens, icons)\n├── db/                     # @sim/db — Drizzle schema, migrations, client\n├── auth/                   # @sim/auth — shared Better Auth verifier\n├── platform-authz/         # @sim/platform-authz — workspace + workflow authz (subpath exports)\n├── audit/ logger/ security/ utils/ runtime-secrets/ deployment-config/\n├── realtime-protocol/ browser-protocol/ terminal-protocol/ desktop-bridge/\n├── workflow-types/ workflow-persistence/ workflow-renderer/\n├── testing/                # @sim/testing — test factories and mocks\n├── tsconfig/               # shared tsconfig presets\n└── cli/ sim-cli/ sim-setup/ ts-sdk/ python-sdk/   # published CLIs and SDKs\n```\n\n### Package boundaries\n\n- `apps/* → packages/*` only. Packages never import from `apps/*`.\n- `apps/realtime` avoids Next.js, React, the block/tool registry, provider SDKs, and the executor. Never add imports from `@/lib/webhooks/providers/*`, `@/executor/*`, `@/blocks/*`, or `@/tools/*` to any package it consumes; it calls back into `apps/sim` only over internal HTTP with `INTERNAL_API_SECRET`. CI enforces this via `scripts/check-monorepo-boundaries.ts` and `scripts/check-realtime-prune-graph.ts`.\n- Auth is shared across both apps via the Better Auth \"Shared Database Session\" pattern (same `BETTER_AUTH_SECRET`, same DB via `@sim/db`).\n\n### Application Operation Boundary\n\n- Every protected read, write, canonical resource lookup, or authorization-sensitive reference resolution enters through an authorized application use case.\n- Define one stable semantic operation with its minimum role, workspace-key policy, allowed principal kinds, and delegated services. Internal APIs, v2 APIs, Copilot, and trusted tools call the same use case when the domain behavior is the same.\n- Surface adapters authenticate and construct a `Principal`, apply request-rate policy, parse contracts, map input, and present their own result. They never query protected data, decide resource authorization, implement business transactions, or record semantic audit (`check:source-conventions` ratchets value imports of `@sim/db` under `app/api/`).\n- Application use cases load canonical context, compare asserted scope, authorize current access, execute managers/repositories, project semantic audit, and trigger shared domain effects. Managers accept canonical IDs and scope, never credentials or principals. Application code stays surface-neutral (`check:boundaries`): never `next/server` or `app/api/**`, and never a runtime import of a route contract object, presenter, or Copilot handler (contract types, schemas, and constants are fine); a surface fact, such as a cursor's route, comes in as input.\n- Copilot is a surface adapter. Use `createCopilotApplicationAdapter` and the domain's registered operation object; never a Copilot-only authorization or business implementation.\n- Protected compound mutations belong in one top-level semantic application operation, never a sequence of independently committing mutations in a route or tool adapter.\n- Never substitute a billing owner, uploader, creator, or API-key owner for the acting principal. Fail fast when the identity model or operation policy cannot express the caller.\n- Use the `migrate-application-operation` skill whenever creating or migrating a protected endpoint, tool command, or resource method.\n\nThe `'use client'` server boundary, the app/worker runtime env split, and feature folder layout are in `.claude/rules/sim-architecture.md`.\n\n## Code Conventions\n\n- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`. Inside `lib/`, `executor/`, `providers/`, `stores/`, `hooks/`, `serializer/`, and a package's `src/`, a file never starts with its folder's name (`logs/views.ts`, not `logs/log-views.ts`), and a file in `utils/` or `helpers/` never repeats that role (`date.ts`, not `date-utils.ts`); `feature/feature.tsx` stays the component convention. `check:file-names` ratchets this.\n- **Imports**: absolute (`@/...`) only, never relative (a barrel `index.ts` re-exports its own siblings relatively). `check:source-conventions` ratchets relative imports in `apps/sim`. 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`.\n- **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)`).\n- **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, or a type parameter kept for API shape. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.\n- **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()`): `.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.\n- **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`.\n- **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.\n- **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`.\n- **Caching**: `lru-cache` with a `max` ceiling, never a hand-rolled TTL `Map`; a lifecycle map is not a cache; cache the gate, never the credential: `.claude/rules/sim-caching.md`.\n\n## API Contracts and Routes\n\n- Request/response shapes for every route under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**`, built with `defineRouteContract`. Export the contract; export a named schema or type alias only when another module imports it (`check:unused-exports`). Routes never import `zod` or define route-local boundary schemas; clients never write ad-hoc wire types or `z.input`/`z.output`.\n- Every route handler runs inside `withRouteHandler`. Ordinary internal and v2 routes use the shared builders (`defineInternalJsonRoute`, `defineV2JsonRoute`, binary/stream variants), which already apply it — never double-wrap. Raw `withRouteHandler` is only for documented protocol or lifecycle exceptions. Never export a bare `async function GET/POST/...` (`check:source-conventions`).\n- Same-origin JSON calls go through `requestJson(contract, ...)` from `@/lib/api/client/request`. A raw `fetch` is only for streaming, binary downloads, multipart uploads, signed URLs, OAuth redirects, or external origins, and carries `// boundary-raw-fetch: <reason>`.\n- The other script-enforced exceptions are `// double-cast-allowed:`, `// boundary-raw-json:`, and `// untyped-response:`. Never add one to silence a fixable finding.\n- `bun run check:api-validation:strict` must pass. Full contract rules, route pattern, annotation placement, the end-to-end order, and the schema review checklist: `.claude/rules/sim-api-contracts.md`. React Query hooks (key factories, named `staleTime` constants, `signal`, invalidation, server prefetch): `.claude/rules/sim-queries.md`.\n\n## Styling and EMCN\n\n- Tailwind only. Inline `style` only for a genuinely dynamic value or a CSS variable; type size is a named token, never `text-[Npx]` (`check:source-conventions` ratchets both). Never update global styles; keep styling local to the component. `cn()` from `@sim/emcn` for conditional classes. `size-*` for equal height and width (icons default `size-[14px]`), never `h-N w-N`.\n- Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons from `@sim/emcn/icons`; CSS modules by file path. Never deep-import other component subpaths.\n- The chip family is the canonical chrome: `ChipInput`, `ChipTextarea`, `ChipModal`/`ChipModalField`, `ChipSelect`/`ChipCombobox`/`ChipDropdown`, `ChipSwitch` (a segmented choice; a boolean toggle stays `Switch`), `ChipDatePicker`, `Chip`/`ChipLink`, `ChipTag`; `DropdownMenu` for context/action menus. Components own their chrome: consumers pass props (`error`, `icon`, `endAdornment`, `inputClassName`) and `className` carries only layout/sizing. Every labeled field inside a `ChipModalBody` is a `ChipModalField`.\n- Consumer rules, tokens, text scale, and modal rhythm: `.claude/rules/sim-styling.md`. Authoring components in `packages/emcn`: `.claude/rules/emcn-components.md`. Product UI copy: `.claude/rules/sim-ui-copy.md`. Marketing copy and positioning: `.claude/rules/constitution.md`.\n\n## Testing\n\nMost unit tests in a codebase like this restate the code they test. They pass on the first run, break on every refactor, and catch nothing that type-check, `next build`, `bun run check:audits`, or a real end-to-end run would miss. Test for confidence, not coverage.\n\n- **Never write unit tests after you write code.** A test written to describe code that already exists restates the implementation and proves nothing. If the change needs proof, prove it end to end.\n- **Highly prefer E2E tests.** Use them to verify complex features work, against the real boundary: real Postgres/Redis (`*.integration.ts`), the running app over real HTTP (`apps/sim/scripts/test-*-e2e.ts`), or the packaged desktop app (`apps/desktop/e2e`, Playwright). At the end of an E2E test, produce a verifiable and repeatable artifact — a JSON report of each check with status and duration, an HTTP status log, a trace, or a screenshot — written to a caller-supplied `<SUITE>_REPORT_PATH` and uploaded by CI on failure. `apps/sim/scripts/test-scim-e2e.ts` is the reference.\n- **If you must test a system in isolation, first write down all the ways it could fail, then write the code.** Each failure mode (bad input, boundary, concurrency, partial failure, permission denial, resource cap) becomes one test that fails before the code exists.\n- A regression test must fail on the pre-fix code. Revert each guard of the fix and watch its test go red before you trust it.\n- Never write tests that restate declarations (block/tool/provider config, registries, constants, schemas accepting valid input), mock every collaborator and assert `toHaveBeenCalledWith` on the mocks, check rendered text or class names, or test mocks and factories themselves.\n- Never hand-roll a mock or test helper that `apps/sim/vitest.setup.ts` or `@sim/testing` already provides; a module mocked in a third file gets one central mock. `bun run check:test-patterns` enforces this.\n\nUse the `test-audit` skill whenever you write, change, review, or sweep tests — it holds the authoring gate, the junk patterns, and the retention bar. Test layers, file naming, and Vitest mechanics (global mocks, `@sim/testing`, performance rules) are in `.claude/rules/sim-testing.md`.\n\n## Integrations\n\nBuild order: **Tools** → **Block** → **Icon** → optional **Trigger**, starting from the service's API docs. Use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`. Two rules the skills assume:\n\n- **Tool IDs are `snake_case`** (`service_action`), registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`, alphabetically).\n- **Type coercions go in `tools.config.params`** (runs at execution, after variable resolution), never in `tools.config.tool` (runs at serialization, where `Number()` destroys dynamic `<Block.output>` references).\n\nRemaining block/tool/trigger rules: `.claude/rules/sim-integrations.md`. Canvas sentences: `apps/sim/blocks/AGENTS.md`.\n\n## Tables\n\nTable column types are registry entries in `apps/sim/lib/table/column-types/` — one file per type owning its label, icon, storage cast, coercion, validation, conversion compatibility, formatting, and editor. `Record<ColumnType, …>` on `registry.ts` and `registry.server.ts` is a compile-time completeness gate: adding a type to the union errors until both entries exist.\n\nNever add a `case 'sometype':` outside `column-types/`, except the one documented import-path switch (`coerceValue` in `lib/table/import.ts`), which every new type extends — a missing arm fails silently (a wrong `jsonbCast` breaks every filter on the column). If a consumer needs per-type knowledge, add a registry field. Use `/add-column-type` for the full procedure.\n\n## How your work is checked\n\nBefore declaring a change done, run the local gate from the repo root; CI runs these and fails on any of them:\n\n```bash\nbun run lint            # biome format + lint, autofixes (CI runs lint:check)\nbun run type-check      # every workspace\nbun run check:audits    # every check:* audit, generated-artifact checks included\nbun run test            # script tests, then every workspace's Vitest suite\ngit fetch origin staging  # the block-registry check diffs against it\nbun run apps/sim/scripts/check-block-registry.ts origin/staging\n```\n\nAuthor a `packages/db` schema change or migration with the `db-migrate` skill. CI also runs `bun run check:migrations` (it diffs against a base ref, `origin/staging` by default, so it is not in `check:audits`; run it 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 `--update` after removing violations; it refuses to admit new debt. The one exception is `check:tool-registry-boundary`, whose module-count baseline is re-recorded with `--update-baseline` when growth is deliberate (see its skill).\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~300 | Category: marketplace) -->\n# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/marketplace.json)\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude-plugin/plugin.json)\n\n<!-- File: .cursor/mcp.json (Tokens: ~300 | Category: mcp-config) -->\n# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/simstudioai/sim/HEAD/.cursor/mcp.json)\n\n<!-- File: .cursorrules (ascii-simulation-game-cursorrules-prompt-file.mdc) (Tokens: ~1002 | Category: cursor-rule) -->\n---\ndescription: \"Cursor rules for ASCII simulation game development.\"\nglobs: **/*\nalwaysApply: false\n---\nyou are an expert game designer and game programmer, you will choose the best game design and coding practices for all decisions in this project.\n\nThe game is based on a 10x10 grid, each square has a 10x10 grid inside of it. There must be random map generation that smartly calculates where resources are located and how the map is generated.\n\nThe player does not control anything in the game the player is simply an observer, therefore there should be logs for almost everything in the game and it should be turn based.\n\nAll nations should operate the same, their capabilities should be balanced. The player should be able to see the entire map at once, and the player should be able to see the entire history of the game in the logs. There should be a way to zoom in on a specific square to see more detail.\n\nNations should be able to trade resources with each other. Nations should be able to go to war with each other. Nations should be able to make peace with each other.\n\nThe time period of the game is constant and there is no technological tree. It takes place in ancient times.\n\nnations should spawn a minimum distance away from eachother\n\nthe entire game should be colored ASCII based in terms of graphics\n\nThere should be neutral land that can be claimed by any nation. Neutral land should be randomly generated each game.\n\nThere should be a way to view the current owner of a square. There should be a way to view the current resources of a square.\n\nvalue of resources should be based on their rarity throughout the entire map. nations can use gold to either buy resources or armies.\n\narmies are the primary way that nations can expand their territory.\n\nthere should be no talent tree or technology tree, nations should be balanced without the need for such a tree\n\npopulation should collect in towns and cities\n\nroads should connect towns and cities\n\nresources are spread throughout nations through roads\n\nnations attempt to spread their resources evenly over their territory\n\ngold is not omni present and must be transported using roads to the location where it is spent to build armies or develop land\n\noceans should be randomly generated to separate continents\n\nrivers should be randomly generated to connect oceans and flow across the map vertically or horizontally\n\nrivers are a food source for the land and farms can be built on them\n\nmountains should be randomly generated throughout the map\n\nmountains should be impassable by armies\n\nmines in mountains provide metal at 20% efficiency\n\nNations should expand towards resources that they have a low amount of of and away from resources that they have a high amount of\n\narmies should spawn at the town or city that issued the order\n\ntowns can only spawn a max level 3 army\n\ntowns have a 3 square radius for gathering resources\n\nas towns grow their radius grows, there are 3 levels of towns and cities\n\na Nation's largest city is its capital\n\npopulation can only live in towns and cities\n\nresources should be spread throughout the map in a way that encourages nations to expand into new squares\n\narmies can travel across oceans at .25x speed\n\narmies can travel on rivers to move across the map at 3x speed\n\nthere is a \"battle list\" that shows all the battles that have happened and stats about them\n\narmies go from level 1 to level 10 based on their funding\n\ninner squares can be developed into farms, forests, mines\n\narmies require wood, food, and metal to be created.\n\nnations must pay upkeep depending on the amount of armies and developed land they have\n\nbattles are resolved by the difference in army level and a RISK esque dice roll mechanic that is effected by army level\n\narmies can build castles that are good defensively and allow for funding of armies\n\narmies can be used to conquer squares from other nations\n\narmies can be used to defend squares from other nations\n\narmies can be used to attack other nations\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\narmies can be used to attack other nations squares\n\narmies can be used to attack neutral squares\n\nnations should start with the same amount of gold and land\n\nthe map should be color coded to show the owner of the square\n\nthere should be effects over the screen that mimic a CRT monitor\n\nthe game should aim to be similar to Conway's Game of Life where the nations are the living organisms.\n\nlike conway's game of life, nations should be able to \"see\" eachother and react to eachother\n\nlike conway's game of life, the nations should be able to \"see\" the resources and react to them\n\nthere should be a chart page that tracks just about everything that can be tracked in the game\n</yakaai_skills>"}