# What is committed, and why

You commit a file only if it **can't be worked out again from today's
source**, or if it is **a promise whose diff a person must read**.
Migrations, renames and the lock hold history that today's source no
longer has. The API record is a promise to your clients. Acceptances are
people's decisions. Anything else would go stale, conflict in merges and
tempt people to edit it by hand, so it is a build output instead.

## Record files

Every record but the lock is a `.jadpo_record` file, kept next to what
it describes:

- `api/main.jadpo_record`, the API record;
- `database/identities.jadpo_record`, the identity registry;
- each `translations/sources.jadpo_record`;
- each installed package's `surface.jadpo_record`;
- the root's `ignored_suggestions.jadpo_record`, the suggestions a team
  keeps quiet ([Suggestions](suggestions.md)), and
  `taught_suggestions.jadpo_record`, the patterns it taught
  `jadpo suggest`.

Each is in Jadpo's readable format, not JSON. Its first line says what
generated it and which command updates it, as the API record's does:

```text
// Generated by Jadpo from the routes of API main; update it with `jadpo api update`, never by hand.
record api 1
```

Every record but the migrations is written in the
[record format](what-is-committed-and-why.md#the-record-format): the header line, then
`record <kind> <format>`, then its entries. `jadpo.lock` keeps its name.
Files people write, such as acceptances, are not records. A migration's
folder keeps its own files ([Migrations](migrations.md)).

## How `jadpo check` keeps records up to date

You never edit a record by hand. `jadpo check` works out each record
again and fails while one disagrees with the source, naming the command
that brings it up to date. A hand edit is an error naming that command.
There are three such errors:

- `record.out_of_date`: the record disagrees with the source.
- `record.edited`: Jadpo didn't write the record that way.
- `record.format_newer`: a newer Jadpo wrote it.

The API record is `api/<api>.jadpo_record`, a view rebuilt from the
routes, with no `version:` line. The OpenAPI document's version is the
first 12 hex digits of the SHA-256 of the record the routes give today,
which is the committed record whenever `jadpo check` passes. The
identity registry, the translation sources, the suggestion records and
`jadpo.lock` are ledgers of one-line entries
([Checking a record](what-is-committed-and-why.md#checking-a-record) explains views and ledgers).

## `jadpo records`

`jadpo records` lists every record, what writes it and whether it is up
to date, and names the command for any record that isn't. It groups
them by the command that writes them:

```text
$ jadpo records
jadpo api update
✗ api/main.jadpo_record             out of date

jadpo rename, jadpo domain or jadpo fmt --layout
✓ database/identities.jadpo_record  up to date

✗ records out of date  1 out of date · 2 records

→ jadpo api update  brings api/main.jadpo_record up to date
```

`jadpo records update` brings every record up to date at once. It is
also how a project moves a record from an older name (`api/<api>.api`,
`database/identities.json`) to its name now
([Checking a record](what-is-committed-and-why.md#checking-a-record)).

## Migrations

Migrations are records too. `jadpo migrate new` writes each folder in
`database/migrations/`: `snapshot.json`, `postgres.sql` and, when the
migration needs a person's decision, `changes.jadpo`. `jadpo check`
fails, naming the command to run, while the entities and the latest
snapshot disagree, while the latest migration waits for a decision, and
when a record can't be read. Each folder is a `migration_snapshot` node
in the manifest.

## Folders at the root

[The root](the-root.md) of a project holds
`api/`, `acceptances/`, `packages/` and `jadpo.lock` beside the rest.
`packages/` holds the installed packages. They are committed, so every
upgrade is an ordinary diff. Each is checked against its digest in the
lock and is read-only, but only its `surface.jadpo_record` is a record.
Your app's own integrations, in `shared/providers/`, are source, written
like any other code ([Integrations](external-services.md)).

## Checking a record

A record is one of two shapes:

- A **view**, such as the API record or a package surface, is rebuilt
  from the source and compared with the committed file byte for byte.
- A **ledger**, such as the identity registry, the translation sources,
  the suggestion records or the lock, is a list of one-line entries that
  hold history.

**A hand edit of a view is told by its shape.** Without the base
commit's build, a view that differs from what its writer gives today is
`record.edited` when Jadpo couldn't have written it, and
`record.out_of_date` otherwise. Jadpo couldn't have written a comment, a
fact outside the kind's closed list, a tab, a trailing space, a layout
Jadpo doesn't write, or a header other than the one it writes. Both
errors name the same command, which writes the record again, so a
careful edit of one fact reads as out of date.

**A ledger is checked against its own rewrite.** Each line must read,
each key must appear once, and the file must equal what Jadpo writes
from its lines: sorted, with the header in the newest format.

- Lines that git's `union` merge left out of order are `record.edited`,
  and `jadpo records update` puts them right.
- Two lines for one key, such as one thing renamed two ways or one
  package locked twice, are `record.edited` naming both lines, and are
  left for a person to settle.

Checking that each line added since the base is one its command could
write needs the base commit's build too.

**`jadpo records update` brings every record up to date at once.** It
writes each view from the source, rewrites each ledger, and moves a
record under its old name. It is the command an error's `next:` line
names for a ledger, because a ledger's own commands (`jadpo rename`,
`jadpo translations update`, `jadpo suggest ignore`) need arguments.

**Old names are moved, not read forever.** `api/<api>.api` and
`database/identities.json` are `record.out_of_date` ("the record's old
name"). `jadpo api update` and `jadpo records update` write the new file
and remove the old one. Until then the old registry's lines are still
read, so no key changes before the move.

**Older formats are read and rewritten.** `ignored_suggestions` and
`taught_suggestions` are at format 2, with the shared header. Their
format 1, with its own header line, is still read, and
`jadpo records update` rewrites it.

**Records don't stop a program running.** `record.out_of_date` and
`record.edited` are promises that `jadpo check` and CI hold you to, so
`jadpo test` and `jadpo dev` run past them. `record.format_newer` stops
them, because this Jadpo can't read what the project holds.

**`jadpo records` shows each record's state** as a symbol and a word
under the command that writes it: `✓ up to date`, `✗ out of date`,
`✗ edited by hand`, `✗ missing`, `✗ old name`, `✗ newer format`, or
`– not checked` while the source has errors. Like `jadpo check`, it fails
while any record needs its command.

What some entries hold:

- **The API record.** An `api <name>` entry carries one
  `authentication <method>` line per method, sorted, and
  `idempotency required`. The entry is left out when it has neither. A
  route's `generated` and `caller` facts come before `input`. Enum
  variants are sorted like fields, as `variant <name>`, and a variant's
  fields sit one indent deeper. Types sort by born name without their
  domain, then by name now. A failure's entry is its `status`, then its
  `message` as a Jadpo string, then its client fields.
- **The lock.** A `git` source records the repository, the tag and the
  tag's commit: `package AcmeCouriers.v1 version 1.4.0 git
  github.com/acme/acme-couriers tag v1.4.0 commit <40 hex> digest
  sha256:<64 hex>`. A `registry` source is
  `version <v> registry digest <d>`, and a `path` source is
  `version <v> path <folder> digest <d>`. A `link` is a live folder, so
  it has no version or digest: `package AcmePricing.v2 link
  ../acme-pricing`. Versions are written like `1.4.0`, with an optional
  pre-release. A commit is 40 or 64 lower-case hex digits. Lines sort by
  name, then by major version as a number.
- A project that pins the Jadpo release it runs with has that pin as the
  lock's first line, `jadpo version 0.1.2`. The release has no
  pre-release, and it must be on the release line `app.jadpo` names, such
  as 0.1. A pin on another release line is `record.out_of_date`
  ([Which jadpo runs](which-jadpo-runs.md)).

## The record format

Every record is written in one small line format, an outline in Jadpo's
own words. A change touches only the lines of the fact that changed, and
git's hunk header names the entry the change is in.

**The header.** Line 1 is `// Generated by Jadpo from <what it
describes>; update it with <command>, never by hand.` Line 2 is `record
<kind> <format>`, where the kind is one of `api`, `events`, `surface`,
`identities`, `translations`, `ignored_suggestions`,
`taught_suggestions` and `lock`. Any other `//` line is a hand edit.
Nothing anywhere in a record changes on its own: it holds no timestamp,
compiler version, count, total, digest of the whole file or alignment.

**Entries.** A heading starts in column 0, with a word from the kind's
closed list (`POST /todos`, `operation bookings.create`). Its members
follow, indented four spaces, and a member's own fields, such as an enum
variant's, are indented eight. Nothing goes deeper. A member is one of
three things:

- a description, `/// text`;
- a field, `name: value`, written as in source;
- a fact, which starts with a lower-case word from the kind's closed
  list: `status 201`, `fails todo_not_found`, `file types/booking.jadpo`.

A list repeats its word, one item per line. A flag is a fact that is
present or absent (`changes`, `idempotent`, `accepted`), never
`changes: true`. A line's kind is known from its first characters, so a
tool can read a record without a Jadpo parser.

**Views and ledgers.** A view's entries are outlines. A ledger's entry
is its heading alone, one self-contained line, so git's `union` merge
can combine what two branches added. `jadpo check` then catches what that
merge gets wrong ([Checking a record](what-is-committed-and-why.md#checking-a-record)).

**Values are Jadpo**, written on one line as the formatter writes them in
source: names, dotted names, strings, numbers and type expressions
(`Text { min_length: 1, max_length: 200 }`). Paths and URLs are never
quoted, and a file path is relative to the record's folder. A digest is
`sha256:` and 64 lower-case hex digits. A hash is 12 hex digits, since it
isn't a security check.

**Layout.** UTF-8, LF line endings, a final newline, no trailing spaces,
no tabs, one space between tokens and no alignment, so a long new name
never re-indents its neighbours. One blank line follows the header and
separates entries that have members; one-line entries have no blank
lines between them. Strings use Jadpo's escapes and only those: `\"`,
`\\`, `\n`, `\r`, `\t`. A description is the declaration's first
paragraph joined onto one `///` line, so rewrapping the comment in source
changes nothing. Records are compared with CRLF read as LF.

**Order is by identity.** Everything is sorted; only a function's
parameters keep the order they are written in. Fields sort by name,
since field order is no part of a JSON contract. Repeated facts (`fails`,
`host`) sort by value. Where the identity registry has a line for a
thing, the thing sorts by its born name, so a rename changes lines in
place instead of moving a block.

| Record | Sections, in order | Within a section |
|---|---|---|
| `api` | `api`, routes, named types, failures | Routes by path, then method; types by born name; failures by code |
| `surface` | `package`, `provider`, `provides`, operations, actions, polls, events, functions, messages, types, failures, recipes | By born name; operations by `resource.operation` |
| `identities` | One | By born name, then kind |
| `translations` | One | By locale, then member |
| `lock` | One | The `jadpo` pin, then by package name, then major version |

**Versions.** Each kind has its own format number, starting at 1. Any
change to what a kind holds or how it is laid out raises it, additions
included, since a view is compared byte for byte. A view in an older
format is out of date, and its command rewrites it. A ledger holds
history, so Jadpo reads it in every format it has ever written for that
kind, and rewrites it in the newest. A record in a newer format than this
Jadpo writes is `record.format_newer`: Jadpo never guesses. The lock's
format 1 already holds the `jadpo version` pin, since no lock had shipped
before the pin was added.

The identity registry's lines are the kind, the born name, `->` and the
name now. A field, variant or rule follows what it belongs to, so its
line gives only its own new name, and renaming `Task` again never touches
it:

```text
entity Todo -> Task
field Todo.priority -> importance
```

A translation source's line holds three facts: the hash of the default
wording it was translated from (`from`), the hash of its own wording when
that was recorded (`wording`), and `accepted` once a person accepted it
([Translations as messages](translations-as-messages.md)).
Whether a translation is stale or changed is worked out from those, never
stored:

```text
translation de_de.order_shipped from 3f9a1c0e2b7d wording 77b2e1d4c8a9 accepted
translation fr_fr.order_shipped from 3f9a1c0e2b7d wording a01c9e44f3b8
```

## Records in git and in your editor

`jadpo new` writes these lines into a new project's `.gitattributes`.
`linguist-generated=false` keeps every record's diff expanded in a pull
request, since records are committed so that their diff is read.
`merge=union` is git's own merge driver for ledgers, and needs no set-up:

```text
*.jadpo_record                       text eol=lf linguist-generated=false
jadpo.lock                           text eol=lf linguist-generated=false merge=union
database/identities.jadpo_record     merge=union
**/translations/sources.jadpo_record merge=union
```

Editors get a record language: a TextMate grammar,
`source.jadpo-record`, in `editors/vscode`, and `jadpo lsp` serving a
record's errors, outline, file links and folding.

Not built yet: the base commit's build, which comes with `jadpo
approval`, so every view is checked by its shape alone, and the record
language for editors ([What is built](records-and-manifest-built.md)).
