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

# Create and update formulas

> For agents: save formulas with edit_table_blocks through visible saved tables or on a Variable, build new tables and rows, declare mappings, set windows and values, write in a Scenario, delete objects, and read what each receipt reports.

A saved formula belongs to its Variable. When the user selects a visible row, column, or cell, the saved Table block gives the target that resolves the right Variable and scope. Every model formula is written through `edit_table_blocks`.

## Shape a call as one action block

`edit_table_blocks` has seven actions: `create`, `update`, `reshape`, `copy`, `delete`, `set_time_rollup`, and `update_calendar`. Fill exactly one block named for the action, and put that action's fields inside it.

* `scenario` sits beside the block and applies to it. It is also accepted inside the block; a call that names two different Scenarios in the two places is refused.
* A field the chosen action does not take is refused, with `accepted_fields` listing that action's own fields plus `scenario`.

[MCP tools and call shapes](/agents/mcp-tools.md#refusals-for-a-wrong-shape) lists every shape refusal.

## Write through the saved table

First read the table with `inspect_table_blocks` to get its visible row and column references:

```json theme={null}
{ "read": { "target": "Operating Plan" }, "scenario": "Main" }
```

Then send the related formula edits together in one `update`, with the same `scenario` as the read, so both resolve the same saved table:

```json theme={null}
{
  "scenario": "Main",
  "update": {
    "target": "Operating Plan",
    "row_updates": [
      {
        "rows": ["Gross Profit"],
        "default_formulas": {
          "actuals": { "formula": "Revenue - `Cost of Goods Sold`" },
          "forecast": { "formula": "Revenue - `Cost of Goods Sold`" }
        }
      }
    ]
  }
}
```

* A row with no Date scope takes `formula`. A row that runs across time takes `default_formulas`, with an `actuals` entry, a `forecast` entry, or both. An entry you leave out stays unchanged.
* `clear_formula: true` in place of `formula` removes the formula the row owns for that range.
* The server derives the Variable, the cell address, the formula range, and the write permissions from the visible target. Copy references from the inspection result, such as `Gross Profit#3f2`, instead of inventing IDs, positions, or range identifiers.

### Tables that save a comparison

A table that saves a Scenario or period comparison takes writes too. Name the selected Scenario's own column, with or without the comparison qualifier the read printed, and the write lands in that Scenario. A compared Scenario's column, a compared period's column, and a difference column take no write: the refusal says the table's saved comparison is left out, because those columns show values saved in another Scenario or period, or calculated.

## Formulas the items beneath a row inherit

`child_defaults` on a `row_updates` or `column_updates` entry holds the formulas the cells beneath the row inherit, one entry per breakdown Dimension at any depth:

* Each entry names its `dimension` and carries `actuals`, `forecast`, or, on a timeless table, `formula`. One `row_updates` entry can set the row's own formula and its children's together.
* A deeper level's entry is saved on the level above it. In Total by Owner by Deal, the Deal entry is the formula on `Total[Owner]`.
* An axis row, such as `Total[Stage]`, sets the formula for every item's children; an item row, such as `Total[Discovery]`, sets it for that item only.
* `clear: true` on an entry removes an owned timeless child formula; dated ranges clear through `actuals` or `forecast`. The item's own `clear_formula` removes only the parent row's formula.

## Create a visible derived row

A new Variable can be defined and placed on an existing saved table in the same change. `structure.insert` names the side, the new Variable, and its formula:

```json theme={null}
{
  "scenario": "Main",
  "update": {
    "target": "Operating Plan",
    "structure": {
      "insert": [
        {
          "side": "rows",
          "node": {
            "variable": "Gross Margin",
            "default_formulas": {
              "forecast": { "formula": "iferror(`Gross Profit` / Revenue, 0)" }
            }
          }
        }
      ]
    }
  }
}
```

`structure` inserts, then moves, then removes rows and columns. A removal drops a row or column and its subtree from this table, never the objects themselves, and every Variable and Dimension must keep a row or column on at least one table. The same update can write the cells of inserted rows with `cell_updates` or `values`.

To start a new table instead, use `create` with its complete `rows` and `columns`:

```json theme={null}
{
  "create": {
    "name": "Margin analysis",
    "rows": [
      {
        "variable": "Gross Margin",
        "default_formulas": {
          "forecast": { "formula": "iferror(`Gross Profit` / Revenue, 0)" }
        }
      }
    ],
    "columns": [{ "time": "MONTH" }]
  }
}
```

The saved row makes the calculation visible, inspectable, and available for follow-up. A Variable row placed beneath a Dimension row saves its `default_formulas` open over that Dimension, so every item calculates the formula, under the same condition a later `row_updates` on that row writes.

### Create several dependent tables in one call

When a build needs more than one table, and a later table's formulas name Variables an earlier table declares, put the tables in a `tables` list inside one `create` instead of making one call per table:

```json theme={null}
{
  "create": {
    "tables": [
      {
        "name": "Workforce assumptions",
        "page": "Workforce",
        "rows": [{ "variable": "Hours per Month", "formula": "160" }],
        "columns": [],
        "window": { "granularity": "NO_GRANULARITY" }
      },
      {
        "name": "Roster",
        "page": "Workforce",
        "rows": [{ "dimension": "Employee", "items": ["E-1", "E-2"] }],
        "columns": [{ "variable": "Monthly hours", "formula": "`Hours per Month`$" }]
      }
    ]
  }
}
```

* `tables` holds up to 20 tables. They save in list order in one change, so a formula in the second table can name a Variable the first table declares.
* Two entries that name the same new `page` share it.
* Each entry carries its own `name`, `page`, `rows`, `columns`, and settings. `scenario` and `dry_run` sit on the request and apply to every entry.
* The response holds one receipt per table, in list order, and every table shares one `change_log_id`. A refusal of any table refuses them all and names the table.

### A new dated row gets a time rollup

A formula on a month row governs month cells only. Quarter, half-year, and year cells read the Variable's time rollup.

* A `create` or `insert` row may name `rollup`: `SUM`, `LAST`, or `AVERAGE`.
* When it names none and the table's periods are at the Model's base grain, the save writes the rollup the request implies, at every grain coarser than the base, for Actuals and Forecast: `SUM` for a number with no aggregation or with `SUM` or `COUNT`, and `LAST` for `aggregation_function: LAST`. It writes none for a timeless `formula`, `format: PERCENTAGE`, a value that is not a number, or any other aggregation.
* A row created on a table whose periods are quarters or years, or on a table without Date, gets no implied rollup.
* The receipt states the implied rollups in one `default_rollup` issue, and does not list them as `rollups` or count them as `default_formulas`.
* A ratio row created without `format: PERCENTAGE` gets `SUM`, so its year cell adds the monthly ratios. `set_time_rollup`, or a named `rollup`, corrects it.

A `default_formulas` formula written through a table whose periods are coarser than the base grain, such as a yearly table in a monthly Model, is saved at that coarser grain, and the receipt warns: the year cells compute the formula whatever their month cells hold, and the month cells do not read it. Write the formula on a row of a base-grain table, or in a `variables` entry, when it should govern the base periods.

### A breakdown's totals are written for you

When a table breaks a Variable down, `child_defaults` on the deepest axis row is the formula for the lines, saved on each line's full path. The save then gives every coarser level the tables show a default that sums the level beneath it, one ordinary formula per level, and the receipt lists them: "Revenue's level defaults, which add up its \[Customer, Line, Product] lines at each level the tables show: saved Revenue\[Customer, Product], Revenue\[Product] and Revenue."

* A value set on any row reaches every total above it.
* A formula an author writes at a level replaces the default there, and a default edited into other text stays with its author.
* The sums exist only while a line formula claims the lines. Clearing the line formula removes the sums left in its range.
* A Variable with `format: PERCENTAGE` gets no sums and no `0` on its lines. Each level above them gets the line formula or `NULL`, and the receipt states which: "saved the line formula on \[rows], so each of those rows computes that formula from its own inputs", or "saved NULL on \[rows], which reads blank, where the lines hold typed-in numbers or several formulas, or where their formula takes its own row's item of a dimension that row lacks or reads a variable with no value on that row".
* A row's `numberFormat` is display only and changes no formula.
* Each sum adds up the one shown level beneath it, never every line dimension at once. A total above lines whose formula has no Date is saved without Date too.
* A sum a person or Ari saved at a level is left alone, even when it is spelled exactly as the sync spells its own sums. The sync replaces or removes only the formulas it wrote itself, which a `variable` read marks `writer: "level_default_sync"`.
* When one call creates several tables, each table's receipt lists the level defaults for every Variable that table shows, including a Variable a row names only by reference.
* A `create` dry run runs the same totals step and rolls it back, so its `model_effects` carry the statements the committed receipt puts under `level_defaults`, for each table in the call.

[Totals, aggregation, and time rollups](/concepts/totals-and-rollups.md) explains why totals work this way.

### Add items and write their values in one call

* An `update` adds the items that `dimensions[].add_items` or `replace_items` names before it resolves `values` and `cell_updates`, so one call can add `E9` to Entity and write `["E9", 42]` in `values`.
* A `create`, or an update that adds rows, can also fill a mapping column on the rows it adds. A Department cell on a new employee row saves the one quoted item, `"Sales"`; a new item joins the Dimension's list; and a value that is not one item is refused before anything saves, naming the cell.
* Cells of one lookup save in one mapping write, so filling 400 Departments costs about the same as filling one.

When a write changes a shared mapping row, the receipt's `shared_mapping_changed` notice names the first changed row and its item before and after, and counts the rest: "The shared mapping changed: EMP-001's Department moved from Engineering to Operations, and 3 more rows changed. Other tables and periods that use it can also change." A first assignment reads "is now Sales", and a cleared cell "was cleared; it was Sales". Every table that reads the mapping recalculates in the background after the call returns; `inspect_dimension_mappings` lists those tables.

### Where a timeless formula is written

`formula` and `clear_formula` write a Variable's timeless formula, which applies in every period, Actuals and Forecast alike. `default_formulas.actuals` and `default_formulas.forecast` write formulas limited to Actuals or to Forecast, and `create` and `update` both accept them.

* A `create` writes a timeless `formula` for a Variable it makes, including on a row of a table with Date columns. It refuses one on a row that places an existing Variable.
* An `update` writes a timeless `formula` on an undated row or column, and refuses one on a dated row or column.
* Both refusals use the same sentence, which says where each kind of formula is written.

A `child_defaults` clear on `create` goes through the same planner an update uses, so a child that owns no formula reports `unchanged`.

## An axis that only lists items has no formula to edit

`formula` and `clear_formula` edit the formula of a calculated value: a Variable, a Dimension shown as a value through its mapping or formula, or the Variable that encloses a Dimension breakdown. An axis that only lists a Dimension's items has no calculated value, so a formula edit aimed at it is refused with a reason instead of a resolution error. The reason:

* opens "this table axis shows its dimension with role DIMENSION: it lists the dimension's members, and no calculated value is on either axis";
* says that `dimensions` `add_items`, `remove_items`, `replace_items`, and `rename_items` edit saved items, and that a retained breakdown edits its enclosing Variable's formula;
* adds that "a dimension axis whose role is VARIABLE holds one value of that dimension for each member on the other axis, and a formula on it saves the rule that calculates that value".

The `role` field's description and the item-condition refusal carry that same sentence. A formula that generates a source-linked Dimension's items has no write in the tool: the person writes it in a table, from the Dimension's label-row menu, with **Edit dimension formula**.

## Save a Variable's formula without a table

When the requested formula has no visible table target, name the Variable in a `variables` entry. No table `target` is needed:

```json theme={null}
{
  "scenario": "Main",
  "update": {
    "variables": [
      {
        "target": "Bookings",
        "default_formulas": { "forecast": { "formula": "last(`Bookings source`)" } }
      }
    ]
  }
}
```

* The formula is saved on the Variable with one Date term at the Model's base grain, the same address a plain row on a base-grain table writes. A later `row_updates` write on a table that shows the Variable edits this formula instead of adding a rival.
* The same entry takes the Variable's settings, wherever it is read: `name`, `data_type`, `aggregation_function`, `preferred_time_property`, `format`, `decimal_places`, `currency`, and `variance_colors`.
* Item edits on a Dimension still name a table that shows it.

## Filter the items an axis shows

`members` on a `row_updates` or `column_updates` entry is the saved item filter of the selected axis. It takes one of two forms.

**A list of item names** replaces the filter, including a saved condition. `[]` clears it.

* The list may name `empty:None`, the rows with no value.
* Each name is checked against the Dimension's hand-added items and the source's item text, then, for a name those do not hold, against the calculation's own item list. So an item a formula or mapping computes is saved, and so is a number written as the table's rows write it, such as `13840.0`. Date and number items match by their saved text, such as `"2026-01-01 00:00:00+00"`.
* A name the calculation does not hold either is reported under `unmatched`. A list that names no item at all is refused with the Dimension's first items.
* Only an item filter may name `empty:None`. `add_items`, `replace_items`, `rename_items`, and a mapped item named `empty:None` are refused with "empty:None is the spelling of a dimension's empty member, which holds the rows with no value and exists without being added".

**A string** is a live condition on the Dimension, evaluated over its items at every calculation to narrow the saved list, so an item the saved list already leaves out stays out. A table read prints a saved condition in this grammar with qualified references, such as ``[`Employee ID`#403 where `Employee ID`#403.Department#2f5 = "Engineering"]``, so it can be written back unchanged, and a `list` read names the home of a Dimension the condition reads from outside the table under `property_homes`. When the condition's text cannot be printed, the read reports `{"saved": true, "shown": false, "reason": ...}` instead. The reason is `it reads a variable or dimension this read cannot name for this person` for a property you cannot read, or `it reads a variable or dimension that is not in this scenario` for one the Scenario no longer holds, such as a deleted Dimension; when both apply, the permission reason is given. Only the `list`, `definitions`, and grid reads can tell the two apart, so an edit receipt gives the permission reason for either.

* Literals are quoted strings. A bare number is a reference offset, not an item.
* `Region not in {"West"}` keeps every named item but West. The empty item has no value, so a comparison on it is unknown; a null check inside `where` keeps it: `Region where (Region = null or not (Region in {"West"}))`.
* A condition may read a Dimension mapped from the axis, such as `Employee where Status = "Active"`. A condition on a Dimension the axis is not mapped from is refused, and the refusal names the step that makes it readable: `edit_dimension_mappings` maps that Dimension from the axis, which gives each axis item one item.
* A condition never reads a Variable. `Vendor where FutureSpend > 0` is refused with "a member condition on Vendor reads only Vendor", and the refusal names the step that works: place a Dimension as a value column on a table whose rows are the axis, give it a formula that reads the Variable, such as `if(FutureSpend > 0, "Yes", "No")`, and save that formula with `edit_table_blocks`. The Dimension then has one value per item, and `Vendor where HasFutureSpend = "Yes"` stays current after the next import.

An item row such as `Total[West]` names its axis. `hidden` on the same entry hides or shows the saved axis the selection belongs to, so `hidden: true` on an item row hides every item of that level.

When a receipt that lists every item would exceed the result allowance, each `member_filters` entry reports `members_omitted`, the count, in place of its `members` list, and the edit still answers `outcome: applied`.

## Edit an address that holds several saved formulas

A table edit names an address: the Variable, its condition, and its formula range. Imports and older writes can leave more than one saved formula at one address.

* An exact `$[...]` formula outranks an inheriting `[...]` formula at its grain, and among formulas of the same form the newest save wins. So cells read one formula: the newest exact one when one exists, otherwise the newest inheriting one.
* A `row_updates`, `child_defaults`, or `cell_updates` write at such an address edits the formula the cells read, and deletes the older copies of the same form in the same change. The receipt names each deleted copy: "This write also deleted an older copy of a Forecast rule that Advertising carried at this address: `1` at `[Vendor in any, Date.Month in any]`. No cell read a deleted copy, so no value changed."
* An exact formula and an inheriting `[...]` formula at the same grain are not copies. The write edits the exact one, keeps the inheriting one, and names it in the receipt.

A table read marks a default that has older copies with the `duplicate_default_formula` issue, which counts the copies and says that a write to the default deletes them. The default itself shows as `defined here` and can be written.

## Set where a source's rows sit on the calendar

Where an imported source's rows sit on the calendar is one fact about the source, and a `sources` entry on `update` or `create` states it once:

```json theme={null}
{
  "update": {
    "sources": [{ "target": "CRM Deals Monthly Export", "preferred_time_property": "synced_month" }]
  }
}
```

* `target` is the query reference a read prints under `sources`, and `preferred_time_property` is one of the source's date columns.
* The write saves that column on every Variable and Dimension of the source in the same change. A `variables` or `dimensions` entry in the same request that sets its own `preferred_time_property` wins for that object.
* A column that is not a date column of the source is refused, naming the columns that are.
* An `update` that carries only `sources` needs no table target.

## Declare a mapping in the create that reads it

A `create` whose formulas read a mapped Dimension can declare the mapping in the same call, instead of calling `edit_dimension_mappings` first. The create's `dimensions` entry takes `target`, `keys`, `items`, and `catch_all`, as `edit_dimension_mappings` `map` takes them:

A table formula or mapping write that would give a Dimension a second lookup is refused, and nothing saves, when the two lookups share no key and both reach every row of the same imported table, because the calculation could not choose between them for a row both match. Lookups that share a key, such as Account alone and Account with Account ID, are not refused: the more specific one wins and the other is its fallback, as [Dimension mappings](/concepts/dimension-mappings.md) describes. The refusal names the Dimension and both lookups: `target dimension "Direct Cash Flow Group" already has a mapping input keyed by [CF Classification Basis] that reaches the same imported source as the proposed input keyed by [CF Line ID]. Both inputs would match every source row, so "Direct Cash Flow Group" would have no defined mapping result`. A key that is itself a mapped Dimension counts through the import it reaches.

```json theme={null}
{
  "create": {
    "name": "Historical P&L",
    "page": "P&L",
    "dimensions": [
      {
        "target": "P&L Line",
        "keys": ["GL Account Name"],
        "items": { "4000 - Subscription Revenue": "Revenue", "5000 - Hosting Costs": "Cost of Revenue" },
        "catch_all": "Unclassified"
      }
    ],
    "rows": [
      {
        "variable": "Total Revenue",
        "default_formulas": { "actuals": { "formula": "sum(`Income Statement Amount`[`P&L Line` = \"Revenue\"])" } }
      }
    ],
    "columns": [{ "time": "MONTH" }]
  }
}
```

* The mapping is written in the same change, before the rows, so a formula such as ``sum(`Income Statement Amount`[`P&L Line` = "Revenue"])`` resolves in this call. The table, the Dimension, its items, and its rows commit together.
* `items` maps single source values to fixed items, so a nonempty `items` needs exactly one key.
* A mapping formula's `condition` is saved as a relative `[...]` formula; a leading `$` before a nonempty condition is dropped. So when one lookup is keyed by Account and another by Account together with Account ID, a row the two-key lookup has no formula for falls back to the one-key formula instead of the catch-all.
* The catch-all `[]` is unchanged, and a formula saved as `$[...]` earlier keeps its exact condition until a mapping write names its row again.
* The receipt's `items` entry for the Dimension carries the mapping's facts: `keys`, `rows_created`, `rows_updated`, `target_items`, and `catch_all`.
* A refusal on either side saves nothing.

[Dimension mappings and shared Dimensions](/concepts/dimension-mappings.md) explains how mappings work.

## Set a table's window and grain

`window` is the table's own date range and grain.

* On a `create`, it holds `start`, `end`, and `granularity`. A dated table names its grain, such as `MONTH`; a timeless table names `NO_GRANULARITY`, as the `tables` example above does.
* On an `update`, the range is `{"type": "ABSOLUTE", "start": …, "end": …}`, and `{"inherit": true}` clears the table's own window.
* `start` and `end` accept a plain calendar date, such as `2026-02-01`, as well as a full timestamp, and `create` and `update` save the same window for either form.
* `update` with `grain` moves the table's Date columns to that grain, so a monthly table asked for `QUARTER` shows quarter columns, and the receipt reports the moved Date column. A table whose Date header shows two grains at once, such as Year above Month, is refused a new grain.
* `grain` on a row or column node sets the display grain of a datetime axis only. On a Variable row it is refused, and the refusal states where a Variable's grain comes from, such as "grain on a row or column sets the display grain of a datetime axis; "Revenue" has data type FLOAT. A variable's default\_formulas save at the table's Date grain: the grain of its Date axis, which time sets on create and grain sets on update or reshape." That sentence describes a formula written through a table; [Save a Variable's formula without a table](/agents/write-formulas.md#save-a-variables-formula-without-a-table) states the grain a `variables` entry saves at.

### Write values into dated columns

* A `values` write names each Date column by the start of its period at the table's grain. On a table with yearly columns, `2024-01`, `2024`, `FY2024`, and `2024-01-01` all write the 2024 cell; `2024-02` is refused as a date inside a YEAR period, and the refusal names the form that works.
* A period outside the table's window is refused with the window and the start or end that would hold it, such as "2025-12 is before this table's window 2026-01…2026-12; a window that starts 2025-12-01 holds it". A call may set `window` and write the new periods together.
* A whole number such as `2026` also selects a number item printed `2026.0`, and a row renamed in the same request resolves under either name.
* A JSON number in exponent form saves as the same value in plain decimal digits, so `1.5e-3` saves as `0.0015`; the formula grammar has no exponent literal. A number outside the 64-bit floating-point range is refused with "`<literal>` is outside the 64-bit floating-point range".
* Dated inputs in `values` or `cell_updates`, and the periods a `readback` selects, leave the table's saved display window unchanged. A report that must show a requested start and end needs `window` set on the same call, or the table keeps showing its old range with the new months outside it. A readback of selected months proves those cells, not the displayed range.

## Write in a Scenario

`scenario` defaults to the Scenario in view, which can change between turns. Through MCP, that is the Scenario `_context.layerId` names, or Main. When a call that saved something omits `scenario`, each table receipt carries a `scenario_warning` issue that says which Scenario the write used:

* With a Scenario in view: "No scenario was named, so this call used `Breakeven Plan#4c1`, the scenario the person is viewing. The scenario in view can change between turns."
* With none in view: "No scenario was named and none is in view, so this call used `Main`."

A refusal, a dry run, or a call that saved nothing carries no such sentence.

A Scenario stores only what differs from the Scenario it branched from. A cell write in a child Scenario that repeats the value the cell already reads from its parent stores nothing: the edit's outcome is `unchanged`, and a `formula_warning` says the Scenario already reads this value from its parent, so the cell keeps following the parent and changes when the parent changes. A different value is saved as the Scenario's own and holds through the parent's later edits. [Scenarios, Snapshots, and version history](/concepts/scenarios.md) explains why.

## Delete a Variable or Dimension

* `delete` with a `target` alone removes the whole table, and deletes only the Variables and Dimensions that no other saved table still shows. An object another table shows stays there, with its formulas, values, and items.
* No table owns an object. A `row_updates` or `reshape` write may take an object off the table it was created on while another table shows it; only removing its last occurrence needs it placed elsewhere first, or deleted outright.
* `delete` with a `variables` or `dimensions` list removes only the listed objects, wherever they are placed. An object on another table, or on no table, is reached by name; the named table only scopes the call. A name that matches no readable object is refused with "delete target … matches no readable variable or dimension".
* When other formulas, blocks, or settings still read one of the objects, the delete is refused and lists what reads it under where each object sits, such as "is shown on table Revenue" or "has no recorded table location", ending "With force, the object is deleted and these references break." `force: true` accepts that those references break.
* `force` on the delete covers every listed entry; `force` on one entry covers that entry alone, so a call can force some entries and not others. `update` entries do not take `force`.
* A refused table delete opens with "Nothing was deleted" and the count of dependents. A completed one carries a `links_end` issue, because links to the table in earlier chat messages no longer open it, and a table rebuilt in its place has a different identity.
* Removing a Dimension item is refused, opening with "Nothing was removed", while a formula, a drill-in, a chart or Canvas condition, a row on another table, or a filter that lists only that item names it. Manual sort orders, and filters that still list other items, drop the item instead.

## A formula belongs to its Variable

A `create` row that names an existing Variable by its plain name places that Variable. Rows match names without regard to case, so `Deferred revenue` places `Deferred Revenue`.

A plain name adds to the Variable only where it holds nothing:

* a formula for a range or level that has none,
* a value at a cell that has none and that no formula of the Variable calculates, or
* a type or format that was not set.

A request that would replace what the Variable holds is refused before anything saves, for every table that shows it. When judging that:

* A formula calculates the cell unless it is pinned to one cell. An exact condition with an open term, such as `$[Date.Month in any]`, is a formula over every month.
* A dated formula counts at the Model's base grain and at the grain of the value given, so a formula saved from a quarterly table refuses a quarterly value.
* The generated formulas a breakdown's save recorded as its own are judged by what they compute. The `0` on the lines supplies what a blank cell reads, so a value on a line is an addition; the sum on a level calculates that level alone, so a value on the Variable's own row is a replacement.
* A value on a timeless Variable that holds nothing is an addition.
* Every aggregation that is set, `DO_NOT_AGGREGATE` included, is a setting a plain name does not replace.
* The check does not read which items a condition names, so a formula for one item refuses a value on another item's cell.

The refusal names the two ways forward:

* The Variable's reference, such as `Budget#3f9`, writes that Variable and replaces what it holds.
* `new: true` on the row creates a separate Variable with the same name and leaves the existing one as it is. Throughout a request with `new: true`, in rows, columns, formulas, values, `variables` entries, and later tables of the same create, the plain name means the new Variable, and each existing namesake answers only to its reference.

The receipt's `items` entry for a reused object carries:

* `home`, the table it was first created on, when that table exists and is readable;
* `added`, with the `rules`, `values`, and `settings` the save gave it;
* `beside_rules`, the count of formulas the Variable already held that calculate its cells, which is 0 whenever values were added;
* `rule: "replaced"` when a write by reference replaced a formula the object already had, in Main or in the Scenario being edited;
* and, on an `update` or `reshape` entry for a reused Variable, `level_defaults`, the sums and `0` the save's breakdown added to it or removed from it.

Giving an existing Variable new `default_formulas` by its reference, such as `Revenue#3f2`, while placing it on another table rewrites the formula every table reads. When the formula it replaced was a formula, the receipt names it and the address the Variable carried it at. Writing a value over a plain number, quoted text, a date, or `null` states no replaced formula; the committed edit's `previousText` carries the old value instead. Placing a Variable without new defaults leaves its existing formula alone.

## Names and references

* A stale table reference whose name no longer exists resolves by its suffix when the suffix has at least three hex digits and exactly one table carries it. `inspect_table_blocks` `list` reports the move in a `renamed` note, such as "Vendors#083: no table has that name; its suffix matches the table now named "Anomalo Vendors"".
* Formula names match case exactly. A formula that names `` `Deferred revenue` `` when only `Deferred Revenue` exists is refused with the object it matches: "`` `Deferred revenue` `` matches the existing `` `Deferred Revenue` `` (names match without case); formula names match case exactly, …".
* A reference field and formula text accept the same written form of a namesake. The short suffix a readback prints, such as `Currency#3` beside `Currency#8`, names the object it prints for, and a suffix of three or more hex digits is checked against the object's retained identity, so a reference issued for a deleted object never moves to a new namesake.
* A backticked name that no object carries, such as `` `Amount#3c2` ``, reads as the reference it quotes. In a table row or column, backticks still mark a literal new name.

## Check before you save

A table write is atomic: the whole change saves, or none of it does. Only `create` takes `dry_run: true`, which validates a new table without writing; edits to saved tables and deletions take no dry run. [Validate and verify formula writes](/agents/validate-and-verify.md#validate-before-you-save) has the details.

## Read back what you saved

* Ask for a readback in the same write with `readback.rows` and `readback.columns`. `rows` alone reads every column, `columns` alone every top-level row, and a renamed Variable or Dimension resolves under either name. `readback.depth` adds child levels: `1` adds each row's items, `2` theirs, and `all` every level; omitted reads the selected rows alone. `readback.also` reads up to 3 other saved tables at the same change, each entry `{"target", "rows", "columns", "depth"}` with the readback's own meanings: omitted `rows` reads every top-level row, omitted `columns` every column, and `depth` nests the same way. Each entry's result comes back under `readback_also` in request order.
* An `update`, `reshape`, or `delete` may carry `based_on`: the `as_of` of the table read, or the `change_log_id` of the table write, it builds on. The edit saves either way. When someone else saved to the table after that point, the receipt adds a `changed_since_read` issue naming each save and who made it: a person by name, "a person in the app", "Ari in another chat", or "an automated process", up to five saves and then a count.
* Check the intended Scenario, representative dates and segments, formula origins, and the actual numbers. When the receipt carries warnings, read them before moving on. [Validate and verify formula writes](/agents/validate-and-verify.md) lists them.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.