# bindings_v2.json Sync

Shared procedure for keeping `bindings_v2.json` in sync after any plugin writes to the bindings array in `caseplan.json`.

Bindings live at top-level `bindings[]` in `caseplan.json`. Output `bindings_v2.json` shape is independent of the source.

## When to Run

**Batched, not per-task.** `bindings_v2.json` is only consumed by `uip solution resources refresh` (which runs once before upload/debug). No intermediate step reads it. Regenerating after every task wastes Read→convert→Write cycles on a growing file.

Run at these three points only:

1. **End of Phase 2 Step 9** (after all non-connector tasks written) — covers all process/agent/rpa/action/api-workflow/case-management bindings
2. **End of Phase 3 Step 9.7** (after all connector tasks populated) — adds Connection bindings + populates IS cache for tasks
3. **End of Phase 3 Step 10.5** (after Phase 2 connector-rule stubs are upgraded across the 4 scopes — stage-entry, stage-exit, case-exit, task-entry) — adds Connection bindings + populates IS cache for resolved rules. Phase 2 stubs add no bindings; without this sync, rule-introduced Connection/Folder bindings + IS-cache entries would be absent when `resource refresh` runs.

Individual task / rule plugins write bindings to `caseplan.json` per-target as normal (top-level `bindings[]`). The batch regeneration reads the full bindings array once and converts everything in one pass.

---

## § Regenerate bindings_v2.json

After writing bindings to top-level `bindings[]`, regenerate `bindings_v2.json`. This file uses a **different format**: `caseplan.json` stores two entries per resource (one per property), `bindings_v2.json` stores one entry per resource with properties nested under `value`.

### Procedure

1. Read top-level `bindings[]` from `caseplan.json`
2. Group bindings by `resourceKey` — entries sharing the same key belong to one resource
3. For each group, produce one resource entry using the shapes below
4. Write the full file (always overwrite, never append) to `<SolutionDir>/<ProjectName>/bindings_v2.json`

### Non-connector resource entry

```json
{
  "resource": "<resource>",
  "key": "<resourceKey>",
  "value": {
    "name": { "defaultValue": "<name binding default>" },
    "folderPath": { "defaultValue": "<folderPath binding default>" }
  },
  "metadata": { "subType": "<resourceSubType — omit metadata key if none>" }
}
```

> **Inline-built sibling exception (agent / api-workflow) — the one case where the shape's `<folderPath binding default>` placeholder does NOT take the caseplan default.** `value.folderPath.defaultValue` is **`"solution_folder"`** (resource identity), NOT the caseplan `folderPath` binding `default` (which is `""` for an inline sibling). `bindings_v2.json` keeps the `solution_folder` sentinel while the caseplan runtime `folderPath` stays `""` — they are intentionally decoupled. `value.name.defaultValue` and `metadata.subType` (`"Agent"` / `"Api"` per kind) follow the caseplan binding as usual. Full rationale: the inline-built-sibling decoupling blockquote later in this file.

### Connector resource entry

```json
{
  "resource": "Connection",
  "key": "<connectionId>",
  "value": {
    "ConnectionId": { "defaultValue": "<connectionId>" },
    "folderKey": { "defaultValue": "<folderKey>" }
  },
  "metadata": { "connector": "<connectorKey>" }
}
```

> **Casing is `ConnectionId` (capital C).** The reader is `@uipath/solution-sdk` `sync-resources-from-bindings.ts`, which looks up `value.ConnectionId.defaultValue` and falls back to the entry's `key`; the api-workflow bindings writer emits the same capital form. The earlier note here about a lowercase reader named `syncConnectionResources` was wrong — no such function exists in the CLI.

File envelope: `{ "version": "2.0", "resources": [ /* one entry per resource */ ] }`

---

## § Populate IS connection cache

`uip solution resources refresh` reads a local IS cache that connector plugins must populate after `get-connection`. Applies to all three connector-resolving paths: connector **tasks** (Step 9.7), connector **triggers** (Step 6.1), and connector **condition-rule upgrades** in any of the 4 scopes (Step 10.5).

**Path:** `~/.uipath/cache/integrationservice/<connectorKey>/connections.json`

**Shape — bare JSON array:**

```json
[
    {
        "id": "<connectionId>",
        "name": "<connectionName>",
        "connectorKey": "<connectorKey>",
        "connectorName": "<connectorName>",
        "folderKey": "<folderKey>",
        "folderName": "<folderName>"
    }
]
```

### Field sources

| Field | Source | Plugin step |
|---|---|---|
| `id` | `connection-id` from `registry-resolved.json` | Planning |
| `name` | `.Data.Connections[selected].name` from `get-connection` | Step 1 |
| `connectorKey` | `connector-key` from `registry-resolved.json` | Planning |
| `connectorName` | `.Data.Connections[selected].connector.name` from `get-connection` | Step 1 |
| `folderKey` | `.Data.Connections[selected].folder.key` from `get-connection` | Step 1 |
| `folderName` | `.Data.Connections[selected].folder.name` from `get-connection` | Step 1 |

### Procedure

After `get-connection` succeeds (Step 1), write or merge the cache:

1. Read existing cache at the path above (may not exist — start with `[]`)
2. If an entry with the same `id` already exists, skip
3. Otherwise append the new entry
4. Write the file as a bare JSON array (NOT wrapped in `{ cachedAt, data }`)

```bash
mkdir -p ~/.uipath/cache/integrationservice/<connectorKey>
```

> Workaround for CLI bugs: (1) tenant-ID prefix in cache path, (2) wrapped `{ cachedAt, data }` format. Direct write bypasses both.

---

## What `resource refresh` produces

With `bindings_v2.json` and IS cache in place, `uip solution resources refresh` creates:

| Input | Output | Purpose |
|---|---|---|
| Non-connector bindings in `bindings_v2.json` | `resources/solution_folder/process/<subType>/<name>.json` **and** `package/<packageName>.json` | Resource declarations imported from Orchestrator — **two** files per binding (the process/app and its package). `resources remove` on the process/app deletes both. |
| Connection binding in `bindings_v2.json` + IS cache | `resources/solution_folder/connection/<connectorKey>/<name>.json` | Connection resource declaration |
| Both | `userProfile/<userId>/debug_overwrites.json` | Maps abstract resources to Orchestrator instances for debug |

All three required for `uip solution upload` and `uip maestro case debug` to work without "Resource is not configured" warnings.

> **`refresh` never removes.** See [§ Prune orphaned solution resources](#prune-orphaned-solution-resources).

> **Inline-built siblings (agent / api-workflow) — `bindings_v2` identity and the caseplan runtime `folderPath` are DECOUPLED.** This is the one case where `bindings_v2.json` does NOT mirror the caseplan binding's `folderPath`. Keep the **resource identity** at the `solution_folder` sentinel everywhere it belongs — `bindings_v2.json` `key` (`"solution_folder.<name>"`) and `value.folderPath.defaultValue` (`"solution_folder"`), plus the caseplan `resourceKey` and the `resources/solution_folder/…` path. **BUT the caseplan task's `folderPath` binding `default` MUST be `""`** (co-located runtime folder), NOT the sentinel — `"solution_folder"` there fails at invocation with `folder not exist`. Prerequisite for deploy/debug: the sibling registered in the `.uipx`. Full rationale (deploy provisioning, runtime invocation): [create-inline-common.md § Step 3](plugins/tasks/create-inline-common.md#step-3--binding-invariants); per-type debug behavior in each type's § Step 3.

---

## Cleanup on task or rule removal

When any task or connector condition rule is removed and its root bindings are pruned (per [case-editing-operations.md](case-editing-operations.md) § Delete a node / § Delete a condition rule / § Delete a task):

1. After pruning root bindings, regenerate `bindings_v2.json` from the updated array.
2. Prune the solution resource the removal orphaned — [§ Prune orphaned solution resources](#prune-orphaned-solution-resources).

---

## Prune orphaned solution resources

A **repoint** (binding `resourceKey` swapped to another resource) or a **removal** (§ Cleanup above) leaves the old resource in the solution registry. `refresh` is additive-only — never prunes, still reports `Warnings: []`.

Symptom: Studio Web `prepareForCustomDebug` fails with `Sequence contains no matching element` (`errorCode 2106`); FE shows *"Resource provisioning failed"*. **`uip maestro case validate` stays `Valid`** — it never reads the registry.

After regenerating `bindings_v2.json` and running `resources refresh`:

1. `uip solution resources list --solution-folder <SolutionDir> --source local --output json`
2. Match each entry against `bindings_v2.json` `resources[]`:
   - `connection` → by `Key` (connection UUID) against the Connection entry's `key`.
   - `process` / `app` → by **`Name`** against `value.name.defaultValue`. **`Key` does NOT match** — registry keys on the Orchestrator entity UUID, `bindings_v2.json` on `<folderPath>.<name>`.
   - **Skip `package` entries** — never target one directly. Removing the owning `process` / `app` cascades to *that resource's own* package declaration (the one its `dependencies[]` names); sibling resources and their packages are untouched.
   - **Never prune the case project's own `process` + `package` pair** (`Name` == the case project) — created by `solution projects add`, never by a binding, so absent from `bindings_v2.json` by design.
3. Remove each unmatched entry using **that entry's `Key` from step 1's output** — the registry GUID, never the `bindings_v2.json` `key`: `uip solution resources remove <ResourceKey> --solution-folder <SolutionDir> --output json`. Offline, no auth, does not touch `bindings_v2.json`.
4. Re-run `resources refresh`, then re-publish via Phase 5 (`uip solution upload` — [phased-execution.md § Phase 5](phased-execution.md#phase-5--publish)) so Studio Web drops its copy; a local removal alone does not clear an orphan already uploaded. The re-upload overwrites the Studio Web solution in place, discarding anything edited there since the last upload (the replaced contents are recorded as a restorable version) — keep it behind the Phase 5 consent gate.

<!-- END: bindings-v2-sync.md -->
