diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index a748853..d6694b2 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "sim", "displayName": "Sim", "version": "0.1.0", - "description": "Build, run, deploy, and operate Sim workflows, tables, and knowledge bases.", + "description": "Build, run, deploy, and sync Sim workflows; operate tables and knowledge bases.", "author": { "name": "Sim", "url": "https://sim.ai" diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 85f7676..e6cebd9 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "sim", "version": "0.1.0", - "description": "Build, run, deploy, and operate Sim workflows, tables, and knowledge bases.", + "description": "Build, run, deploy, and sync Sim workflows; operate tables and knowledge bases.", "author": { "name": "Sim", "url": "https://sim.ai" @@ -14,7 +14,7 @@ "interface": { "displayName": "Sim", "shortDescription": "Build and operate Sim workflows", - "longDescription": "Use the Sim CLI to build, test, deploy, and debug workflows, plus manage tables and knowledge bases.", + "longDescription": "Use the Sim CLI to build, test, deploy, and debug workflows, import and sync workspaces, and manage tables and knowledge bases.", "developerName": "Sim", "category": "Productivity", "capabilities": ["Read", "Write"], diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7762c8..318caca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,3 +48,4 @@ jobs: test -f "$PACKAGE_DIR/package/.codex-plugin/plugin.json" test -f "$PACKAGE_DIR/package/.claude-plugin/plugin.json" test -f "$PACKAGE_DIR/package/skills/build-workflow/SKILL.md" + test -f "$PACKAGE_DIR/package/skills/sync-workspaces/SKILL.md" diff --git a/README.md b/README.md index d2d55de..7a47b7a 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Native plugin installs expose the skills under the `sim` namespace: - `sim:build-workflow` - `sim:run-workflow` - `sim:deploy-workflow` +- `sim:sync-workspaces` - `sim:table` - `sim:knowledge-base` @@ -83,7 +84,11 @@ Direct installs through `bunx sim-skills` install the selected skills without th - `build-workflow` — discover blocks and author a draft graph with atomic workflow operations. - `run-workflow` — test saved state, exercise triggers, resume from a block, and diagnose runs. - `deploy-workflow` — publish and manage workflows as APIs, chats, or MCP tools. +- `sync-workspaces` — import portable workflows with bindings, fork workspaces, and preview, apply, and reconcile push/pull sync. - `table` — design typed tables, load and query rows, import data, and run workflow groups. - `knowledge-base` — ingest and index documents, configure connectors and tags, and verify retrieval. The skills assume the `sim` CLI is installed and authenticated. They never store or print API keys. + +Workspace sync guidance requires a CLI and server exposing the v2 import-preview and fork sync +commands. The skill checks command availability before changing resources. diff --git a/scripts/validate-skills.ts b/scripts/validate-skills.ts index 02419d8..80d486c 100644 --- a/scripts/validate-skills.ts +++ b/scripts/validate-skills.ts @@ -16,6 +16,7 @@ const expectedSkillNames = [ "knowledge-base", "run-tool", "run-workflow", + "sync-workspaces", "table", ] as const; const expectedPackageFiles = [ diff --git a/skills/build-workflow/SKILL.md b/skills/build-workflow/SKILL.md index 1e6fe6d..f9ba477 100644 --- a/skills/build-workflow/SKILL.md +++ b/skills/build-workflow/SKILL.md @@ -12,6 +12,10 @@ A request that is one action against one connected service needs no graph at all directly with `sim tools execute` (see the `run-tool` skill). Build a workflow when the task needs more than one call, branching, or a schedule. +For an existing workflow JSON import or environment promotion, use the `sync-workspaces` skill. +It preserves registered reference identities through preview and destination binding before graph IDs +are regenerated; do not recreate that flow with a sequence of graph edits. + ## Establish context - Use the profile the user named. If none was named, inspect configured profiles and current context; diff --git a/skills/deploy-workflow/SKILL.md b/skills/deploy-workflow/SKILL.md index c18ba4d..af17211 100644 --- a/skills/deploy-workflow/SKILL.md +++ b/skills/deploy-workflow/SKILL.md @@ -56,28 +56,22 @@ Do not choose a surface from convenience. Ask when the intended caller does not ## Promoting across workspaces with fork sync -When a workflow moves between workspaces via a fork push, the sync has its own semantics; do not -reason about it as a copy. - -- The push creates resources that are missing in the target and rebinds selector-bound references - to them. Do not pre-create tables in the target as a promotion prerequisite - pre-creating - defeats the mapping and leaves references pointing at the source. -- A table the push creates arrives holding the source's rows. Re-seed environment-specific values, - feature flags and configuration especially, immediately after the push, before anything reads - them. Where possible design flag rows so the source's value is also the safe value in every - target. -- Deployment state travels. A workflow deployed in the source is live in the target as soon as the - sync completes, and a schedule trigger starts firing there on its own - there is no separate - deploy step in the target. Before syncing anything scheduled or triggered, state plainly what - will start running where and when. -- Bind every resource through its selector and leave the manual id fields empty. The push remaps - selectors but carries a hardcoded manual id verbatim, silently pointing the promoted workflow at - the source workspace's resource - and a cross-workspace read succeeds, so no error surfaces. -- The push does not preserve a block's basic/advanced mode: some blocks arrive rebound and working, - others arrive carrying the source's manual id and broken, with nothing surfacing which is which. - "Works in the source workspace" is therefore never the completion condition. Verify each target - environment after promotion - run its workflows or audit its bindings - rather than inferring - health from the source. +Use the `sync-workspaces` skill for portable imports, workspace forks, and push/pull promotion. +That flow owns mapping discovery, preview fingerprints, stable request IDs, and operation polling. + +- Sync transfers deployed source versions along a direct fork edge. Push sends current → other; + pull receives other → current, regardless of which workspace is the child. +- Select resource copies explicitly or map to existing authorized destination resources. Creating a + destination table is valid when mapping to it; sync does not automatically copy every missing + resource. Selected table copies include rows, so review environment-specific configuration. +- Import and fork create drafts. Sync deploys eligible admitted snapshots after background work; + inspect operation and deployment readiness before declaring the target live. Schedules and + webhooks can begin receiving traffic when their deployment activates. +- Registered selector references are remapped using canonical field modes. Manual values remain + literal; verify their intended destination instead of assuming they were rebound or that access + across workspaces is permitted. +- Review trigger URL changes and required configuration. A committed operation can still need + configuration or have failed follow-up work; an HTTP success alone does not establish readiness. ## Verify and report diff --git a/skills/sync-workspaces/SKILL.md b/skills/sync-workspaces/SKILL.md new file mode 100644 index 0000000..e774a27 --- /dev/null +++ b/skills/sync-workspaces/SKILL.md @@ -0,0 +1,182 @@ +--- +name: sync-workspaces +description: Import portable Sim workflow JSON with destination bindings, create workspace forks, or push and pull deployed workflows along a fork edge through the sim CLI. Use for environment promotion, mapping and selector discovery, preview/apply retries, and operation readiness; not for authoring a new graph or ordinary deployment. +--- + +# Import and Sync Sim Workflows + +Use a reviewed preview, explicit destination bindings, and a durable request ID. Verify the returned +operation; sync also requires target deployment readiness. Imports and forks complete as drafts. + +## Establish scope + +- Use the user's profile, API origin, and explicit workspace. Inspect context before selecting an + environment; never silently change accounts. Do not print or store authentication secrets. +- Check `sim workflows import-preview --help` or `sim workspaces push-preview --help` first. These + commands require a CLI and server with the v2 sync feature. If unavailable, report the missing + capability and required upgrade; do not substitute private browser endpoints. +- Fork creation requires source admin. Sync and mapping changes require admin on both workspaces. + Fork administration accepts personal API keys or OAuth, not workspace keys. Imports retain their + existing workflow-write permissions; credential binding still requires an eligible acting user. +- Inspect `workspaces fork-availability` before forking. Enterprise/self-hosting and workspace + creation policies still apply. OAuth scopes and permission groups can further restrict access. +- Use `--output json`. Single-resource CLI results are unwrapped objects; paged results contain + `data` and `nextCursor`. Raw v2 HTTP single-resource responses wrap the object in `data`. + +## Choose the operation + +| Intent | Commands | Result | +| --- | --- | --- | +| Import workflow JSON into an authorized destination | `workflows export`, `import-preview`, `import` | New undeployed draft with regenerated graph IDs | +| Create a child environment | `workspaces fork-preview`, `fork` | Child workspace with eligible deployed source workflows copied as drafts | +| Send current workspace changes to its direct fork neighbor | `workspaces push-preview`, `push` | Replace eligible target workflows from deployed source versions | +| Receive changes from a direct fork neighbor | `workspaces pull-preview`, `pull` | Same sync with the other workspace as source | + +Push always means current → other; pull means other → current, on either side of the parent/child +edge. Inspect `workspaces lineage` and `children`; do not infer direction from the word child. +Arbitrary workspace transfers and draft sync are outside this flow. + +## Portable import + +Export with reference metadata and preview in the destination: + +```sh +sim --profile source --workspace "$SOURCE_WORKSPACE" --output json \ + workflows export "$WORKFLOW_ID" --include-references > workflow.json +sim --profile destination --workspace "$DESTINATION_WORKSPACE" --output json \ + workflows import-preview --workflow @workflow.json > preview.json +``` + +Default export remains sanitized without reference metadata. `--include-references` records +registered non-secret source IDs and every block/field occurrence alongside the sanitized graph. +Treat imported IDs and provenance as untrusted labels, not authorization to read a source workspace. + +Read `unresolvedBindings`, `unresolvedConfiguration`, and target-discovery instructions. Discover +destination credentials, tables, files, sandboxes, and other resources through their existing CLI +commands. Candidate matches are suggestions; verify provider, resource type, and parent resource. +Missing OAuth connections can require human provider authorization; report that requirement rather +than inventing a connection or substituting a different user's credential. + +Import resource mappings use `kind`, `sourceId`, and `targetId`: + +```json +[ + { "kind": "credential", "sourceId": "source-connection", "targetId": "destination-connection" }, + { "kind": "sandbox", "sourceId": "source-sandbox", "targetId": "destination-sandbox" } +] +``` + +For older exports, `--bindings` addresses a specific registered source occurrence. For example: + +```json +[ + { "kind": "sandbox", "blockId": "source-function", "subBlockKey": "sandboxId", "targetId": "destination-sandbox" } +] +``` + +`valuePath` defaults to `[]` and `encoding` to `scalar`. Use preview/manifest occurrence paths for +nested or multi-value fields; do not guess them. Conflicting mappings and bindings are rejected. +Import dependent values use `{blockId, subBlockKey, value}`, such as +`{"blockId":"source-agent","subBlockKey":"tools[0].folder","value":"destination-label"}`. +Keep the original source IDs and tool indexes until apply returns `idMap`. + +Save the exact input files and a client-generated stable request ID before applying: + +```sh +sim --profile destination --workspace "$DESTINATION_WORKSPACE" --output json \ + workflows import-preview --workflow @workflow.json \ + --mappings @mappings.json --dependent-values @values.json > preview.json +sim --profile destination --workspace "$DESTINATION_WORKSPACE" --output json \ + workflows import --workflow @workflow.json \ + --mappings @mappings.json --dependent-values @values.json \ + --preview-fingerprint "$(jq -r .previewFingerprint preview.json)" \ + --request-id "$REQUEST_ID" --wait +``` + +Include the same name, folder, bindings, and other choices on both requests. JSON flags accept +`@file` and `@-` for stdin; only one input can consume stdin. Mapped import creates nothing while +required bindings remain unresolved. It commits the draft, graph, variables, inline custom tools, +and receipt together. Mappings are local to this request; importing does not create a workflow +correspondence on a fork edge. Plain imports without mapping options +retain legacy behavior; use preview/apply for automation that needs binding guarantees. + +## Fork and sync choices + +Inspect `workspaces fork-resources`, then preview with explicit copy selections. A fork's +`--copy` selects source resources; sync uses `--copy-resources`. Consult command help for each +shape: fork file selections are workspace file IDs, while sync file selections are storage keys. +Copying is opt-in. Selected table copies include rows and selected knowledge bases include content; +account for environment-specific configuration before enabling the destination. + +Existing destination resources can be mapped instead of copied. Sync mapping entries use +`{resourceType, sourceId, targetId}`; they differ from import's `kind` entries. In particular, +portable `credential` corresponds to sync `oauth_credential` or `service_account_credential`. +Verify the actual credential type through resource discovery. A mapping inspection row also has +`id`; project it before reuse with `jq '.data | map({resourceType,sourceId,targetId})'`. Follow all +pages first. Newly referenced resources may have no persisted mapping row yet. + +Previews evaluate inline mappings without saving them. Accepted inline sync mappings persist on +the canonical edge in the same transaction as sync; a refusal before commit saves neither. +Dependent sync values use `{sourceWorkflowId, sourceBlockId, subBlockKey, value}`. Never use fresh +target IDs from preview as override identities. Omitted overrides reuse saved sync choices; values +that exist only in a target draft are not saved choices. A supplied `dependentValues` array replaces +the saved choices for affected workflows; `[]` clears them. Start from every preview configuration +field and its `currentValue`, edit the intended selections, and submit the complete set of choices +to retain, rather than only the changed fields. + +For every dependent field, use its returned selector key, context, and `discoveryWorkspaceId`: + +```sh +sim --profile destination --workspace "$DISCOVERY_WORKSPACE" --output json \ + selectors list --selector-key gmail.labels \ + --context '{"oauthCredential":"destination-connection"}' +``` + +The discovery workspace is the source when the parent resource will be copied, and the destination +when using an existing mapping. Use a profile authorized for that workspace. Follow `nextCursor`, +inspect truncation metadata, and use `selectors get --help` to verify a particular option. A clipped +list does not prove an option is absent. Resolve dependent chains in order, re-previewing with the +new choices. Use exactly the returned context for MCP tool discovery as well. + +```sh +sim --profile destination --workspace "$DESTINATION_WORKSPACE" --output json \ + workspaces pull-preview --other-workspace-id "$SOURCE_WORKSPACE" \ + --mappings @sync-mappings.json --dependent-values @sync-values.json > sync-preview.json +sim --profile destination --workspace "$DESTINATION_WORKSPACE" --output json \ + workspaces pull --other-workspace-id "$SOURCE_WORKSPACE" \ + --mappings @sync-mappings.json --dependent-values @sync-values.json \ + --preview-fingerprint "$(jq -r .previewFingerprint sync-preview.json)" \ + --request-id "$SYNC_REQUEST_ID" --yes --wait +``` + +Use `--yes` within the user's authorized destructive sync scope. Review replacements, exclusions, +resource selections, required configuration, and trigger URL changes before apply. If preview offers +trigger adoption choices, use its stable source workflow/block identities and offered paths; never +invent a path. Sync `ready` indicates commit readiness, not a live deployment. Scheduled or webhook +workflows can begin receiving traffic once admitted deployments activate. + +## Completion and recovery + +- Save `operationId`, `requestId`, and the receipt's `workspaceId`. Import receipts belong to the + destination; fork and push/pull receipts belong to the workspace on which the command was invoked. + Poll that scope even when the created child or sync target is a different workspace. +- `applied: true` means the transaction committed, including when later copy/deployment work fails. + Use `workspaces operations get ` or `wait --wait-timeout 300` to refresh + readiness. Operation lists are stored snapshots; filter with `--request-id` to recover a lost ID. +- Require terminal readiness and inspect issues/trigger URL changes before declaring the environment + ready. Completed-with-warnings exits 0 but still needs review. Required configuration exits 3, + failed completion exits 1, and wait timeout exits 4. Timeout diagnostics retain reconciliation IDs. +- After an uncertain response, retry identical inputs with the original request ID, or poll the + existing operation. Never retry an uncertain mutation with a fresh ID. Same ID with changed input + returns 409. Authorize access again before reconciliation; a stored receipt is not an auth bypass. +- A stale preview with no committed operation needs a fresh preview and a new request ID for the + revised inputs. Resolve structured 409 issues; do not loop blindly or remove safety flags. +- Sync transfers deployed source versions. Merely undeploying a source does not archive its target; + deleting a mapped source can. Respect explicit sync exclusions. +- Rollback restores the latest target sync from prior deployed versions. It does not recover + arbitrary prior drafts or remove every copied resource. Use rollback/unlink/exclusion controls + only for the requested recovery scope and verify their result. + +For a new imported draft, use the `run-workflow` skill to test it and `deploy-workflow` only when +publication is requested. Never copy secret values, signed URLs, arbitrary headers, or opaque +credential payloads between environments to make a binding pass.