Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 17 additions & 4 deletions .agents/skills/v2-api-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ Order matters because each layer is checked against the one before it.
3. **Route** with `defineV2JsonRoute`, declaring `contract`, `auth: v2ApiKeyAuth`, `operation`, `rateLimit`, `errorPolicy`, `mapInput`, `useCase`, `present`. Auth and rate limiting run before parsing.
4. **OpenAPI description** in `lib/api/contracts/v2/openapi/<domain>.ts`, then `bun run generate:openapi`. A description that claims behaviour the route does not have is the same class of bug as a wrong schema.

### Public descriptions

Use the [API description conventions](../../../apps/sim/lib/api/contracts/v2/openapi/README.md) when writing or auditing endpoint and field descriptions. Keep a short action-and-resource summary; use the description for behavior that changes the caller's choice, input, interpretation, or next action. Ordinary operations usually need one to three sentences, with no mandatory minimum.

Keep archive versus permanent-delete behavior, replacement versus partial-update semantics, partial success, retry safety, redaction, and asynchronous completion explicit. Verify these claims against the implementation. Describe observable behavior without exposing storage formats, locking mechanisms, internal identifiers, deployment architecture, or implementation history unless that detail changes how the caller must use the API.

Reuse wording across resource families when behavior matches: “Omitted fields remain unchanged,” “Archive,” and “permanently delete.” Prefer “during the request” or “asynchronously” to “settled inline.” Preserve real semantic differences; do not standardize them away.

Put field-specific rules in the source schema and reuse shared authentication and pagination wording. Shared schema descriptions also feed CLI help, so refer to related operation names rather than HTTP paths. Regenerate OpenAPI, CLI metadata, and CLI docs after changing their source descriptions; never hand-edit generated output.

## Rule 6 — a transient failure says when to come back

A response the caller is *expected* to retry must say how long to wait. Two statuses qualify, and both are wired:
Expand Down Expand Up @@ -186,11 +196,14 @@ Audited against the primary specs and against Stripe, GitHub, and Google's AIPs.

## Idempotency: at-most-once, not replay

`POST /workflows/{id}/execute` accepts `X-Run-Id`, a caller-supplied run identifier claimed through the `idempotency_key` table (`execution-id-claim.ts`). It is a **uniqueness claim, not an idempotency key**, and the distinction is deliberate and already published in the operation description:
`POST /workflows/{id}/execute` accepts `X-Run-Id` from API-key and OAuth callers; anonymous requests ignore it. It is a **uniqueness claim, not an idempotency key**:

- An available ID is claimed before execution starts.
- An already claimed ID returns **409** with `error.details.code: "RUN_ID_CONFLICT"`, the run id in `error.details.runId`, and an `X-Run-Id` response header. It never replays the earlier run's result. Get Workflow Run can retrieve an existing run, but a claim does not guarantee a retrievable run.
- IDs of runs that started remain reserved after their execution logs are deleted.
- An ambiguous enqueue can retain the claim indefinitely without creating a retrievable run. A **409** followed by **404** is an unresolved outcome, not proof that execution never started or that the ID will become reusable.

- First use wins and runs.
- Any reuse returns **409** with `error.details.code: "RUN_ID_CONFLICT"`, the run id in `error.details.runId`, and an `X-Run-Id` response header. It never replays the earlier run's result — the client recovers it by polling the runs resource.
- Claims are durable tombstones, so deleting execution logs cannot make an id reusable.
For an uncertain execution outcome, reuse the same run ID if retrying and check Get Workflow Run. Do not promise polling will eventually find a run. If the outcome cannot be verified, do not automatically restart with a fresh ID or an omitted header: either can start another execution. Failures before a run starts can release the claim, so phrase the conflict rule as an ID that is already claimed.

That makes the money path safe against double-execution **for callers that opt in**. What it is not: a Stripe-style `Idempotency-Key` that stores and replays the original status and body. Building that means a request fingerprint, a retention window, an in-flight-vs-completed distinction (the expired IETF draft would have these be 422 and 409 respectively), and somewhere to put a large synchronous execution body. It is a designed piece of work, not an increment — do not half-build it by aliasing the header name, which would invite clients written against Stripe semantics to treat our 409 as a hard failure.

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/cli/blocks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ sim blocks list [options]
| `--search <value>` | No | Case-insensitive substring match against the block id, name, and description. |
| `--category <value>` | No | Restrict to one toolbar category. Accepted values: `blocks`, `tools`, `triggers`. |
| `--capability <value>` | No | Restrict to blocks that can start a workflow — the `triggers` category, blocks declaring `triggerAllowed`, and blocks with trigger-mode fields. Accepted values: `trigger`. |
| `--source <value>` | No | Restrict to shipped blocks or to this workspaces deployed custom blocks. Accepted values: `builtin`, `custom`. |
| `--source <value>` | No | Restrict to built-in blocks or this workspace's deployed custom blocks. Accepted values: `builtin`, `custom`. |
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `id`, `name`, `category`. |
| `--sort-order <value>` | No | Sort direction. Accepted values: `asc`, `desc`. |
| `--limit <n>` | No | Maximum items to return (0 for everything). Defaults to `100`. |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/cli/files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ sim files list [options]
| Option | Required | Description |
| --- | --- | --- |
| `--folder <value>` | No | Folder path as shown in the app; the leading / is optional. |
| `--recursive` | No | Whether the folder filter includes files in subfolders. Defaults to true when a search is set, false otherwise, so listing a folder shows that folder while searching one looks through everything in it. Ignored when no folder filter is set, which already spans the workspace. |
| `--recursive` | No | Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter. |
| `--no-recursive` | No | Send --recursive as false. |
| `--scope <value>` | No | Which lifecycle set to list: `active` (default) for live files, `archived` for files a delete soft-deleted. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. |
| `--search <value>` | No | Case-insensitive substring match against the file name. |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/cli/knowledge.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ sim knowledge list [options]

| Option | Required | Description |
| --- | --- | --- |
| `--scope <value>` | No | Which lifecycle set to list: `active` (default) for live knowledge bases, `archived` for knowledge bases a `DELETE` archived and `POST /knowledge/&#123;knowledgeBaseId&#125;/restore` can bring back. `folderPath` resolves against active folders only, so pairing it with `scope=archived` returns an empty page when the containing folder was archived too. Accepted values: `active`, `archived`. |
| `--scope <value>` | No | Lifecycle scope: active or archived knowledge bases. Use Restore Knowledge Base to recover archived entries. Folder paths resolve only active folders, so filtering by an archived folder returns no matches. Accepted values: `active`, `archived`. |
| `--folder <value>` | No | Folder path as shown in the app; the leading / is optional. |
| `--search <value>` | No | Case-insensitive substring match against the resource name. |
| `--sort-by <value>` | No | Field used to sort the result. Sorting by `name` is case-sensitive and follows the storage collation, so do not rely on a case-insensitive order. Accepted values: `name`, `createdAt`, `updatedAt`. |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/cli/logs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ sim logs stats [options]
| `--level <value>` | No | Severity level to include. Accepted values: `info`, `error`. |
| `--start-date <value>` | No | Only include runs started at or after this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
| `--end-date <value>` | No | Only include runs started at or before this UTC ISO 8601 timestamp, e.g. `2026-08-06T00:00:00Z`. A date without a time, or a timestamp carrying a UTC offset instead of `Z`, is rejected, as is year `0000`, which names no storable instant. |
| `--segment-count <value>` | No | Number of equal time buckets to divide the window into, from 1 to 500. Exactly this many buckets are always returned. Buckets are never narrower than one minute, so on a short window the series extends past the end of the window rather than being compressed, and the trailing buckets are empty. |
| `--segment-count <value>` | No | Number of time buckets, up to 500. Exactly this many are returned, each at least one minute wide. Short windows extend past the requested end and include empty trailing buckets. |

</CommandTable>

Expand Down
18 changes: 9 additions & 9 deletions apps/docs/content/docs/cli/mcp-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ sim mcp-servers create [options]
| --- | --- | --- |
| `--name <value>` | Yes | Server display name. |
| `--description <value>` | No | Optional server description. |
| `--transport <value>` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. |
| `--transport <value>` | No | Transport protocol. Defaults to `streamable-http` on creation. Accepted values: `streamable-http`. |
| `--url <value>` | Yes | Absolute HTTP or HTTPS endpoint URL without `&#123;&#123;ENV_VAR&#125;&#125;` references. It determines server identity and is immutable: delete and recreate the server to change endpoints. |
| `--auth-type <value>` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. |
| `--headers <json\|@file>` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). |
| `--timeout <value>` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. |
| `--retries <value>` | No | Number of retries per request. Applied server-side as 3 when omitted on create. |
| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. |
| `--timeout <value>` | No | Per-request timeout in milliseconds. Defaults to 30000 on creation. |
| `--retries <value>` | No | Number of retries per request. Defaults to 3 on creation. |
| `--enabled` | No | Whether workflows can use the server's tools. Defaults to true on creation. |
| `--no-enabled` | No | Send --enabled as false. |
| `--oauth-client-id <value>` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. |
| `--oauth-client-secret <value>` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. (--oauth-client-secret null sends the word, not JSON null). |
Expand Down Expand Up @@ -121,7 +121,7 @@ List MCP Server Tools (OAuth login or personal API key required)

| Option | Required | Description |
| --- | --- | --- |
| `--refresh` | No | Bypass the short-lived per-workspace tool cache and reconnect under your own credentials. A cached result reflects whichever workspace member last ran discovery, so this is the only way to pick up a tool added since then; it costs a live round trip. |
| `--refresh` | No | Refresh tools using your credentials. Otherwise results may reuse another workspace member's recent discovery and omit newly added tools. |
| `--no-refresh` | No | Send --refresh as false. |

</CommandTable>
Expand Down Expand Up @@ -150,13 +150,13 @@ sim mcp-servers update <mcpServerId> [options]
| --- | --- | --- |
| `--name <value>` | No | Server display name. |
| `--description <value>` | No | Optional server description. |
| `--transport <value>` | No | Transport used to communicate with the server. Applied server-side as `streamable-http` when omitted on create. Accepted values: `streamable-http`. |
| `--transport <value>` | No | Transport protocol. Defaults to `streamable-http` on creation. Accepted values: `streamable-http`. |
| `--url <value>` | No | Immutable server URL. When provided, it must equal the current URL; use delete and create to change endpoints. |
| `--auth-type <value>` | No | Authentication method. When omitted, and no `headers` are sent, registration probes the endpoint once to classify it, falling back to `headers` when the probe fails or the server does not advertise OAuth. A server publishing RFC 9728 metadata is therefore stored as `oauth`, and headers configured afterwards will not authenticate — send this field explicitly to pin the method. Accepted values: `none`, `headers`, `oauth`. |
| `--headers <json\|@file>` | No | Write-only request headers sent to the server. Replaced wholesale rather than merged on update: sending this field drops every stored header it does not repeat. (JSON, or @path / @- to read a file or stdin). |
| `--timeout <value>` | No | Per-request timeout in milliseconds. Applied server-side as 30000 when omitted on create. |
| `--retries <value>` | No | Number of retries per request. Applied server-side as 3 when omitted on create. |
| `--enabled` | No | Whether the server tools are available to workflows. Applied server-side as true when omitted on create. |
| `--timeout <value>` | No | Per-request timeout in milliseconds. Defaults to 30000 on creation. |
| `--retries <value>` | No | Number of retries per request. Defaults to 3 on creation. |
| `--enabled` | No | Whether workflows can use the server's tools. Defaults to true on creation. |
| `--no-enabled` | No | Send --enabled as false. |
| `--oauth-client-id <value>` | No | Pre-registered OAuth client identifier. Changing it on update revokes the stored OAuth grant and forces reauthorization. |
| `--oauth-client-secret <value>` | No | Write-only pre-registered OAuth client secret. Sending it on update as null or a new value revokes the stored OAuth grant and forces reauthorization, as does switching away from OAuth authentication. (--oauth-client-secret null sends the word, not JSON null). |
Expand Down
Loading
Loading