Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7438455
fix(audits): guard the route wrapper against lib/mothership, not the …
waleedlatif1 Oct 2, 2026
fcfecd7
improvement(audits): add check:guidance-refs and fix the stale refere…
waleedlatif1 Oct 2, 2026
8570531
improvement(audits): check rule frontmatter globs in check:guidance-refs
waleedlatif1 Oct 2, 2026
bb165d2
docs(agents): consolidate guidance and add the local gate to CLAUDE.md
waleedlatif1 Oct 2, 2026
8f26f89
docs(agents): fix stale facts in connector, model, column-type, and s…
waleedlatif1 Oct 2, 2026
973ebbc
docs(agents): fix contradictions and stale facts in platform skills
waleedlatif1 Oct 2, 2026
ac9bded
docs(agents): align UI skills with the settings, emcn, and state rules
waleedlatif1 Oct 2, 2026
54c89b8
docs(agents): fix contradictions and stale facts in integration skills
waleedlatif1 Oct 2, 2026
469e741
docs(agents): map the new ratchets and lint rules in the guardrail table
waleedlatif1 Oct 2, 2026
0a0a698
docs(agents): correct audit findings in guidance and gate docs
waleedlatif1 Oct 2, 2026
79639a4
docs(agents): drop the hand-kept rule-to-check table from CLAUDE.md
waleedlatif1 Oct 2, 2026
8e126e8
improvement(audits): share rule frontmatter parsing and fail on dead …
waleedlatif1 Oct 2, 2026
446b4eb
fix(audits): resolve wildcard and root package exports, ignore delete…
waleedlatif1 Oct 2, 2026
b4ac006
docs(agents): correct review findings in skills and rules
waleedlatif1 Oct 2, 2026
351622b
fix(audits): resolve import specifiers to module files only; tighten …
waleedlatif1 Oct 2, 2026
5c0d278
docs(agents): keep the repo-wide ES2023 ban in sim-components after r…
waleedlatif1 Oct 2, 2026
30e80f9
fix(audits): require exact export targets to exist and resolve markdo…
waleedlatif1 Oct 2, 2026
ffccca6
fix(audits): check require() specifiers and keep path resolution insi…
waleedlatif1 Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/add-block-preview/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ To pull an already-GA block from discovery surfaces on hosted (incident, depreca
- **Clone-not-remove:** gated blocks stay in `getAllBlocks()` output as clones with `hideFromToolbar: true` — `.find`-by-type consumers rely on this. Never filter them out.
- **Keys are registry block types.** Never `custom_block_*` (parse drops them — custom blocks have their own enabled/disabled lifecycle).
- **The shared hidden-predicate is `isHiddenUnder`** (`apps/sim/blocks/visibility/context.ts`). Never restate the preview/disabled rule inline at a new consumer.
- **Process-global caches stay ungated.** `getStaticComponentFiles` (VFS) and `getExposedIntegrationTools` build the ungated universe; per-viewer filtering happens at stamp/consumer time. Never move gating into a shared builder.
- **Process-global caches stay ungated.** Shared builders such as `getExposedIntegrationTools` (`lib/integrations/tool-catalog.ts`) build the ungated universe; per-viewer filtering happens at consumer time via `isHiddenUnder`. Never move gating into a shared builder.
- Gating is **surface hiding, not secrecy** — the full config ships in the client JS bundle. Anything truly secret cannot be a registered block.

## Tests
Expand Down
66 changes: 36 additions & 30 deletions .agents/skills/add-block/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export const {ServiceName}Block: BlockConfig = {
docsLink: 'https://docs.sim.ai/integrations/{service}',
category: 'tools', // 'tools' | 'blocks' | 'triggers'
integrationType: IntegrationType.X, // Primary category (see IntegrationType enum)
tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type)
bgColor: '#HEXCOLOR', // Brand color
icon: {ServiceName}Icon,

Expand Down Expand Up @@ -63,7 +62,7 @@ export const {ServiceName}Block: BlockConfig = {
},

inputs: {
// Optional: define expected inputs from other blocks
// Required: the params the block accepts, keyed by tool param / canonical id
},

outputs: {
Expand All @@ -74,7 +73,7 @@ export const {ServiceName}Block: BlockConfig = {

## SubBlock Types Reference

**Critical:** Every subblock `id` must be unique within the block. Duplicate IDs cause conflicts even with different conditions.
**Critical:** Give every subblock a unique `id`: duplicates collide silently (the last definition wins). `blocks.test.ts` fails a duplicate within one condition unless the copies are a basic/advanced mode-swap pair, one basic plus trigger-mode copies, or all carry `canonicalParamId`. The only sanctioned cross-condition reuse is the hosted-key `apiKey` pair (`add-hosted-key` skill), where both fields deliberately share one value.

### Text Inputs
```typescript
Expand Down Expand Up @@ -129,6 +128,7 @@ export const {ServiceName}Block: BlockConfig = {
id: 'credential',
title: 'Account',
type: 'oauth-input',
canonicalParamId: 'oauthCredential',
serviceId: '{service}', // Must match OAuth provider service key
requiredScopes: getScopesForService('{service}'), // Import from @/lib/oauth/utils
placeholder: 'Select account',
Expand Down Expand Up @@ -370,8 +370,6 @@ Declare the **canonical** id with `type: 'json'` — the subblock ids never reac
```typescript
inputs: {
file: { type: 'json', description: 'File to upload (UserFile or reference)' },
// Legacy field for backwards compatibility
fileContent: { type: 'string', description: 'Legacy: base64 encoded content' },
}
```

Expand Down Expand Up @@ -500,6 +498,7 @@ Controls which UI view shows the field.
- `'advanced'` - Only in advanced view
- `'both'` - Both views (default if not specified)
- `'trigger'` - Only in trigger configuration
- `'trigger-advanced'` - The advanced side of a trigger field (a canonical pair member, or a standalone field under the block-level advanced toggle)

### canonicalParamId Pattern

Expand Down Expand Up @@ -616,7 +615,7 @@ tools: {
- `items` property - This is only for tool outputs with array types

Block outputs only support:
- `type` - The data type ('string', 'number', 'boolean', 'json', 'array')
- `type` - The data type ('string', 'number', 'boolean', 'json', 'array', 'file', 'file[]', 'any')
- `description` - Human readable description
- `condition` - Optional visibility condition
- `hiddenFromDisplay` - Optional flag to hide from the output display
Expand Down Expand Up @@ -679,7 +678,7 @@ export const ServiceV2Block: BlockConfig = {
access: ServiceBlock.tools?.access?.map(id => `${id}_v2`) || [],
config: {
tool: createVersionedToolSelector({
baseToolSelector: (params) => (ServiceBlock.tools?.config as any)?.tool(params),
baseToolSelector: (params) => ServiceBlock.tools.config?.tool(params) ?? 'service_default',
suffix: '_v2',
fallbackToolId: 'service_default_v2',
}),
Expand All @@ -697,7 +696,7 @@ export const ServiceV2Block: BlockConfig = {
Register the block in `apps/sim/blocks/registry-maps.ts` — add the import and an entry to each map alphabetically:

```typescript
import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/service'
import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/{service}'

export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
// ... existing blocks ...
Expand Down Expand Up @@ -726,11 +725,23 @@ export const ServiceBlock: BlockConfig = {
docsLink: 'https://docs.sim.ai/integrations/service',
category: 'tools',
integrationType: IntegrationType.DeveloperTools,
tags: ['oauth', 'api'],
bgColor: '#FF6B6B',
icon: ServiceIcon,
authMode: AuthMode.OAuth,

// Sentence rules: apps/sim/blocks/AGENTS.md → "Canvas sentences"
canvasPresentation: {
defaultTitle: 'Create Resource',
sentences: {
byOperation: {
create: [{ text: 'Create resource', field: 'name', core: true }],
read: [{ text: 'Read resource', field: 'resourceId', core: true }],
update: [{ text: 'Update resource', field: 'resourceId', core: true }],
delete: [{ text: 'Delete resource', field: 'resourceId', core: true }],
},
},
},

subBlocks: [
{
id: 'operation',
Expand All @@ -748,6 +759,7 @@ export const ServiceBlock: BlockConfig = {
id: 'credential',
title: 'Service Account',
type: 'oauth-input',
canonicalParamId: 'oauthCredential',
serviceId: 'service',
requiredScopes: getScopesForService('service'),
placeholder: 'Select account',
Expand Down Expand Up @@ -778,6 +790,13 @@ export const ServiceBlock: BlockConfig = {
},
},

inputs: {
operation: { type: 'string', description: 'Operation to perform' },
oauthCredential: { type: 'string', description: 'Service access token' },
resourceId: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
},

outputs: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
Expand Down Expand Up @@ -937,30 +956,17 @@ tool IDs through `tools.access` and does not change any tool's shape.

But if the same change also adds, edits **or removes** a tool, run `bun run tool-metadata:generate` and commit the result, or CI fails on stale artifacts. That matters here because a block's `outputs` are authored to match its tools' outputs, and the UI reads those from the generated metadata, not the executable registry — an unregenerated tool change makes the block's outputs disagree with what the panel renders. See `.agents/skills/tool-registry-boundary/SKILL.md`.

A visible integration block does require the generated integration catalog and docs to be refreshed.
After adding or changing one, run:

```bash
bun run scripts/generate-docs.ts
bun run deployment-config:generate
bun run integration-catalog:check
bun run deployment-config:check
bun run docs:check
```

The catalog check independently derives deployment metadata from the executable block registry and
compares it with the committed `packages/deployment-config/src/integrations.json`. The deployment
config check verifies the generated service-account facts against the canonical OAuth registry and
catalog. `docs:check` re-renders every generated docs artifact in memory and fails on any committed
file that differs — it runs in CI via `check:audits`, so commit the full generator output. If the
generator also trues up pages an earlier PR left stale, commit that catch-up too; reverting it as
"unrelated drift" makes `docs:check` fail. Review the generated diff and keep only intentional
changes.
A visible integration block does require the generated integration catalog and docs to be refreshed:
`bun run tool-metadata:generate` (only when a tool changed), `bun run scripts/generate-docs.ts`,
`bun run deployment-config:generate`, then `bun run check:audits`. Also run
`bun run apps/sim/scripts/check-block-registry.ts origin/staging` (CI runs it outside `check:audits`). Commit the
full generator output. For what each check verifies, see the `validate-integration` skill →
Regenerate Derived Artifacts.

## Checklist Before Finishing

- [ ] `integrationType` is set to the correct `IntegrationType` enum value
- [ ] `tags` array includes all applicable `IntegrationTag` values
- [ ] `{Service}BlockMeta.tags` lists every applicable `IntegrationTag` (tags live on the meta, not the block)
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
- [ ] Conditions use correct syntax (field, value, not, and)
- [ ] DependsOn set for fields that need other values
Expand Down Expand Up @@ -996,7 +1002,7 @@ Validate the block against every tool in `tools.access`:
2. **For each tool, verify the block has correct:**
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
- `tools.config.params` correctly maps subBlock IDs to tool param names (if they differ)
- Each subBlock (or its `canonicalParamId`) is named exactly after the tool param it fills. A required `user-only` param that is only renamed in `tools.config.params` fails `bun run apps/sim/scripts/check-block-registry.ts origin/staging`; remap only optional or `user-or-llm` params
- Type coercions in `tools.config.params` for any params that need conversion (Number(), Boolean(), JSON.parse())
3. **Verify block outputs** cover the key fields returned by all tools
4. **Verify conditions** — each subBlock should only show for the operations that actually use it
Expand Down
16 changes: 8 additions & 8 deletions .agents/skills/add-column-type/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A `case 'yourtype':` outside `column-types/` fails **silently** when missed (a w

## Hard Rule: the compiler tells you what to do

Do **not** hunt for places to edit. Add your type to the `ColumnType` union first and let `tsc` produce the list:
Do **not** hunt for places to edit. Append your type's id to the `COLUMN_TYPES` array in `column-types/types.ts` first (`ColumnType` derives from it) and let `tsc` produce the list:

```bash
cd apps/sim && bun run type-check
Expand Down Expand Up @@ -86,7 +86,7 @@ export function Type{Pascal}(props: SVGProps<SVGSVGElement>) {

## Step 3: Write the type file

`apps/sim/lib/table/column-types/{name}.ts`. Copy the closest existing type and change what differs. Every field is required by the interface, so the compiler enumerates them for you — read the TSDoc in `types.ts` rather than guessing.
`apps/sim/lib/table/column-types/{name}.ts`. Copy the closest existing type and change what differs. Required fields are compiler-enforced; optional hooks (`isCompatibleWith`, `salvage`, `valueForEquality`, `filterOperatorsFor`, …) default sensibly — read the TSDoc in `types.ts` before overriding.

The three that are easy to get wrong:

Expand Down Expand Up @@ -132,18 +132,18 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not,
| `column-types/types.ts` `TYPE_SPECIFIC_COLUMN_KEYS` | it is never stripped on conversion, and poisons the target type |
| `lib/api/contracts/tables.ts` — the schema slot in all three column schemas, plus `refineColumnOptions` | zod strips it at the boundary; silently never saved |
| `columns/service.ts` `addTableColumn` param type | callers cannot pass it |
| A metadata-only update path (`updateColumnCurrency` is the model) + a branch in both column routes + the copilot tool | changing it on an existing column is a silent 200 no-op |
| A metadata-only update in `lib/table/columns/service.ts` (`updateColumnCurrency` is the model) + a branch in `performUpdateTableColumn` in `lib/table/orchestration/columns.ts` | changing it on an existing column is a silent 200 no-op |
| `column-config-sidebar.tsx` | no UI to set it |
| `table-grid.tsx` delete-column undo + `use-table-undo.ts` restore | undo silently resets it to the default |

`normalizeColumn`, `buildConvertedColumn`, and the undo snapshot read `TYPE_SPECIFIC_COLUMN_KEYS` generically, so those three are already zero-edit.

**Known gap:** the metadata-only update path is ~6 near-identical copies (service + 2 routes + copilot). A `metadataUpdate` descriptor on `ColumnTypeServerDefinition` would collapse them; until that exists, copy `currency`'s.
Copy `currency`'s service function and orchestration branch.

## Checklist Before Finishing

- [ ] Added to the `ColumnType` union in `column-types/types.ts`
- [ ] `column-types/{id}.ts` created, every interface field filled in
- [ ] Id appended to `COLUMN_TYPES` in `column-types/types.ts`
- [ ] `column-types/{id}.ts` created, every required field filled in
- [ ] Registered in **both** `registry.ts` and `registry.server.ts`
- [ ] Icon added, centered on the family's optical center, exported alphabetically
- [ ] `migrateCellsTo` / `migrateCellsFrom` added if the stored bytes change
Expand All @@ -155,6 +155,6 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not,

1. **`cd apps/sim && bun run type-check`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry.
2. **Grep for leaks** — `grep -rnE "(===|!==) '{id}'|case '{id}':" apps/sim --include='*.ts' --include='*.tsx' | grep -v column-types/`. (All three forms: a plain `!==` and a `case` are how half of `currency`'s real branches are written.) Hits are expected; judge each. A hit is fine when it mounts a specific React component or encodes a genuinely one-off behavior (`json`'s mono textarea, `date`'s timezone-aware parsing). A hit is a **leak** when it restates something the registry could answer — an icon, a label, a colour, an operator set, a cast, a coercion. Leaks get a registry field, not a new branch.
3. **Run the suite** — `bun run --cwd apps/sim test lib/table 'app/workspace/[workspaceId]/tables' lib/api app/api/table app/api/v1 lib/copilot/tools/server/table`. Existing tests must pass **unchanged**; needing to edit one means you changed behavior for the other types.
4. **`bun run lint`, `bun run check:api-validation`, `bun run check:client-boundary`** from the repo root.
3. **Run the suite** — `bun run --cwd apps/sim test lib/table 'app/workspace/[workspaceId]/tables' lib/api app/api/table app/api/v1`. Existing tests must pass **unchanged**; needing to edit one means you changed behavior for the other types.
4. **`bun run lint`, `bun run check:api-validation:strict`, `bun run check:client-boundary`** from the repo root.
5. **Exercise it in the running app** on a table with one column of every type: create, edit inline / in the expanded popover / in the row modal, paste from a spreadsheet, filter, sort, convert to and from other types, export CSV, undo a column delete.
Loading
Loading