# Naming rules

> Every name in the fluid is lowercase, and a name is also a key. What sanda changes when you save, and how to add another word without making a duplicate.

Every name in the semantic fluid is lowercase, whoever wrote it: a table's card title, a dataset, a metric, a dimension, a filter, a category, a business term, a join key. `invoices`, never `Invoices`.

You can type capitals if you like. sanda lowercases the name when it saves. The same rule is given to cherry and to any AI that proposes a name, so its output does not need correcting.

## Why a name is a key

A name in the fluid is more than a label. It is how sanda finds the thing again:

- A metric is referenced by name, as `{net revenue}`.
- The query composer, cherry and AI assistants look up metrics, dimensions and filters by name.
- Saving a definition under a name that already exists replaces it. That is how a suggestion is corrected on acceptance, and how a metric is edited in place.

Every one of those ignores case. So `Revenue` and `revenue` are one row to sanda and two to your eye. If names could differ by a capital, correcting a capital by hand would not rename anything. It would create a duplicate.

Lowercase everywhere means the list you read and the vocabulary sanda resolves are the same list.

## What changes when you save

| You type | sanda stores | What happened |
|---|---|---|
| `Net Revenue` | `net revenue` | Case moved |
| `customer_country` | `customer country` | Underscores became spaces |
| `  invoice   lines ` | `invoice lines` | Whitespace tidied |
| `On-Time Delivery %` | `on-time delivery %` | Only the case moved. The hyphen and the sign are yours |

An underscore is not punctuation someone types into a name. It is a column's word-break, and it reaches a name when a person, a model or an agent copies a column name into the name field. Left alone, it made two rows for one column, `customer_country` and `customer country`, which sanda treats as one name when it looks things up.

Three things are never touched:

- **Identifiers.** A column is `_becca_synced_at` or `mapping.billing__invoices.total`, exactly as the warehouse spells it.
- **Sentences.** Notes, descriptions, grains and sample values keep their capitals: "Paid invoices, after discounts. Excludes GST." It is names that are lowercase, not the prose around them.
- **Proper nouns inside a sentence.** Xero is still Xero, AUD is still AUD.

A join key is tidied a little more, turning hyphens into spaces as well, because it is usually taken from a column name. Every other name keeps a hyphen you type, including a new metric's name (tidied as you leave the field) and a new category's name.

## Names sanda generates

When sanda names something for you, it applies the same rule to the identifier it started from:

- A table's name comes from its stream: a stream called `invoices` gives `invoices`, and a view called `daily_revenue` gives `daily revenue`.
- If that name is taken, the second is qualified with its connection, `invoices (shopify)`, and then numbered, `invoices 2`.
- A connection's dataset is named after the connection, lowercased: Billing becomes `billing`.
- A relationship's key is lowercased and its separators turned into spaces.

A name you type yourself is folded the way the table above shows.

## Lookups are looser than storage

When an agent, cherry or an MCP client names a table, sanda ignores case and treats underscores, hyphens and spaces as one separator. `order_details`, `order details` and `Order-Details` are the same table. A relation works with or without its schema: `mapping.order_details` and `order_details`.

That is all it does. A name is matched, not guessed at: `product` does not resolve to `product_name`. When a name matches nothing, the refusal offers the two or three nearest names.

## Aliases

An [alias](https://docs.sanda-os.com.au/reference/glossary#alias) is another name for a table or a column: the words your team actually says. `amt_due` is "amount owing", also called "outstanding" and "balance". A table called `billing__invoices` is "sales ledger", also called "invoice book".

Press **aliases** on the rail to open the whole vocabulary at once. It lists every column of every table on the map with two fields:

- **called**, the name your team uses.
- **also called**, other words, separated by commas.

The panel shows how much of each table you have named ("6 of 9 named"), and **only the unnamed** narrows the list to the gaps. Each table has a first row, **the table itself**, for its own name. A **save** button appears when you change a row, and <kbd>Enter</kbd> saves too. If you fill in only **also called**, sanda uses the column's own name as the term.

For one column or table, press the tag icon on a column in its card, or choose **aliases…** in the card's menu.

Press **suggest aliases** and sanda reads the tables on the map, with sample values, and proposes the words a business would use, plus metrics your values make measurable (a `status` column that holds `refunded` makes a refund rate possible). They wait in the [review queue](https://docs.sanda-os.com.au/semantic-fluid/review), and the AI call is billed.

Aliases are handed to the agent, so a question phrased in any of them finds the right column.

:::note
A name cannot reuse the term of a join key on the same column. Naming a column `customer` when a relationship already uses `customer` as its key would silently overwrite the relationship, so sanda refuses and says which two things collided: "“customer” is already the name of the join key on that column, and a relationship is using it."
:::

## Synonyms instead of second spellings

When a thing has another name, do not add a second row. Add the other name as a synonym of the first.

- A metric, dimension or filter has **Also called**.
- A column or table has **also called** in the aliases panel.

Synonyms are lowercased, and any underscores you type are kept, so `amt_due` stays findable by the name people see in a report. A synonym finds a definition only when it names exactly one. If two metrics both answer to `revenue`, that word finds neither, and sanda does not pick one for you.

For dimensions the rule is enforced. A column can have only one dimension, and a second name for it is refused with an instruction to add it as a synonym instead. See [dimensions and filters](https://docs.sanda-os.com.au/semantic-fluid/dimensions-and-filters#one-dimension-per-column).

## Labels for stored codes

A value label changes how a code is shown, not what it is stored as. `AUTHORISED` can read as `approved` in a report, while a query still filters on `AUTHORISED`.

Add one in the list view. Open the **mappings** tab, press **Add mapping**, set **What kind** to "Replacement values for reports", give the mapping a name under **Human name**, name the column under **Source column**, and enter one pair per line under **Stored value = label**:

```text
AUTHORISED = approved
VOIDED = cancelled
```

Labels are kept exactly as you type them, capitals included. The agent is told to filter on the stored value and label the output.

## Rename something

- **A metric.** Press **edit**, change the name and save. sanda repoints every metric that refers to it. See [metrics](https://docs.sanda-os.com.au/semantic-fluid/metrics#build-on-other-metrics).
- **A filter.** Save it under the new name and delete the old one.
- **A dimension.** Delete it and add it again under the new name.
- **A table.** The list view has no rename: saving under a new name adds another table. Give the table an alias, or ask cherry to rename it.

:::links
- [Metrics](https://docs.sanda-os.com.au/semantic-fluid/metrics): References and renames.
- [Dimensions and filters](https://docs.sanda-os.com.au/semantic-fluid/dimensions-and-filters): One dimension per column.
- [What the agent is told](https://docs.sanda-os.com.au/semantic-fluid/agent-context): Where names and aliases end up.
:::
