> ## Documentation Index
> Fetch the complete documentation index at: https://agents.concierca.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# Writes, dry-run & readable≠writable

> How to write safely: dry-run first, replacement vs additive fields, and the human gate.

Writes change a live company. This page is the safety contract for making them: preview first, know which fields are actually writable, understand when an update **replaces** versus **adds**, and never resolve a human approval on your own judgment.

## The three write patterns

Data rows have three write tools — pick by whether you're editing, inserting, or upserting:

| Tool              | Use when                                            | Keyed on                                 |
| ----------------- | --------------------------------------------------- | ---------------------------------------- |
| `data_row_update` | Editing an existing row you already have the id for | row `id`, fields in `set`                |
| `data_row_create` | Inserting a brand-new row                           | —                                        |
| `data_row_save`   | Upsert: update if present, else insert              | `external_id` (products: `identity_key`) |

Full parameter detail is in [Row Write Tools](/api/write-tools).

## Readable ≠ writable

A field appearing in `data_view_list` is **not** proof you can write it. Some columns are read-only projections, labels, or computed values even though they're visible.

Live example — on `products`:

* `products.product_family` **accepts** an update (`dry_run` returns **VALID**).
* `products.product_family_label` is **rejected** with `"No updatable columns provided"`.

Both are readable; only one is writable. The lesson: **use `dry_run: true` first** for any unfamiliar or sensitive field instead of probing with a real write. A dry-run tells you VALID or the exact rejection with no side effect.

<Note>
  Richer per-field capability flags (a machine-readable "writable" marker on each column) are on the roadmap. Until then, a `dry_run` is the reliable way to learn whether a specific column accepts writes.
</Note>

## The dry-run convention

`dry_run` previews a write — it **validates** (and where supported, shows the planned change) **without committing**. It is available on:

* Data writes — `data_row_update`, `data_row_create`, `data_row_save`
* `agent_update`
* `agent_schedule_manage`
* Mission draft & control tools (see [Mission Recipes](/api/ai/mission-recipes))

Always distinguish **validate / preview** from **commit**. Run the dry-run, show the user what will change, then run the same call without `dry_run` to commit. After committing, **verify** — re-read the row or re-run the query to confirm the new value landed.

## Replacement vs additive

Some updates add to a set; others replace the whole set. Getting this wrong silently deletes data.

<Warning>
  `skill_link` **adds** one skill to an agent. `agent_update.skills` **replaces the entire skill set** — anything not in the array you send is removed. To add a skill without disturbing the rest, use `skill_link`, not `agent_update`.
</Warning>

The same caution applies to any array or object field on any update tool: **treat an update array as a full replacement unless the tool explicitly says it merges.** When you only mean to add or remove one item, read the current set first, or use the dedicated additive tool if one exists.

## The human gate

Some actions must be decided by a person. You may **prepare** and **preview** them, but you may not decide them.

* `agent_run` creates a **pending approval** — it does not silently execute the agent. Present it and wait.
* `approval_resolve` and `mission_approve_step` may be called **only when the human actually decided**. Never approve on your own judgment or because it "seems fine".
* `mission_cancel`, `mission_pause`, `mission_resume`, and `mission_budget_increase` follow **preview → confirm**: show what the action will do, get the human's explicit go-ahead, then commit.

<Warning>
  Do not resolve approvals or run gated actions based on inference. A human approval means a human decided — your role is to surface the decision cleanly, not to make it.
</Warning>
