Skip to content

Commit 086e896

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Merge staging into codex/emcn-next-resource-row
Preserve staging's BrandTile adapter while sourcing shared resource chrome from EMCN. Retain staging's intentional removals of obsolete connector and browser tests.
2 parents b744f87 + d9a6769 commit 086e896

7,579 files changed

Lines changed: 562437 additions & 592405 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/add-block-preview/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ To pull an already-GA block from discovery surfaces on hosted (incident, depreca
5353
- **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.
5454
- **Keys are registry block types.** Never `custom_block_*` (parse drops them — custom blocks have their own enabled/disabled lifecycle).
5555
- **The shared hidden-predicate is `isHiddenUnder`** (`apps/sim/blocks/visibility/context.ts`). Never restate the preview/disabled rule inline at a new consumer.
56-
- **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.
56+
- **Process-global caches stay ungated.** Shared builders such as `getExposedIntegrationTools` (`apps/sim/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.
5757
- Gating is **surface hiding, not secrecy** — the full config ships in the client JS bundle. Anything truly secret cannot be a registered block.
5858

5959
## Tests

‎.agents/skills/add-block/SKILL.md‎

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export const {ServiceName}Block: BlockConfig = {
3535
docsLink: 'https://docs.sim.ai/integrations/{service}',
3636
category: 'tools', // 'tools' | 'blocks' | 'triggers'
3737
integrationType: IntegrationType.X, // Primary category (see IntegrationType enum)
38-
tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type)
3938
bgColor: '#HEXCOLOR', // Brand color
4039
icon: {ServiceName}Icon,
4140

@@ -63,7 +62,7 @@ export const {ServiceName}Block: BlockConfig = {
6362
},
6463

6564
inputs: {
66-
// Optional: define expected inputs from other blocks
65+
// Required: the params the block accepts, keyed by tool param / canonical id
6766
},
6867

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

7574
## SubBlock Types Reference
7675

77-
**Critical:** Every subblock `id` must be unique within the block. Duplicate IDs cause conflicts even with different conditions.
76+
**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.
7877

7978
### Text Inputs
8079
```typescript
@@ -129,6 +128,7 @@ export const {ServiceName}Block: BlockConfig = {
129128
id: 'credential',
130129
title: 'Account',
131130
type: 'oauth-input',
131+
canonicalParamId: 'oauthCredential',
132132
serviceId: '{service}', // Must match OAuth provider service key
133133
requiredScopes: getScopesForService('{service}'), // Import from @/lib/oauth/utils
134134
placeholder: 'Select account',
@@ -310,7 +310,7 @@ When several fields are mutually exclusive alternatives, mark them all `required
310310
other paths ever get a chance to supply the value.
311311

312312
**Constraints (block-wide):**
313-
- `canonicalParamId` must not equal any subblock `id` in the block.
313+
- `canonicalParamId` may equal only the `id` of a member of its own group, as `channel` does in the canonicalParamId Pattern below; it must never equal any other subblock's `id`. (`blocks.test.ts` enforces the case of a subblock with no `canonicalParamId`.)
314314
- One canonical id links exactly one basic/advanced pair for one logical parameter. Groups are keyed by canonical id across every subblock and hold one `basicId`, so two operations that each need a pair need two canonical ids.
315315
- All members of a group share the same `required` status.
316316

@@ -370,8 +370,6 @@ Declare the **canonical** id with `type: 'json'` — the subblock ids never reac
370370
```typescript
371371
inputs: {
372372
file: { type: 'json', description: 'File to upload (UserFile or reference)' },
373-
// Legacy field for backwards compatibility
374-
fileContent: { type: 'string', description: 'Legacy: base64 encoded content' },
375373
}
376374
```
377375

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

504503
### canonicalParamId Pattern
505504

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

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

699698
```typescript
700-
import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/service'
699+
import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/{service}'
701700

702701
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
703702
// ... existing blocks ...
@@ -726,11 +725,23 @@ export const ServiceBlock: BlockConfig = {
726725
docsLink: 'https://docs.sim.ai/integrations/service',
727726
category: 'tools',
728727
integrationType: IntegrationType.DeveloperTools,
729-
tags: ['oauth', 'api'],
730728
bgColor: '#FF6B6B',
731729
icon: ServiceIcon,
732730
authMode: AuthMode.OAuth,
733731

732+
// Sentence rules: apps/sim/blocks/AGENTS.md → "Canvas sentences"
733+
canvasPresentation: {
734+
defaultTitle: 'Create Resource',
735+
sentences: {
736+
byOperation: {
737+
create: [{ text: 'Create resource', field: 'name', core: true }],
738+
read: [{ text: 'Read resource', field: 'resourceId', core: true }],
739+
update: [{ text: 'Update resource', field: 'resourceId', core: true }],
740+
delete: [{ text: 'Delete resource', field: 'resourceId', core: true }],
741+
},
742+
},
743+
},
744+
734745
subBlocks: [
735746
{
736747
id: 'operation',
@@ -748,6 +759,7 @@ export const ServiceBlock: BlockConfig = {
748759
id: 'credential',
749760
title: 'Service Account',
750761
type: 'oauth-input',
762+
canonicalParamId: 'oauthCredential',
751763
serviceId: 'service',
752764
requiredScopes: getScopesForService('service'),
753765
placeholder: 'Select account',
@@ -778,6 +790,13 @@ export const ServiceBlock: BlockConfig = {
778790
},
779791
},
780792

793+
inputs: {
794+
operation: { type: 'string', description: 'Operation to perform' },
795+
oauthCredential: { type: 'string', description: 'Service access token' },
796+
resourceId: { type: 'string', description: 'Resource ID' },
797+
name: { type: 'string', description: 'Resource name' },
798+
},
799+
781800
outputs: {
782801
id: { type: 'string', description: 'Resource ID' },
783802
name: { type: 'string', description: 'Resource name' },
@@ -902,16 +921,16 @@ Every block declares a one-line prose summary that replaces its card's field row
902921

903922
```
904923
Slack ← header (already names the block)
905-
Posts ⟨Ship it 🚀⟩ to ⟨#eng⟩ ← the sentence; ⟨…⟩ are live value chips
924+
Post ⟨Ship it 🚀⟩ to ⟨#eng⟩ ← the sentence; ⟨…⟩ are live value chips
906925
```
907926

908927
Write one `byOperation` entry per operation dropdown option (or a single `default`
909928
when the block has no operation dropdown).
910929

911-
**The full authoring contract — voice, structure, and the two mistakes that break
930+
**The full authoring contract — voice, structure, and the four mistakes that break
912931
cards silently — is `apps/sim/blocks/AGENTS.md` → "Canvas sentences". Read it
913-
before writing any.** The two failures worth repeating here, because both are
914-
invisible at runtime:
932+
before writing any.** Two of those four are worth repeating here, because both
933+
are invisible at runtime:
915934

916935
1. A clause naming only one member of a `canonicalParamId` pair drops the sentence
917936
for every advanced-mode user. List all members:
@@ -937,30 +956,17 @@ tool IDs through `tools.access` and does not change any tool's shape.
937956

938957
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`.
939958

940-
A visible integration block does require the generated integration catalog and docs to be refreshed.
941-
After adding or changing one, run:
942-
943-
```bash
944-
bun run scripts/generate-docs.ts
945-
bun run deployment-config:generate
946-
bun run integration-catalog:check
947-
bun run deployment-config:check
948-
bun run docs:check
949-
```
950-
951-
The catalog check independently derives deployment metadata from the executable block registry and
952-
compares it with the committed `packages/deployment-config/src/integrations.json`. The deployment
953-
config check verifies the generated service-account facts against the canonical OAuth registry and
954-
catalog. `docs:check` re-renders every generated docs artifact in memory and fails on any committed
955-
file that differs — it runs in CI via `check:audits`, so commit the full generator output. If the
956-
generator also trues up pages an earlier PR left stale, commit that catch-up too; reverting it as
957-
"unrelated drift" makes `docs:check` fail. Review the generated diff and keep only intentional
958-
changes.
959+
A visible integration block does require the generated integration catalog and docs to be refreshed:
960+
`bun run tool-metadata:generate` (only when a tool changed), `bun run scripts/generate-docs.ts`,
961+
`bun run deployment-config:generate`, then `bun run check:audits`. Also run
962+
`bun run apps/sim/scripts/check-block-registry.ts origin/staging` (CI runs it outside `check:audits`). Commit the
963+
full generator output. For what each check verifies, see the `validate-integration` skill →
964+
Regenerate Derived Artifacts.
959965

960966
## Checklist Before Finishing
961967

962968
- [ ] `integrationType` is set to the correct `IntegrationType` enum value
963-
- [ ] `tags` array includes all applicable `IntegrationTag` values
969+
- [ ] `{Service}BlockMeta.tags` lists every applicable `IntegrationTag` (tags live on the meta, not the block)
964970
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
965971
- [ ] Conditions use correct syntax (field, value, not, and)
966972
- [ ] DependsOn set for fields that need other values
@@ -996,7 +1002,7 @@ Validate the block against every tool in `tools.access`:
9961002
2. **For each tool, verify the block has correct:**
9971003
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
9981004
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
999-
- `tools.config.params` correctly maps subBlock IDs to tool param names (if they differ)
1005+
- 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
10001006
- Type coercions in `tools.config.params` for any params that need conversion (Number(), Boolean(), JSON.parse())
10011007
3. **Verify block outputs** cover the key fields returned by all tools
10021008
4. **Verify conditions** — each subBlock should only show for the operations that actually use it

‎.agents/skills/add-column-type/SKILL.md‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A `case 'yourtype':` outside `column-types/` fails **silently** when missed (a w
1212

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

15-
Do **not** hunt for places to edit. Add your type to the `ColumnType` union first and let `tsc` produce the list:
15+
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:
1616

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

8787
## Step 3: Write the type file
8888

89-
`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.
89+
`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.
9090

9191
The three that are easy to get wrong:
9292

@@ -132,29 +132,29 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not,
132132
| `column-types/types.ts` `TYPE_SPECIFIC_COLUMN_KEYS` | it is never stripped on conversion, and poisons the target type |
133133
| `lib/api/contracts/tables.ts` — the schema slot in all three column schemas, plus `refineColumnOptions` | zod strips it at the boundary; silently never saved |
134134
| `columns/service.ts` `addTableColumn` param type | callers cannot pass it |
135-
| 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 |
135+
| 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 |
136136
| `column-config-sidebar.tsx` | no UI to set it |
137137
| `table-grid.tsx` delete-column undo + `use-table-undo.ts` restore | undo silently resets it to the default |
138138

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

141-
**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.
141+
Copy `currency`'s service function and orchestration branch.
142142

143143
## Checklist Before Finishing
144144

145-
- [ ] Added to the `ColumnType` union in `column-types/types.ts`
146-
- [ ] `column-types/{id}.ts` created, every interface field filled in
145+
- [ ] Id appended to `COLUMN_TYPES` in `column-types/types.ts`
146+
- [ ] `column-types/{id}.ts` created, every required field filled in
147147
- [ ] Registered in **both** `registry.ts` and `registry.server.ts`
148148
- [ ] Icon added, centered on the family's optical center, exported alphabetically
149149
- [ ] `migrateCellsTo` / `migrateCellsFrom` added if the stored bytes change
150150
- [ ] New metadata keys added to `TYPE_SPECIFIC_COLUMN_KEYS` + `FOREIGN_METADATA_VERB`
151-
- [ ] Unit tests for `coerce` / `isCompatibleWith` round-trips, verified to fail without the code
151+
- [ ] Unit tests for `coerce` / `isCompatibleWith` round-trips only if they pass the `test-audit` authoring gate, verified to fail without the code
152152
- [ ] Docs row added to `apps/docs/content/docs/tables/index.mdx`
153153

154154
## Final Validation (Required)
155155

156156
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.
157157
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.
158-
3. **Run the suite** — `bunx vitest run 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.
159-
4. **`bun run lint:check`, `bun run check:api-validation`, `bun run check:client-boundary`** from the repo root.
158+
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.
159+
4. **`bun run lint`, `bun run check:api-validation:strict`, `bun run check:client-boundary`** from the repo root.
160160
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.

0 commit comments

Comments
 (0)