# Stable identity

Jadpo knows a declaration or field by **the name it was born with**, so a
rename never looks like something removed and something added. The
identity registry holds one line for each thing that has been renamed
since, mapping its name now to its born name, and nothing else: a line
for every declaration would grow with every new action. Children follow
their parent, so renaming a domain or an entity is one line.

Three things write the registry: `jadpo rename`, the `jadpo domain`
commands, and promoting a function or type declared in a domain to
`shared/`.

```text
// Generated by Jadpo from renames and moves; update it with `jadpo rename`, `jadpo domain` or `jadpo fmt --layout`, never by hand.
record identities 1

entity Todo -> Task
field Todo.priority -> importance
subscriber send_reminder -> notify_owner
```

- **One registry for data and code.** Stored rows name code: outbox
  events, delivery records and schedule state in the
  [system tables](system-tables.md) refer to events, subscribers
  and schedules. Renaming a subscriber must not orphan its pending
  deliveries, any more than renaming a field may lose its column.
- **Physical names never change.** The table stays `todo` and the column
  `priority`. Migration snapshots hold the whole schema with physical
  names, so a rename made by hand shows as a removal and an addition, and
  `jadpo migrate new` refuses it, pointing you to `jadpo rename`
  ([Tooling](tooling.md)).
- **Inside a callable**, branches and fault sites are numbered afresh in
  each build. Only declarations and fields need to be recognised across
  builds.
- `jadpo incident`, [path keys](path-ids.md) and the
  review all compare through this registry, so a rename reads as
  "renamed", never as "removed and added".

## Keys

A key is the kind and the born name, in the registry's own form:
`action complete_todo`, `field Todo.title`,
`function rounding.half_even` (a library keeps its name in `shared/`).
Names are unique across the project and across kinds
([Names are unique](files-and-names.md#names-are-unique)),
so the domain is a field of the node, not part of the key.

The exception is a function or type declared in a domain, whose name
needs to be unique only within its domain. Its key carries the domain,
whether it is public or not: `type billing.Address`,
`function billing.format_address`. Its fields and variants follow it:
`field billing.Address.street`.

Adding `public`, or a route starting to return the type, never changes a
key. Promoting one to `shared/` does. So when a type moves to
`shared/types/`, the edits `layout.promotion_required` asks for add its
registry line, `type billing.Address -> Address`, which
`jadpo fmt --layout` writes. The key stays `type billing.Address`, and the
move reads as a move, not as a removal and an addition. That is why the
registry's header names `jadpo fmt --layout` beside `jadpo rename` and
`jadpo domain`.

A field's, variant's or rule's line gives only its own new name
(`field Todo.priority -> importance`), read back through its owner's
line. Every other line gives the whole name now. The registry is
`database/identities.jadpo_record`. Jadpo reads it once `jadpo rename`
writes it; until then every born name is the name now.

A test's key is the file it sits beside and its sentence, with no path:
`test create_todo "rejects a blank title"`. Moving its domain keeps the
key. Two tests with one sentence anywhere in the project are
`semantic.duplicate_declaration`.

Changing what a key holds is a new manifest `format`.
