You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Preserve organization invitation and navigation behavior alongside staging OAuth and provenance changes. Enforce the OAuth app capability for organization-scoped reads, with regression coverage.
Keep staging migration history intact, regenerate snapshots 0326-0328, and retain the reviewed search, approval, and connected-account SQL unchanged. Update migration fixtures and documentation references.
Copy file name to clipboardExpand all lines: .agents/skills/v2-api-conventions/SKILL.md
+17-4Lines changed: 17 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -149,6 +149,16 @@ Order matters because each layer is checked against the one before it.
149
149
3.**Route** with `defineV2JsonRoute`, declaring `contract`, `auth: v2ApiKeyAuth`, `operation`, `rateLimit`, `errorPolicy`, `mapInput`, `useCase`, `present`. Auth and rate limiting run before parsing.
150
150
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.
151
151
152
+
### Public descriptions
153
+
154
+
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.
155
+
156
+
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.
157
+
158
+
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.
159
+
160
+
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.
161
+
152
162
## Rule 6 — a transient failure says when to come back
153
163
154
164
A response the caller is *expected* to retry must say how long to wait. Two statuses qualify, and both are wired:
@@ -186,11 +196,14 @@ Audited against the primary specs and against Stripe, GitHub, and Google's AIPs.
186
196
187
197
## Idempotency: at-most-once, not replay
188
198
189
-
`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:
199
+
`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**:
200
+
201
+
- An available ID is claimed before execution starts.
202
+
- 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.
203
+
- IDs of runs that started remain reserved after their execution logs are deleted.
204
+
- 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.
190
205
191
-
- First use wins and runs.
192
-
- 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.
193
-
- Claims are durable tombstones, so deleting execution logs cannot make an id reusable.
206
+
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.
194
207
195
208
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.
Copy file name to clipboardExpand all lines: apps/docs/content/docs/cli/blocks.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -38,7 +38,7 @@ sim blocks list [options]
38
38
|`--search <value>`| No | Case-insensitive substring match against the block id, name, and description. |
39
39
|`--category <value>`| No | Restrict to one toolbar category. Accepted values: `blocks`, `tools`, `triggers`. |
40
40
|`--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`. |
41
-
|`--source <value>`| No | Restrict to shipped blocks or to this workspace’s deployed custom blocks. Accepted values: `builtin`, `custom`. |
41
+
|`--source <value>`| No | Restrict to built-in blocks or this workspace's deployed custom blocks. Accepted values: `builtin`, `custom`. |
42
42
|`--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`. |
Copy file name to clipboardExpand all lines: apps/docs/content/docs/cli/files.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -286,7 +286,7 @@ sim files list [options]
286
286
| Option | Required | Description |
287
287
| --- | --- | --- |
288
288
|`--folder <value>`| No | Folder path as shown in the app; the leading / is optional. |
289
-
|`--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. |
289
+
|`--recursive`| No |Include subfolders in the folder filter. Defaults to true when searching and false otherwise. Ignored without a folder filter. |
290
290
|`--no-recursive`| No | Send --recursive as false. |
291
291
|`--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`. |
292
292
|`--search <value>`| No | Case-insensitive substring match against the file name. |
0 commit comments