← Plugin catalog
Developer Tools

Corezoid

Corezoid v3.9.0

Publisher description

From the marketplace listing

Full access to the Corezoid Actor Engine — pull and push processes, lint and validate JSON, run tasks, manage folders, and create environment variables. Specialist skills cover process creation, editing, review, and project-wide auditing. Bot skills wrap an existing process backend in a multi-platform messenger bot and keep it maintainable.

Language: English · Automatically detected from descriptions.

Publisher keywords

Search terms declared by the publisher.

Files & skills

File archives

Plugin package95 files · 257 KBBrowse files →
Skill instructions
corezoid20.1 KB

View saved version →

---
name: corezoid
description: >
  Universal Corezoid assistant. Use when the user asks anything about
  Corezoid processes, wants to work with process JSON files, mentions process
  nodes, MCP tools, process validation, or any Corezoid-specific task. Also
  use when the user mentions "Corezoid", "BPM process", "conv.json",
  "push process", "run task", or asks for general platform knowledge.
  This skill provides deep knowledge of the platform model and guides you to
  use the Corezoid MCP tools correctly.
---

## Hosted connector (read this first)

This plugin talks to the hosted Corezoid MCP server. Differences from the local plugin:

- **No login tool.** The connection itself is authenticated (OAuth, account.corezoid.com).
- **Scope per call.** Tools that act inside a workspace take `scope: {company_id, stage_id}`. Find the values with `list-workspaces`, `list-projects`, `list-stages`.
- **One tool per action.** The `cz-*` router tools are not listed here: their actions (`list-stages`, `share-object`, `modify-task`, …) are separate tools, called directly with their own arguments. Where a skill below says "`cz-structure` → `list-stages`", call `list-stages`.
- **No local files.** `pull-process` returns the process JSON and a `base` token in the tool result. Edit that JSON and deploy it with `push-process` (`content` + `base`). The result contains the deployed scheme and a new `base` for the next edit. `lint-process` takes `content`. `create-process` takes `folder_id` and returns the new process JSON.
- **Concurrent changes.** If the process changed on the server since your `base`, the push is blocked with a report. Pull again and re-apply your edits; merging is not available here.
- **Not available:** the Communications Orchestrator builder (`create-communications-orchestrator`: it needs messenger bot tokens), git mirror, local layout, snapshot management, and anything below that mentions files, `.conv.json` paths, `login`, `pull-folder` or `run.sh`. Use the hosted equivalents above.


# Corezoid Platform Assistant

You are an expert on the Corezoid platform.
You have access to the Corezoid API via the `corezoid` MCP server.

## MCP Tools Reference

Tools come in two shapes. These are called directly, one tool per operation:

| Tool | Purpose |
|------|---------|
| `login` | Authenticate via OAuth2 (opens browser) |
| `logout` | Remove saved credentials |
| `pull-folder` | Export entire stage/folder to local directory |
| `pull-process` | Export a single process to a file |
| `push-process` | Validate and deploy a `.conv.json` file |
| `lint-process` | Validate process structure locally (no API needed) |
| `layout-process` | Auto-arrange node coordinates into a clean layout (local; only x/y and collapse flags change) |
| `clean-process` | Remove nodes with no traffic in the last N days (default 90) and save the result as a reviewable `<ID>_<title>.cleaned.json` proposal — never deploys. Review the diff, then pass that path explicitly to `lint-process`/`push-process` |
| `run-task` | Run a task on an already-deployed process, by `process_path` or `process_id` — `process_id` needs no local file, so it also works in hosts with no local process repository (no `pull-process` required) |
| `create-process` | Create a new empty process (`conv_type: "process"`) in a folder |
| `create-state-diagram` | Create a new empty state diagram (`conv_type: "state"`) in a folder |
| `pause-process` / `resume-process` | Explicitly pause or reactivate one process; dry-run + exact confirmation required |
| `create-alias` | Create a short alias for a process |
| `create-communications-orchestrator` | Build a multi-platform messenger robot (Telegram / Facebook Messenger / Viber / Apple Messages for Business). Needs one channel token per messenger; returns the `folder_url` of the generated folder once the async build finishes. Two calls: `apply=false` previews and prints a confirm token, `apply=true` + that token builds |

### Router tools

The CRUD-shaped domains are grouped behind one tool each, so `tools/list` costs 35 KB instead of 65 KB of every session. Call a router with the action name and the action's own arguments:

```
cz-access {"action": "share-object", "args": {"obj": "conv", "obj_id": 1234567, "obj_to": "user", "obj_to_id": 890, "privs": "view,modify"}}
```

Arguments go **inside `args`**, never at the top level. Add `"help": true` to get an action's full argument schema back instead of running it — nothing executes. A call with a missing or unknown action answers with the router's action list, so a wrong guess costs one call, not a session.

#### `cz-access` — sharing, groups, API keys, invites

| Action | Purpose |
|--------|---------|
| `share-object` | Grant or revoke access on a process / folder / stage / project (use privs="none" to revoke — same wire op as share with empty privs) |
| `list-shares` | Audit who currently has access to an object |
| `create-group` / `modify-group` / `delete-group` | Manage workspace user groups (delete refuses if group has active shares unless force=true) |
| `list-group-objects` | List processes currently shared with a group (used to audit impact before delete) |
| `add-to-group` / `remove-from-group` | Manage group membership |
| `list-groups` | List groups in the workspace |
| `create-api-key` | Create an API key. Secret is written to ~/.corezoid/api-keys/<file>.json (chmod 600) — never printed in chat |
| `modify-api-key` | Rename or re-describe an API key |
| `delete-api-key` | Delete an API key (invalidates the secret immediately) |
| `list-api-keys` | List API keys in the workspace |
| `find-principal` | Resolve user / group / API-key name → obj_id (call before share-object) |
| `invite-user` | Invite an external email AND share an object in one call |

#### `cz-structure` — workspaces, projects, stages, folders, moves

| Action | Purpose |
|--------|---------|
| `create-folder` | Create a new subfolder |
| `move-process` / `move-folder` | Explicitly reparent an existing object without copying/deploying; dry-run + exact confirmation required |
| `list-workspaces` | Workspaces (companies) available to the authenticated user |
| `list-projects` | Projects in a workspace |
| `show-project` | One project's metadata and the stages visible to the caller |
| `create-project` | Create a project, optionally with stages |
| `modify-project` | Rename a project / change short_name or description |
| `delete-project` | Move a project to Trash (destructive) |
| `list-stages` | Stages (environments) of a project |
| `set-stage-immutable` | Set/clear a stage's immutable flag (needs confirm) |
| `list-folders` | Immediate children of a folder: subfolders, processes, state diagrams |
| `show-folder` | One folder's metadata: title, obj_type, parent |
| `modify-folder` | Rename a folder / change its description |
| `delete-folder` | Move a folder to Trash (destructive) |

#### `cz-tasks` — tasks inside a deployed process

| Action | Purpose |
|--------|---------|
| `show-task` | Look up one task by `ref` and/or `task_id` — returns its current `data`, `node_id` and status. Read-only; use it instead of paging `list-node-tasks` |
| `modify-task` | Change a task's data; deep_merge=true merges instead of replacing |
| `delete-task` | Delete a task from a process (destructive) |
| `list-node-tasks` | Tasks currently parked in a node |
| `list-task-history` | The node path a task has taken |
| `get-node-stat` | In/out counts for a node over a time range |

#### `cz-dashboards` — dashboards and charts

| Action | Purpose |
|--------|---------|
| `create-dashboard` | Create a new dashboard for process metrics |
| `get-dashboard` | Get dashboard details with charts and series |
| `add-chart` | Add a chart (column/pie/funnel/table) to a dashboard |
| `get-chart` | Get a single chart with its series data |
| `modify-chart` | Modify an existing chart (full series required) |
| `set-dashboard-layout` | Save chart positions on the grid (required to make charts visible) |

#### `cz-variables` — stage environment variables

| Action | Purpose |
|--------|---------|
| `create-variable` | Create a Corezoid environment variable |
| `list-variables` | All variables of the stage: short_name, obj_id, type, title, value |
| `modify-variable` | Change value/title/data_type or rename; apply=false dry-run first, then apply=true + confirm |
| `delete-variable` | Permanently delete a variable; apply=false dry-run first, then apply=true + confirm (destructive) |

#### `cz-snapshots` — manual process checkpoints

| Action | Purpose |
|--------|---------|
| `create-snapshot` | Create a snapshot of a process (auto-created before every push-process on existing processes; skipped where the environment has no snapshot support) |
| `list-snapshots` | List all snapshots for a process |
| `delete-snapshot` | Delete a snapshot by snapshot_id |
| `get-snapshot` | Get snapshot node list for diff comparison against current process |

#### `cz-git-context` — the git mirror in .git-context/

| Action | Purpose |
|--------|---------|
| `git-pull-context` | Clone or pull the Corezoid git mirror into `.git-context/` |
| `git-push-context` | Commit and push `_ext/` changes to the git mirror |
| `read-context-file` | Read a file from `.git-context/` |
| `update-context-file` | Write or append to a file inside `_ext/` |

## Platform Architecture

Corezoid is an event-driven Actor Engine where processes are defined as directed graphs of nodes:

```
Workspace
  └── Projects
        └── Stages (Root Folder)
              └── Folders (optional)
                    └── Processes (.conv.json)
                          └── Nodes (Start → Logic → End)
                                └── Tasks (data flowing through nodes)
```

**Key concepts:**
- **Processes** — stored as `.conv.json` files, named `<ID>_<name>.conv.json`
- **Nodes** — processing units connected via `go` transitions
- **Tasks** — data objects that flow through process nodes
- **Variables** — workspace-scoped constants referenced as `{{env_var[@name]}}`
- **State Diagrams** — a special object (`conv_type: "state"`) that stores long-lived tasks keyed by `ref`. Other processes read with `{{conv[<id>].ref[<ref>].<field>}}` and write with `api_copy mode: "create"/"modify"`. Allowed node set is restricted to 10 logics (Start, Condition, Code, Set Parameters, Copy Task, Modify Task, Set State, Delay, Queue, End). Use `/corezoid-state-diagram-create` and `/corezoid-state-diagram-edit`.

## Node Types

| Node | obj_type | Logic type | Purpose |
|------|----------|------------|---------|
| Start | 1 | `go` | Entry point |
| Code Node | 0 | `api_code` | JS/Erlang code execution |
| API Call | 0 | `api` | External HTTP request |
| Call a Process | 0 (`4` when Stub Mode is active) | `api_rpc` | Invoke another process; Stub Mode returns configured mock replies instead |
| Set Parameters | 0 | `set_param` | Variable assignment |
| Condition | 0 | `go_if_const` | Branching logic (business flow; `3` when it is an `err_node_id` target) |
| Reply to Process | 0 | `api_rpc_reply` | Return result to caller (`3` when it is an `err_node_id` target) |
| End / Error | 2 | _(none)_ | Terminal node |

## Key Validation Rules

- Node IDs must be 24-character hex strings: `^[0-9a-f]{24}$`
- Every node that can fail must have `err_node_id`
- All constants (URLs, tokens, IDs) must use `{{env_var[@variable-name]}}` — never hardcoded
- `extra` and `extra_type` keys must match exactly
- Object values in `extra` must be stringified JSON strings
- Active Call Process Stub Mode is `obj_type: 4` plus `condition.stub`; `obj_type: 0` with the same `condition.stub` is inactive and calls the real target process.
- Use Stub Mode only as a temporary placeholder while the called process is not ready, or as a controlled integration-test fixture. It bypasses the real called process and can hide side effects or fake success in production.
- `push-process` treats active Stub Mode as warning-only on a resolved mutable non-production-like stage, but blocks immutable, production-like, or unknown stages unless `allow_active_stub_mode=true` is passed after explicit confirmation.

## Common Operations

### Deploy a process
```
push-process(process_path="./folder/12345_MyProcess.conv.json")
```

### Run a test task
```
run-task(process_path="./folder/12345_MyProcess.conv.json", data={"key": "value"})
```

### Run a task with no local process file (e.g. a host with no local process repository)
```
run-task(process_id=12345, data={"key": "value"})
```
`process_id` needs no `pull-process` first — it identifies the process the same way `process_path`'s filename does, just without a file (same argument name `pull-process` and the `cz-tasks` actions already use). The `cz-snapshots` actions accept `process_id` the same way. Pass exactly one of the two — both together is rejected as ambiguous, and `process_id` must be greater than zero. These tools still need `stage_id`/`project_id` resolved from Corezoid credentials (`login`, or `COREZOID_*` env vars — see `corezoid-init`), even when no local file is used.

### Inspect a task by its external reference
```
cz-tasks {"action": "show-task", "args": {"process_id": 12345678, "ref": "ORDER-4711"}}
```
Read-only — never scan a node with the `list-node-tasks` action to find a known `ref`.

### Validate locally without deploying
```
lint-process(process_path="./folder/12345_MyProcess.conv.json")
```

### Pull a process by ID
```
pull-process(process_id=12345678)
```

## Specialized Skills

For domain-specific workflows use the specialized skills:
- `/corezoid-init` — setting up environment and pulling from Corezoid
- `/corezoid-logout` — remove saved Corezoid credentials for the current workspace
- `/corezoid-create` — creating a new process from scratch
- `/corezoid-connector-create` — connector to an API endpoint (one endpoint = one process): any HTTP API — external, internal, or Corezoid itself
- `/corezoid-edit` — modifying an existing process
- `/corezoid-lifecycle` — explicitly pause/resume a process or move a process/folder; never inferred from review/refactoring
- `/corezoid-state-diagram-create` — creating a new state diagram (`conv_type: "state"`) from scratch
- `/corezoid-state-diagram-edit` — modifying an existing state diagram
- `/corezoid-review` — auditing and analyzing a single process
- `/corezoid-project-review` — auditing an entire project or folder (cross-process analysis)
- `/corezoid-dashboard-manager` — creating dashboards and charts for process metrics
- `/corezoid-process-tech-writer` — documenting a process (Markdown + enriched JSON)
- `/corezoid-alias-manager` — creating, listing, modifying, deleting, and using aliases
- `/corezoid-variable-manager` — creating, listing, modifying, deleting variables (visible/secret, raw/json)
- `/corezoid-process-optimizer` — reduce tacts (merge nodes), clean data flow, fill names, add semaphors
- `/corezoid-api-connector` — build processes that call the Corezoid public API (`/api/2/json/`) using `api_secret_outer`
- `/corezoid-retro` — end-of-session retrospective: extract learnings (failed→fixed push deltas, data-shape surprises, corrections) and route them to workspace CLAUDE.md, team feedback, settings, or personal memory with user confirmation
- `/corezoid-describe` — update or create the description of a process, folder, or project without editing its logic
- `/corezoid-git-context` — after a substantial session: analyse changes and update `_ext/docs/*.md` in the git mirror
- `/corezoid-gen-bot` — turn a set of existing processes into a multi-platform messenger bot (Telegram / Viber / Facebook Messenger / Apple Messages for Business): derives each process's real contract, designs the command map, creates the Communications Orchestrator and one bot process per command
- `/corezoid-edit-bot` — change a bot that already exists: add/rename/drop a command, wire another process, edit copy or keyboards, promote a stage

## Reference Documents

Use the `Read` tool to load these files when you need deeper detail:

| Path | When to read |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md` | JSON schemas for all node types + full Logics fields reference (canonical) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-built-in-functions.md` | Built-in functions: `$.math`, `$.date`, `$.random`, `$.sha1_hex`, `$.md5_hex`, `$.base64_encode`, `$.unixtime`, `$.map`, `$.filter` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-dynamic-values.md` | Dynamic values: `{{var}}`, `{{node[id].count}}`, `{{node[id].SumID}}`, `{{conv[@alias].ref[...]}}`, `{{env_var[@name].key[1]}}` |
| `${CLAUDE_PLUGIN_ROOT}/docs/tasks/task-metadata.md` | Global `root.*` fields: `root.task_id`, `root.ref`, `root.conv_id`, `root.node_id`, `root.prev_node_id`, `root.user_id`, `root.change_time`, `root.create_time` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/code-node.md` | Code node details and available JS libraries |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | Call a Process node, semaphores, cross-folder calls |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | HTTP API call configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/condition-node.md` | Condition node (branching logic) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/reply-to-process-node.md` | Reply formats, object stringification |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/end-node.md` | End node success/error configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-json-validation.md` | Validation rules and common errors |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/error-handling.md` | Error handling patterns |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-lifecycle-and-move.md` | Verified pause/resume semantics, move wire contract, confirmation and cross-stage safety rules |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-overview.md` | State diagram concepts and allowed nodes |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-node-structures.md` | JSON schemas for nodes inside a state diagram |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-process-interaction.md` | How driver processes read / create / modify state tasks |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variable naming rules, creation workflow, usage examples |

## Description Update Rule

After any successful change to a process, folder, or project, always set or refresh its description. Full authoring rules are in `/corezoid-describe`.

Summary:
- **Process** — update `description` in `.conv.json` root **before** `push-process` (no second push needed). 1–2 sentences, start with a verb (*Calls*, *Creates*, *Validates*…), under 200 characters, no *"This process…"*
- **Folder** — call `cz-structure` action `modify-folder` with `description` if the folder was structurally changed. Resolve `folder_id` from the process's parent or by name via the `list-folders` action; if unresolvable, skip.
- **Project** — call `cz-structure` action `modify-project` with `description` if project scope changed.

---

## Tips

- Always `lint-process` before `push-process` to catch errors early
- Use `pull-folder` to sync the full stage to disk before editing
- Node IDs are 24-char hex — generate with `crypto.randomBytes(12).toString('hex')`
- Variables are workspace-scoped — check `_ENV_VARS_.json` before creating new ones
- `push-process` is mandatory after any edit — changes exist only in memory until pushed

## Proactive improvement/bug reporting

When responding to a user message that reveals a **platform-level mistake** — wrong node type, wrong API choice (Corezoid vs Simulator), wrong process structure, wrong MCP tool, missing required platform field — add one line to your response, adapted to the situation:

- Bug / broken behavior → "Хотите сообщить о баге команде Corezoid?"
- Unexpected plugin choice → "Хотите сообщить об этом команде Corezoid?"
- User hints something could be better → "Хотите отправить пожелание команде Corezoid?"

This is one extra line in the same response, not a separate action. Offer once per problem; do not repeat if the user declines.

**Do not add this line** for business-logic iterations: changing a value, adding a field, renaming, adjusting a condition — these are normal user changes, not platform issues.
corezoid-access10.1 KB

View saved version →

---
name: corezoid-access
description: >
  Corezoid access-control specialist. Use when the user wants to share a process,
  folder, stage or project with another user / group / API key, create or delete
  a user group, create or rotate an API key, invite an external user, or audit
  who currently has access to a Corezoid object. Activate when the user says
  "share", "give access", "grant access", "share to", "доступ", "пошарь",
  "create group", "создай группу", "create api key", "создай API ключ",
  "invite user", "пригласи", "revoke access", "unshare", "who has access".
---

# Corezoid Access Control

You are the specialist for sharing Corezoid objects and managing principals
(users, groups, API keys) inside a workspace. You drive `share-object`,
`create-group`, `create-api-key`, `find-principal`, `invite-user` and the
related operations.

## How to call them

All of them are **actions of the single `cz-access` MCP tool** — there is
no `share-object` tool to call on its own:

```
cz-access {"action": "share-object", "args": {"obj": "conv", "obj_id": 834936, "obj_to": "user", "obj_to_id": 78545, "privs": "view"}}
```

Every argument goes inside `args`. The examples in this skill use a shorthand —
`share-object obj=conv obj_id=834936` means exactly the call above. When unsure
about an action's arguments, call `cz-access {"action": "<action>", "help":
true}`: it returns the action's full schema and runs nothing.

## Mental model

Everything you share — a single process, a folder, a whole stage, or an
entire project — uses the **same** API operation: a `link` op against the
Corezoid `/api/2/json` endpoint with a `privs` payload. The MCP tools below
are thin wrappers around that one operation.

```
Workspace (company)
  ├── Projects ─────────► share-object obj=project
  │   └── Stages ───────► share-object obj=stage
  │       └── Folders ──► share-object obj=folder
  │           └── Processes (conv) ► share-object obj=conv
  │
  ├── Users      ── obj_to=user   (real human accounts)
  ├── API keys   ── obj_to=user   (API keys are users with logins.type=api)
  └── Groups     ── obj_to=group  (bundles of users + api keys)
```

**Key consequence:** when you share to an API key, pass `obj_to=user`
with the key's `obj_id` — *not* `obj_to=api_key`. The link API does not
accept `api_key` as a recipient kind; the data model treats API keys as
user records.

## Privilege model

Four privileges, applied independently:

| Priv     | What it lets the principal do                             | UI label         |
|----------|-----------------------------------------------------------|------------------|
| `view`   | Read process/folder content and run-time data             | View             |
| `create` | Create new tasks in a process or new objects in a folder  | Task management  |
| `modify` | Edit the process / folder / stage definition              | Modify           |
| `delete` | Delete objects                                            | Delete           |

In tools the `privs` argument accepts a comma-separated list (`"view,modify"`),
a JSON array (`'["view","create"]'`), or one of the keywords `"all"` /
`"none"`. Default when omitted is `"all"`. Pass `"none"` (or the equivalent
literal `"[]"`) to revoke — under the hood Corezoid uses the same `link`
op for grant and revoke, distinguished only by whether the privs array is
populated, so there is no separate "unshare" tool.

## The standard share workflow

The recipient is usually identified by **name**, not obj_id. Resolve first,
then share:

```
1. find-principal   name="<search>" kind=user|group|api_key
        → returns obj_id(s) and titles
2. share-object     obj=<conv|folder|stage|project>
                    obj_id=<numeric>
                    obj_to=<user|group>          # user covers API keys
                    obj_to_id=<obj_id from step 1>
                    privs="view,create"          # or "all"
```

Always confirm the match with the user when `find-principal` returns more
than one row — the wrong `obj_id` silently shares to the wrong person.

## Common scenarios

### Share a folder with a user (full access)

```
find-principal name="Andrii"
# → obj_id 78545, Andrii Chaban
share-object obj=folder obj_id=671259 obj_to=user obj_to_id=78545 privs="all"
```

### Share a process with a group (read-only)

```
find-principal name="Smart API" kind=group
# → obj_id 170464, Smart API Team
share-object obj=conv obj_id=834936 obj_to=group obj_to_id=170464 privs="view"
```

### Share an entire project with multiple principals

Call `share-object` once per recipient. Corezoid does support multi-op
batching, but the MCP tool keeps one share per call so partial failures
are obvious and easy to retry.

### Create a group, edit it, add members

```
create-group title="Backend Team" description="Owns the payment integration"
# → group_id=170800
modify-group group_id=170800 title="Payments Backend"          # rename
modify-group group_id=170800 description="Owns checkout flow"  # update description

find-principal name="@corezoid.com"
# → list of users; pick the user_ids you need
add-to-group group_id=170800 user_id=78545
add-to-group group_id=170800 user_id=97636
list-groups name="Payments"   # size column shows current member count
# then share folders/processes once to the group instead of each user
```

### Audit a group's impact before deletion

```
list-group-objects group_id=170800
# → lists every process the group has access to
```

Use this before `delete-group` to understand who loses what. The endpoint
returns processes only — folders, stages and projects shared with the
group are not retrievable through this call.

### Delete a group safely

```
delete-group group_id=170800
# → if any process is still shared with the group, the call refuses:
#     "Refused to delete group #170800 — still has 3 active share(s):
#        conv #1648675  Escalation
#        conv #1839904  Deprecated
#        conv #1840144  Deprecated_2
#      Re-run with force=true to delete anyway."

delete-group group_id=170800 force=true   # confirms destructive intent
```

Once the group is deleted, every share that referenced it is revoked
server-side — group members lose any access they inherited through the
group (this is automatic; no extra revocation calls needed).

### Create and use an API key

```
create-api-key title="Integration: Salesforce sync" description="Pulls leads hourly"
# → obj_id=29299
#   login=61e566e382ba963bcb25be3
#   secret written to: ~/.corezoid/api-keys/Integration-Salesforce-sync-29299.json
#                       (chmod 600 JSON: title, description, obj_id, login, secret, created_at)
#   ⚠ never paste the secret into chat — point the user at the file
share-object obj=conv obj_id=834936 obj_to=user obj_to_id=29299 privs="view,create"
```

**Secret hygiene** — the agent NEVER prints the raw secret in chat. The
secret lives in `~/.corezoid/api-keys/<title-slug>-<obj_id>.json` with
mode 0600 and the parent directory at 0700. Tell the user to read it
from that file, copy it into their integration's secret store, then
delete the file. If they ask the agent to "show the secret", point at
the file path rather than reading the secret aloud.

### Edit an API key

```
modify-api-key api_key_id=29299 title="Integration: Salesforce v2" description="…"
```

**Gotcha** — Corezoid rejects `modify-api-key` with "User has no rights"
when the key is not a member of any group. Before renaming a freshly
created key, attach it to any group (e.g., create a host group with
`create-group` then `add-to-group`).

### Deactivate an API key

```
delete-api-key api_key_id=29299
```

Corezoid does not expose a non-superadmin "block/unblock" operation on
API keys. Use `delete-api-key` — the secret is invalidated immediately
(subsequent requests get 401) and objects owned by the key are
reassigned to the workspace owner.

### Invite an external user (not yet in the workspace)

```
invite-user email="dev@external.com" login_type="google"
            obj=folder obj_id=671259 privs="view"
# → returns invite URL the recipient must open
```

`login_type` is usually `google`; use `corezoid` for password-based
accounts. The invite always grants access to one object; subsequent
shares for other objects use `share-object` after the user accepts.

### Audit who has access

```
list-shares obj=folder obj_id=671259
# → table of users + groups + api keys + privs each holds
```

Use this before changing share state — verify expectations first.

### Revoke access

```
share-object obj=conv obj_id=834936 obj_to=user obj_to_id=97636 privs=none
```

Same wire operation as a grant — the server distinguishes by whether
`privs` is an empty array. Multiple revocations in one batch (when the
admin UI does it) ship as one request with several link ops, each with
`privs:[]`.

## Validation rules and pitfalls

- **`obj_to` is `user` or `group` — never `api_key`.** API keys are users.
- **`obj_id` and `obj_to_id` are numeric.** The 24-char hex format is only
  for node IDs inside processes — totally unrelated.
- **`company_id` is auto-injected** from the active workspace (`WORKSPACE_ID`
  env var). If sharing fails with `company_id` errors, the user is on a
  personal workspace and the MCP server already drops the empty value —
  retry usually succeeds.
- **API key secrets are unrecoverable.** Surface them in the very next
  message after `create-api-key`. Never log them to disk silently.
- **Inviting then re-inviting the same email** produces a new URL; the old
  one stops working. Use `find-principal` with `kind=user` to check if the
  invite already turned into an active user.
- **Sharing a stage gives access to every process below it.** If the user
  only needs one process, share at `obj=conv` instead.

## When NOT to use this skill

- Creating processes / folders / variables → `corezoid-init`, `corezoid-create`
- Editing process JSON → `corezoid-edit`
- Reviewing process structure → `corezoid-review`, `corezoid-project-review`

Hand control back to the main `corezoid` skill once access changes are done.
corezoid-alias-manager14 KB

View saved version →

---
name: corezoid-alias-manager
description: >
  Manages Corezoid aliases — create, list, modify, delete, link/unlink, and use aliases
  in process JSON. Activate when the user mentions "alias", "short_name", "short name",
  "create alias", "list aliases", "delete alias", "modify alias", "rename alias",
  "unlink alias", "link alias", "get callback hash", "conv[@", or asks how to reference
  a process by name instead of numeric ID. Also activate when reviewing a process that
  uses numeric conv_id values and the user wants to replace them with aliases.
---

# Corezoid Alias Manager

## What aliases are

An alias is a human-readable `short_name` (e.g. `payment-service`, `send-otp`) that
maps to a process (`conv`). Aliases serve two purposes:

1. **Process reference in JSON** — use `@alias-name` as `conv_id` instead of a numeric
   process ID. This makes processes portable across environments and removes hardcoded IDs.
2. **External HTTP entry point** — each alias generates a `callback_hash` used to send
   tasks to the process via the API Gateway URL.

Alias naming rules (same as Corezoid short names):
- Only lowercase letters `[a-z]`, digits `[0-9]`, and hyphens `-`
- Must be at least 3 characters
- Must be unique within the stage
- Good: `payment-checkout`, `send-otp`, `create-user-v2`
- Bad: `MyAlias`, `PAYMENT`, `a`

---

## MCP Tools

| Tool | Purpose |
|------|---------|
| `create-alias` | Create an alias and link it to a process in one step |
| `cz-structure` `list-aliases` | List the aliases of a project's stage (short_name → target conv_id); pass `short_name` to resolve one exact alias instead of scanning the table |

> **Note:** `modify`, `delete`, and `unlink` operations are not yet exposed as
> MCP tools. Use the direct API calls documented below to perform them.

---

## Using aliases in process JSON

Once an alias exists, replace the numeric `conv_id` with `@alias-name` in any node that
calls another process:

### Call a Process node (`api_rpc`)
```json
{
  "type": "api_rpc",
  "conv_id": "@payment-checkout",
  "extra": { "amount": "{{amount}}", "currency": "{{currency}}" },
  "extra_type": { "amount": "number", "currency": "string" },
  "err_node_id": "<error_node_id>"
}
```

### Copy Task node (`api_copy`)
```json
{
  "type": "api_copy",
  "conv_id": "@send-notification",
  "ref": "{{unique_ref}}",
  "mode": "create",
  "err_node_id": "<error_node_id>"
}
```

### State store read in `set_param` or condition
```
{{conv[@user-states].ref[{{task_ref}}].status}}
```

This reads the `status` field of the task with reference `{{task_ref}}` from the
`@user-states` state diagram process.

---

## Workflow: Create an alias

### Step 1 — Resolve the process

Check whether the user provided a file path, process name, or process ID.
If a file path is given, the process ID is the leading number in the filename
(e.g. `1834583_My_Process.conv.json` → `process_id = 1834583`).

If only a name is given, search locally:
```bash
find . -name "*.conv.json" | xargs grep -l '"title": "My Process"'
```

### Step 2 — Check if the alias already exists

Before creating, verify the `short_name` is not taken. Call the list aliases API
(see "Workflow: List aliases" below) and scan the `short_name` fields.
If a conflict is found, suggest an alternative name to the user.

### Step 3 — Decide the short_name

Apply the naming rules: lowercase, hyphens, no spaces or underscores, at least 3 chars.
Suggest a name derived from the process title if the user hasn't specified one.

### Step 4 — Create the alias

Call MCP tool **`create-alias`** with:
- `process_path`: relative path to the `.conv.json` file
- `short_name`: the alias short name

```
create-alias(
  process_path="./671255_develop/1834583_My_Process.conv.json",
  short_name="payment-checkout"
)
```

The tool creates the alias, links it to the process, and returns the `alias_id`.
Requires a `<id>_<name>.stage.json` marker at the workspace root (run `corezoid-init` if missing).

### Step 5 — Update and redeploy referencing processes

After creating the alias, replace any numeric `conv_id` references to this process
across the project with `@short-name`:

```bash
grep -rl '"conv_id": 1834583' . --include="*.conv.json"
```

For each file found, replace `"conv_id": 1834583` with `"conv_id": "@payment-checkout"`.
Then for each modified file, run **`lint-process`** and on success **`push-process`**.

> After pushing, tell the user: "Changes deployed. Please **refresh the page** in Corezoid to see the updated process."

---

## Workflow: List aliases

Call `cz-structure` with action `list-aliases`:

```
cz-structure {"action": "list-aliases", "args": {
  "company_id": "<WORKSPACE_ID>",
  "project_id": <PROJECT_ID>,
  "stage_id": <STAGE_ID>,
  "short_name": "<optional: filter to one exact alias>"
}}
```

`company_id`, `project_id` and `stage_id` identify the project/stage to search — resolve
them with `list-projects`/`list-stages` (`cz-structure`) when searching a stage other than
the one currently configured. Pass `short_name` when you already know the alias you need
(e.g. resolving a receiver by name) to get a one-line answer instead of the full table.

**Returned per alias:**
| Field | Description |
|-------|-------------|
| Alias ID | Numeric ID (needed for modify/delete/link via the raw API — see below) |
| Title | Human-readable display title |
| Short name | The `@short-name` used in `conv_id` references |
| Target conv_id | Process (`conv`) ID this alias points to |

---

## Workflow: Modify an alias

To rename an alias or change its title/description (does NOT change which process it
points to — use unlink + link for that).

**API call:**
```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type": "modify",
    "obj": "alias",
    "obj_id": <ALIAS_ID>,
    "title": "New Display Title",
    "short_name": "new-short-name",
    "description": "Updated description",
    "company_id": "<WORKSPACE_ID>",
    "project_id": <PROJECT_ID>,
    "stage_id": <STAGE_ID>
  }]
}
```

> ⚠️ Changing `short_name` invalidates all `"conv_id": "@old-name"` references
> across every process in the project. Grep all `.conv.json` files and update them,
> then push each modified process.

---

## Workflow: Repoint an alias to a different process

Use two API calls: unlink from the current process, then link to the new one.

### Step 1 — Unlink from current process
```
POST {corezoid_url}/api/2/json

{
  "ops": [{
    "type": "link",
    "obj": "alias",
    "link": false,
    "obj_id": <ALIAS_ID>,
    "obj_to_id": <CURRENT_PROCESS_ID>,
    "obj_to_type": "conv",
    "company_id": "<WORKSPACE_ID>"
  }]
}
```

### Step 2 — Link to new process
```
POST {corezoid_url}/api/2/json

{
  "ops": [{
    "type": "link",
    "obj": "alias",
    "link": true,
    "obj_id": <ALIAS_ID>,
    "obj_to_id": <NEW_PROCESS_ID>,
    "obj_to_type": "conv",
    "company_id": "<WORKSPACE_ID>"
  }]
}
```

---

## Workflow: Delete an alias

> ⚠️ Before deleting, check all `.conv.json` files for `"conv_id": "@alias-name"` references.
> Deleting an alias breaks every process that uses it.

**API call:**
```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type": "delete",
    "obj": "alias",
    "obj_id": <ALIAS_ID>,
    "company_id": "<WORKSPACE_ID>"
  }]
}
```

After deletion, replace all `"conv_id": "@alias-name"` references with the numeric
process ID (or create a new alias), then push each affected process.

---

## Workflow: Get callback hash (external task submission URL / Direct URL webhook)

The callback hash is the secret in a process's or alias's **Direct URL** — the public
webhook endpoint external systems POST tasks to. A process and each of its aliases
have **independent** hashes; rotating or deleting one does not affect the others.

**Get the hash — by process (`conv_id`):**
```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type": "get",
    "obj": "callback_hash",
    "conv_id": <PROCESS_ID>,
    "company_id": "<WORKSPACE_ID>"
  }]
}
```

**Get the hash — by alias (`alias_id`):**
```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type": "get",
    "obj": "callback_hash",
    "obj_type": "alias",
    "alias_id": <ALIAS_ID>,
    "company_id": "<WORKSPACE_ID>"
  }]
}
```

**Response (both):**
```json
{ "request_proc": "ok", "ops": [{ "proc": "ok", "callback_hash": "19e339a865d676db68b776f440443821c49a0e30" }] }
```

Other `type` values on the same `obj: "callback_hash"` (swap `"type": "get"` for one of
these, keeping the same `conv_id`/`alias_id` field):

| `type` | Effect |
|---|---|
| `create` | Enables the Direct URL and issues the first hash |
| `get` | Returns the current hash without changing anything |
| `modify` | **Rotates** the hash — the previous URL stops working immediately |
| `delete` | Disables the Direct URL entirely |

**External submission URL:**

⚠️ The URL format is **installation-specific** — always verify against the Direct URL
shown in the Corezoid UI for that process/alias before handing it to another system.
Two formats have been observed:

- **Cloud (dedicated API Gateway):**
  ```
  POST {COREZOID_APIGW_URL}/api/1/json/<WORKSPACE_ID>/<callback_hash>
  Content-Type: application/json

  { "ops": [{ "ref": "unique-task-ref", "type": "create", "obj": "task", "data": { "key": "value" } }] }
  ```
  Where `COREZOID_APIGW_URL` defaults to `https://api-apigw.corezoid.com`.

- **Self-hosted (served off the same account host, no separate gateway):**
  ```
  By conv_id:  https://<host>/api/2/json/public/<conv_id>/<callback_hash>
  By alias:    https://<host>/api/2/json/public/@<alias_short_name>/<project_short_name>/<stage_short_name>/<company_id>/<callback_hash>
  ```
  `<host>` is the same host as `corezoid_url` on the current Folder. Some installations serve this under
  `/api/1/json/public/...` instead of `/api/2/` — don't assume without confirming once
  per installation.

Either way, the hash in the URL **is** the authentication — treat it like a secret.
Prefer alias-based URLs for anything referenced by external systems: an alias can be
re-pointed at a new process without invalidating URLs already handed out.

---

## Resolving environment values

`create-alias` resolves `stage_id`/`project_id` automatically from the target process's own
location — no argument needed. `list-aliases` (`cz-structure`) does not: it takes
`project_id`/`stage_id`/`company_id` explicitly, because it is meant to look up aliases in
any stage, not only the one currently configured. For **direct** `/api/2/json` calls (the raw
workflows above) collect the values from:

| Value | Where to find it |
|-------|------------------|
| `company_id` | `workspace_id` field in current Folder (`~/.corezoid/config.json`) |
| `stage_id` | `obj_id` in `<id>_<name>.stage.json` at the workspace root |
| `project_id` | `parent_id` in the same `<id>_<name>.stage.json` |
| API URL | `corezoid_url` field in current Folder |
| Access token | `access_token` field in current Folder |

If the marker file is missing, run the `corezoid-init` skill.

---

## Two-step create (alternative manual flow)

The `create-alias` MCP tool creates and links in a single request. If you need
finer control (create first, link later), use the two-step API flow:

### Step 1 — Create the alias object
```json
{
  "ops": [{
    "type": "create",
    "obj": "alias",
    "title": "My Alias Title",
    "short_name": "my-alias",
    "description": "",
    "company_id": "<WORKSPACE_ID>",
    "project_id": <PROJECT_ID>,
    "stage_id": <STAGE_ID>
  }]
}
```
Response: `{ "obj_id": <ALIAS_ID>, "proc": "ok" }`

### Step 2 — Link the alias to a process
```json
{
  "ops": [{
    "type": "link",
    "obj": "alias",
    "link": true,
    "obj_id": <ALIAS_ID>,
    "obj_to_id": <PROCESS_ID>,
    "obj_to_type": "conv",
    "company_id": "<WORKSPACE_ID>"
  }]
}
```

---

## Common pitfalls

| Mistake | Correct approach |
|---------|-----------------|
| `"conv_id": "@My-Alias"` (uppercase) | `"conv_id": "@my-alias"` — always lowercase |
| Deleting an alias without updating referencing processes | Search all `.conv.json` first with `grep -r "@alias-name"` |
| Renaming `short_name` without updating process JSON | Same — grep, replace, push each changed process |
| Creating an alias with `short_name` that already exists | Use `list aliases` API call first to check |
| Forgetting to push processes after replacing numeric IDs with aliases | Always `lint-process` then `push-process` for every modified file |
| Using alias when the workspace has no `<id>_<name>.stage.json` marker | Run `corezoid-init` |

---

## Decision guide

| Situation | Action |
|-----------|--------|
| Process has numeric `conv_id` references from other processes | Create alias, then replace all numeric IDs |
| Setting up a new process that others will call | Create alias at creation time, use `@alias` from the start |
| Want to swap which process an alias points to (blue/green deploy) | Unlink + Link (keep the `short_name`, update the target) |
| Decommissioning a process | Check alias references, delete alias, update or deprecate callers |
| External system needs to send tasks to a process | Get callback hash for the alias, build API Gateway URL |
| Reviewing a process for hardcoded numeric `conv_id` | Flag each one, suggest alias creation and replacement |

---

## Reference Documents

| Path | When to read |
|------|-------------|
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | `api_rpc` node — where `conv_id: "@alias"` is used |
| `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md` | JSON schemas for `api_rpc` and `api_copy` (both use `conv_id`) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-dynamic-values.md` | `{{conv[@alias].ref[...].field}}` state store read pattern |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variable naming rules (same convention as alias short names) |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-review/SKILL.md` | Step 10: External Dependencies — alias audit and creation |
corezoid-api-connector4.18 KB

View saved version →

---
name: corezoid-api-connector
description: >
  Corezoid API connector specialist. Use when the user wants to create a process
  that calls the Corezoid public API (/api/2/json/), works with Corezoid objects
  (nodes, processes, tasks, users) via the API, or needs to automate Corezoid
  platform operations. Activate when the user says "call Corezoid API",
  "Corezoid API connector", "node list", "api/2/json", "api_secret_outer",
  or references openapi.corezoid.com operations.
---

# Corezoid API Connector

You are a specialist in building Corezoid processes that call the **Corezoid public API**.

> **Always read the reference doc before generating any JSON:**
> `${CLAUDE_PLUGIN_ROOT}/docs/process/corezoid-api-integration.md`

---

## Step 1: Identify the Operation

Ask the user:

- Which Corezoid API operation? (e.g. node list, process list, task list)
- What filtering/pagination params are needed? (limit, offset, obj_id, etc.)
- Where should the process be created? (folder path)

Check [openapi.corezoid.com](https://openapi.corezoid.com) for the `ops` fields of the target operation.

---

## Step 2: Create the Empty Process

Call **`create-process`** with:

- `process_name`: descriptive name, e.g. `"Corezoid API - Node List"`
- `folder_path`: target folder (omit for project root)

Check `scheme.nodes` in the created file — do not add a Start node if one already exists.

---

## Step 3: Define Input Parameters

Every Corezoid API connector requires these base params:

| Name          | Type   | Description                                 |
| ------------- | ------ | ------------------------------------------- |
| `api_login`   | string | API key login                               |
| `api_secret`  | string | API key secret (used in `api_secret_outer`) |
| `workspaceId` | string | Workspace (company) ID                      |

Add operation-specific params (e.g. `processId`, `nodeId`, `limit`, `offset`).

---

## Step 4: Design the Process

### Process flow — no Code Node needed

```
Start → API Call → Reply Success → Done [obj_type:2]
              │
         (err)└──► Error Escalation [obj_type:3] ──► Error [obj_type:2]
         (time)└─► Timeout Escalation [obj_type:3] ──► Timeout [obj_type:2]
```

### API Call node — key fields

```json
{
  "type": "api",
  "api_secret_outer": "{{api_secret}}",
  "method": "POST",
  "url": "https://api.corezoid.com/api/2/json/{{api_login}}",
  "format": "",
  "extra": {
    "ops": "[{\"type\":\"<op_type>\",\"obj\":\"<obj>\",\"company_id\":\"{{workspaceId}}\", ...}]"
  },
  "extra_type": {
    "ops": "array"
  },
  "extra_headers": { "content-type": "application/json; charset=utf-8" },
  "send_sys": true,
  "rfc_format": true,
  "is_migrate": true,
  "customize_response": false,
  "response": { "body": "{{body}}", "header": "{{header}}" },
  "response_type": { "body": "object", "header": "object" },
  "version": 2,
  "max_threads": 5,
  "debug_info": false,
  "cert_pem": "",
  "err_node_id": "<error_escalation_id>"
}
```

Add a 30-second timeout semaphor to the API Call node.

### Reply Success node

```json
{
  "type": "api_rpc_reply",
  "mode": "key_value",
  "res_data": { "result": "success", "response": "{{ops[0]}}" },
  "res_data_type": { "result": "string", "response": "object" },
  "throw_exception": false
}
```

### Critical rules

- `api_secret_outer` handles all authentication — **never** use a Code Node to compute SHA1
- `ops` value must be a **stringified JSON string**, type `"array"`
- `format` must be `""` (empty string) — not `"raw"`
- `send_sys: true` is required
- `customize_response: false` — use default `body`/`header` response mapping

---

## Step 5: Validate and Deploy

```
lint-process → fix errors → push-process → run-task (smoke test)
```

---

## Reference

| Resource         | Path                                                             |
| ---------------- | ---------------------------------------------------------------- |
| Full pattern doc | `${CLAUDE_PLUGIN_ROOT}/docs/process/corezoid-api-integration.md` |
| Sample process   | `${CLAUDE_PLUGIN_ROOT}/samples/corezoid-api-node-list.conv.json` |
| API reference    | https://openapi.corezoid.com                                     |
corezoid-connector-create12.2 KB

View saved version →

---
name: corezoid-connector-create
description: >
  API connector builder. Use when the user wants a Corezoid process that calls ONE endpoint
  of ANY HTTP API — external (Stripe, OpenWeather, Google, banks, CRMs), internal
  (Simulator.Company, own services) or Corezoid itself — built from a text description, a
  documentation link, an OpenAPI/Swagger file, or a list of endpoints produced while
  reasoning about a task. Produces an atomic connector: input contract, validation when
  needed, a single API Call, Reply to Process on every outcome, unified response format,
  test, and Smart API registration. Activate on: "create a connector", "API connector",
  "integrate with <API>", "connect to <API>", "wrap this endpoint",
  "connector from this openapi.yaml", "сделай коннектор", "коннектор к API",
  "интеграция с", "подключи API", "обёртка над API", "зроби конектор", "конектор до API",
  "інтеграція з", "підключи API".
  NOT for processes with business logic or several calls — use /corezoid-create.
---

# Create an API Connector Process

You build **connector processes**: a Corezoid process that atomically calls **one endpoint**
(method + path) of a Target API and always answers its caller.

A connector is called by other processes through `api_rpc`, so it MUST reply on every path.
After a successful test it is registered as a Smart API Connector in Simulator.Company.

## Routing — check first

| The request is… | Use |
|---|---|
| the user explicitly called `/corezoid-api-connector` | that skill — do not intercept |
| a process with business logic, several calls, orchestration | `/corezoid-create` — stop here |
| one endpoint of any HTTP API, Corezoid API included (or several endpoints → several connectors) | **this skill** |
| an endpoint that needs files, git or a protocol the API Call node does not support | this skill, the call node is `git_call` (read `/corezoid-gitcall` first) |

## Hard rules (every connector, no exceptions)

1. **One process = one endpoint.** Several operations → several processes, one by one.
2. **Input contract in `params`.** Every business parameter is declared with type, `required`
   flag and regex where the format is known. Output fields are declared too (flag `output`).
3. **No secrets, hosts or keys in task data or hardcoded.** Host, API keys, tokens — stage
   variables only: `{{env_var[@<name>]}}` (create with `cz-variables`).
4. **Validation when the contract needs it** (required fields, formats, ranges) — before the
   API Call, with its own Reply. A Code node is allowed for validation.
5. **Exactly one call node**: `api`, or `git_call` only by the exception in Routing.
6. **`debug_info: true`** on the API Call node — the connector reads `__conveyor_api_debug__`
   (HTTP code, timings) to build its response.
7. **Reply to Process before EVERY final** — success, validation error, API error, connection
   error, timeout. A path that reaches a final without `api_rpc_reply` hangs the caller.
8. **Unified response format** — see `references/reply-format.md`. Result fields are named
   after the endpoint (`customer`, `forecast`, `items` + `total`), never a generic
   `data` / `response`.
9. **No Sum nodes, no metric sub-processes.** Metrics are collected outside the connector
   (HTTP-worker logs → Elasticsearch).
10. **A connector is ready only after a successful positive test** (Step 9).

## Workflow

```
1 Intake → 2 Contract → 3 Duplicate check → 4 Create process, params, variables
→ 5 Build nodes → 6 Layout → 7 Lint → 8 Push → 9 Test → 10 Register → 11 Report
```

Do the steps in order. Do not skip Contract confirmation, Test or Register.

---

## Step 1: Intake

Accept any of:

- a text description of the API / endpoint;
- a documentation link or an OpenAPI / Swagger URL — fetch and read it;
- an OpenAPI / Swagger / Postman file — read it;
- a list of connectors produced earlier in the conversation ("which connectors does this task need").

Output of the step — a numbered list of endpoints: `METHOD path — purpose`. Show it to the
user. If there are several, confirm which to build and build them **one at a time**
(Steps 2–11 per endpoint).

Also ask (if not obvious): target folder for the process, Corezoid stage (default: develop).

## Step 2: Contract

Fill the contract table from `references/contract-template.md` for the chosen endpoint:
endpoint, provider + base host, input, output, expected errors, auth, test data.

Rules:

- Take everything you can from the documentation; ask the user only for what is missing.
- Auth goes to variables, never to input params (e.g. `stripe-api-key` variable,
  header `Authorization: Bearer {{env_var[@stripe-api-key]}}`).
- Decide **"validation needed?"**: yes if there are required fields, formats (email, UUID,
  dates, enums) or ranges that the Target API would reject.
- **Show the contract to the user and get explicit confirmation** before building.

Keep the confirmed contract — Steps 9 and 10 use it.

## Step 3: Duplicate check

Search the exported `.conv.json` files of the project (run `pull-folder` if needed) for an
`api` node with the same method and the same host + path (after resolving variables).
If found — tell the user and offer: reuse it, update it (→ `/corezoid-edit`), or build a new
one anyway.

## Step 4: Create the process, params and variables

1. **Variables.** For host, keys and tokens: list existing ones (`cz-variables`
   `list-variables`), create missing ones (`create-variable`; secrets as secret variables).
   Naming: `<provider>-api-host`, `<provider>-api-key`.
2. **Process.** Call `create-process` with `process_name` = `<Provider>: <METHOD> <path>`
   (e.g. `Stripe: GET /v1/customers/{id}`) and the target `folder_path`. Check
   `scheme.nodes` of the created file — do not add a second Start.
3. **Description.** 1–2 sentences, starting with a verb: what the endpoint does and what it
   returns (see Description Update Rule in `corezoid/SKILL.md`).
4. **Params.** Declare input and output params in the full shape:

```json
{"name": "customer_id", "type": "string", "descr": "Stripe customer id",
 "flags": ["required", "input"], "regex": "^cus_[A-Za-z0-9]+$",
 "regex_error_text": "customer_id must look like cus_..."}
```

Output params: same shape with `"flags": ["output"]`, one per result field of the reply.

## Step 5: Build the nodes

Use the skeleton in `references/process-skeleton.md`. Main path:

```
Start
→ [Validate input]            (Code node, only if validation is needed)
→ [Check validation]          (Condition: invalid → Reply validation error)
→ [Prepare request]           (Code / Set Parameters, only if the body/query needs building)
→ API Call                    (single api node, debug_info: true, time semaphore)
→ Reply ok                    (result = ok + named result fields + http_code)
→ Final
```

Error paths (each with its own Reply and a named Error final):

| Failure | How it is caught | Reply |
|---|---|---|
| input invalid | Condition after validation | `error_type: validation` + `invalid_params` |
| Target API answered non-2xx | `err_node_id` → Condition on `__conveyor_api_return_type_tag__` = `api_bad_answer` (also `api_bad_answer_format`) | `error_type: api` |
| connection / DNS / TLS | same Condition, tag `api_connection_error` | `error_type: connection` |
| Target API call itself timed out (hardware) | same Condition, tag `api_timeout` — routes to the same Reply as the semaphore below | `error_type: timeout` |
| no answer within the process-level wait | time semaphore of the API Call (default 30 s) | `error_type: timeout` |
| anything else from the API Call | same Condition, default branch | `error_type: api` |

Node rules — follow the **Core rules** of `/corezoid-create` (Step 4 there), especially:
24-hex temporary node IDs; connect only via `go`; every `err_node_id` / semaphore target is
`obj_type: 3`; never mix an action logic with `go_if_const` in one node; error clusters
collapsed and named after the failure (`Customer Not Found Error`, not `Error`); a dedicated
error cluster per failing node (validation Code node, prepare node, API Call).

API Call node — author the **full canonical `api` logic** (see `docs/nodes/api-call-node.md`,
"Required node shape"); the reference shape is in `references/process-skeleton.md`.
`debug_info` MUST be `true`.

## Step 6: Layout

Leave coordinates at `x: 0, y: 0` while building, then call `layout-process`.

## Step 7: Lint

Call `lint-process` (with `profile: "connector"` once the plugin supports it). Fix every
error and re-run until clean. In addition, check by hand until the profile exists:

- [ ] exactly one `api` (or `git_call`) node;
- [ ] `debug_info: true` on it;
- [ ] every final is reachable only through an `api_rpc_reply`;
- [ ] no hardcoded `http://` / `https://` host in the URL, no keys in params;
- [ ] no `api_sum` nodes.

## Step 8: Push

Call `push-process`. Then `pull-process` to get canonical node IDs (needed for `nodeId` in
Step 10).

## Step 9: Test

Follow `references/test-rules.md`. In short:

1. Build test data from the contract (docs examples; ask the user for what is missing).
2. **Positive test** via `run-task` — expect `result = ok`, the expected `http_code`, result
   fields matching the output params by name and type.
3. **Negative validation test** (if validation exists) — invalid input → `error_type =
   validation` + `invalid_params`, no API call made.
4. Unsafe methods (POST / PUT / PATCH / DELETE) — only after the user confirms, or against a
   sandbox host variable.
5. On failure — show the node where the task stopped and the reply, propose a fix, re-test.
6. Record the test result: `conv_id`, task ref, time, result.

**No successful positive test → the connector is not ready. Do not register it.**

## Step 10: Register the Smart API Connector

Follow `references/registration.md`. In short:

1. Find the receiver in the current workspace by names: project short_name "smart-api" → its
   stage "production" → alias "api-gw-create-smart-api" → the process it points to. A missing
   project/stage/alias is an expected outcome, not an error — the user may lack access, or
   the resource may have been renamed/removed; don't guess which. Stop here and finish: the
   connector is ready, Smart API was not registered (not found). Never block.
2. `run-task` into the receiver process, exactly ONCE, with the task data in
   `references/registration.md`. Task ref = `conv_id` of the connector.
3. Report whatever comes back, verbatim, and stop there: a successful Reply → show the user
   the Smart API actor id; any error, rejection, timeout, or no answer → tell the user Smart
   API was not registered and why, quoting the receiver's own error. **Never** retry, guess a
   different payload and resend, or pull/inspect the receiver process or any of its
   sub-processes to "fix" a rejection — that is debugging someone else's production system,
   not this skill's job. One attempt, one honest report, done.

## Step 11: Report

Tell the user, briefly: process name and link, contract summary (method, path, inputs,
outputs), test result, Smart API id (or why it was not registered), variables created.

Then do the **Final Step: Update Git Context** exactly as in `/corezoid-create`.

---

## References

| Path | When |
|---|---|
| `references/contract-template.md` | Step 2 — contract table and examples |
| `references/process-skeleton.md` | Step 5 — node skeleton, API Call shape, error routing |
| `references/reply-format.md` | Steps 5, 9 — response format, error mapping |
| `references/test-rules.md` | Step 9 |
| `references/registration.md` | Step 10 |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-create/SKILL.md` | Core rules for nodes and error clusters |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | API Call fields, error tags, semaphores |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/reply-to-process-node.md` | Reply formats, stringification |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-with-parameters.md` | `params` shape |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variables |
| `${CLAUDE_PLUGIN_ROOT}/samples/api-post.json` | HTTP POST example (note: its `debug_info: false` and generic `response` reply do NOT meet the rules above) |

Referenced files: 5

corezoid-create15.9 KB

View saved version →

---
name: corezoid-create
description: >
  Corezoid process creation specialist. Use when the user wants to create a new
  Corezoid process from scratch, build a new automation flow, or design a new business
  process. Activate when the user says "create a process", "build a new flow",
  "new process", "design from scratch", "create an automation", or "add a new process".
  Not for API connectors — use /corezoid-connector-create.
---

# Create a New Corezoid Process

You are a specialist in creating Corezoid processes using the `corezoid` MCP server.

## Step 1: Gather Requirements

> ⚠️ If the request is a connector to one API endpoint (any HTTP API — external, internal, or Corezoid's own public API), stop here and use `/corezoid-connector-create` instead — it builds the atomic single-endpoint pattern (input contract, validation, API Call, Reply on every outcome, test, Smart API registration). Exception: the user explicitly invoked `/corezoid-api-connector` — do not intercept that.

Ask the user for the following before proceeding:

- **Process purpose** — what should it do?
- **Input parameters** — what data does it receive?
- **Expected output** — what should it return on success?
- **Process type** — business logic that orchestrates other Corezoid processes (several calls; a single-endpoint API connector belongs to the warning above, not here)

If any required information is missing, ask the user before proceeding.

---

## Step 2: Create the Empty Process

Call MCP tool **`create-process`** with:
- `folder_path`: Relative path to the folder directory. Omit to use the current directory.
- `process_name`: the process name

This creates an empty process in Corezoid and saves the file as `<ID>_<Name>.conv.json` inside `folder_path`. The returned file path is `PROCESS_PATH` — all subsequent steps use it.

> ⚠️ Always verify `folder_path` points to the intended target folder. Omitting it places the process in the project root, which may not be the correct location.

> ⚠️ After `create-process`, Corezoid may create default template nodes (Start, a placeholder process node, Final) even with `create_mode: without_nodes`. Before generating the full JSON, check the current `scheme.nodes` array in the created file. If a Start node already exists, do **not** add another — doing so will cause a validation error.

If the process type is **business logic** and it needs to call existing processes, find their `conv_id` values by browsing the already-exported `.conv.json` files in the project folder.

---

## Step 3: Design the Process Structure

Every process follows this base structure:

| # | Node | obj_type | Purpose |
|---|------|----------|---------|
| 1 | Start | 1 | Entry point |
| 2 | Code Node _(optional)_ | 0 | Prepare / transform input data |
| 3 | **API Call** _or_ **Call a Process** | 0 | Core action (one or more) |
| 4 | Reply to Process (Success) | 0 | Return result to caller |
| 5 | Reply to Process (Error) | 3 | Return error to caller — **one dedicated node per failure point**; `err_node_id` targets are escalations (`obj_type: 3`) |
| 6 | Error | 2 | Terminal error node — **one dedicated, descriptively-named node per failure point** |
| 7 | Final | 2 | Terminal success node |

**Business logic** uses `type: "api_rpc"` in Step 3 (one node per sub-process call; Code Nodes between calls are allowed). An individual step may still use an `api` Call node alongside `api_rpc` calls — a standalone single-endpoint connector belongs in `/corezoid-connector-create`.

### Node type quick reference

| Node | obj_type | Logic type |
|------|----------|------------|
| Start | 1 | `go` |
| Code Node | 0 | `api_code` |
| Call a Process | 0 (`4` only for active Stub Mode) | `api_rpc` |
| API Call | 0 | `api` |
| Condition (business flow) | 0 | `go_if_const` |
| Reply to Process (success, via `go`) | 0 | `api_rpc_reply` |
| Any `err_node_id` target (error Reply, retry Condition/Delay) | 3 | _(escalation)_ |
| End / Error | 2 | _(no logics)_ |

For complete JSON schemas see `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md`.

> **Node choice — do NOT default to `git_call`.** `git_call` is deliberately absent
> from the table above: hosted sandbox measurements show an approximately 60 s
> execution deadline, so handlers need to finish comfortably below 50 s. Its default
> 50 MB RAM / 0.1 CPU allocation comes from a shared, super-admin-configurable pool;
> concurrent calls can contend for it, and local storage is ephemeral. Writing one
> code block is often *easier* than modelling the flow — that is not a reason to use
> it. Prefer, in order: native nodes
> (`set_param`, `condition`, `delay`, `api`/`api_rpc`, `db_call`) → a **Code node
> (`api_code`)** for transforms/parse/compute → `git_call` **only** when a file to
> parse, an external library, or a custom runtime leaves no other option. **Never**
> put long-running work or a latency-critical path that requires predictable
> execution time in `git_call`. Model loops/polling as process state with
> `condition`+`delay`, and external waits with a callback; those patterns avoid
> holding one handler open and remain subject to normal platform/process limits.
> Full guidance and the decision gate: the `corezoid-gitcall` skill.

---

## Step 4: Generate the Process JSON

Produce a valid `.conv.json` file.

### Root object

```json
{
  "obj_type": 1,
  "obj_id": null,
  "parent_id": null,
  "title": "Process Name",
  "description": "",
  "status": "active",
  "params": [],
  "ref_mask": true,
  "conv_type": "process",
  "scheme": {
    "nodes": [],
    "web_settings": [[], []]
  }
}
```

Fill in `description` based on the requirements gathered in Step 1 (see Description Update Rule in `corezoid/SKILL.md`): 1–2 sentences starting with a verb, under 200 characters, no *"This process…"* preamble.

`params` — declare all input parameters the caller must pass. See `${CLAUDE_PLUGIN_ROOT}/docs/process/process-with-parameters.md`.

### Core rules

- Node IDs must be unique 24-character hex strings: `^[0-9a-f]{24}$`. These are **temporary placeholders** for new nodes — on `push-process` Corezoid reassigns its own canonical IDs (and rewires references within the push). Run `pull-process` after pushing to get the canonical IDs before any further edits. See [Node ID Lifecycle](${CLAUDE_PLUGIN_ROOT}/docs/process/process-development-guide.md#node-id-lifecycle-server-assignment--stability-on-push).
- Connect nodes only through the `go` field
- **Dedicated error cluster per error-prone node.** Every node that can fail (`set_param`, `api`, `api_rpc`, `api_code`, `api_copy`, `db_call`, `git_call`, `api_sum`) gets its **own** error path — never funnel several failing nodes into one shared Reply/Error node. Each cluster is:
  1. A **Reply to Process** node (`api_rpc_reply`, `obj_type: 3` — it is an escalation, being an `err_node_id` target) that returns the error to the caller — set to **collapsed**: `"extra": "{\"modeForm\":\"collapse\",\"icon\":\"\"}"`.
  2. → an **Error** end node (`obj_type: 2`, **collapsed** like the rest of the cluster: `"extra": "{\"modeForm\":\"collapse\",\"icon\":\"error\"}"`) **named after the specific failure** so the error is obvious on hover/selection (e.g. `Charge Payment Error`, not generic `Error`).
  - Wire `err_node_id` of the failing node → its Reply node; the Reply node's `go` → its Error node.
  - Separate Error nodes per failure point (instead of one shared terminal) make the process far more readable.
  - For fire-and-forget processes that do not reply to a caller, skip the Reply node and wire `err_node_id` directly to the dedicated named Error node.
  - A single node's error MAY fan through its own Condition (several `go_if_const` branches) into one Error terminal — that cluster still belongs to that one node. A NEIGHBOUR's error must never join it (direct `err_node_id` or a converging tail) — `lint-process` flags this as a shared error cluster.
  - When the error path routes a retry (Condition → Delay → back to the failing node), set the error-path **Condition** and the **Delay** to collapsed (`"extra": "{\"modeForm\":\"collapse\",\"icon\":\"\"}"`), same as the Reply node. Business-logic Conditions stay expanded.
  - **Every `err_node_id` target is `obj_type: 3`** — the error Reply AND the retry/fatal Condition alike. An `obj_type: 0` err target is the legacy old format: the UI shows "Convert process to new format" and rewrites it. Business-flow Conditions reached via `go` stay `obj_type: 0`.
  - **Never mix an action logic with `go_if_const` in one node** (e.g. `set_param` + a conditional branch). That is old format too — the UI converter splits it. Author it as two nodes: the action node's `go` → a separate Condition node. `lint-process` flags both old-format shapes.
  - Never create an Escalation node (`obj_type: 3`) that only contains a bare `go` — that is a passthrough anti-pattern flagged by `lint-process`.
- **A process invoked via Call a Process (`api_rpc`) must execute `api_rpc_reply` on EVERY path — success included.** The caller's task waits in the Call node until the callee replies; a path that reaches a final without a Reply hangs the caller until its timeout semaphor. Put a collapsed success Reply right before the success final. `lint-process` flags finals reachable without a Reply in any process that replies elsewhere.
- **Do not create active Stub Mode unless the user explicitly asks for a temporary mock.** Stub Mode is `obj_type: 4` plus `condition.stub`; it bypasses the called process and returns configured mock replies. Use it only while the target process is not ready or for controlled integration tests, and avoid production unless the user explicitly confirms `allow_active_stub_mode=true`.
- All constants (URLs, tokens, IDs) must be Corezoid variables — never hardcoded:
  1. Check for existing variables: read `_ENV_VARS_.json` (from `pull-folder`) or `.processes/variables.json` (from this session)
  2. Create a new variable if needed: call **`cz-variables`** with `action: "create-variable"` and `args` `name`, `description`, `value`
  3. Reference in logic: `{{env_var[@variable-name]}}`
- Use descriptive `title` values (e.g., "Call Payment Process", not "RPC")
- Position main-flow nodes top-to-bottom, incrementing `y` by 200–250px
- **Pin each error cluster tight to the node it protects.** Place the Reply node at the **same `y`** as its failing node and just to the right (`x + ~250`) so the collapsed Reply sits right next to it; place its Error node immediately to the right of the Reply (`x + ~500`), same `y`. Same `y` gives a straight horizontal connector; the small offset keeps the cluster visually attached instead of drifting off with a large gap

### Common pitfalls

- Using `"type": "call_process"` instead of `"type": "api_rpc"` — will fail validation
- Missing `extra`/`extra_type` in Call a Process node — both required even if empty (`{}`)
- Leaving active Stub Mode (`obj_type: 4`) in a production path — it returns mock data instead of calling the real process
- Raw JSON objects as values in `extra` — must be stringified: `"{\"key\":\"val\"}"`
- Keys in `extra` and `extra_type` must match exactly
- Missing `rfc_format: true`, `customize_response: true`, or `version: 2` in API Call node

---

## Step 5: Lay Out the Nodes

You built this process, so its positions are yours to arrange: leave the
node coordinates at `x: 0, y: 0` while generating the JSON (never hand-place
them by eye) and call MCP tool **`layout-process`** with
`process_path: "<PROCESS_PATH>"` once the nodes and edges are final. It
arranges everything deterministically (no overlaps, business flow top-down,
errors railed to the right) and reports the strategy and canvas size. See the
`corezoid-node-layout` skill for details and density options.

---

## Step 6: Validate with Lint

Call MCP tool **`lint-process`** with `process_path: "<PROCESS_PATH>"`.

Fix all reported errors and re-run until the output is clean. Do not proceed with lint errors.

---

## Step 7: Deploy and Test

Call MCP tool **`push-process`** with `process_path: "<PROCESS_PATH>"`.

After a successful deploy, run a test task to verify the process behaves as expected:

Call MCP tool **`run-task`** with:
- `process_path`: `<PROCESS_PATH>`
- `data`: `{"param1": "value1"}`

---

## Reference Documents

Use the `Read` tool to load these files when specific node or validation details are needed:

| Path | When to read |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md` | JSON schemas for all node types + full Logics fields reference (canonical) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-built-in-functions.md` | Built-in functions: `$.math`, `$.date`, `$.random`, `$.sha1_hex`, `$.md5_hex`, `$.base64_encode`, `$.unixtime`, `$.map`, `$.filter` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-dynamic-values.md` | Dynamic values: `{{var}}`, `{{node[id].count}}`, `{{node[id].SumID}}`, `{{conv[@alias].ref[...]}}`, `{{env_var[@name].key[1]}}` |
| `${CLAUDE_PLUGIN_ROOT}/docs/tasks/task-metadata.md` | Global `root.*` fields: `root.task_id`, `root.ref`, `root.conv_id`, `root.node_id`, `root.prev_node_id`, `root.user_id`, `root.change_time`, `root.create_time` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/code-node.md` | Code node details and available JS libraries |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | Call a Process node, semaphores, cross-folder calls |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/reply-to-process-node.md` | Reply formats, object stringification |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | HTTP API call configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/delay-node.md` | Delay/timer node; 30s limit is static-literal only — dynamic absolute-timestamp `value` for scheduled or sub-30s delays |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/end-node.md` | End node success/error configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-json-validation.md` | Validation rules and common errors |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/error-handling.md` | Error handling patterns |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/node-positioning-best-practices.md` | Coordinate system and layout guidelines |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variable naming rules, creation workflow, usage examples |

## Example Processes

| Path | Description |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/samples/api-post.json` | A single `api` Call node inside a business-logic process — not the connector pattern; for a standalone connector use `/corezoid-connector-create` (`references/process-skeleton.md` there has the compliant shape: `debug_info: true`, named result fields) |
| `${CLAUDE_PLUGIN_ROOT}/samples/corezoid-api-node-list.conv.json` | Corezoid API connector (Node List, `api_secret_outer` pattern) |
| `${CLAUDE_PLUGIN_ROOT}/samples/stripe-checkout.json` | Stripe payment checkout flow |
| `${CLAUDE_PLUGIN_ROOT}/samples/create-actors.json` | Business logic with multiple process calls |
| `${CLAUDE_PLUGIN_ROOT}/samples/gpt-calculator.json` | GPT integration example |
| `${CLAUDE_PLUGIN_ROOT}/samples/create-user.json` | User creation process |

---

## Final Step: Update Git Context

> **Do NOT report the task as complete and do NOT stop until this step is evaluated.**
> The main task being deployed does not mean the session is over — this step is next.

Immediately after `push-process` or `create-process` succeeds, check whether
**at least one** of the following is true:

- `push-process` or `create-process` was actually called (not just previewed);
- a new external host/API/service appeared that is not yet in `dependencies.md`;
- an architectural decision was made (one approach chosen over another);
- an issue was found or closed during this session.

**If yes → activate `corezoid-git-context` skill right now.** Do not wait for the
user to ask. Do not skip because the main task "looks done".

**If none of the above → skip** and tell the user the session is complete.

The skill handles everything autonomously: reads current `_ext/docs/`, proposes
a unified diff, asks confirmation, writes files, and pushes — one invocation.
corezoid-dashboard-manager8.82 KB

View saved version →

---
name: corezoid-dashboard-manager
description: >
  Creates and manages Corezoid dashboards — adds charts (column, pie, funnel, table), binds metrics
  to process nodes, configures real-time mode, and sets up drill-down linking between dashboards.
  Activate whenever a user asks to create a dashboard, add a chart, visualize process metrics,
  set up reporting for a Corezoid process, configure real-time monitoring, or asks what
  dashboards or charts exist. Also activate when the user wants to show task counts, completion
  rates, error rates, or any other process statistics visually in Corezoid.
---

# Corezoid Dashboard Manager

## How to call these tools

Every operation in this skill is an **action of the single `cz-dashboards` MCP tool** —
the individual names below are action strings, not tools of their own:

```
cz-dashboards {"action": "add-chart", "args": {"dashboard_id": 1234, "name": "Errors", "chart_type": "column", "series": "[…]"}}
```

Arguments always go inside `args`; the shorthand used in the examples below — `add-chart(dashboard_id=1234, …)` — means exactly that call. When unsure about an action's
arguments, call `cz-dashboards {"action": "<action>", "help": true}` — it returns the
full schema and runs nothing.

## What dashboards are

A Corezoid dashboard visualizes **task counters in process nodes** — it shows how many tasks
are accumulated in (or have passed through) specific nodes. The data comes directly from
running processes, not from external databases.

Key implication: processes must be deployed and have tasks flowing through them for data to appear.

---

## Actions of `cz-dashboards`

| Action | Purpose |
|--------|---------|
| `create-dashboard` | Create a new dashboard, returns `obj_id` (= dashboard_id) |
| `get-dashboard` | Get dashboard details including all charts and series |
| `add-chart` | Add a chart to a dashboard, returns `obj_id` (hex chart ID) |
| `get-chart` | Get a single chart with its series |
| `modify-chart` | Modify an existing chart — always provide full series array |
| `set-dashboard-layout` | Save chart positions on the grid — **required** to make charts visible |

`pull-process` is a tool of its own (not an action): call it directly to pull the
process JSON and find the node IDs a series points at.

---

## Workflow: Create a dashboard with charts

### Step 1 — Identify the process (MANDATORY FIRST STEP)

**Before doing anything else**, resolve the target process:

1. Check whether the user already provided a process identifier — a file path, process name, or process ID — in the current message or conversation history.
2. If no identifier is provided, ask:

   > "Which process(es) do you want to monitor? You can provide a file path (e.g. `123_payment.conv.json`), a process name, or a process ID."

   Do **not** call any MCP tools until the user provides an identifier.
3. If the user gave a **name or ID** (not a file path), search the local working directory for the matching `.conv.json` file using the `find` or `grep` Bash tools (the project is already pulled locally).
4. Once the file path is known and the file exists locally, open and read it to find node IDs in `scheme.nodes[].id` — note the IDs of nodes you want to measure. End nodes are best as primary metric sources.

   If the process is not available locally, fall back to:
   ```
   pull-process(process_id=<PROC_ID>)
   ```

5. Also clarify with the user (if not already clear):
   - Which nodes represent the metrics they care about?
   - What kind of visualization: comparison (column), proportions (pie), sequential drop-off (funnel), or tabular (table)?

### Step 2 — Create the dashboard

```
create-dashboard(title="Payment Monitoring", description="Real-time payment flow metrics")
```

Note the returned `dashboard_id` — needed for adding charts.

### Step 3 — Add charts

One chart per visualization. The `add-chart` action returns a hex `obj_id` for the chart — save it for `modify-chart` calls.

```
add-chart(
  dashboard_id=<dashboard_id>,
  name="Payment Outcomes",
  chart_type="column",
  series='[{"conv_id": 123456, "node_id": "507f1f77bcf86cd799439016", "title": "Success"}, {"conv_id": 123456, "node_id": "507f1f77bcf86cd799439017", "title": "Error"}]'
)
```

Chart types:

| Type | When to use |
|------|------------|
| `column` | Comparing values across nodes or time periods |
| `pie` | Showing how tasks split across outcomes |
| `funnel` | Visualizing sequential drop-off through a flow |
| `table` | Tabular display of metric values |

> **Critical:** Use `column` (NOT `bar`) — `bar` is not a valid Corezoid chart type.

### Step 4 — Verify series after creation

After creating a chart, call the `get-chart` action to verify that `series` is populated. If it's empty, use `modify-chart` to add the series.

```
get-chart(chart_id=<hex_obj_id>, dashboard_id=<dashboard_id>)
```

If `series` is empty, call the `modify-chart` action with the full series array.

### Step 5 — Save the dashboard layout (MANDATORY)

**Charts are invisible until the grid layout is saved.** After all charts are created and have series, call the `set-dashboard-layout` action:

```
set-dashboard-layout(
  dashboard_id=<dashboard_id>,
  grid='[
    {"chart_id":"<hex1>","x":0,"y":0,"width":6,"height":4},
    {"chart_id":"<hex2>","x":6,"y":0,"width":6,"height":4},
    {"chart_id":"<hex3>","x":0,"y":4,"width":12,"height":4}
  ]'
)
```

Grid layout rules:
- Grid is **12 columns wide**
- `x` + `width` must not exceed 12
- Standard chart: `width: 6, height: 4` (two charts per row)
- Wide chart: `width: 12, height: 4` (full row)
- Charts stack vertically by incrementing `y` (use the previous row's `height` as the next `y`)
- `chart_id` is the hex `obj_id` returned by `add-chart`

### Step 6 — Advise on real-time mode

Real-time mode works ONLY for these node types:
- **End nodes** (`obj_type: 2`) — tasks that finished the process
- **Waiting for Callback** — tasks waiting for external HTTP callback
- **Delay** — tasks paused for a time period
- **Set State** — tasks in a named state

Intermediate nodes (Code, API Call, Condition) pass tasks through instantly — real-time shows 0.

---

## Modifying charts — full payload required

When modifying a chart, **always include the full `series` array**. Partial updates are NOT
supported — omitting any field returns a validation error.

- `chart_id` — hex string from `add-chart` response (`obj_id`)
- `chart_type` sets `obj_type` in the API — must be `"column"`, `"pie"`, `"funnel"`, or `"table"`

```
modify-chart(
  chart_id="6a043a89e552e86e908941aa",
  dashboard_id=136542,
  name="Updated Chart Title",
  chart_type="column",
  series='[{"conv_id": 123456, "node_id": "507f1f77bcf86cd799439016", "title": "Success"}]'
)
```

---

## Dashboard grid layout

Charts are positioned on a grid. Use `width` and `height` fields (NOT `w`/`h`) — using
`w`/`h` causes a validation error. Standard sizes: `width: 6, height: 4`.

---

## Funnel chart — node ordering matters

For funnel charts, add metrics in the **same order as the process flow**:

```json
[
  {"conv_id": 123, "node_id": "start-node-id", "title": "Started"},
  {"conv_id": 123, "node_id": "mid-node-id",   "title": "Processed"},
  {"conv_id": 123, "node_id": "final-node-id", "title": "Completed"}
]
```

The funnel visualizes drop-off from each step to the next.

---

## Best practices

- Use **End nodes** as primary metric sources — they reliably accumulate completed tasks
- For error rate charts: add both Final (success) and Error end nodes as metrics on one column chart
- Name metrics descriptively — labels appear directly on charts
- Create separate dashboards: one for real-time ops monitoring, one for historical reporting
- Group all metrics from one business flow (e.g., payment processing) on a single dashboard
- Always verify `series` after chart creation — empty series means the chart won't render
- Always call the `set-dashboard-layout` action after all charts are ready — charts are invisible without it
- For drill-down: create the high-level dashboard first, then the detail dashboard, then link charts

---

## Dashboard operations reference

| Goal | Tool / operation |
|------|-----------------|
| Create dashboard | `create-dashboard` → returns `obj_id` (dashboard_id) |
| View dashboard + charts list | `get-dashboard(dashboard_id=...)` |
| Add chart | `add-chart` → returns `obj_id` (hex chart ID) |
| Get chart details + series | `get-chart(chart_id=<hex>, dashboard_id=...)` |
| Modify chart | `modify-chart(chart_id=<hex>, dashboard_id=..., ...)` |
| **Make charts visible** | `set-dashboard-layout(dashboard_id=..., grid='[...]')` |
| Get node IDs for series | `pull-process` → read `scheme.nodes[].id` |

**ID types to remember:**
- `dashboard_id` — integer (e.g. `136542`)
- `chart_id` — hex string (e.g. `"6a043a89e552e86e908941aa"`)
- `node_id` in series — 24-char hex string from `scheme.nodes[].id`
corezoid-describe4.67 KB

View saved version →

---
name: corezoid-describe
description: >
  Updates or creates the description of a Corezoid process, folder, or project
  without touching its logic. Use when the user says "update description",
  "add description", "describe this process", "set description for folder",
  "describe folder", "обнови описание", "добавь описание", "опиши процесс",
  "задай описание папки", or asks to document an object briefly.
  Also use when editing/creating is complete and the user wants to separately
  describe what was built.
---

# Update Process / Folder / Project Description

You are a specialist in writing clear, factual descriptions for Corezoid objects.

## Step 1: Identify the Object

Determine what needs to be described:
- **Process** — user provides a file path, process name, or process ID
- **Folder** — user provides a folder name, folder ID, or path
- **Project** — user provides a project name or project ID

If not clear, ask:
> "Which process, folder, or project should I describe? Provide a file path, name, or ID."

---

## Step 2: Read the Current State

**For a process:**
1. Open and read the `.conv.json` file
2. Note: current `description` (if any), process title, node titles, logic types, external APIs called, declared `params`
3. Also consider any changes the user described in the conversation — they are the primary source of intent

**For a folder:**
1. Resolve `folder_id` using the priority order below (Step 2a)
2. List `.conv.json` files inside the folder (use `find` Bash tool)
3. Read titles and existing descriptions of contained processes to understand the folder's scope

**Step 2a — Resolve folder_id (for folder targets):**

Use the first available source in this order:
1. **Explicit ID** — user provided a numeric folder ID → use it directly
2. **Process context** — a `.conv.json` is known → read its `parent_id` field → that is the `folder_id`
3. **Name search** — user provided only a folder name → call `cz-structure {"action": "list-folders", "args": {"folder_id": 0}}` and find the matching entry by title
4. **Not found** — name search returns no match → skip the folder description update silently (do not error)

**For a project:**
1. Call `cz-structure` with `action: "list-projects"` or `"show-project"` to get current metadata
2. Inspect the top-level folder structure to understand what the project covers

---

## Step 3: Generate the Description

### Process description

Write 1–2 sentences:
- **Sentence 1** — what the process does: verb + action + subject.
  - *"Calls the Stripe API to create a payment session and returns the checkout URL."*
  - *"Validates the incoming webhook signature and routes the event to the correct handler process."*
  - *"Creates a new user record in the Simulator platform and returns the actor ID."*
- **Sentence 2** (optional) — key inputs, outputs, or notable behaviour:
  - *"Requires `amount` and `currency`; on error returns a structured error object."*

Rules:
- Start with a verb in third person: *Calls*, *Creates*, *Validates*, *Routes*, *Aggregates*, *Sends*, *Fetches*
- Name the external service, Corezoid object type, or business action specifically
- Do NOT write *"This process…"*, *"The purpose of this…"*, or *"This skill…"*
- Keep under 200 characters
- If the current description is already accurate and recent, say so — do not update for the sake of updating

### Folder description

Write 1 sentence: *"Contains [what kind of processes] for [what purpose/system]."*

Examples:
- *"Contains Stripe payment integration processes (checkout, refund, webhook handling)."*
- *"Contains internal user management processes for the CRM project."*

### Project description

Write 1–2 sentences describing the project's overall purpose and the main systems or workflows it covers.

---

## Step 4: Apply the Description

**For a process:**
1. Update the `description` field at the root of the `.conv.json` file
2. Call MCP tool **`push-process`** with `process_path`
3. Confirm: *"Description updated and deployed."*

**For a folder:**
Use the `folder_id` resolved in Step 2a. If it was not resolved (source 4 — not found), skip this step.
Call **`cz-structure`** with `action: "modify-folder"` and `args` `folder_id` + `description`.

**For a project:**
Call **`cz-structure`** with `action: "modify-project"` and `args` `company_id`, `project_id`, `description`.

---

## Notes

- If the user describes what changed (*"I just added a retry node"*), incorporate that context into the description
- Never fabricate process behaviour not visible in the JSON or stated by the user
- For processes that already have a good description, confirm with the user before overwriting
corezoid-edit13.8 KB

View saved version →

---
name: corezoid-edit
description: >
  Corezoid process editing specialist. Use when the user wants to modify, update,
  or fix an existing Corezoid process, add or remove nodes, change node behavior,
  add an API call, fix an error, or update process logic. Activate when the user
  says "edit a process", "modify", "update", "fix", "add a node", "change
  behavior", "add a call", "remove a node", or "update the logic".
---

# Edit an Existing Corezoid Process

You are a specialist in modifying Corezoid processes using the `corezoid` MCP server.

## Identify the Process (MANDATORY FIRST STEP)

**Before doing anything else**, resolve `PROCESS_PATH`:

1. Check whether the user already provided a process identifier — a file path, process name, or process ID — in the current message or conversation history.
2. If no identifier is provided, ask:

   > "Please specify the process — you can provide a file path (e.g. `123_payment.conv.json`), a process name, or a process ID."

   Do **not** call any MCP tools until the user provides an identifier.
3. If the user gave a **name or ID** (not a file path), search the local working directory for the matching `.conv.json` file using the `find` or `grep` Bash tools (the project is already pulled locally).
4. Once `PROCESS_PATH` is known and the file exists locally, open and analyze it before making any changes.

---

## Step 1: Analyze the Process

Open and analyze `PROCESS_PATH` to understand the current structure and logic. Pay attention to:

- Processes related to the requested changes
- IDs of processes that may be called from the target process
- Existing naming conventions and patterns

---

## Step 2: Implement the Changes

Apply changes to `PROCESS_PATH`.

### Core rules

- Connect nodes only through the `go` field
- Every node that can fail must have `err_node_id` — point it **directly at a Final Error node** (`obj_type: 2`) unless the error path needs logic (reply to caller, retry routing). Never create an Escalation node (`obj_type: 3`) that only contains a bare `go` — that is a passthrough anti-pattern flagged by `lint-process`
- Node IDs must be unique 24-character hex strings: `^[0-9a-f]{24}$`. **Always `pull-process` before editing** and reference only canonical, server-assigned IDs — IDs you invented in a previous push were reassigned by the server and no longer exist. New nodes added now get placeholder IDs that the server will likewise reassign on push. Existing nodes' IDs are preserved. See [Node ID Lifecycle](${CLAUDE_PLUGIN_ROOT}/docs/process/process-development-guide.md#node-id-lifecycle-server-assignment--stability-on-push).
- Use descriptive node `title` values (e.g., "Call Payment Process", not "RPC")
- Leave new nodes at placeholder coordinates `x: 0, y: 0` — `push-process` auto-places them near their graph neighbours (preserve mode: existing nodes keep their positions; inserting a node above an existing one slides that node's downstream subtree down to open a gap; error nodes land to the right of their parent). **Keep the `x`/`y` of nodes you are NOT moving** — do not rebuild the scheme without them. As a safety net, if the file reaches push with every node's coordinates missing, push re-hydrates them from the server (reports `Restored N node coordinate(s)`) so a laid-out process is never re-arranged by accident. Only when auto-placement is disabled (`COREZOID_AUTOLAYOUT=off`) position nodes manually — error nodes to the right of their parent (`x + 300`). Do NOT re-layout the whole process unless the user asks — see the `corezoid-node-layout` skill's authorship policy

### Variables for constants

All constants (URLs, tokens, endpoints, hosts) must be stored as variables — never hardcoded:

1. Check `_ENV_VARS_.json` (from `pull-folder`) or `.processes/variables.json` (from this session) for existing variables
2. Create a new variable if needed: call **`cz-variables`** with `action: "create-variable"` and `args` `name`, `description`, `value`
3. Reference in logic using `{{env_var[@variable-name]}}`

See `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` for details.

### Node type quick reference

| Node | obj_type | Logic type |
|------|----------|------------|
| Start | 1 | `go` |
| Code Node | 0 | `api_code` |
| Call a Process | 0 (`4` only for active Stub Mode) | `api_rpc` |
| API Call | 0 | `api` |
| Reply to Process | 0 (3 as `err_node_id` target) | `api_rpc_reply` |
| End / Error | 2 | _(no logics)_ |

For complete JSON structures see `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md`.

### Common pitfalls

- **Regenerating the whole scheme instead of editing it in place** — make targeted edits to the pulled file (change the one node/logic you need). Do NOT rewrite every node from scratch: that drops the existing `x`/`y` (the `x:0,y:0` authoring habit is for *brand-new* nodes only), and a scheme where every node is at `0,0` forces `push-process` to re-lay-out the entire process, discarding its arrangement. push re-hydrates coordinates from the server as a safety net, but editing surgically is the right habit.
- Using `"type": "call_process"` instead of `"type": "api_rpc"` — will fail validation
- Missing `extra`/`extra_type` in Call a Process node — both required even if empty (`{}`)
- Treat active Stub Mode (`obj_type: 4` + `condition.stub`) as a temporary mock only: it bypasses the called process and can fake success. Preserve `condition.stub` during round trips, but do not leave `obj_type: 4` enabled on production paths unless the user explicitly confirms `allow_active_stub_mode=true`.
- Raw JSON objects as values in `extra` — must be stringified: `"{\"key\":\"val\"}"`
- Keys in `extra` and `extra_type` must match exactly

---

## Step 3: Update the Description

Before deploying, update the root-level `description` field in `PROCESS_PATH` to reflect the process's current behaviour after your edits.

**Guard — preserve existing descriptions when appropriate:** if the process already has a non-empty description and your edits did not change the overall purpose (e.g. you fixed a bug, adjusted a timeout, or rewired an error path without altering what the process fundamentally does), preserve the existing description rather than replacing it.

Follow the **Description Update Rule** from the `corezoid` skill:
- 1–2 sentences, starting with a verb (*Calls*, *Creates*, *Validates*, *Routes*, *Aggregates*)
- Sentence 1: what the process does — verb + action + subject
- Sentence 2 (optional): key inputs/outputs or notable behaviour
- Under 200 characters, no *"This process…"* preamble

Write the updated `description` directly into the JSON file. This costs nothing extra — the description rides the same `push-process` call.

If the parent folder was structurally affected (process added, removed, or renamed), also call **`cz-structure`** with `action: "modify-folder"` and a one-sentence `description` in `args`.

---

## Step 4: Deploy the Changes

**MANDATORY: Always run this step whenever any changes were made to the process file — even if there are open questions or the work is not fully complete. Without deploying, all changes are lost.**

Deploy the modified process by calling MCP tool **`push-process`** with `process_path: "<PROCESS_PATH>"`.

If deployment fails, fix the reported errors and re-run `push-process` until it succeeds. Do not skip this step or postpone it — changes exist only in memory until pushed.

> **Auto-snapshot:** if the process already existed on the server (`obj_id` ≠ null), `push-process` automatically creates a snapshot of the current server state before deploying your changes. No action needed — this is transparent. The snapshot appears in the Corezoid UI and can be managed with the `cz-snapshots` actions `list-snapshots` / `get-snapshot` / `delete-snapshot`. Environments whose API has no snapshot object (some on-prem / older installations) are detected by a read-only probe, confirmed against control requests and re-checked periodically per project/stage: the snapshot is skipped, the push proceeds, and the push result says so — keep the `.conv.json` under version control there, because the platform holds no rollback point.

> **Concurrent-change detection & 3-way merge:** `pull-process`/`pull-folder` capture the server version **before** export in a per-folder `.corezoid-baseline.json` sidecar, plus a copy of the pulled process under `.corezoid-baseline/` (add both to `.gitignore`). If someone else changed the process between pull and push, `push-process` **blocks** — a plain push could silently drop their edits — and reports local/server/overlap changes across nodes and process-level fields such as `title`, `description`, `params`, and `scheme.web_settings`. Reconcile one of three ways: re-pull and re-apply; `merge=true` to save the original as `<process>.pre-merge` and graft non-overlapping server changes into the local file for review; or `overwrite_server_change=true` to overwrite after being shown the report (a server snapshot is attempted first, and recovery is possible only if it succeeds). `force=true` is the **lint** override only and does not waive this gate — pass `overwrite_server_change` in reply to the block report, never ahead of it, because set in advance it authorises dropping a change that has not happened yet and that nobody will see. Overwriting live server state that was never compared (`overwrite_server_change`, or `adopt_existing` on a file with no baseline) while no pre-push snapshot could be taken is refused outright unless `allow_no_snapshot=true` is passed as well: neither reported nor recoverable is not a state a single flag should reach. A never-deployed process is exempt — there is no previous version to protect, so the create → push flow never needs the second flag. Every waived gate is named in the push result. Files with no baseline push with an advisory; an unreadable/corrupt baseline blocks until a re-pull rebuilds it, because silently ignoring it would disable lost-update protection. A pre-v3.1.3 sidecar with no recorded merge ancestor cannot run the same-second content check: the push proceeds, says so, and records the ancestor from the live server scheme, so only that one push is unchecked.

After a successful deploy, notify the user:

> "Changes have been deployed. Please **refresh the page** in Corezoid to see the updated process."

---

## Reference Documents

Use the `Read` tool to load these files when specific node or validation details are needed:

| Path | When to read |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md` | JSON schemas for all node types + full Logics fields reference (canonical) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-built-in-functions.md` | Built-in functions: `$.math`, `$.date`, `$.random`, `$.sha1_hex`, `$.md5_hex`, `$.base64_encode`, `$.unixtime`, `$.map`, `$.filter` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-dynamic-values.md` | Dynamic values: `{{var}}`, `{{node[id].count}}`, `{{node[id].SumID}}`, `{{conv[@alias].ref[...]}}`, `{{env_var[@name].key[1]}}` |
| `${CLAUDE_PLUGIN_ROOT}/docs/tasks/task-metadata.md` | Global `root.*` fields: `root.task_id`, `root.ref`, `root.conv_id`, `root.node_id`, `root.prev_node_id`, `root.user_id`, `root.change_time`, `root.create_time` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/code-node.md` | Code node details and available JS libraries |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | Call a Process node, semaphores, cross-folder calls |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/reply-to-process-node.md` | Reply formats, object stringification |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | HTTP API call configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/end-node.md` | End node success/error configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/condition-node.md` | Condition node (branching logic) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/delay-node.md` | Delay node (timers and waiting); 30s limit is static-literal only — dynamic absolute-timestamp `value` for scheduled or sub-30s delays |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/copy-task-node.md` | Copy Task node (task duplication) |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-json-validation.md` | Validation rules and common errors |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/error-handling.md` | Error handling patterns (hardware vs software errors) |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/node-positioning-best-practices.md` | Coordinate system and layout guidelines |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variable naming rules, creation workflow, usage examples |

## Example Processes

| Path | Description |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/samples/stripe-checkout.json` | Stripe payment checkout flow |
| `${CLAUDE_PLUGIN_ROOT}/samples/create-actors.json` | Creating actors/users |
| `${CLAUDE_PLUGIN_ROOT}/samples/create-user.json` | User creation process |
| `${CLAUDE_PLUGIN_ROOT}/samples/gpt-calculator.json` | GPT integration example |
| `${CLAUDE_PLUGIN_ROOT}/samples/api-post.json` | HTTP POST API call example |

---

## Final Step: Update Git Context

> **Do NOT report the task as complete and do NOT stop until this step is evaluated.**
> The main task being deployed does not mean the session is over — this step is next.

Immediately after `push-process` or `create-process` succeeds, check whether
**at least one** of the following is true:

- `push-process` or `create-process` was actually called (not just previewed);
- a new external host/API/service appeared that is not yet in `dependencies.md`;
- an architectural decision was made (one approach chosen over another);
- an issue was found or closed during this session.

**If yes → activate `corezoid-git-context` skill right now.** Do not wait for the
user to ask. Do not skip because the main task "looks done".

**If none of the above → skip** and tell the user the session is complete.

The skill handles everything autonomously: reads current `_ext/docs/`, proposes
a unified diff, asks confirmation, writes files, and pushes — one invocation.
corezoid-edit-bot26.8 KB

View saved version →

---
name: corezoid-edit-bot
description: Iteratively edits an already-built messenger bot from corezoid-gen-bot — a Corezoid Communications Orchestrator serving Telegram, Viber, Apple Messages for Business and Facebook Messenger over a backend of Corezoid processes called with api_rpc. Runs a lean plan → confirm → apply → lint → push → smoke-test loop — see "Modes" for plan/execute/deploy. Trigger on "измени/поправь бота", "добавь/убери/переименуй команду", "подключи ещё процесс", "поменяй текст/клавиатуру бота", "добавь язык боту", "добавь шаг в диалог", "бот отвечает пустым/дважды", "почему бот не отвечает", "edit/modify bot", "add command to bot", "wire another process into the bot", "change bot copy", "fix the bot", "задеплой бота", "promote stage", or when the user references PLAN.md/CHANGE.md for a bot. Assumes CWD is the workspace corezoid-gen-bot built (`.corezoid-gen-bot/PLAN.md`, a `*.stage.json` marker, the pulled orchestrator folder). For a Smart Form web app over the same processes use `simulator-app-generator`.
---

# corezoid-edit-bot

Follow-up skill to [[corezoid-gen-bot]]. Edits an orchestrator that already
exists. Reuses the same PLAN.md as the architectural source of truth, layers a
per-change `CHANGE.md` on top, and never re-audits the domain processes unless
the user asks for `corezoid-gen-bot refresh`.

An edit is riskier than a build, for one reason: **the bot is live and someone
is talking to it.** A build that is wrong is a bot nobody has used yet; an edit
that is wrong breaks a working conversation, and most of the ways it breaks are
silent — nothing fails at push time, the chat just misbehaves. So this skill's
weight is not in generation, it is in knowing what a request is allowed to
touch and in proving afterwards that the *user* sees the right thing, which is
strictly more than proving the graph ran.

Read these before touching anything:

| Document | What it settles |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/change_kinds.md` | what each request kind may touch, its verification, and what this skill deliberately cannot do |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/invariants.md` | the send-side and dispatch invariants every change must preserve — all of them silent when broken |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/repair_loop.md` | symptom → cause → the layer at fault, and when to stop looping and report |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-gen-bot/references/template_map.md` | the orchestrator's own contracts (Router, Send Message, Localization, Attachments) |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-gen-bot/references/contract_extraction.md` | how to read a domain process — needed for `wire-process` and `process-remap` |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-gen-bot/references/rpc_call.nodes.json` | the `api_rpc` / `api_copy` / state-read fragments |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-gen-bot/references/bot_reply.skeleton.json`, `bot_dialog.skeleton.json` | the two command shapes, for a new command |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-gen-bot/references/attachments.seed.json`, `localization.seed.json` | per-channel attachment shapes, per-page caps, interpolation rules |

Load them with the `Read` tool. `${CLAUDE_PLUGIN_ROOT}` resolves to the
installed plugin root; a bare `references/…` does **not** — this skill runs with
the Corezoid workspace as the working directory. Every short
`references/<file>` named later in this document is under
`${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/`, and every
`corezoid-gen-bot/references/<file>` under
`${CLAUDE_PLUGIN_ROOT}/skills/corezoid-gen-bot/references/`.

Steps 4–7 (execute mode) are each detailed in their own reference file — load
the one for the step you're about to run, not all of them up front:

| Document | Step |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/step4_apply_change.md` | Step 4 — apply the change |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/step5_lint_push.md` | Step 5 — lint and push |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/step6_verify.md` | Step 6 — verify (and Step 6b — deploy mode) |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/step7_report.md` | Step 7 — update PLAN.md and report |
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-edit-bot/references/rules.md` | Full rule list — read before Step 4 |

## When to use

- CWD is a corezoid-gen-bot workspace: `.corezoid-gen-bot/PLAN.md` with a
  non-null `orchestrator.folder_id`, a `*.stage.json` marker, and the pulled
  orchestrator folder on disk.
- User wants to add/remove/rename a command, wire in another Corezoid process,
  change copy or a keyboard, add a language, add or drop a dialog step, remap a
  command onto a different process or argument, fix a bug, or promote a stage.
- **Not for adding or removing a messenger channel** — the wizard is not
  incremental. See `references/change_kinds.md`.
- **Not for contract drift in the domain processes** — if a handed-in process
  changed (lost a reply node, got paused, gained an alternate outcome), run
  `/corezoid-gen-bot refresh` first so PLAN.md and `bot-contract.json` reflect
  reality, then come back.
- **Not for building from scratch** — that is [[corezoid-gen-bot]], and running
  it again here would build a whole second orchestrator that steals the
  webhooks from this one.

## 0. Preflight — the tools, and the three things none of them can do

Corezoid MCP tools this skill uses directly: `pull-process`, `pull-folder`,
`push-process`, `lint-process`, `layout-process`, `create-process`,
`delete-process`, `pause-process`, `resume-process`, `create-alias`,
`run-task`, `deploy-stage`.

The rest are **actions of a router tool** — call them as
`<router> {"action": "<action>", "args": {…}}`, every argument inside `args`:

| Action | Router |
|--------|--------|
| `show-task`, `modify-task`, `list-task-history`, `list-node-tasks` | `cz-tasks` |
| `create-variable`, `modify-variable` | `cz-variables` |
| `create-snapshot` | `cz-snapshots` |

Add `"help": true` to any of them to get its full argument schema back without
running it. Missing → tell the user to install the Corezoid plugin and run
`/corezoid-init`.

Three things are **not possible** with this toolset. Say so plainly rather than
improvising something that looks like it worked:

1. **An alias cannot be deleted, repointed or renamed.** `create-alias` is the
   only alias tool in the plugin — there is no delete, modify, unlink or list
   counterpart. So a name once taken is taken for good, a rename means
   *creating a second alias* and leaving the first one dangling, and removing a
   command does not free its name. Removing or repointing an alias needs raw
   Corezoid API calls — hand that to `corezoid:corezoid-alias-manager` with the
   user's agreement, and until it is done, describe the alias as still live.
2. **A channel cannot be added to or removed from an existing orchestrator.**
   `create-communications-orchestrator` builds a whole folder including the
   per-channel receiver and API-method processes; there is nothing to graft onto.
3. **An orchestrator build cannot be undone.** Which is why this skill never
   calls the wizard: a second build steals the webhook from the first and leaves
   ~150 dead processes behind.

**Stage resolution: probe, do not refuse.** Most tools read the stage from the
`<id>_<name>.stage.json` marker at the workspace root, but the MCP server also
resolves it from `~/.corezoid/config.json` keyed by the working directory — so
a workspace that has been logged into but never pulled works with no marker.
If a marker is absent, try one `pull-process` before declaring a `login`
problem (see `corezoid-init`).

## Modes

| Invocation | Mode | What runs |
|---|---|---|
| `/corezoid-edit-bot plan <ask>` (or "спланируй изменение", "just plan") | **plan** | Steps 1–3: understand → write `.corezoid-gen-bot/CHANGE.md` → summary, stop |
| `/corezoid-edit-bot execute` (or "погнали", "execute", "сделай") | **execute** | Steps 4–7: apply → lint → push → seed tasks → smoke-test → report |
| `/corezoid-edit-bot <ask>` no subcommand | **full** | Plan, print summary, **pause**, continue to execute after the user confirms |
| `/corezoid-edit-bot deploy` (or "задеплой", "promote stage") | **deploy** | Step 6b only: `deploy-stage` dry-run → confirm → apply → verify |

## Preconditions (every mode)

Verify before doing anything else. If any fails, stop and say what to run.

- `.corezoid-gen-bot/PLAN.md` exists and `orchestrator.folder_id` is non-null.
  Missing or null → point at `/corezoid-gen-bot plan <process ids>` (or, if the
  folder exists in Corezoid but not in PLAN.md, ask for the folder id and fill
  the block in — do **not** call the wizard).
- `template_ids` in PLAN.md is populated. If it is not, pull (below) and fill it
  in before planning anything — a change that guesses an id writes into another
  workspace's process.
- `.corezoid-gen-bot/bot-contract.json` exists. It is the derived contract of
  every domain process the bot calls; without it any change to a domain call is
  a guess. Missing → run `/corezoid-gen-bot refresh` to regenerate it.
- **The pulled mirror is current.** Pull before planning: someone may have
  edited the orchestrator in the Corezoid UI since the last pull, and
  `push-process` will block on the concurrency gate anyway — pulling first turns
  that block into a diff you can read.

  ```
  pull-folder  folder_id: {obj_id from <stage_id>_<name>.stage.json}   # the STAGE id
  ```

  **`folder_id` is a required argument, and the value must be the stage id — never
  the orchestrator's `folder_id`.** `pull-folder` unzips a server-produced archive
  into the **stage root** (the `RootPath` registered for this workspace, not the
  cwd) and picks the archive by what the id turns out to be: the stage id fetches
  the whole stage, a *sub*folder id fetches only that folder and still unzips it
  at the stage root. So passing the orchestrator's id spills its contents over
  the stage root, the `<folder_id>_Communications_Orchestrator/` directory never
  appears, and every id read afterwards comes from the wrong tree. (`folder_id: 0`
  is a third thing again — a workspace-root "No Project" pull.)

  After the pull, find the orchestrator by its `<folder_id>_` name prefix, where
  `folder_id` is `orchestrator.folder_id` from PLAN.md. If that directory is
  absent, stop and say so — do not plan against whatever else the pull produced.

  ```bash
  stage_root="$(dirname "$(ls */*.stage.json *.stage.json 2>/dev/null | head -n1)")"
  orch="$stage_root/{orchestrator.folder_id}_Communications_Orchestrator"
  ls -d "$orch" || { echo "orchestrator folder not found — wrong pull scope or wrong stage"; exit 1; }
  ```

- **Re-read `template_ids` off that directory** rather than trusting PLAN.md's
  copy blindly: the wizard mints fresh ids per build and a stale id fails
  silently — the task simply never arrives. Ids in
  `corezoid-gen-bot/references/template_map.md` are examples from one build and
  must never be copied into a process.
- If the workspace is a git repo, the working tree is clean **or** the only
  dirty files are ones the user is currently discussing. `pull-folder` and
  `pull-process` overwrite local files; a dirty file is somebody's unpushed work.

## Step 1: Understand the ask

Parse the request into one kind from `references/change_kinds.md`. Ask at most
one `AskUserQuestion`, and only for a slot you genuinely cannot infer.

| Kind | Mandatory slots | Inferable from PLAN.md |
|---|---|---|
| add-command | backing process, command name | shape, skeleton, texts, keyboard, menu position |
| remove-command | command | dependent texts, buttons, alias (which survives) |
| rename-command | old, new | process title, payloads, menu; the new alias |
| copy | which text and the new wording | `text_id`, locales, which processes send it |
| keyboard | which command, what changes | `attachment_id`, per-channel shapes |
| add-locale | the new language code | every key needing a translation |
| dialog-step | which command, what the new step asks | `text_id`, validation regex from the contract |
| wire-process | process id, what the command should do with it | callability, inputs, outcomes — from `bot-contract.json`, or re-derived if absent |
| process-remap | command, new process or new argument mapping | `api_rpc` `extra`, outcome mapping, whether namespacing is now required |
| bugfix | symptom + one repro | root cause, the smoke test to add |
| template-edit | the exact process and why no command-level change works | — (never inferred) |
| deploy | source stage, target stage | project and workspace ids |

Cross-reference PLAN.md's `Commands`, `Coverage`, `Localization keys`,
`Attachments`, `Menu wiring` and `template_ids`, plus `bot-contract.json`,
before answering anything yourself — those are the contract. **Never re-extract
a domain contract for an edit** unless the user asks for `refresh`; use what
Phase 2 captured.

### 1.1 The blast radius is bigger than the request, in two specific ways

Work both out here, not in Step 6 when the fix is expensive:

- **New or changed copy that carries a `{{placeholder}}` is a process change,
  not a task change.** Send Message is called with `group: ""`, so it receives
  only the fields named in the calling node's `data`; a `{{var}}` in the
  resolved text that was not forwarded arrives in the chat as the literal
  `{{var}}`. So "just change the wording" stops being Localization-only the
  moment the new wording interpolates something the sending node does not
  already forward. Find every sender before promising a one-task change:

  ```bash
  grep -rln '"text_id": *"balanceDone"' "$orch"          # who sends this text
  ```

  Then check the placeholders against that node's `data` and against the
  process's `success.keys` in `bot-contract.json`. Full rule in
  `references/invariants.md` §1.
- **A shared key is a global change.** `mainMenu`, `mainKeyboard`,
  `serviceError`, `timeout`, `commandNotFound`, `selectError`, `yes`/`no` and
  `carouselPattern` are referenced by every command and by the Router itself.
  Editing one has a `template-edit`-sized regression scope even though it
  touches no process: set `regression_scope: all` in CHANGE.md and smoke-test
  every command.

### 1.2 For `wire-process` and `process-remap`

Answer the callability question first (`contract_extraction.md` §2): a
`process` with a reachable `api_rpc_reply` gets `api_rpc`; one without gets
`api_copy` and a command that can only acknowledge; a `state` gets an inline
`set_param`; a paused one gets nothing until the user says otherwise. An
`api_rpc` into a paused or reply-less process parks the task until the semaphore
fires — the user waits the full 30 s for an error, and `params` does not reveal
it.

To answer it you need the process's file. **Look on disk before pulling:**
`find "$stage_root" -name "<id>_*.conv.json"` — the workspace is an
already-pulled stage and the process is very likely there. Match the `<id>_`
prefix exactly, so `1906756_` is not satisfied by `11906756_`. Only
`pull-process(process_id=<id>)` when it is absent, and never over a file that is
dirty in git without asking: `pull-process` overwrites, taking the unpushed edit
with it. Record in CHANGE.md whether the contract came from a reused file (with
its mtime) or a fresh pull — a reused snapshot is of unknown age, and wiring
against a stale contract is how a call starts sending an empty `extra` key.

**Never probe a domain process automatically.** `run-task` against somebody
else's process can send a real SMS or charge a real card. Show the side-effect
classification from `bot-contract.json` and let the user authorise each probe.
The same caution applies to the smoke test in Step 6: a command whose side
effects are `likely` or `unknown` fires them for real, so agree a safe test
input with the user first, or exercise only the dialog up to the confirm step.

### 1.3 For `add-command` and `rename-command`, settle the alias now

The alias *is* the dispatch mechanism: the Router computes
`commandAlias = command.replace("/","")` and calls `@{{commandAlias}}`. There is
no registration table to edit, and no way to fix a bad name later — see §0.

- `^/[a-z0-9][a-z0-9-]{2,}$` after the slash. A camelCase command deploys fine
  and is unreachable forever.
- Never `/start`, `/end` or `/exit` — Main, Router and System Diagram consume
  them.
- **Check the name against `_ALIASES_.json` at the stage root, which is the
  authoritative list** — a fresh orchestrator ships ~60 aliases. Do not work
  from a remembered list of sample-bot names.

  ```bash
  python3 - <<'EOF'
  import json
  taken = {a['short_name']: a.get('obj_to_id') for a in json.load(open('_ALIASES_.json'))}
  for c in ['order-status']:                       # candidate commands, minus the slash
      print(c, '->', 'FREE' if c not in taken else 'TAKEN by %s' % taken[c])
  EOF
  ```

  Watch for `obj_to_id: null`: a dangling row holds the name just as firmly as a
  live one, and `@name` currently resolves to nothing, so anything already
  dispatching there answers `commandNotFound`. You cannot reclaim it from the
  plugin — pick another name, or ask the owner to delete the row in the Corezoid
  UI, and say which you did. If `_ALIASES_.json` is not on disk, say the name is
  unverified rather than assuming it is free.
- **A rename does not move the old name.** The new command needs a new alias;
  the old alias stays and keeps pointing at the (now renamed) process, so the
  old command keeps working unless its alias is deleted through the raw API.
  State that in CHANGE.md and in the report rather than claiming the old command
  is gone.

## Step 2: Write `.corezoid-gen-bot/CHANGE.md`

Overwrite (do not append):

````markdown
---
kind: {add-command|remove-command|rename-command|wire-process|process-remap|copy|keyboard|add-locale|dialog-step|bugfix|template-edit|deploy}
title: {one-line title}
ask: {verbatim user request, single paragraph}
commands_affected: [{/command}, …]
processes_touched: [{path}, …]
processes_created: [{name → folder}, …]
processes_deleted: [{id, path}, …]
tasks_touched: [{process: localization|attachments, ref: {ref}}, …]
text_ids_touched: [{text_id}, …]
attachment_ids_touched: [{attachment_id}, …]
forwarding_impact: [{process path → send node → keys that must now be forwarded}, …]
env_vars_touched: [{short_name}, …]
aliases_to_create: [{short_name → process}, …]
aliases_left_dangling: [{short_name → why it cannot be removed}, …]
domain_processes: [{id, title, callable: api_rpc|api_copy|state|paused, source: local <mtime>|pulled}, …]
namespacing_required: {true|false}     # true once a command makes 2+ domain calls
side_effects: {none|unlikely|unknown|likely}   # of anything the smoke test will fire
regression_scope: {touched|all}        # all for a shared key or a template edit
mirror_pulled: {ISO-8601}
plan_impact: {sections of PLAN.md this change edits, or "none"}
template_edit_confirmed: {true|false|n/a}
timestamp: {ISO-8601}
---

## Scope
- Processes to create: {name → folder — what it does}
- Processes to edit: {path — which nodes, by title}
- Processes to delete or pause: {path — which, and why that one}
- Aliases to create: {short_name → process}
- Aliases that will survive this change: {short_name → consequence}
- Domain calls to add / change: {process id, wiring, extra keys, semaphore}
- Tasks to write: {process, ref, which keys, deep_merge yes/no}
- Send-side forwarding to add: {process → send node → keys}
- Menu changes: {mainKeyboard buttons, mainMenu text}
- Coverage impact: {which PLAN.md coverage rows change}

## Out of scope
{Anything adjacent the user did not ask for. Name it so the diff stays honest.}

## Smoke test that proves this change
- {command} — `run-task` on the Router with `{data}` → expect {observable result}
- {command} — `run-task` on **Send Message** with `{channel, chat_id, text_id, attachment_id, + interpolated values}` → expect `data.text` == {rendered string} and `reply_markup` {shape}
- {for dialogs} follow-up `modify` into `{process}` ref `{channel}_{chat_id}` → expect {next question}
- {for copy/keyboard} `show-task` on `{process}` ref `{ref}` → expect {keys}

## Verification checklist
- [ ] no `{{UPPER_CASE}}` placeholders left in any touched process
- [ ] no channel token anywhere in the tree
- [ ] `lint-process` clean on every touched process
- [ ] alias present and equal to the command minus its slash
- [ ] every `api_rpc` into a domain process has a `time` semaphore ≥ 30 s routed to a node that tells the user and copies `/end`
- [ ] namespacing `api_code` present after every call, if the command now makes 2+
- [ ] every referenced `text_id` exists in every locale the wizard created
- [ ] every `{{placeholder}}` in a text is (a) a key of the sending node's `data` **and** (b) a key the process actually produces
- [ ] `{{t'key}}` written with no dot; no dotted `{{a.b}}` in any text
- [ ] every referenced `attachment_id` has one key per channel in `channels` (`facebook`, not `fbmessenger`)
- [ ] dynamic attachments: `items` + `currentPage` forwarded; empty path sends `items: ""`, not `[]`
- [ ] every path ends `text_id: ""` + non-empty `attachment_id` into the Router, or the reply is sent twice
- [ ] ask/confirm steps use a reply `keyboard`, not `inline_keyboard`
- [ ] smoke test above observed green, including the Send Message render
- [ ] regression per `regression_scope` observed green
````

### Chat summary format

Print this and stop:

```
📝 CHANGE.md готов — {kind}: {title}.
Команды: {commands_affected}.
Процессы: {N} ({add} новых, {edit} правок, {del} удалений/пауз).
Задачи: {tasks_touched summary}.
Проброс значений: {forwarding_impact summary, or "не требуется"}.
Регрессия: {touched commands | все команды — общий ключ/шаблон}.
Проверка: {one line naming the smoke test}.
{One bullet per risk, if any — dangling alias, live side effects, shared key.}

Поправить или /corezoid-edit-bot execute?
```

Do not proceed to Step 4 until the user says `execute` / `погнали` / `go`. For
`kind: template-edit`, the confirmation must name the process — a bare "go" is
not consent to edit Router. For a change whose smoke test fires `likely` side
effects, the confirmation must name that too.

## Step 3: Refining CHANGE.md

Same rules as corezoid-gen-bot §3a: targeted `Edit`s, re-print the summary, one
sentence of prose at most. Never widen the scope silently — if the user's
follow-up is a different change kind, rewrite CHANGE.md rather than appending
to it. Never re-extract a domain contract for a CHANGE.md edit.

## Step 4: Apply the change

Read `references/step4_apply_change.md` before running this step.

Work only inside `processes_touched`, `tasks_touched`, `env_vars_touched` and
the create/delete lists CHANGE.md named. Covers: `push-process` cannot create a
process (create → pull for baseline → write → push); a new command starts from
the right skeleton with every `{{UPPER_CASE}}` placeholder substituted
(`parent_id`/`user_id` are numeric, not string); edited commands are referenced
by title, never a remembered id; removing a command prefers `pause-process` or
`delete-process` over deletion, and the alias survives either way; a second
domain call in a command needs a namespacing `api_code`, not a `set_param`;
every new `{{var}}` in a text or a repointed call changes the Send Message
`data` block too; and a handed-in domain process is never edited from this
skill.

## Step 5: Lint and push

Read `references/step5_lint_push.md` before running this step.

Per touched process: `layout-process` → `lint-process` → `push-process` →
`create-alias` for new commands. Never `force=true` past a structural finding.
A concurrent server change resolves with `merge=true` or a re-pull, never
`overwrite_server_change` without showing the user the report first. A
never-deployed process is exempt from the snapshot gate; anything else that
blocks on a snapshot should wait, not waive.

## Step 6: Verify

Read `references/step6_verify.md` before running this step.

Run the CHANGE.md verification checklist in order, stopping at the first
failure: no leftover placeholders or tokens in the tree, clean lint, every
`text_id`/`attachment_id` resolves in every seeded language and channel, the
forwarding check (the one nothing else catches), the `/end` check, the touched
command on its own via `run-task`, the message rendered through Send Message
(not just the command's own task data), the CHANGE.md smoke test observed, and
regression per `regression_scope`. `references/repair_loop.md` maps a symptom
to the layer at fault. A clean lint is not a passing test.

**Step 6b — deploy mode:** `deploy-stage` dry-run first, show the diff, then
`apply: true` with the exact `confirm: "<source>-><target>"`. Re-run the Step 6
smoke test against the **target** stage afterward — aliases, env vars,
Localization/Attachments task data and domain processes are not guaranteed to
carry over, and a numeric `conv_id` promotes into a stage where it means
something else or nothing. Full detail in `references/step6_verify.md`.

## Step 7: Update PLAN.md and report

Read `references/step7_report.md` before running this step.

Apply `plan_impact` to PLAN.md (`Commands`, `Coverage`, `Localization keys`,
`Attachments`, `Menu wiring`, `locales`, `template_ids`) so it still describes
the live orchestrator, leave CHANGE.md as the record of this change, then
report: what changed, aliases left dangling, smoke-test results per command
including the Send Message render, regression result, any waived gate, what
the user must do by hand, and `folder_url`.

## Rules

Full rule list, one per line with its reasoning: `references/rules.md`. Read it
before Step 4 — it is a checklist to re-check against, not new material. The
handful that matter most, everywhere: never call
`create-communications-orchestrator` from this skill (no undo, steals the
webhook from a live bot); `create-alias` is the only alias tool — an alias
cannot be deleted, repointed or renamed; never hardcode a template process id,
always re-read `template_ids` from the pulled mirror; never rename a pulled
`<ID>_<Title>.conv.json`; every `api_rpc` into a domain process needs a ≥30 s
`time` semaphore and `group: ""` with an explicit `extra`; never edit a
handed-in domain process from this skill; never probe or smoke-test a
`likely`/`unknown` side effect without the user's agreement; tokens and
credentials never touch a file; never call `EnterPlanMode`; cap the repair loop
at about three passes per defect and report precisely what fails; report only
what was observed — no command is green on the strength of a clean lint.

Referenced files: 8

corezoid-gitcall13 KB

View saved version →

---
name: corezoid-gitcall
description: >
  Corezoid Git Call node specialist — run custom code (Python, Go, Java, PHP,
  JavaScript, Clojure, Lisp, Prolog, or a custom Dockerfile) as a step inside a
  process. Use when the user needs logic that plain nodes cannot do: parsing
  files, using external libraries, cryptography, building email/attachments, or
  any custom runtime. Activate on "git call", "gitcall", "run my code",
  "parse a file", "use a library", "custom code node", "python/go/php in a
  process", or "why does push-process hang on git_call". Load this skill only
  when the task is actually about git_call — it is not needed for ordinary flows.
---

# Corezoid Git Call

A Git Call node runs your code (9 built-in languages, or a custom Docker image)
in an isolated container as one step of a process. Each task is delivered to a
`handle` function over JSON-RPC 2.0; the value you return becomes the payload of
the next node.

## 1. When to use it — the selection rule

Git Call is one of the most constrained nodes on the platform. Use it **only**
when a step needs a capability that native nodes and a Code (`api_code`) node
cannot provide — parsing a file, an external library, cryptography, or a custom
runtime — **and** the work finishes comfortably within the observed runtime
budget. For everything else use the native alternative. "One code block is
easier to write" is not one of those capabilities.

### Runtime and resource constraints

- **Observed execution deadline:** hosted sandbox measurements terminated the
  handler at approximately 60 s, wall-clock from the moment the task entered the
  node. Keep handler work comfortably below 50 s rather than relying on the exact
  cutoff, which is an observed platform behavior rather than a portable contract.
  On overrun the task was killed and routed to the error path
  (`__conveyor_git_call_return_type_tag__ = git_call_executing_error`, description
  `usercode: timeout`). In those measurements, inline cold and warm runs ended at
  the same point; custom images may have different startup overhead.
- **Default resource allocation:** 50 MB RAM and 0.1 CPU from a pool shared by
  Git Call nodes. These are defaults, not immutable hard limits: a super-admin can
  change the allocation. Shared capacity can still cause contention under
  concurrency; one live concurrent probe observed a starved task.
- **No persistent local storage:** runtime-local files, including writable
  `/tmp`, are ephemeral and must not be used as state between runs.
- **Build-time network dependency:** Git-repo mode and dependency installation
  need network access. Runtime network access is required only when the handler
  itself calls an external service.

For work that waits, polls, or retries over time, model progress as process state
transitions (`condition`+`delay`) or resume through a callback. Those patterns do
not hold one Git Call handler open, but they remain subject to normal
platform/process limits.

### Decision gate — before adding a Git Call, confirm ALL of these

1. It genuinely cannot be done with native nodes (`set_param`, `condition`, `delay`,
   `api`/`api_rpc`, `api_code`, `db_call`, `api_sum`, `api_copy`).
2. It cannot be done in a **Code (`api_code`)** node (JS with the platform's
   built-ins) — try this first for any transform/parse/compute.
3. It comfortably finishes below the observed ~50 s working budget and the
   surrounding flow does not require tighter, predictable latency under load.
4. It is **not** a long-running loop, poll, or wait — those belong in
   process state transitions (`condition`+`delay`) or a callback, never in one
   Git Call invocation.
5. It fits the configured resource allocation and does **not** need persistent
   local state.

If any answer is "no", do **not** use Git Call.

### Use Git Call ONLY for (native nodes truly cannot):

- Parse a file (download a URL and read a 1C `.1CD`, XML, PDF, QR, image, …).
- Use an external library the platform lacks (crypto, moment, pandas, okhttp, …).
- Cryptography / bespoke binary formats the platform can't express.
- A custom runtime (custom Dockerfile) for something none of the above covers.

### NEVER use Git Call for:

- Work that might approach the observed ~50 s budget, or a latency-critical path
  that requires predictable execution time under concurrency.
- **Long-running** work — loops, polling, ret/backoff, waiting on an external
  system (→ process state with `condition`+`delay`, or a callback).
- Plain HTTP calls (→ `api`/`api_rpc`).
- Simple transforms, JSON shaping, math, string work (→ `api_code`).
- Large-data / high-memory processing that does not fit the configured shared
  resource allocation.

| Aspect        | Native nodes / `api_code` | Git Call |
|---------------|---------------------------|----------|
| Runtime model | Platform node execution | Container handler; ~60 s deadline observed in hosted tests |
| Warm-up       | None for ordinary native nodes | Container build + dispatch |
| Memory / CPU  | Platform-managed | 50 MB / 0.1 CPU defaults from a shared, configurable pool |
| Concurrency   | Node-specific | Shared capacity can introduce contention |
| State/storage | Task/process state | No persistent local storage; `/tmp` is ephemeral |
| External deps | Built-in capabilities | External libraries and custom runtimes supported |

Selection rule: **default to native nodes + `api_code`. Use Git Call only when a
file to parse, an external library, cryptography, or a custom runtime leaves no
native way to do it, and keep each invocation short, bounded, and within the
configured resource allocation.**

## 2. Supported languages and runtimes

| Language   | Version       | Package manager | OS               |
|------------|---------------|-----------------|------------------|
| JavaScript | node v20      | yarn, npm       | alpine 3.17      |
| Go         | v1.23         | go mod          | alpine 3.20      |
| Python     | v3.12         | pip             | alpine 3.17      |
| Java       | v22           | gradle          | alpine 3.15      |
| PHP        | v8.3          | composer 2.5.4  | alpine 3.17      |
| Clojure    | v1.11.1       | lein 2.10       | alpine 3.17      |
| Lisp       | v2.4.8        | roswell         | Ubuntu 18.04     |
| Prolog     | swipl v9.2.7  | swipl           | debian bullseye  |
| Dockerfile | any           | —               | your own image   |

Git Call requests originate from `54.171.15.37`, `108.128.68.222`,
`63.33.226.230`. Whitelist these if a private repo or resource blocks access.

## 3. Handler contract (per language)

The handler receives the task payload and returns the next payload (or throws to
produce an error). In Git-Repo mode the entry file is `usercode.<ext>`.

```python
# python — usercode.py
def handle(data):
    data['result'] = 'ok'
    return data
```
```javascript
// js — usercode.js  (CommonJS; for ESM use import/export default, .mjs, or "type":"module")
module.exports = (data) => { data.result = 'ok'; return data; };
```
```go
// go — usercode.go
package main
import ("context"; "github.com/corezoid/gitcall-go-runner/gitcall")
func usercode(_ context.Context, data map[string]interface{}) error { data["result"]="ok"; return nil }
func main() { gitcall.Handle(usercode) }
```
```php
// php — usercode.php
<?php
function handle($data) { $data['result']="ok"; return $data; }
```
```java
// java — Usercode.java  (fully-qualified name com.corezoid.usercode.Usercode is mandatory)
package com.corezoid.usercode;
import com.corezoid.gitcall.runner.api.UsercodeHandler;
import java.util.Map;
public class Usercode implements UsercodeHandler<Map<String,String>,Map<String,String>> {
  public Map<String,String> handle(Map<String,String> data) throws Exception { data.put("result","ok"); return data; }
}
```
```prolog
% prolog — usercode.pl
:- module(usercode, [handle/2]).
handle(Data, Result) :- put_dict(result, Data, "ok", Result).
```
```lisp
;; lisp — usercode.lisp
(defpackage #:usercode (:use #:cl) (:export :handle))
(in-package #:usercode)
(defun handle (data) (setf (gethash 'result data) 'ok) data)
```
```clojure
;; clojure — usercode.clj
(ns usercode.usercode)
(defn handle [data] (assoc data :result "ok"))
```

Runnable examples (`hello_world`, `http_request`, `user_error`, dependency demos)
for every language: <https://github.com/corezoid/gitcall-examples>.

## 4. Two modes

- **Code editor (inline)** — paste the code straight into the node.
- **Git Repo** — set the repo URL, the branch/tag/commit, the path (leave empty
  if the entry file is in the repo root), and the entry file. Use an SSH key on
  the node for private repos.

## 5. Dependencies (Build command)

Install dependencies with a Build command (Code editor) or a manifest file
(Git Repo):

| Language | Build command (Code editor)                              | Manifest (Git Repo) |
|----------|----------------------------------------------------------|---------------------|
| JS       | `npm install crypto-js@4.1.1 moment@2.29.4`              | `package.json`      |
| Python   | `pip install 'pycryptodomex==3.20'`                      | `requirements.txt`  |
| Go       | (latest versions resolved automatically)                | `go.mod`            |
| Java     | a gradle command                                         | `build.gradle` (+ `./gradlew build`) |
| PHP      | `composer require guzzlehttp/guzzle:^7.0`                | `composer.json`     |
| Clojure  | `lein change :dependencies conj '[...]' && lein install` | `project.clj`       |
| Lisp     | none — `(ql:quickload '(:cl-mustache) :silent t)` in code | —                  |
| Prolog   | `swipl -g "pack_install(matrix,[interactive(false)])."`  | (same command)      |

No dependencies → empty Build command.

## 6. Deploy with the MCP

`push-process` deploys Git Call nodes automatically (as of the git_call build
support in this plugin). Just author the node like any other and push:

1. Add a node whose logic `type` is `git_call`, set `lang` and either `code`
   (inline) or `repo`/`commit`/`path`/`script` (Git Repo).
2. `push-process` — it uploads the source, builds the container on the build
   service, and commits. Every runtime (JavaScript included) is built before the
   commit; JavaScript just builds fastest (a few seconds) since it installs no
   compiler toolchain.

Builds take ~5 s (JavaScript) to ~20–120 s (compiled runtimes, first build with
dependency install), then build from cache. `run-task` reports the task settling
on a non-final node while the build runs — that is expected; the result merges
into the task payload asynchronously.

Override the build endpoint for on-prem installs with `COREZOID_WS_URL`.

### What push-process does under the hood
The container build runs on Corezoid's build service and is driven over a
WebSocket (`wss://ws.<host>/api/1/sock_json`), authenticated with the same
Simulator access token as the HTTP API. `push-process` opens the socket after
uploading the source, sends a `monitor_show`/`function_build`/`status:"on"`
frame to start the build, keeps the socket alive (client sends `"0"`, server
answers `"1"`), waits for `log:{"type":"done"}`, then commits. You do not need to
do any of this by hand — it is only useful to know when debugging a build.

## 7. JSON-RPC 2.0 protocol

Request to your code: `{"jsonrpc":"2.0","method":"handle","id":"…","params":{…}}`.
Success: `{"jsonrpc":"2.0","id":"…","result":{…}}`. Error:
`{"jsonrpc":"2.0","id":"…","error":{"code":…,"message":"…"}}`.

For a **custom Dockerfile**, run an HTTP server on `$GIT_CALL_PORT`, handle POST
requests per JSON-RPC 2.0, run as user `501:501`, and treat the container as
read-only (`/tmp` is writable).

## 8. Errors and troubleshooting

On failure the task carries these fields (routed to the auxiliary condition
output): `__conveyor_git_call_return_type_error__` (`Hardware` = system, retry;
`Software` = code/settings), `__conveyor_git_call_return_type_tag__`
(`git_call_return_format_error`, `git_call_executing_error`,
`git_call_is_not_supported` = the node is v1, use v2, `code_return_size_overflow`,
`git_call_fatal_error`), and `__conveyor_git_call_return_type_description__`.

Common issues:

- **push-process hangs / `no response from server`** — an older plugin without
  git_call build support. Upgrade the plugin.
- **`source has to be built`** — the container was not built before commit
  (should not happen via push-process; if authoring by hand, build first).
- **`usercode module has no handle function`** — the entry `handle` is missing,
  or a stale build instance — rebuild.
- **Build fails on root access** — Corezoid forbids root; keep to allowed dirs.
- **No internet** — Git Call needs network to fetch the repo/dependencies.

## 9. Resources

Each container defaults to 100 millicpu (0.1 CPU) and 50 MB RAM from a resource
pool shared by Git Call nodes. The exact scope and allocation are
deployment-specific; ask a super-admin to inspect or adjust them before relying
on Git Call for heavier or highly concurrent workloads.

## Reference

- Examples (all languages + custom Dockerfiles): <https://github.com/corezoid/gitcall-examples>
- Go runner: <https://github.com/corezoid/gitcall-go-runner>
corezoid-lifecycle4.92 KB

View saved version →

---
name: corezoid-lifecycle
description: >
  Safely pauses, resumes, or moves existing Corezoid processes and folders.
  Use only when the user explicitly asks to pause/unpause/resume/activate a
  process or to move/relocate/reorganize a specific process or folder. Trigger
  on "pause process", "resume process", "move process", "move folder",
  "поставь процесс на паузу", "сними с паузы", "возобнови процесс",
  "перемести процесс", "перемести папку", "перенеси процесс", or
  "перенеси папку". Do not activate merely because a review, edit, cleanup,
  or refactor is in progress; those are not authorization to mutate lifecycle
  or location.
---

# Corezoid Lifecycle and Move Operations

Use this skill for deliberate operational changes to existing Corezoid
objects. These tools act on live server state and require explicit user intent.

Read `${CLAUDE_PLUGIN_ROOT}/docs/process/process-lifecycle-and-move.md` before
the first lifecycle/move operation in a session.

## Non-negotiable authorization rule

Never pause, resume, or move an object automatically.

The following are not authorization:

- a process appears unused;
- a review or refactor is in progress;
- edits/tests have completed;
- a folder layout looks untidy;
- pausing or moving would be a convenient safety precaution.

The user must directly ask for the specific action. If the exact object or
destination is ambiguous, resolve candidates read-only and ask the user to
choose. Do not infer a target.

## Required two-step workflow

For all four tools:

1. Resolve and repeat back the exact object ID/title. For moves, also resolve
   the exact destination ID/path.
2. Call the tool with `apply=false` (or omit `apply`).
3. Present the dry-run, including current state/location, target, operational
   effects, and the exact confirmation token.
4. Ask for explicit confirmation. Do not invent confirmation from earlier
   general approval. The token is reconstructable and is not itself evidence
   that the user approved the operation.
5. Only after confirmation, call `apply=true` with the exact token returned by
   the fresh dry-run.
6. Report the tool's verified result. If it says verification failed or state
   may already have changed, stop and run a new dry-run; never retry blindly.

## Pause a process

Use:

```text
pause-process(process_id=<id>)
pause-process(process_id=<id>, apply=true,
              confirm="process#<id>:<live_status>->paused")
```

Explain before confirmation:

- Corezoid will reject new task creation with `conveyor_is_not_active`.
- The graph and deployment are unchanged.
- Already-running or parked tasks are not modified by this tool and must be
  inspected separately.
- Pause is temporary admission control, not evidence that a process is safe
  to delete.

Pause can support a user-chosen maintenance/observation window. Never choose
that window on the user's behalf.

## Resume a process

Use:

```text
resume-process(process_id=<id>)
resume-process(process_id=<id>, apply=true,
               confirm="process#<id>:<live_status>->active")
```

Before confirmation, verify with the user that maintenance is complete and
that the process may receive traffic. New tasks can arrive immediately from
API callers, schedules, callbacks, and other processes. Never auto-resume just
because this session originally paused the process.

## Move a process

Use:

```text
cz-structure {"action": "move-process", "args": {"process_id": <id>, "destination_folder_id": <folder>}}
cz-structure {"action": "move-process", "args": {"process_id": <id>, "destination_folder_id": <folder>,
                    "apply": true, "confirm": "<exact context-bound token from the fresh dry-run>"}}
```

The operation reparents the existing process. It preserves the same ID and
graph; it does not copy, import, or deploy.

If the dry-run reports a project/stage/root context change, explain the alias,
environment-variable, access, and deployment risks. Proceed only after the
user accepts those risks, adding `allow_cross_stage=true` to the confirmed
call.

## Move a folder

Use:

```text
cz-structure {"action": "move-folder", "args": {"folder_id": <id>, "destination_folder_id": <folder>}}
cz-structure {"action": "move-folder", "args": {"folder_id": <id>, "destination_folder_id": <folder>,
                    "apply": true, "confirm": "<exact context-bound token from the fresh dry-run>"}}
```

Only normal folders can be moved. The action rejects projects/stages, self-move,
and moving a folder into a descendant. For cross-stage/root moves, the risks
apply to every descendant and `allow_cross_stage=true` is required after the
user accepts them.

## After a move

The MCP tool does not relocate local mirror files/directories. Re-pull the
destination and verify it before removing any stale local copy. Local cleanup
also requires explicit user intent; never delete the old local path merely
because the server move succeeded.
corezoid-process-optimizer12.1 KB

View saved version →

---
name: corezoid-process-optimizer
description: >
  Optimizes a Corezoid process JSON — reduces tact consumption by merging nodes,
  cleans data flow, fills missing node titles, and adds resilience patterns.
  Activate when the user says "optimize", "improve", "reduce tacts", "merge nodes",
  "clean up process", "what can be improved", "show optimizations", or any phrase
  implying they want to make a process faster, cheaper, or more readable.
  Two modes: plan-only (analysis + report, no changes) and auto (plan + execute immediately).
---

# Corezoid Process Optimizer

## Mode and scope detection

Determine **mode** and **scope** from the user's phrasing before doing anything else.

### Mode

| User intent | Mode |
|-------------|------|
| "optimize", "apply", "fix", "improve" — action verb | **AUTO** — analyze, plan, execute |
| "show", "what can", "suggest", "check" — analysis verb | **PLAN** — analyze, report, wait |

In PLAN mode, after presenting the report ask:
> "Apply all? Apply by group? (1 — tacts, 2 — data, 3 — naming, 4 — resilience)"

### Scope

The user may request a specific optimization group. Detect from keywords:

| Keyword(s) in request | Scope — run only |
|-----------------------|------------------|
| "tacts", "tact", "state changes", "nodes", "merge" | Group 1 |
| "data", "payload", "cleanup", "garbage", "fields" | Group 2 |
| "names", "naming", "titles", "readability", "descriptions" | Group 3 |
| "resilience", "semaphors", "timeouts", "stability" | Group 4 |
| No group keyword — general request | All groups |

If scope is a single group — run Phase 1 analysis only for that group. Skip all others entirely.
Still run `lint-process` first (its findings feed Group 1 regardless).

Examples:
- "optimize by tacts" → AUTO + Group 1 only
- "show tact optimizations" → PLAN + Group 1 only
- "add missing semaphors" → AUTO + Group 4 only
- "optimize" → AUTO + all groups

---

## Step 0 — Resolve process

Resolve `PROCESS_PATH` before calling any tools:
1. Check if the user provided a path, name, or ID.
2. If not — ask: "Which process? Provide a file path, name, or ID."
3. If name or ID — search locally: `find . -name "*.conv.json"`.
4. Read and parse the file.
5. Call **`lint-process`** — record findings. They become Group 1 quick-wins.

---

## Step 1 — Analyze

Build a node map: `id → { title, obj_type, logics[], sems[], outgoing edges }`.

Trace the execution graph from the Start node following `go.to_node_id` and `err_node_id` edges.

Collect candidates for all four groups below.

---

## Group 1 — Tact Reduction

> Formula: SC = (N – 1) × T. Every node transition costs one state change. Fewer nodes = fewer tacts.

### 1.1 Merge consecutive set_param nodes

Detect chains A → B → C where all nodes have `type: "set_param"`, connected sequentially with no branching.

Merge condition: all nodes share the same `err_node_id` (or all have none).
If `err_node_id` values differ — flag as candidate, do not merge automatically.

Merge action: combine all `extra` and `extra_type` objects into the first node. Remove subsequent nodes. Reconnect routing to where the chain ended.

Tacts saved: (chain length − 1) per task.

---

### 1.2 Merge consecutive code nodes

Detect chains of `type: "api_code"` nodes connected sequentially with no branching.

Merge condition: all share the same `err_node_id`.
If different — flag only; note that error handling must be unified first.

Merge action: concatenate `src` fields in order, separated by `\n// ---\n`. Keep one `err_node_id`. Remove subsequent nodes. Reconnect routing.

Tacts saved: (chain length − 1) per task.

---

### 1.3 Merge consecutive condition nodes checking the same field

Detect chains of `type: "go_if_const"` nodes where all check the **same `arg` field**.

Merge action: combine all `conditions[]` arrays into the first node. Each original branch keeps its own `to_node_id`. Remove subsequent condition nodes.

Do NOT merge if conditions check different fields — different semantics, merging hurts readability.

After merging, add a note to the plan:
> "Merged conditions on field '{{field}}'. Review combined node for readability."

Tacts saved: (chain length − 1) per task.

---

### 1.4 Replace api_rpc with api_copy when reply is unused

Detect `api_rpc` nodes where no downstream node references any field that could only originate from the called process's reply.

Check: scan all downstream `extra`, condition `arg`/`val`, and `src` fields for parameter names not present in the task before the call. If none found — candidate for api_copy.

This change requires confirmation even in AUTO mode. Present:
> "Node '[title]' calls process but does not use the reply. Replace with api_copy (fire-and-forget)? [yes/no]"

If confirmed: change `type: "api_rpc"` → `type: "api_copy"`. Switch `extra`/`extra_type` to `data`/`data_type` per the api_copy schema (see `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md`).

---

### 1.5 Remove dead nodes

Apply lint findings:
- **Orphaned nodes** — remove from `scheme.nodes`.
- **No-op conditions** — re-route the incoming edge to the single destination; remove the condition node.
- **Unused set_params** — if the node sets only unused variables, remove the node. If mixed, remove only the unused keys from `extra`/`extra_type`.

---

## Group 2 — Data Cleanup

### 2.1 Inline payload cleanup after API calls

After each `type: "api"` node, identify response fields not referenced by any downstream node.

Do NOT add a new cleanup node — inline the cleanup into the nearest existing downstream node:
- **code node**: prepend `delete data.<field>;` at the top of `src`.
- **set_param node**: set_param cannot delete keys. Find the next code node and add the delete there. If no downstream code node exists — flag for the user, do not create a new node.

---

### 2.2 Remove dead code inside code nodes

Inside each `api_code` node's `src`, detect:
- `data.x = data.x` — self-assignment, remove.
- Variables declared but never read after declaration — flag for user review.
- Large commented-out blocks — flag for user review.

---

## Group 3 — Readability

Run this group when:
- User explicitly requested it, OR
- AUTO mode is active.

**Critical nodes always get titles filled** regardless of mode:

| Node type | Always fill `title` if empty |
|-----------|------------------------------|
| `api_code` | Yes |
| `api` | Yes |
| `api_rpc` | Yes |
| `api_copy` | Yes |
| `obj_type: 2` (End/Error) | Yes |

### Title inference rules

| Node type | Inference |
|-----------|-----------|
| `api` | `"[METHOD] [hostname][path]"` from the `url` field |
| `api_rpc` | `"Call @[alias]"` or `"Call [conv_id]"` |
| `api_copy` | `"Copy → @[alias]"` or `"Copy → [conv_id]"` |
| `api_code` | First meaningful line of `src` (strip `data.`, max 40 chars) |
| `set_param` | `"Set [key1], [key2], ..."` (first 3 keys) |
| `go_if_const` | `"Check [arg field]"` |
| `obj_type: 2`, icon `error` | `"Error"` |
| `obj_type: 2` | `"Final"` |

Never overwrite an existing non-empty `title`.

### 3.1 Fill process params array

If `params: []` and the Start node clearly receives input (inferred from downstream references to fields never set internally) — propose a `params` array.

Always ask for confirmation before applying — field types cannot be reliably inferred.

---

## Group 4 — Resilience

### 4.1 Add missing time semaphors

| Node type | Severity | Default timeout |
|-----------|----------|-----------------|
| `api_callback` (Waiting for Callback) | 🔴 Critical — always add | 3600 sec |
| `api` (API Call) | 🟡 Important — add without asking | 30 sec |
| `api_rpc` (Call a Process) | 🟢 Recommended — add without asking | 60 sec |

Semaphor `to_node_id` must point to a valid error node.
If no suitable error node exists — create one at `x + 300`, same `y` as the parent node.
Use `obj_type: 2` with `title: "Timeout"` and connect the semaphor to it.

Semaphor JSON:
```json
{
  "type": "time",
  "value": 30,
  "dimension": "sec",
  "to_node_id": "<error_node_id>"
}
```

---

## Step 2 — Plan report

Present findings in this format before executing anything:

```
## Optimization Plan: <Process Title> (<ID>)

### Group 1 — Tact Reduction
| # | Type               | Nodes                                 | Tacts saved |
|---|--------------------|---------------------------------------|-------------|
| 1 | Merge set_param    | "Set ref" → "Set amount" → "Set cur"  | 2/task      |
| 2 | Merge code         | "Parse" → "Validate"                  | 1/task      |
| 3 | Remove orphaned    | "Old handler" (abc123)                | 1/task      |
| 4 | rpc→copy ⚠️ confirm | "Send notification"                   | wait saved  |

Total nodes removed: N | Tacts saved: X/task

### Group 2 — Data Cleanup
| # | After node          | Fields to remove          | Inline into          |
|---|---------------------|---------------------------|----------------------|
| 1 | "Call Stripe API"   | payment_method_details... | "Parse response"     |

### Group 3 — Readability
| # | Node (id)      | Suggested title                        |
|---|----------------|----------------------------------------|
| 1 | api (abc123)   | "POST api.stripe.com/v1/charges"       |
| 2 | api_rpc (def)  | "Call @payment-process"                |

### Group 4 — Resilience
| # | Node                    | Issue                         | Action             |
|---|-------------------------|-------------------------------|--------------------|
| 1 | "Call SMS API"          | Missing timeout semaphor      | Add 30sec          |
| 2 | "Wait callback" 🔴      | Missing timeout semaphor      | Add 3600sec        |

### Requires action outside this process
- Hardcoded URL in "Call Stripe API" → use /corezoid-variable-manager
- Numeric conv_id 1307813 (×3 nodes)  → use /corezoid-alias-manager
```

---

## Step 3 — Execute

### Execution order

Always apply in this sequence:
1. Group 1 — tact reduction (graph changes first)
2. Group 4 — resilience (semaphors reference the now-clean graph)
3. Group 2 — data cleanup (inline into final node set)
4. Group 3 — naming (operates on final nodes)

### Confirmation rules

| Change | Confirm in AUTO | Confirm in PLAN |
|--------|----------------|-----------------|
| Merge set_param / code / condition | No | Yes per group |
| api_rpc → api_copy | **Always** | **Always** |
| Add semaphors | No | Yes per group |
| Fill titles (critical nodes) | No | No |
| Fill titles (other nodes) | No | Yes per group |
| Fill `params` array | **Always** | **Always** |
| Create variable for hardcoded value | **Always** | **Always** |

### After all changes

1. Write updated JSON to `PROCESS_PATH`.
2. Call **`lint-process`** — fix any errors before proceeding.
3. Call **`push-process`**.
4. Notify the user: "Deployed. Please **refresh the page** in Corezoid to see the updated process."

---

## Boundaries — what the optimizer does not do

| Finding | Action |
|---------|--------|
| Hardcoded URLs / tokens | Flag + point to `/corezoid-variable-manager` |
| Numeric conv_id without alias | Flag + point to `/corezoid-alias-manager` |
| Full Markdown documentation | Point to `/corezoid-process-tech-writer` |
| Cross-process audit | Point to `/corezoid-project-review` |
| Extract subprocess (architecture) | Discuss with user + point to `/corezoid-create` |

---

## Reference Documents

| Path | When to read |
|------|-------------|
| `${CLAUDE_PLUGIN_ROOT}/docs/node-structures.md` | JSON schemas for all node types |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-node.md` | set_param merge rules |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/code-node.md` | Code node structure |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | API Call semaphor configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | api_rpc vs api_copy decision |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/copy-task-node.md` | api_copy structure |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/waiting-for-callback-node.md` | api_callback critical semaphor |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/error-handling.md` | Error node patterns |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/node-positioning-best-practices.md` | Positioning new nodes |
corezoid-process-tech-writer6.15 KB

View saved version →

---
name: corezoid-process-tech-writer
description: >
  Documents a Corezoid process — produces a human-readable Markdown file AND enriches
  the process JSON with descriptions on every node and parameter. Output is designed for
  team wikis, internal portals, and future product integration.
  Activate whenever a user asks to document a process, write docs for a connector,
  add descriptions to a process, create documentation for a logic, describe what a
  process does, or any similar phrasing. Also activate when the user shares a process
  JSON and asks to explain it or make it self-documenting. Always produce BOTH outputs
  (Markdown file + enriched JSON) — never just one.
---

# Corezoid Process Tech Writer

Always produce **two outputs** for every process:
1. Markdown documentation file at `.processes/<name>-docs.md`
2. Enriched process JSON (same file, `description` fields filled in) at `.processes/<name>-enriched.json`

---

## Step 0 — Load the process

If the user provides a file path, read it directly. If they provide a process name or ID, use
`pull-process` to fetch it first.

---

## How to extract information from the process JSON

### Inputs
Read the `params` array. Each entry has:
- `name` — parameter name
- `type` — data type
- `descr` — description (may be empty — infer from context)
- `flags` — `"required"` flag means mandatory; `"input"` = input param, `"output"` = output param
- `regex` — validation pattern (document if non-empty)

### Outputs
Find all nodes with `api_rpc_reply` logic in `condition.logics`:
- `throw_exception: false` → success response — document `res_data` keys and types
- `throw_exception: true` → error response — document what triggers it (node title, `exception_reason` if present)

### Process flow
Walk `scheme.nodes` following `go` entries from the Start node (`obj_type: 1`):
- Start → node with `id` matching the `to_node_id` in Start's `go` logic
- Continue following `go` entries to map the happy path
- Note branches at Condition nodes or `go_if_const` entries
- Note error paths via `err_node_id` references

### External dependencies
- API Call nodes (`api` logic): extract `url`, `method`, `extra_headers`
- `{{env_var[@name]}}` references: list all unique variable names used
- Code nodes (`api_code`): look for referenced services or data transformations
- Call Process nodes (`api_rpc`): extract `conv_id` values (called process IDs)

---

## Output 1: Markdown documentation

Save to `.processes/<process-name-in-snake-case>-docs.md`.

Use this exact structure:

```markdown
# <Process Title>

## Overview
<1-2 sentences: what this process does and when to call it. Be specific about the business purpose.>

## Input Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| field_name | string | Yes | Description from params.descr |
| optional_field | number | No | Description |

<If any field has regex validation, add a "Validation" subsection listing the rules.>

## Output

### Success response
| Field | Type | Description |
|-------|------|-------------|
| response | object | The API response body |

### Error cases
| Error | Trigger condition |
|-------|------------------|
| "Code node error" | JavaScript execution failed in the preparation step |
| "API call error" | External API returned an error or was unreachable |

## How to Call

Example `task_data` with realistic values:
​```json
{
  "field_name": "example_value",
  "optional_field": 42
}
​```

## Process Flow

1. **Start** — Entry point, receives the task
2. **<Code Node title>** — <plain English: what this step does>
3. **<API Call / Call Process title>** — <plain English: what is called and why>
4. **<Reply node title>** — <what is returned on success>
5. **Final** — Task stored, process complete

<For error paths, describe them after the happy path:>

**Error path (Code Node failure):** If the preparation step fails, an error reply is returned
with the exception description, and the task ends at the Error node.

## External Dependencies

| Dependency | Type | Variable / URL |
|-----------|------|----------------|
| <service name> | HTTP API | `{{env_var[@variable-name]}}` |
| <process name> | Corezoid process | ID: `<conv_id>` |

## Notes

- <Any timeouts configured via semaphors — e.g. "API call has a 30-second timeout">
- <Rate limiting (max_threads setting)>
- <Any other relevant technical notes>
```

---

## Output 2: Enriched process JSON

Read the original process JSON, add `description` fields to every node, and write the result
to `.processes/<name>-enriched.json`.

### Rules for node enrichment

**What to fill:**
- `description` field on every node — one plain English sentence: what this node does in the context of this process
- `params[].descr` — if empty, infer from the field name and process context

**What NOT to change:**
- `id`, `obj_type`, `condition`, `logics`, `semaphors` — never touch these
- `x`, `y`, `extra`, `options` — leave as-is
- `title` — only fill if the field is completely empty (`""`)

**Description style:**
- One sentence, active voice, present tense
- Specific to this process — not generic ("Handles errors" is bad; "Returns an error reply if the actor creation API call fails" is good)
- Reference actual data fields and external services where relevant

**Examples:**

| Node type | Bad description | Good description |
|-----------|----------------|-----------------|
| Code node | "Prepares data" | "Builds the request body with actor_name, form_id, and authorization_header for the Simulator API call" |
| API Call | "Makes API call" | "Sends POST request to Simulator API to create a new actor with the prepared parameters" |
| Reply Success | "Returns response" | "Returns the created actor data from the Simulator API back to the calling process" |
| Reply Error | "Returns error" | "Returns error reply with throw_exception:true when the actor creation API call fails" |
| Final | "Final" | "Stores the completed task with actor creation result and marks the process as successful" |

---

## Both files must be produced in the same response

Do not produce one without the other. If the process JSON is very large, produce the Markdown
first, then the enriched JSON.
corezoid-project-review10.4 KB

View saved version →

---
name: corezoid-project-review
description: >
  Corezoid project review and audit specialist. Use when the user wants to
  review or audit an entire Corezoid project or folder — multiple processes at
  once. Activate when the user says "review project", "audit project",
  "review all processes", "audit all processes", "review folder",
  "review all processes in", "project-wide review", "cross-process analysis",
  "find issues across processes", "review the whole project", or "audit folder".
---

# Review a Corezoid Project

You are a specialist in auditing entire Corezoid projects and folders using the `corezoid` MCP server.

Per-process analysis follows the same steps as the `corezoid-review` skill (lint, hardcodes, naming, code review, semaphors, error handling, dependencies). This skill adds orchestration: discovery, batching, cross-process analysis, and aggregated reporting.

---

## Phase 0 — Project Discovery

### Step 0.1: Verify Environment

Check whether the workspace root contains a `<id>_<name>.stage.json` marker file (its `obj_id` is the stage ID). Practical test: call any Corezoid MCP tool with no arguments (e.g. `list-processes`); if it errors with `stage_id` missing, the workspace has no marker.

- If the marker is **missing** → stop and invoke the `corezoid-init` skill. Do not proceed until init completes.
- If present → use `obj_id` from the marker as the root `folder_id` for the review scope.

### Step 0.2: Build Process Inventory

Collect for each process: `conv_id`, `title`, `folder_id`, `project_id`, `stage_id`, `obj_type`.
Store as `process_inventory[]`. Skip objects where `obj_type != conveyor` unless explicitly requested.

### Step 0.3: Announce Scope

Before starting, report:

```
Found N processes in project "<project_name>":
  - Process A (conv_id: 12345)
  - Process B (conv_id: 67890)
  ...
Proceeding with full review.
```

Process all sizes automatically without confirmation. Stream progress in batches of 10.

---

## Phase 1 — Per-Process Audit

For each process in `process_inventory[]`:

1. Pull the process with MCP tool **`pull-process`** using `process_id`
2. Run the full per-process audit (same steps as `corezoid-review` skill):
   - **Step 1** Structural lint (`lint-process`)
   - **Step 2** Load and parse nodes
   - **Step 3** Hardcode check
   - **Step 4** Repeated logic
   - **Step 5** Cycle verification
   - **Step 6** Node naming
   - **Step 7** Code node analysis
   - **Step 8** Semaphor coverage
   - **Step 9** Error handling review
   - **Step 10** External dependencies inventory
3. Store result as `process_reports[conv_id]`
4. Log progress: `Reviewed N/total: "<title>" — X findings`

Reference: `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-review/SKILL.md`

---

## Phase 2 — Cross-Process Analysis

Requires all `process_reports[]` from Phase 1.

### Step 2.1: Dependency Graph

Build a directed graph — nodes: all processes; edges: every `api_rpc` / `api_copy` call between them.

Flag:
- ⚠️ **Circular dependencies** — A calls B which calls A
- ⚠️ **Orphaned processes** — never called and no direct external input
- ⚠️ **High fan-in** — called by > 5 other processes (single point of failure)
- ⚠️ **High fan-out** — calls > 7 other processes (coupling risk)
- ℹ️ **External calls** — `conv_id` values pointing outside the project inventory

### Step 2.2: Duplicate Logic Across Processes

- Two processes with identical or >80% similar `api_code` nodes → candidate for shared subprocess
- Two processes calling the same external URL with same parameters → candidate for shared wrapper process

### Step 2.3: Shared Hardcoded Values

Aggregate only normalized `hardcode.*` findings from per-process reports. Do **not** aggregate dynamic Corezoid expressions, `dependency.state_store_ref`, or values fully wrapped in `{{...}}` as shared hardcodes.

- Same URL in > 2 processes → recommend shared `env_var` (use `/corezoid-variable-manager` to create)
- Same numeric `conv_id` in > 1 process → one alias fix resolves all
- Same token/key fragment in > 1 process → security risk, centralize immediately via `/corezoid-variable-manager` as `secret` variable
- Same status string in > 2 processes → recommend shared constant or `env_var`
- Same error text in > 1 process → recommend shared text constant

False-positive guard (same as per-process review):
- Ignore values that are fully dynamic expressions, e.g. `{{conv[@storage].ref[{{key}}].field}}`
- If a shared value is a state-store alias, report it under dependency analysis instead of `cross_process.shared_hardcode_value`
- Recompute aggregate summary counts after removing false positives

### Step 2.4: Alias Consistency

Flag:
- Same alias used with both `create` and `modify` in different processes → race condition risk
- Alias defined in one process but used with numeric `conv_id` in another → inconsistency
- Aliases referenced in processes but absent from project inventory → undocumented external dependency

To fix alias issues found here (create missing aliases, rename, repoint, delete conflicts),
use the `/corezoid-alias-manager` skill.

### Step 2.5: Naming Consistency

Flag:
- Same vague name used widely (e.g. 10+ nodes named `"error"` across project) → recommend naming standard
- Mixed conventions across processes (`Create_X` vs `createX`)

---

## Phase 3 — Aggregate Report

Produce two output files: `project-review-<date>.json` and `project-review-<date>.md`.

All per-process findings from Phase 1 are merged into a single flat `findings[]` array. Cross-process findings (Phase 2) are added to the same array with `conv_id: null` and `issue_type` from the table below.

Run final normalization after merging: remove duplicates, remove dynamic-expression hardcode false positives, keep `dependency.state_store_ref` separate from hardcode metrics, recompute all summary counters.

### Cross-Process Issue Types

| issue_type | issue_subtype | severity |
|------------|---------------|----------|
| `cross_process` | `circular_dependency` | high |
| `cross_process` | `orphaned_process` | warning |
| `cross_process` | `high_fan_in` | warning |
| `cross_process` | `high_fan_out` | warning |
| `cross_process` | `external_call` | low |
| `cross_process` | `shared_hardcode_url` | high |
| `cross_process` | `shared_hardcode_token` | high |
| `cross_process` | `shared_hardcode_value` | medium |
| `cross_process` | `duplicate_logic` | low |
| `cross_process` | `alias_conflict` | warning |
| `cross_process` | `naming_convention` | low |

Cross-process finding example:

```json
{
  "conv_id": null,
  "process_title": null,
  "node_id": null,
  "node_title": null,
  "issue_type": "cross_process",
  "issue_subtype": "shared_hardcode_url",
  "severity": "high",
  "value": "https://api.openai.com",
  "location": "found_in: [1779750, 1779754, 1782365]",
  "recommendation": "extract to shared env_var OPENAI_API_URL"
}
```

### Step 3.1: Per-Process Summary Table (Markdown)

| Process | Findings | High | Medium | Warning | Low |
|---------|----------|------|--------|---------|-----|
| Process A (12345) | 12 | 2 | 3 | 4 | 3 |
| Process B (67890) | 5 | 0 | 1 | 2 | 2 |
| Cross-process | 4 | 1 | 1 | 2 | 0 |
| **Total** | **N** | | | | |

### Step 3.2: Top Issues List (Markdown)

```
🔴 CRITICAL (fix before release)
  1. [Process A / Node X / semaphor / api_callback_missing] — tasks will hang
  2. [Cross-process / shared_hardcode_token] — token "sk-xxx" in 3 processes — revoke & move to env_var
  3. [Process B / Node Y / hardcode / url] — external URL hardcoded — extract to env_var

🟡 IMPORTANT (fix in next sprint)
  4. [Cross-process / circular_dependency] — Process B → Process A → Process B
  5. [Cross-process / shared_hardcode_url] — https://api.example.com in 4 processes
  6. [Process D / Node Z / dependency / missing_alias] — numeric conv_id 44444 — replace with @alias

⚠️ WARNINGS (technical debt)
  7. [Cross-process / orphaned_process] — Process C never called — possible dead code
  8. [Cross-process / duplicate_logic] — Process A, Process D — identical code nodes
```

### Step 3.3: JSON Output Schema

```json
{
  "project_name": "<name>",
  "project_id": "<id>",
  "review_date": "YYYY-MM-DD",
  "process_count": 0,
  "summary": {
    "total_findings": 0,
    "per_process_findings": 0,
    "cross_process_findings": 0,
    "by_severity": { "high": 0, "medium": 0, "warning": 0, "low": 0 },
    "by_type": {
      "hardcode": 0,
      "semaphor": 0,
      "structural": 0,
      "naming": 0,
      "error_handling": 0,
      "code_quality": 0,
      "response_mapping": 0,
      "dependency": 0,
      "cycle": 0,
      "repeated_logic": 0,
      "cross_process": 0
    }
  },
  "dependency_graph": {
    "edges": [
      { "from_conv_id": 11111, "from_title": "Process A", "to_conv_id": 22222, "to_title": "Process B", "call_type": "api_rpc", "count": 3 }
    ],
    "circular_dependencies": [],
    "orphaned_processes": [],
    "high_fan_in": [],
    "high_fan_out": [],
    "external_calls": []
  },
  "findings": []
}
```

---

## Scope Modifiers

| Request | Behavior |
|---------|----------|
| `"review all processes in folder X"` | Phase 0 scoped to folder_id |
| `"review only stage prod"` | Filter `process_inventory` by `stage_id` |
| `"skip cross-process analysis"` | Phase 1 only, skip Phase 2 |
| `"quick review"` | Skip code node analysis (Step 7) and duplicate logic (Step 2.2) |
| `"review only hardcodes"` | Hardcode check per process + Step 2.3 only |
| `"review only N processes"` | Prioritize by last modified date |

---

## MCP Tool Map

| Step | MCP call |
|------|----------|
| 0.1 List processes | `list folder filter:"conveyor" obj_id:<stage_id from current Folder>` |
| 1 Pull process | `pull-process process_id:<conv_id>` |
| 1 Lint process | `lint-process process_path:<path>` |
| 2.1 List & resolve aliases | `/corezoid-alias-manager` → "Workflow: List aliases" |

---

## Reference Documents

| Path | When to read |
|------|-------------|
| `${CLAUDE_PLUGIN_ROOT}/skills/corezoid-review/SKILL.md` | Per-process audit steps (Steps 1–14) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/code-node.md` | Code node details and available JS libraries |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | Call a Process node, semaphores |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | HTTP API call configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/error-handling.md` | Error handling patterns |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variable naming rules and usage examples |
corezoid-review16.3 KB

View saved version →

---
name: corezoid-review
description: >
  Corezoid process review and audit specialist. Use when the user wants to
  analyze, review, audit, or improve an existing Corezoid process. Activate
  when the user says "review a process", "analyze", "check", "audit", "find
  issues", "explain this process", "what's wrong with", "optimize", or
  "check for hardcoded values".
---

# Review a Corezoid Process

You are a specialist in auditing and analyzing Corezoid processes using the `corezoid` MCP server.

## Identify the Process (MANDATORY FIRST STEP)

**Before doing anything else**, resolve `PROCESS_PATH`:

1. Check whether the user already provided a process identifier — a file path, process name, or process ID — in the current message or conversation history.
2. If no identifier is provided, ask:

   > "Please specify the process — you can provide a file path (e.g. `1278273_Business.folder/2778176_payment.conv.json`), a process name, or a process ID."

   Do **not** call any MCP tools until the user provides an identifier.
3. If the user gave a **name or ID** (not a file path), search the local working directory for the matching `.conv.json` file using the `find` or `grep` Bash tools (the project is already pulled locally).
4. Once `PROCESS_PATH` is known, begin the audit below.

---

## Step 1: Structural Lint

Run the linter to detect structural issues automatically:

Call MCP tool **`lint-process`** with `process_path: "<PROCESS_PATH>"`.

This checks for:
- **Orphaned nodes** — unreachable nodes not connected from Start
- **No-op conditions** — all branches of a condition leading to the same node
- **Unused set_param** — variables set but never referenced downstream

Record all findings. They will be included in the final report.

---

## Step 2: Load and Parse the Process

Read the `.conv.json` file and extract nodes:

- `ops[0]['scheme']` is a **list** — always index `[0]`
- `node['condition']` is a dict with keys `logics` (list) and `semaphors` (list)
- `node['extra']` is a **string** (escaped JSON) — not a dict
- Conditions in `go_if_const` logics live in `lg['conditions']`, NOT in `lg['extra']`

Collect node groups for analysis:

```python
code_nodes  = [n for n in nodes for lg in n['condition']['logics'] if lg['type'] == 'code']
api_nodes   = [n for n in nodes for lg in n['condition']['logics'] if lg['type'] == 'api']
rpc_nodes   = [n for n in nodes for lg in n['condition']['logics'] if lg['type'] == 'api_rpc']
copy_nodes  = [n for n in nodes for lg in n['condition']['logics'] if lg['type'] == 'api_copy']
cond_nodes  = [n for n in nodes for lg in n['condition']['logics'] if lg['type'] == 'go_if_const']
```

---

## Step 3: Hardcode Check

- **`code` nodes** — look for hardcoded IDs, URLs, tokens
- **`api` nodes** — check URLs; must use `{{env_var[@name]}}`, not literals
- **`api_rpc` / `api_copy`** — check `conv_id` values; numeric IDs instead of `@alias` are a flag
- **`api_rpc` extra fields** — check for hardcoded values that should be variables

Flag each hardcoded value for extraction to env_var (see `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md`).

### Root-level process metadata — do NOT flag as hardcoded

The `.conv.json` file has top-level fields that are process metadata assigned by the platform. Do **not** report them as hardcoded values:

| Field | Description |
|-------|-------------|
| `conv_id` | The ID of this process itself |
| `user_id` | Owner/author user ID |
| `company_id` | Company/tenant identifier |
| `folder_id` | Folder identifier |
| `project_id` | Project identifier |
| `stage_id` | Stage/environment identifier |

These are read-only platform metadata, not configuration that should be extracted to env_vars.

---

## Step 4: Repeated Logic

- Compare similarly named nodes (e.g. multiple `CREATE ACTOR`, `MANAGE ACCESS RULES`)
- If structure is identical → mark as duplicated logic, recommend extracting into a subprocess

---

## Step 5: Cycle Verification

### Internal cycles

- Detect nodes with `semaphors` of type `time` or `go_if_const` that create loops
- Verify exit conditions exist and iteration limits are enforced

### Cross-process cycles

Any automatic action that causes a task to appear in another process is a potential cycle point. Before adding such a call, verify that the called process cannot return control back to the originating process — neither directly nor through a chain of intermediate processes — without user interaction.

A cycle is acceptable only if it contains an explicit break point: a user action, an iteration counter, or a timeout.

**Checklist for every automatic inter-process call (`api_copy`, `api_rpc`, `api`, or any other mechanism that creates a task in another process):**

1. Can the called process return a call to the originating process — directly or through a chain?
2. If yes — is there a break point?
3. If no break point — a bypass parameter or a different route is required.

**How to check** *(apply after Step 11 — outbound call data from Steps 10–11 is required)*:

1. Using the outbound dependency list from Step 10, collect all direct outbound calls.
2. For each dependency pulled in Step 11, collect *its* outbound calls (sub-dependencies listed there).
3. Continue tracing until either:
   - the originating process reappears in the chain → **cycle found**, or
   - every branch reaches a terminal node or a user-interaction gate → **safe**.

> ⚠️ Step 11 is explicitly 1-level deep and will not surface 3+-hop cycles on its own. For processes that act as dispatchers (high out-degree, parameter-based routing), always trace at least one level deeper manually before concluding no cycle exists.

Flag any chain where the originating process appears as a downstream target without a break point.

> **Execution note:** Record the checklist questions now (Step 5) and run the "How to check" trace at the end of Step 11, once all dependency schemes have been pulled.

Report format:

```markdown
## 4. Cycles

### Internal
- [ ] Node W: no exit condition → add iteration limit

### Cross-process
- [ ] conv_id X → conv_id Y → conv_id Z → back to this process without break point
  → add bypass parameter or change routing
```

---

## Step 6: Node Naming

- Identify nodes with empty `title`
- Check for duplicate or vague names
- Recommended format: `Action_Object_Context` (e.g. `Create_Stream_Active`)

---

## Step 7: Code Node Analysis

### JavaScript nodes — check for:

- `try/catch` wrapping all external calls
- No hardcoded values (IDs, tokens, URLs)
- Safe type conversions (`parseInt`, `Number`)
- Input validation (`if (!data.var) { ... }`)
- No `eval` usage

### Erlang nodes — check for:

- Pattern matching covers all cases
- `catch` or `case` for invalid data
- No recursion without termination conditions

### set_param optimization

Code nodes that only do simple assignments should be replaced with `set_param`. Flag these patterns:

| Pattern in code node                    | Replace with set_param                      |
|-----------------------------------------|---------------------------------------------|
| `data.x = data.y;`                      | `"x": "{{y}}"`                              |
| `data.x = data.a + "_" + data.b;`       | `"x": "{{a}}_{{b}}"`                        |
| `data.x = data.a + data.b;` (numeric)   | `"x": "$.math({{a}}+{{b}})"`                |
| `data.x = data.a * data.b;`             | `"x": "$.math({{a}}*{{b}})"`                |
| `data.x = "constant";`                  | `"x": "constant"`                           |
| `data.x = data.x;`                      | remove entirely (self-assignment, no-op)     |

`$.math()` takes exactly **two operands**. For 3+, nest: `$.math($.math({{a}}+{{b}})+{{c}})`. Supported operators: `+`, `-`, `*`, `/`. Use `extra_type: "number"` when the result should be numeric.

Operations that genuinely require a code node: `str.length`, regex, `JSON.parse/stringify`, array `.map/.filter`, complex `if/else`, object key iteration.

---

## Step 8: Semaphor Coverage

Check for missing semaphors by severity:

- 🔴 **`api_callback`** — MUST have a `time` semaphor. Without one tasks hang forever if the user abandons the session.
- 🟡 **`api`** (outbound HTTP) — Should have a `time` semaphor as safety net against unresponsive endpoints.
- 🟢 **`api_rpc`** — Lower severity; target process handles its own timeouts. Still recommended.
- 🟢 **`api_copy` with `is_sync: true`** — Informational; target process manages its own lifecycle.

---

## Step 9: Error Handling Review

- Every error node (obj_type 3) must transition to a final error node (obj_type 2)
- Every error reply node must have `throw_exception: true`
- Every success reply node must have `throw_exception: false`
- Each error node should have a meaningful `errorText`
- Detect duplicated error nodes with identical titles/messages

---

## Step 10: External Dependencies Inventory

Scan all nodes and collect every outbound reference:

1. **api_rpc** — unique `conv_id` values
2. **api_copy** — unique `conv_id` values
3. **State reads** — `conv[@alias]` references inside `set_param` extra values or condition parameters

Flag:
- ⚠️ Numeric `conv_id` without `@alias` — flag in the report; suggest a `short_name` derived from the process title (lowercase, hyphens). Do **not** call `create-alias` automatically — only create aliases when the user explicitly requests it.
- ⚠️ Same alias called with both create and modify modes
- ⚠️ More than 5 unique dependencies — note coupling risk
- ⚠️ `conv[@alias]` state reads — implicit dependencies that break if the referenced process changes schema

To manually verify unused set_param findings, search for each variable name across the `.conv.json` file — check all logics, extras, conditions, and semaphors.

---

## Step 11: Dependency Process Reviews

Perform a **1-level deep** review of all unique outbound dependencies. Review each direct dependency but do NOT recurse into their sub-dependencies — only list them.

For each dependency:

1. Collect all unique `conv_id` values from the main process
2. Pull the dependency process using MCP tool **`pull-process`** with `process_id` set to the `conv_id` value, then read the resulting `.conv.json`
3. Run a lightweight review covering:
   - Node count and type distribution
   - Untitled node count
   - JS/Erlang code nodes: `try/catch`, hardcoded values
   - API nodes missing semaphors
   - Hardcoded values in RPC extra fields / URLs
   - Sub-dependencies (list but do NOT recurse)
   - Flag processes with 200+ nodes as needing their own dedicated review

After pulling all dependencies, apply the **cross-process cycle trace** from Step 5 ("How to check"): use the sub-dependency lists collected above to trace whether any chain leads back to the originating process without a break point.

Report format:

```markdown
## Dependency Process Reviews

### @alias-name (conv_id=XXXXX) — "Process Title"

NN nodes. MM/NN untitled.

- [ ] ⚠️ X API nodes missing semaphors
- [ ] ⚠️ JS code without try/catch in node "Y"
- [ ] Sub-dependencies: @a, @b, 12345
- [ ] **Needs own dedicated review** (200+ nodes)

## Dependency Health Summary

| Dependency | Nodes | Untitled | Missing Semaphors | Hardcoded conv_ids | JS no try/catch | Needs Own Review |
|-----------|-------|----------|-------------------|-------------------|-----------------|--------------------|
| @alias    | 154   | 74       | 6                 | 0                 | 10              | —                  |
```

---

## Step 12: Dependency Graph

Based on the dependency data collected in Steps 10–11, produce a Mermaid diagram of direct process-to-process dependencies:

````markdown
```mermaid
graph TD
    MainProcess["Process Name"] --> Dep1["@alias-name (conv_id=XXXXX)"]
    MainProcess --> Dep2["@alias2 (conv_id=YYYYY)"]
    Dep1 --> SubDep1["@sub-alias"]
```
````

Include in the report:

```markdown
## 12. Dependency Graph

\`\`\`mermaid
graph TD
    ...
\`\`\`

N direct dependencies, N total processes mapped.
```

---

## Step 13: Generate Report

Produce a Markdown report:

```markdown
# Process Review: <process name>

## 1. Structural Issues (lint-process)

- [ ] 🔴 N orphaned nodes — list each: (id, title, type)
- [ ] ⚠️ No-op condition in node "X" (id) — all branches route to same node "Y"
- [ ] ⚠️ Unused set_param in node "Z" (id) — variable `{{var}}` not referenced downstream

## 2. Hardcode

- [ ] Node X: API key hardcoded → move to env_var

## 3. Repeated Logic

- [ ] Nodes Y, Z: identical structure → extract into subprocess

## 4. Cycles

### Internal
- [ ] Node W: no exit condition → add iteration limit

### Cross-process
- [ ] conv_id X → conv_id Y → conv_id Z → back to this process without break point
  → add bypass parameter or change routing

## 5. Naming

- [ ] Node without title → rename to "Validate Token"
- [ ] Duplicate titles "error manage access rules" → make unique

## 6. Code Review

- [ ] JS: Node "Code_123" has no try/catch → add error handling
- [ ] Erlang: Node "Code_456" has recursion without termination condition

## 7. Code Node Optimization (set_param migration)

- [ ] ⚠️ Node "X": `data.a = data.b + "__" + data.c` → set_param: `"a": "{{b}}__{{c}}"`
- [ ] ⚠️ Node "Y": `data.total = data.x + data.y` → set_param: `"total": "$.math({{x}}+{{y}})"`
- [ ] ⚠️ Node "Z": `data.x = data.x` → remove (self-assignment, no-op)

## 8. Semaphor Coverage

- [ ] 🔴 api_callback node "X" — missing time semaphor (tasks will hang)
- [ ] 🟡 api node "Y" — missing time semaphor (risk on unresponsive endpoint)

## 9. Error Handling

- [ ] Missing err_node_id on set_param in node "X"
- [ ] Duplicated error messages across nodes "Y", "Z"
- [ ] Node "Z" reply node missing throw_exception: true

## 10. External Dependencies

| # | Alias / conv_id | Call Type | Count | Usage Summary | Notes |
|---|----------------|-----------|-------|---------------|-------|
| 1 | @send-message  | api_rpc   | 10    | OTP prompt, errors, success | — |
| 2 | 21123          | api_copy  | 2     | Send report   | ⚠️ hardcoded numeric |

### State Store References

- `conv[@user-profile]` — reads language, registration_ban

## 11. Dependency Process Reviews

### @alias-name (conv_id=XXXXX) — "Process Title"

NN nodes. MM/NN untitled.

- [ ] ⚠️ X API nodes missing semaphors
- [ ] Sub-dependencies: @a, @b

## Dependency Health Summary

| Dependency | Nodes | Untitled | Missing Semaphors | Hardcoded conv_ids | JS no try/catch | Needs Own Review |
|-----------|-------|----------|-------------------|-------------------|-----------------|--------------------|
| @alias    | 154   | 74       | 6                 | 0                 | 10              | —                  |

## 12. Dependency Graph

```mermaid
graph TD
    ...
```

N direct dependencies, N total processes mapped.
```

---

## Reference Documents

Use the `Read` tool to load these files when specific node or validation details are needed:

| Path | When to read |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/code-node.md` | Code node details and available JS libraries |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/call-process-node.md` | Call a Process node, semaphores |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | HTTP API call configuration |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/error-handling.md` | Error handling patterns |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-json-validation.md` | Validation rules and common errors |

---

## Final Step: Update Git Context

> **Do NOT report the task as complete and do NOT stop until this step is evaluated.**
> The main task being deployed does not mean the session is over — this step is next.

Immediately after `push-process` or `create-process` succeeds, check whether
**at least one** of the following is true:

- `push-process` or `create-process` was actually called (not just previewed);
- a new external host/API/service appeared that is not yet in `dependencies.md`;
- an architectural decision was made (one approach chosen over another);
- an issue was found or closed during this session.

**If yes → activate `corezoid-git-context` skill right now.** Do not wait for the
user to ask. Do not skip because the main task "looks done".

**If none of the above → skip** and tell the user the session is complete.

The skill handles everything autonomously: reads current `_ext/docs/`, proposes
a unified diff, asks confirmation, writes files, and pushes — one invocation.
corezoid-state-diagram-create11.4 KB

View saved version →

---
name: corezoid-state-diagram-create
description: >
  Corezoid state diagram creation specialist. Use when the user wants to create
  a new Corezoid state diagram, build a state machine, design a status / lifecycle
  store, or set up a "state" object with conv_type "state". Activate when the
  user says "create a state diagram", "build a state machine", "new state diagram",
  "design states", "track status", "user lifecycle", "состояния", "стейт диаграмма",
  "создать state diagram", or mentions storing state-by-ref between processes.
---

# Create a New Corezoid State Diagram

You are a specialist in creating Corezoid **state diagrams** (`conv_type: "state"`) using the `corezoid` MCP server.

A state diagram is a long-lived data store: each task is one entity's state, referenced by a stable `ref`. Other processes read, create, and modify these state tasks. The state diagram itself only contains states (parked tasks), transitions between them, and a tiny subset of helper nodes.

Before you start, make sure you understand:
- A state diagram has `conv_type: "state"` at the root (not `"process"`).
- Only 10 node types are allowed: Start, Condition, Code, Set Parameters, Copy Task, Modify Task, Set State (= a state node), Delay, Queue, End: Success, End: Error.
- API Call, Call a Process, Reply to Process, DB Call, Git Call, Sum, API Form are **forbidden** inside a state diagram — they belong in the driver process.

Read `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-overview.md` if you need a refresher on the model.

---

## Step 1: Gather Requirements

Ask the user for the following before proceeding:

- **What entity is being tracked?** (user, order, device, subscription, account, …)
- **What is the `ref`?** — the stable identifier (e.g. `userId`, `orderId`). This is the lookup key every reader and writer will use.
- **List the states.** A name and a one-line description for each (e.g. `Pending`, `Active`, `Suspended`, `Closed`).
- **List the transitions.** For every state, what data change causes it to move to which other state? (e.g. `Active → Suspended when status == "suspended"`).
- **Initial state.** Which state does a newly created task enter first?
- **Terminal states.** Which states are "End: Success" / "End: Error", if any?
- **Side effects on entry / exit (optional).** Should entering a state trigger a notification, stamp a timestamp, etc.? (These are Copy Task / Modify Task nodes between states.)

If any of the above is missing, ask the user before continuing.

---

## Step 2: Create the Empty State Diagram

Call MCP tool **`create-state-diagram`** with:
- `folder_path`: Relative path to the folder directory. Omit to use the current directory.
- `process_name`: the state diagram name.

This creates an empty state diagram in Corezoid with `conv_type: "state"` and writes its skeleton JSON to `<ID>_<Name>.conv.json` inside `folder_path`. The returned file path is `PROCESS_PATH` — all subsequent steps use it.

> ⚠️ Always verify `folder_path` points to the intended target folder. Omitting it places the diagram in the project root, which may not be the correct location.

> ⚠️ Open the new file and confirm `"conv_type": "state"` at the root before doing anything else. The push pipeline now accepts both `"process"` and `"state"`, but if `conv_type` is accidentally `"process"`, the next push will redeploy it as a regular process.

**Already exists in Corezoid?** If the user pre-created the diagram in the Corezoid UI, pull it instead: call MCP tool **`pull-process`** with `process_id: <id>`. `pull-process` works for both processes and state diagrams — the resulting file preserves `conv_type: "state"`.

---

## Step 3: Design the State Diagram Structure

A state diagram is structured as:

| # | Node | obj_type | Purpose |
|---|------|----------|---------|
| 1 | Start | 1 | Entry — routes a newly-created task to its initial state |
| 2 | _(optional)_ Set Parameters / Code | 0 | Compute / normalise data on entry |
| 3 | One state node per state | 0 (logic begins with `api_callback`) | Park the task until externally modified |
| 4 | _(optional)_ Copy Task / Modify Task between states | 0 | Side effects on transition |
| 5 | _(optional)_ Delay node | 0 | Time-bounded states (e.g. trial expiry) |
| 6 | End: Success | 2 | Terminal state for "happy" closure |
| 7 | End: Error | 2 | Terminal state for failure closure |

### State node anatomy (memorise this shape)

```json
{
  "id": "<24-hex>",
  "obj_type": 0,
  "condition": {
    "logics": [
      { "type": "api_callback" },
      {
        "type": "go_if_const",
        "to_node_id": "<other_state_id>",
        "conditions": [
          { "param": "status", "const": "blocked", "fun": "eq", "cast": "string" }
        ]
      },
      { "type": "go", "to_node_id": "<self_id>" }
    ],
    "semaphors": []
  },
  "title": "Active",
  "x": 880, "y": 400,
  "extra": "{\"modeForm\":\"expand\",\"icon\":\"state\"}",
  "options": null
}
```

Key invariants for every state node:
- **First logic is `api_callback`** (with no other fields). This is what "parks" the task.
- One `go_if_const` per outbound transition. Order matters — first match wins.
- **Last logic is `go` pointing back to the node's own id** (the "stay here" fallback).
- `extra` must include `"icon":"state"` so the UI renders the state pill correctly.
- Do not add `err_node_id` — `api_callback` does not surface the regular error path.

---

## Step 4: Generate the State Diagram JSON

Produce a valid `.conv.json` file with the following root envelope:

```json
{
  "obj_type": 1,
  "obj_id": <id from step 2>,
  "parent_id": <folder_id>,
  "title": "<State Diagram Name>",
  "description": "",
  "status": "active",
  "params": [],
  "ref_mask": true,
  "conv_type": "state",
  "scheme": {
    "nodes": [],
    "web_settings": [[], []]
  }
}
```

### Core rules

- `conv_type` **must** be `"state"`.
- Node IDs are 24-character hex: `^[0-9a-f]{24}$`. Generate with `crypto.randomBytes(12).toString('hex')` or any equivalent.
- Connect nodes only through the `go` / `go_if_const` `to_node_id` fields.
- Every node that uses logic with `err_node_id` (Code, Set Parameters, Copy Task, Modify Task, Queue) must point at a dedicated End: Error node.
- Use descriptive node `title` values — they are the state names visible on the canvas and in dashboards.
- Layout: spread states **horizontally** (different `x` per state), keep the Start above them. State nodes sit around `y ≈ 400`, Start at `y = 100`. Increment `x` by ≈ 320–400 between adjacent states. Place End nodes at the bottom (`y ≈ 700–900`).

### Allowed logics inside a state diagram

| Node | Logic `type` | Notes |
|---|---|---|
| Start | `go` (`obj_type: 1`) | Exactly one per diagram |
| State (Set State) | `api_callback` + `go_if_const`s + self-`go` | The structural heart of the diagram |
| Condition | `go_if_const` | For pre-state routing |
| Code | `api_code` | Avoid unless necessary; prefer `set_param` |
| Set Parameters | `set_param` | Compute / stamp fields |
| Copy Task | `api_copy` with `mode: "create"` | Fan out to another process (notifications, audit) — **not** to write back to this same diagram |
| Modify Task | `api_copy` with `mode: "modify"` | Update a task by `ref` in some target process — note: in-place edits to the current task should use `set_param` instead |
| Delay | semaphor-only | Time-bounded states |
| Queue | `api_queue` | Ordered / throttled processing |
| End | (`obj_type: 2`) | Terminal node (success or error icon) |

### Variables for constants

If a node references an external id (e.g. another process to notify), store it as a Corezoid variable and reference it as `{{env_var[@variable-name]}}` — never hardcode. Use **`cz-variables`** with `action: "create-variable"` if the variable does not yet exist. See `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md`.

### Common pitfalls

- Forgetting `"icon":"state"` in `extra` for a state node — the node renders as a plain logic node.
- Missing the trailing self-loop `go` on a state node — the task escapes the state on every callback.
- Putting an API Call, Call a Process, or Reply to Process node in a state diagram — these are **forbidden**. Move them into the driver process.
- Using `api_copy mode: "modify"` from inside the state diagram targeting its own ref — that creates an infinite re-callback loop. Use `set_param` to update the current task in place instead.
- Raw JSON objects as `extra` / `data` values — must be stringified (`"{\"k\":\"v\"}"`).

---

## Step 5: Validate with Lint

Call MCP tool **`lint-process`** with `process_path: "<PROCESS_PATH>"`.

Fix every reported error and re-run until the output is clean. Do not proceed with lint errors.

> If the linter complains about a forbidden logic (`api`, `api_rpc`, `api_rpc_reply`, `db_call`, `git_call`, `api_sum`, `api_form`), remove the node and re-design the side effect to live in the driver process.

---

## Step 6: Deploy

Call MCP tool **`push-process`** with `process_path: "<PROCESS_PATH>"`.

If the push fails:
- Re-read the file and confirm `"conv_type": "state"` is present at the root.
- Confirm every state node ends in `go → self`.
- Confirm only allowed logic types are present.

After a successful push, notify the user:

> "State diagram deployed. Refresh the Corezoid page to see the new diagram. To start using it, create the driver process that calls `api_copy mode:create` with a `ref` to add entities, and `mode:modify` to drive transitions."

---

## Step 7 (optional): Build the Driver Process

A state diagram is useless without a driver process that creates and modifies its tasks. If the user has not already built one, offer to:

1. Hand off to `/corezoid-create` to scaffold the driver process.
2. Wire it with three node patterns:
   - **Read state:** `set_param` with `{{conv[<sd_id>].ref[{{ref}}].<field>}}`
   - **Create state task:** `api_copy` with `conv_id: <sd_id>`, `ref: {{<ref>}}`, `mode: "create"`, `data: {...}`
   - **Modify state task:** `api_copy` with `conv_id: <sd_id>`, `ref: {{<ref>}}`, `mode: "modify"`, `data: {...}`

Read `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-process-interaction.md` for full templates.

Recommend creating an alias (`/corezoid-alias-manager`) for the state diagram so the driver references `@user-states` instead of a numeric id.

---

## Reference Documents

| Path | When to read |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-overview.md` | Concepts, allowed nodes, root structure |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-node-structures.md` | Canonical JSON for every allowed node type |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-process-interaction.md` | How driver processes read / create / modify state tasks |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-state-node.md` | Background on Set State (legacy `obj_type:3` form) and the `{{conv[...]}}` template |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/copy-task-node.md` | Error catalogue for `api_copy` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/condition-node.md` | `go_if_const` reference |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variables (`{{env_var[@…]}}`) |

## Example Files

| Path | Description |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/samples/state-diagrams/user-status-state-diagram.conv.json` | Minimal two-state diagram (`Active` ⇄ `Inactive`) |
| `${CLAUDE_PLUGIN_ROOT}/samples/state-diagrams/user-status-driver-process.conv.json` | Companion driver process that reads + modifies the state |
corezoid-state-diagram-edit9.63 KB

View saved version →

---
name: corezoid-state-diagram-edit
description: >
  Corezoid state diagram editing specialist. Use when the user wants to modify,
  update, or fix an existing Corezoid state diagram — add or remove a state, change
  a transition, add a side effect on transition, fix the wiring of an api_callback
  state node, or rework transitions. Activate when the user says "edit a state
  diagram", "add a state", "remove a state", "change transitions", "fix state
  diagram", "update state machine", "поправить state diagram", "изменить состояния",
  or refers to modifying a .conv.json with conv_type "state".
---

# Edit an Existing Corezoid State Diagram

You are a specialist in modifying Corezoid **state diagrams** (`conv_type: "state"`) using the `corezoid` MCP server.

A state diagram is a long-lived data store; modifying it means changing the set of states, the data conditions that drive transitions between them, or the side effects performed on transition. The driver processes that read / write the diagram are usually edited separately via `/corezoid-edit`.

Read `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-overview.md` for a refresher on the model before editing.

---

## Identify the State Diagram (MANDATORY FIRST STEP)

**Before doing anything else**, resolve `PROCESS_PATH`:

1. Check whether the user already provided an identifier — a file path, state diagram name, or numeric id — in the current message or conversation history.
2. If no identifier is provided, ask:
   > "Please specify the state diagram — a file path (e.g. `1863140_User_Status.conv.json`), a name, or a state diagram id."
   Do **not** call any MCP tools until the user provides one.
3. If the user gives a **name or id**, search the local working directory for the matching `.conv.json` using `find` / `grep`.
4. Open the file and **confirm `"conv_type": "state"`** at the root. If `conv_type` is `"process"`, this is a regular process — hand off to `/corezoid-edit` instead.
5. Once `PROCESS_PATH` is confirmed, analyze the file before changing anything.

---

## Step 1: Analyze the State Diagram

Read the file and map out:

- The list of state nodes (every node with `obj_type: 0` whose first logic is `api_callback`). Note each state's `id`, `title`, and outbound `go_if_const` transitions.
- The Start node and which state it routes to (the initial state).
- Any helper nodes between states (Set Parameters, Code, Modify Task, Copy Task, Delay, Queue).
- Terminal nodes (`obj_type: 2`).

> 🔍 If you see `obj_type: 3` state nodes, you are looking at the legacy state-node format. The current Corezoid format uses `obj_type: 0` with `api_callback` as the first logic. Convert old nodes to the new format only if the user explicitly asks — otherwise leave them as-is and edit in place.

Make sure you also locate any **driver processes** that reference this state diagram (search the project for `conv[<sd_id>]`, `conv[@<alias>]`, and `api_copy` nodes with `conv_id: <sd_id>`). When editing, you may need to update those drivers too.

---

## Step 2: Plan the Edit

Categorise the change before touching JSON:

| Change type | What to touch |
|---|---|
| Add a new state | Insert a new `obj_type: 0` state node with `api_callback` + transitions + self-loop. Wire at least one inbound transition from an existing state's `go_if_const`. |
| Remove a state | Delete the node and re-route every inbound transition that pointed to it. Search the file for `to_node_id: "<deleted_id>"`. |
| Change a transition condition | Edit the `conditions` array of the relevant `go_if_const` in the source state. |
| Re-target a transition | Change `to_node_id` of the relevant `go_if_const`. |
| Add a side effect on transition | Insert a Modify Task / Copy Task / Set Parameters node between the source state and the target state. Update the source state's `go_if_const.to_node_id` to point at the new helper, and have the helper `go` to the original target. |
| Rename a state | Change `title` only — the `id` must stay the same to preserve drivers that reference it. |
| Add an alias | Hand off to `/corezoid-alias-manager`. |

---

## Step 3: Apply Changes

Edit `PROCESS_PATH` directly.

### Core rules

- Connect nodes only through `go` / `go_if_const` `to_node_id` fields.
- Every node that has `err_node_id` (Code, Set Parameters, Copy Task, Modify Task, Queue) must point at a real End: Error node — never to a state node.
- Node ids are 24-character hex: `^[0-9a-f]{24}$`. For new nodes, generate fresh ids; **never reuse** an old id, even if its node was deleted.
- Use descriptive `title` values — they are the state names visible on the canvas and dashboards.
- Layout: keep states roughly on the same `y` lane (≈ 400); increment `x` by ≈ 320–400 between adjacent states. Start at `y = 100`. End nodes at the bottom.

### State node invariants (do not break these)

For every state node:

- `obj_type: 0`
- First logic is exactly `{ "type": "api_callback" }` (no extra fields)
- Every outbound transition is a `go_if_const` between the `api_callback` and the trailing `go`
- The final logic is `{ "type": "go", "to_node_id": "<self_id>" }` — the "stay here" fallback
- `extra` contains `"icon":"state"`
- No `err_node_id` on `api_callback`

If you add or modify transitions, the order matters — **first matching `go_if_const` wins**. Put more specific conditions first.

### Allowed logics

Only these logics may appear inside a state diagram. Adding anything else will fail validation on push.

| Allowed | Type |
|---|---|
| Start | `go` (`obj_type: 1`) |
| State | `api_callback` + `go_if_const`s + self-`go` |
| Condition | `go_if_const` |
| Code | `api_code` |
| Set Parameters | `set_param` |
| Copy Task (fan-out) | `api_copy` with `mode: "create"` |
| Modify Task (by ref) | `api_copy` with `mode: "modify"` |
| Delay | semaphor-only |
| Queue | `api_queue` |
| End | (`obj_type: 2`) |

**Forbidden:** `api`, `api_rpc`, `api_rpc_reply`, `db_call`, `git_call`, `api_sum`, `api_form`. If the user asks to add one of these, push back: move the side effect into the driver process and explain why.

### Common pitfalls

- Adding a state but forgetting to wire any inbound transition → unreachable state.
- Deleting a state but leaving a `go_if_const` somewhere with `to_node_id` pointing to the deleted id → push will fail with "unknown node".
- Reordering transitions accidentally: the **first matching `go_if_const` wins**, so order is semantically meaningful.
- Using `api_copy mode: "modify"` from inside the state diagram targeting its own ref — that re-triggers `api_callback` and can loop. Use `set_param` to update the current task in place instead.
- Forgetting to update the driver process when you rename a state and the driver compares against its name (e.g. `{{conv[…].ref[…].status}} == "Active"`). The state name is `title`; the driver compares against a stored **value**, not the title — confirm with the user which they're checking.

---

## Step 4: Deploy the Changes

**MANDATORY: Always push after any change — even if work is in-flight. Without push, the changes exist only on disk.**

Call MCP tool **`push-process`** with `process_path: "<PROCESS_PATH>"`.

If push fails:
- Re-read the file and confirm `"conv_type": "state"` is still at the root (a stray editor save or auto-format may have flipped it).
- Lint with **`lint-process`** to localise the issue.
- Confirm every state node still ends in `go → self_id`.
- Confirm no forbidden logic types were introduced.

After a successful push, notify the user:

> "State diagram updated. Refresh the Corezoid page to see the new states / transitions. Any tasks already parked in renamed states keep their `id` references intact, but tasks parked in **deleted** states are now stranded — check the workspace before deleting a populated state."

> ⚠️ **Live data warning:** Unlike regular processes, a state diagram usually has **live tasks parked in its states**. Deleting or restructuring a state can strand those tasks. Before deleting a state, ask the user whether they want to migrate parked tasks first (e.g. by modifying their `ref` so they transition out of the doomed state).

---

## Step 5 (optional): Update Driver Processes

If your edit changed the **observable interface** of the state diagram — added a new field that drivers should now read, renamed a field drivers compare against, or removed a state drivers used to detect — hand off to `/corezoid-edit` for each driver process that needs updating.

To find driver processes affected by the edit, search the project:

```
grep -rn "conv\[<sd_id>\]" .
grep -rn "conv_id\": <sd_id>" .
grep -rn "conv\[@<alias>\]" .
```

---

## Reference Documents

| Path | When to read |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-overview.md` | Concepts, allowed nodes, root structure |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-node-structures.md` | Canonical JSON for every allowed node type |
| `${CLAUDE_PLUGIN_ROOT}/docs/state-diagrams/state-diagram-process-interaction.md` | How driver processes read / create / modify state tasks |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-state-node.md` | Background and the `{{conv[...]}}` template |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/copy-task-node.md` | Error catalogue for `api_copy` |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/condition-node.md` | `go_if_const` reference |
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Variables (`{{env_var[@…]}}`) |

## Example Files

| Path | Description |
|---|---|
| `${CLAUDE_PLUGIN_ROOT}/samples/state-diagrams/user-status-state-diagram.conv.json` | Minimal two-state diagram (`Active` ⇄ `Inactive`) |
| `${CLAUDE_PLUGIN_ROOT}/samples/state-diagrams/user-status-driver-process.conv.json` | Companion driver process |
corezoid-variable-manager15 KB

View saved version →

---
name: corezoid-variable-manager
description: >
  Manages Corezoid environment variables (env_var) — create, list, modify, delete, and
  use variables in process JSON. Activate when the user mentions "variable", "env var",
  "environment variable", "secret", "create variable", "list variables", "delete variable",
  "modify variable", "env_var", "{{env_var", or asks how to store a URL, token, API key,
  or any constant that should not be hardcoded in a process. Also activate when a process
  references {{env_var[@name]}} and the variable does not exist yet.
---

# Corezoid Variable Manager

## How to call these tools

Every operation in this skill is an **action of the single `cz-variables` MCP tool** —
the individual names below are action strings, not tools of their own:

```
cz-variables {"action": "list-variables"}
```

Arguments always go inside `args`; the shorthand used in the examples below — `create-variable(name="payment-api-url", …)` — means exactly that call. When unsure about an action's
arguments, call `cz-variables {"action": "<action>", "help": true}` — it returns the
full schema and runs nothing.

## What variables are

Environment variables store constants (URLs, tokens, API keys, IDs, configuration values)
that must not be hardcoded in process logic. The reference syntax `{{env_var[@name]}}` is
resolved at runtime — changing a variable value takes effect immediately without
redeploying any process.

Variables are **stage-scoped**: shared across all processes within a stage.

---

## Variable types

### By data type

| `data_type` | When to use | Value format |
|-------------|-------------|--------------|
| `raw` | Plain string (URL, token, ID, any scalar) | `"https://api.example.com"` |
| `json` | Structured config, multi-field config, feature flags | `{"key":"value","nested":{...}}` |

### By visibility

| `env_var_type` | UI display | Accessible from | Scopes |
|----------------|------------|-----------------|--------|
| `visible` | Value shown in plain text | All node types | `[{"type":"*","fields":"*"}]` |
| `secret` | Value masked, shows only fingerprint | API Call nodes only | `[{"type":"api_call","fields":"*"}]` |

> ⚠️ **Secret variables** are designed for tokens, passwords, and API keys. They are
> never returned in plain text by the API after creation — only an MD5/SHA256 fingerprint
> is available. Use `visible` for non-sensitive configuration.

---

## Actions of `cz-variables`

| Action | Purpose |
|--------|---------|
| `create-variable` | Create a `raw` + `visible` variable in one step |
| `list-variables` | List a stage's variables with obj_id, types, values (secrets masked) |
| `modify-variable` | Change value/title/data_type or rename — dry-run + confirm-gated |
| `delete-variable` | PERMANENTLY delete (no recycle bin) — dry-run + confirm-gated |

> **Note:** creating `secret` or `json` variables is not yet exposed as an action —
> use the direct API calls documented below for creation; manage them afterwards with
> the actions above.

## Double-confirmation etiquette (modify / delete)

`modify-variable` and `delete-variable` are consequential: a deleted variable is gone
FOREVER (env vars have NO recycle bin), a renamed one breaks every
`{{env_var[@old-name]}}` reference, and a changed value takes effect immediately in
running processes. The actions enforce a two-step gate, and you must drive it honestly:

1. Call the action WITHOUT `apply` — you get a dry-run: a current → new diff (modify) or
   a red `🔴 PERMANENT DELETION` block (delete), including a local reference scan.
2. Show that dry-run output to the user **verbatim** — do not summarize away the
   warnings, especially the red block and the list of files that still reference the
   variable.
3. Ask the user explicitly whether to proceed, and wait for their clear agreement in
   the conversation.
4. Only then re-run with `apply=true` and the exact `confirm="<short_name>#<obj_id>"`
   from the dry-run output. Never fabricate the confirm string without steps 1–3, and
   never treat an earlier, unrelated "yes" as agreement for this action.

Server facts the tools rely on (verified live): modify is PARTIAL — omitted fields
keep their value, so modifying a secret's title does not require (or touch) its value;
`env_var_type` (visible/secret) can NOT be changed after creation — the server
silently ignores such attempts; delete requires project_id + stage_id and is
irreversible.

---

## Using variables in process JSON

Once a variable exists, reference it with `{{env_var[@short-name]}}` anywhere a value
is expected.

### API Call node — URL field
```json
{
  "type": "api",
  "url": "{{env_var[@payment-api-url]}}/charge",
  "method": "POST",
  "extra_headers": {},
  "max_threads": 5,
  "err_node_id": "<error_node_id>"
}
```

### API Call node — header field
```json
{
  "type": "api",
  "url": "{{env_var[@payment-api-url]}}/charge",
  "method": "POST",
  "extra_headers": {},
  "max_threads": 5,
  "extra": { "Authorization": "Bearer {{env_var[@payment-api-token]}}" },
  "extra_type": { "Authorization": "string" },
  "err_node_id": "<error_node_id>"
}
```

### Set Parameters node
```json
{
  "type": "set_param",
  "extra": {
    "baseUrl": "{{env_var[@service-url]}}",
    "token":   "{{env_var[@service-token]}}"
  },
  "extra_type": {
    "baseUrl": "string",
    "token":   "string"
  },
  "err_node_id": "<error_node_id>"
}
```

### Call a Process node — passing variable as parameter
```json
{
  "type": "api_rpc",
  "conv_id": "@target-process",
  "extra": { "endpoint": "{{env_var[@service-endpoint]}}" },
  "extra_type": { "endpoint": "string" },
  "err_node_id": "<error_node_id>"
}
```

### Condition node (`go_if_const`)

Variable references work in condition expressions as both the left-hand value and the
comparison value:

```json
{
  "type": "go_if_const",
  "conditions": [
    {
      "fun": "equal",
      "arg": "{{env_var[@feature-flag]}}",
      "val": "enabled"
    }
  ],
  "to_node_id": "<next_node_id>"
}
```

### Code node — variables must be pre-loaded via set_param
Variables are not directly accessible inside `api_code` JavaScript. First assign them
to task fields using a `set_param` node upstream, then read via `data.*` in code:
```javascript
// In set_param upstream: "apiUrl": "{{env_var[@my-api-url]}}"
var url = data.apiUrl + "/endpoint";
```

---

## Naming rules

- Only lowercase letters `[a-z]`, digits `[0-9]`, and hyphens `-`
- Name and description must be **at least 3 characters**
- Must be unique within the stage
- Good: `stripe-secret-key`, `payment-api-url`, `db-host-prod`
- Bad: `URL`, `TOKEN`, `x`, `My_Var`

---

## Local cache files

Two files store variable information locally. Check **both** before creating a new variable:

| File | Created by | Contains |
|------|------------|---------|
| `_ENV_VARS_.json` | `pull-folder` (ZIP export from Corezoid) | All variables in the stage |
| `.processes/variables.json` | the `create-variable` action | Only variables created in this session |

If neither file exists, run `pull-folder` or call the list API (see below) to get the
current state.

---

## Workflow: Create a visible raw variable (MCP tool)

### Step 1 — Check if variable already exists

Read `_ENV_VARS_.json` (or `.processes/variables.json`) and search for the `short_name`.
If found, reuse it — do not create a duplicate.

### Step 2 — Create the variable

Call **`cz-variables`** with `action: "create-variable"` and these `args`:
- `name`: the `short_name` (kebab-case, e.g. `stripe-api-key`)
- `description`: human-readable label (min 3 chars), used as `title` in the API
- `value`: the actual value

```
cz-variables {"action": "create-variable", "args": {
  "name": "payment-api-url",
  "description": "Payment Service Base URL",
  "value": "https://api.payments.example.com"
}}
```

The action creates the variable in Corezoid and appends it to `.processes/variables.json`.

### Step 3 — Reference in process JSON

Use `{{env_var[@payment-api-url]}}` wherever this value is needed.

---

## Workflow: Create a secret variable (direct API)

Use when storing tokens, passwords, API keys — values that must be masked in the UI.

```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type":         "create",
    "obj":          "env_var",
    "obj_type":     0,
    "status":       "active",
    "data_type":    "raw",
    "env_var_type": "secret",
    "title":        "Stripe Secret Key",
    "short_name":   "stripe-secret-key",
    "description":  "",
    "value":        "sk_live_...",
    "company_id":   "<WORKSPACE_ID>",
    "project_id":   <PROJECT_ID>,
    "stage_id":     <STAGE_ID>,
    "scopes":       [{"type": "api_call", "fields": "*"}]
  }]
}
```

Response: `{ "obj_id": 2192, "proc": "ok", "fingerprints": [...] }`

> The value is never returned after creation. Store it securely before calling this API.

---

## Workflow: Create a JSON variable (direct API)

Use when a variable holds a structured config object or array.

```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type":         "create",
    "obj":          "env_var",
    "obj_type":     0,
    "status":       "active",
    "data_type":    "json",
    "env_var_type": "visible",
    "title":        "Service Config",
    "short_name":   "service-config",
    "description":  "",
    "value":        "{\"host\":\"db.example.com\",\"port\":5432,\"name\":\"prod\"}",
    "company_id":   "<WORKSPACE_ID>",
    "project_id":   <PROJECT_ID>,
    "stage_id":     <STAGE_ID>,
    "scopes":       [{"type": "*", "fields": "*"}]
  }]
}
```

> The `value` field must be a **JSON string** (the JSON content encoded as a string).
> A secret JSON variable uses `"env_var_type": "secret"` and
> `"scopes": [{"type": "api_call", "fields": "*"}]`.

---

## Workflow: List variables (direct API)

```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type":       "list",
    "obj":        "env_var",
    "sort":       "date",
    "order":      "asc",
    "id":         "<WORKSPACE_ID>",
    "company_id": "<WORKSPACE_ID>",
    "project_id": <PROJECT_ID>,
    "stage_id":   <STAGE_ID>
  }]
}
```

**Response fields per variable:**

| Field | Description |
|-------|-------------|
| `obj_id` | Numeric ID (needed for modify/delete) |
| `short_name` | The `@name` used in `{{env_var[@name]}}` |
| `title` | Human-readable display label |
| `data_type` | `raw` or `json` |
| `env_var_type` | `visible` or `secret` |
| `value` | Actual value (empty for `secret` after creation) |
| `fingerprints` | MD5 + SHA256 hashes — use to detect value changes |
| `scopes` | Access scope rules |
| `create_time` / `change_time` | Unix timestamps |
| `uuid` | Variable UUID |

---

## Workflow: Modify a variable (direct API)

Modify updates all mutable fields in one call. Always send the full payload — partial
updates are not supported.

```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type":         "modify",
    "obj":          "env_var",
    "obj_id":       <VAR_OBJ_ID>,
    "data_type":    "raw",
    "env_var_type": "visible",
    "title":        "Updated Display Title",
    "short_name":   "new-short-name",
    "description":  "",
    "value":        "new-value",
    "company_id":   "<WORKSPACE_ID>",
    "project_id":   <PROJECT_ID>,
    "stage_id":     <STAGE_ID>,
    "scopes":       [{"type": "*", "fields": "*"}]
  }]
}
```

> ⚠️ Changing `short_name` invalidates all `{{env_var[@old-name]}}` references across
> every process in the stage. After renaming, grep all `.conv.json` files for the old
> name and update them, then `push-process` each affected file.

---

## Workflow: Delete a variable (direct API)

> ⚠️ Before deleting, verify no process references `{{env_var[@short-name]}}`.
> `push-process` validates env_var references and will fail if the variable is missing.

```bash
# Check which processes reference this variable
grep -r "env_var\[@variable-name\]" . --include="*.conv.json"
```

```
POST {corezoid_url}/api/2/json
Authorization: Simulator {access_token}
Content-Type: application/json

{
  "ops": [{
    "type":       "delete",
    "obj":        "env_var",
    "obj_id":     <VAR_OBJ_ID>,
    "company_id": "<WORKSPACE_ID>",
    "project_id": <PROJECT_ID>,
    "stage_id":   <STAGE_ID>
  }]
}
```

---

## Resolving environment values

When calling the `create-variable` / `modify-variable` / `delete-variable` / `list-variables` actions you do **not** need to look up `stage_id` or `project_id` — MCP resolves both from the `<id>_<name>.stage.json` marker at the workspace root.

For **direct** `/api/2/json` calls (the raw workflows below) you need the values explicitly:

| Value | Where to find it |
|-------|------------------|
| `company_id` | `workspace_id` field in current Folder in `~/.corezoid/config.json` |
| `stage_id` | `obj_id` in `<id>_<name>.stage.json` at the workspace root |
| `project_id` | `parent_id` in the same `<id>_<name>.stage.json` |
| API URL | `corezoid_url` field in current Folder |
| Access token | `access_token` field in current Folder |
| `obj_id` of variable | List API response, or `_ENV_VARS_.json` |

If the marker file is missing, run the `corezoid-init` skill.

---

## Runtime behaviour

- Variables are resolved **before** the node executes — the `{{env_var[@name]}}` token
  is replaced with the live value at the moment the task reaches that node
- Updating a variable value takes effect **immediately** — no process redeploy needed
- `push-process` validates all `{{env_var[@name]}}` references: if the variable does not
  exist in the stage, deployment fails with an error

---

## Common pitfalls

| Mistake | Correct approach |
|---------|-----------------|
| `{{env_var[payment-url]}}` — missing `@` | `{{env_var[@payment-url]}}` — `@` is required |
| `{{env_var[@Payment-URL]}}` — uppercase | `{{env_var[@payment-url]}}` — always lowercase |
| Storing secrets as `visible` variables | Use `env_var_type: "secret"` for tokens and passwords |
| Trying to read a `secret` variable in a Code node | Secret variables are only accessible from `api` (API Call) nodes |
| Duplicate variable creation | Always read `_ENV_VARS_.json` or call the list API first |
| Renaming `short_name` without updating process files | Grep all `.conv.json`, update references, push each changed process |
| Deleting a variable used by active processes | `push-process` will fail; remove all references first |
| Passing large JSON config as raw string | Use `data_type: json` for structured values |

---

## Reference Documents

| Path | When to read |
|------|-------------|
| `${CLAUDE_PLUGIN_ROOT}/docs/variables-guide.md` | Naming rules and usage examples (quick reference) |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/set-parameters-node.md` | How `set_param` feeds variables into task data for Code nodes |
| `${CLAUDE_PLUGIN_ROOT}/docs/nodes/api-call-node.md` | How variables are used in URL, headers, and body fields |
| `${CLAUDE_PLUGIN_ROOT}/docs/process/process-json-validation.md` | How `push-process` validates `{{env_var[@name]}}` references |
Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package license
MIT
Package author
Corezoid
Keywords
See publisher keywords

Declared capabilities

  • Create and deploy processes as .conv.json files
  • Pull and push processes to Corezoid workspace
  • Lint and validate process structure locally
  • Run and inspect tasks on deployed processes
  • Manage folders, aliases, and environment variables
  • Build and maintain messenger bots over existing processes

Package observed Oct 10, 2026.

Technical details
First seen
Oct 9, 2026 · 00:00 UTC
Last seen
Oct 10, 2026 · 18:00 UTC
Collection status
Collected

plugin_asdk_app_6ac4c3b919f48191a49821b96bdc33a2

Download plugin data (JSON)

Before you connect Corezoid

How do I connect it?

Open the publisher's marketplace listing to check current availability and follow its connection instructions. This directory does not install plugins. Check the requested access and any account requirements before connecting.

Check marketplace availability ↗

Does it require paid access?

We have not established the pricing or subscription requirements for this plugin. An absent price does not mean free access.

Compare researched pricing and access models →

How can I evaluate it?

Check the declared skills and available files, then try a small task whose result you can verify. Our archived descriptions and instructions establish publisher claims, not tested runtime quality. Review sources and coverage limits.