axgf-rs 0.2.0

Reference implementation of the Axiom Genealogy Format (AXGF) 1.0 — a stateless JSON-boundary library for creating, validating, importing, exporting, and converting genealogy bundles (GEDCOM 5.5.1 supported).
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
<!-- SPDX-License-Identifier: Apache-2.0 -->
# `axgf` — command-line reference

`axgf` is the standalone command-line entry point to the `axgf-rs` library.
Every V1 boundary function is exposed as one subcommand. By default each
prints a concise human summary on stdout; `--json` selects the raw JSON
envelope for piping through `jq`; `-q/--quiet` prints nothing and carries
the result in the exit code.

**Exit codes.** They are the machine-readable answer:

| Code | Meaning |
|---|---|
| `0` | Operation succeeded (`status: ok`). Warnings may still be present. |
| `1` | Operation was refused (`status: error`). The diagnostic is on stderr. |
| `2` | Reserved for `axgf validate`: the report contains at least one `error`-severity diagnostic. |

The `1` vs `2` split exists so a CI job can gate saves on `axgf validate`
without misinterpreting an invalid bundle as a broken pipeline.

---

## Installation

Pick whichever is convenient. All three produce the same binary named `axgf`.

### 1. Precompiled binary from GitHub Releases *(fastest)*

Each tagged release attaches archives for five targets. On Linux the
statically-linked musl build is the one to grab — it runs on every
distribution regardless of libc:

```bash
# Replace v0.2.0 with the tag you want.
curl -L https://github.com/plkarin/axgf-lib/releases/download/v0.2.0/axgf-v0.2.0-x86_64-unknown-linux-musl.tar.gz \
  | tar -xz
sudo mv axgf-v0.2.0-x86_64-unknown-linux-musl/axgf /usr/local/bin/
```

Other archives in the same release: `x86_64-unknown-linux-gnu`,
`aarch64-unknown-linux-gnu`, `x86_64-apple-darwin`, `aarch64-apple-darwin`,
`x86_64-pc-windows-msvc` (`.zip`). Every archive ships with a `.sha256`
sidecar.

### 2. `cargo install` from crates.io

```bash
cargo install axgf-rs
```

The crate publishes under the name **`axgf-rs`** but the binary it installs
is **`axgf`** (in `~/.cargo/bin/axgf`). The `cli` feature is on by default;
library-only consumers can opt out with `default-features = false, features
= ["gedcom"]` to avoid the `clap` dependency.

### 3. Build from source

```bash
git clone https://github.com/plkarin/axgf-lib
cd axgf-lib
cargo build --release
./target/release/axgf --help
```

Requires Rust ≥ 1.88 (the crate's MSRV).

---

## Output modes

Every subcommand honours three top-level flags:

| Flag | Behavior |
|---|---|
| *(default)* | Concise human summary on stdout. Grouped diagnostic counts on stderr. |
| `--json` | The raw envelope on stdout and nothing else — pipes cleanly into `jq`. |
| `-q`, `--quiet` | Nothing on stdout. Errors still print on stderr. Result in exit code. |

Errors are reported on stderr as `CODE: message`, one per line, and the
process exits `1`.

---

## Bundle inputs and outputs

**Input.** Every subcommand that reads a bundle takes it as a positional
`PATH`. `--input <PATH>` is still accepted as an alias for backward
compatibility within this unreleased 0.2.0. `-` reads bytes from stdin
so pipelines can compose without touching disk. The reader autodetects
the on-disk form by extension: `.axgf` is decoded as a ZIP archive
(via `import_bundle`); anything else is read as flat JSON.

**Output.** Commands that produce a bundle take `-o/--output <PATH>`:

- **`create`** and **`convert-gedcom`** require `-o` (they have no input
  file that could be edited in place).
- **`add`**, **`update`**, **`delete`**, **`dedup`**, **`export`** edit
  the input file *in place* when `-o` is omitted. The write is atomic:
  the new bytes go to a sibling tempfile first, then rename over the
  target, so a mid-write failure never leaves you with a truncated
  bundle.
- Read-only commands (`inspect`, `validate`, `import`) never take `-o`.

The output form is chosen by extension: `.axgf` → ZIP archive (calls
`export_bundle` internally), anything else → flat JSON.

---

## 60-second quickstart

Convert a GEDCOM file, validate the result, inspect it — three commands,
plain bash:

```console
$ axgf convert-gedcom tests/fixtures/small.ged -o /tmp/t.axgf
converted small.ged
  persons       3
  families      1
  events        1
  links         0
  occupations   1
  sources       1
  places        2
  documents     2
wrote t.axgf (8 KiB)

$ axgf validate /tmp/t.axgf
validated t.axgf
  errors                     0
  warnings                   3
  SCHEMA_VALIDATION_FAILED   3

$ axgf inspect /tmp/t.axgf
t.axgf
  axgf          1.0
  persons       3
  families      1
  events        1
  links         0
  occupations   1
  sources       1
  places        2
  documents     2
```

---

## Entity kinds

The kind positional argument on `add`, `update`, and `delete` accepts the
singular schema names. The list is fixed by the AXGF specification:

| kind | Collection in the flat bundle | Purpose |
|---|---|---|
| `person`     | `persons/`     | An individual. |
| `family`     | `families/`    | Union of persons + children. |
| `event`      | `events/`      | A dated fact touching one or more entities. |
| `link`       | `links/`       | Typed relationship outside the family graph. |
| `occupation` | `occupations/` | Dated profession/state of a person. |
| `source`     | `sources/`     | A cited piece of evidence. |
| `place`      | `places/`      | Geographic entity, reused across others. |
| `document`   | `documents/`   | A byte-carrying attachment (with optional metadata). |

---

## Subcommands

Every subcommand accepts `-h`/`--help`. Where an input path is documented
as `PATH`, passing `-` reads from stdin.

### `axgf create`

Create an empty bundle stamped with the current spec version.

```
axgf create [--name <NAME>] -o <PATH>
```

`--name` populates `manifest.family.name`. `-o` chooses `.axgf` (ZIP)
or `.json` (flat) by extension.

```console
$ axgf create --name "Demo" -o /tmp/demo.axgf
created bundle
  persons       0
  families      0
  events        0
  links         0
  occupations   0
  sources       0
  places        0
  documents     0
wrote demo.axgf (4 KiB)
```

Exit codes: `0` always (the library never refuses this operation).

---

### `axgf inspect` *(read-only)*

Return the manifest as-was plus freshly computed stats.

```
axgf inspect <PATH>
```

```console
$ axgf inspect /tmp/demo.axgf
demo.axgf
  axgf          1.0
  family        Demo
  persons       0
  families      0
  events        0
  links         0
  occupations   0
  sources       0
  places        0
  documents     0
```

Useful for detecting manifest drift: compare `.data.manifest.stats`
against `.data.stats` in `--json` mode — if they differ, the bundle's
header is out of sync with its entities.

Exit codes: `0` on ok, `1` on unparseable input or unsupported spec version.

---

### `axgf validate` *(read-only)*

Run structural (JSON Schema) and semantic checks.

```
axgf validate <PATH>
```

Validation is non-blocking: the library returns `Status::Ok` even on
error-severity findings so callers can decide what to do about them. The
CLI escalates to **exit code 2** when any error-severity diagnostic is
present — the report *is* the answer.

```console
$ axgf validate /tmp/t.axgf
validated t.axgf
  errors                     0
  warnings                   3
  SCHEMA_VALIDATION_FAILED   3
```

Exit codes: `0` clean or warnings-only, `1` unparseable / unsupported
spec version, `2` at least one error-severity diagnostic.

---

### `axgf add`

Insert a new entity of the given kind.

```
axgf add <KIND> <PATH> --data <PATH> [-o <PATH>]
```

The bundle path is positional; `--data` (alias `--entity`) points at the
entity JSON. A missing `id` on the entity is filled in with a fresh UUID
v4; the minted id is echoed in the summary and in `data.id` under
`--json`. Without `-o` the bundle is written back to the input path in
place.

```console
$ axgf add person /tmp/demo2.axgf --data /tmp/p.json
added person 4d9d022a-f9ed-494f-93f6-d03285262248
wrote demo2.axgf (4 KiB)
```

Exit codes: `0` on ok (schema warnings do not block the add), `1` on
`ENTITY_ALREADY_EXISTS` or bad input.

---

### `axgf update`

Replace an existing entity in full. The incoming JSON *must* carry the
target `id`.

```
axgf update <KIND> <PATH> --data <PATH> [-o <PATH>]
```

Exit codes: `0` on ok, `1` on `ENTITY_NOT_FOUND` or bad input.

---

### `axgf delete`

Delete an entity by id under a caller-chosen referential-integrity policy.

```
axgf delete <KIND> <PATH> --id <UUID> [--policy <POLICY>] [-o <PATH>]
```

`--policy` is `reject` (default), `cascade`, or `orphan`. Semantics match
[`DeletePolicy`](API.md#delete_entity):

- `reject` — refuse the delete if anything references the target;
- `cascade` — remove the target and physically remove all references;
- `orphan` — remove the target but preserve the shape of referring
  containers (scalar refs become `null`, array items keep their slot).

```console
$ axgf delete person /tmp/family.axgf --id f293... --policy reject
DELETE_BLOCKED_BY_REFERENCE: cannot delete persons/f293... under Reject: still referenced by 1 entities: ["families/a71c..."]
```

The error goes to stderr; the input file is not touched. Exit code `1`.

Exit codes: `0` on ok, `1` on `DELETE_BLOCKED_BY_REFERENCE`,
`ENTITY_NOT_FOUND`, or bad input.

---

### `axgf dedup`

Run the two safe deduplication passes. Ambiguous merges are flagged with
`MANUAL_REVIEW_REQUIRED` diagnostics rather than performed.

```
axgf dedup <PATH> [-o <PATH>]
```

```console
$ axgf dedup /tmp/family.axgf
deduplicated family.axgf
  merged persons    0
  merged families   0
  manual review     0
wrote family.axgf (…)
```

Exit codes: `0` on ok, `1` on bad input.

---

### `axgf import` *(read-only)*

Decode a `.axgf` ZIP archive and print a summary. Useful as the last
step of a pipeline that produced ZIP bytes on stdin; use `--json` to
capture the flat form for a follow-up command.

```
axgf import <PATH>
```

Exit codes: `0` on ok, `1` on `ZIP_READ_ERROR`, `INVALID_JSON`,
`INVALID_BUNDLE_STRUCTURE`, or `UNSUPPORTED_SPEC_VERSION`.

---

### `axgf export`

Rebuild a `.axgf` ZIP (or flat `.json`) from an input bundle. Stats are
recomputed before writing so the artifact is always internally
consistent.

```
axgf export <PATH> -o <PATH>
```

The output form is chosen by extension: `.axgf` → ZIP, anything else →
flat JSON.

Exit codes: `0` on ok, `1` on `ZIP_WRITE_ERROR` or bad input.

---

### `axgf convert-gedcom`

Convert a GEDCOM 5.5.1 byte stream to an AXGF bundle.

```
axgf convert-gedcom <PATH> -o <PATH> [--confidence <FLOAT>] [--place-lang <TAG>]
```

- `--confidence` (default `0.8`) is applied to imported facts that carry
  no explicit confidence signal in the source.
- `--place-lang` (default `en`) is the BCP 47 tag stored on imported place
  names when the GEDCOM has no explicit language.

Feature-gated behind `gedcom` (default-on).

```console
$ axgf convert-gedcom tests/fixtures/small.ged -o /tmp/t.axgf
converted small.ged
  persons       3
  families      1
  events        1
  links         0
  occupations   1
  sources       1
  places        2
  documents     2
wrote t.axgf (8 KiB)
```

Exit codes: `0` on ok (unrecognized tags surface as warnings), `1` on
unreadable input.

---

## Scripting patterns

### The `jq` pipeline

`--json` prints the raw envelope; `jq -c .data` extracts the payload:

```bash
axgf create --name "Karin" --json \
  | jq -c .data \
  | axgf add person - --data elise.json --json \
  | jq -c .data.bundle \
  | axgf validate -
```

Because every step is a pure function on JSON, the pipeline can be
resumed at any point by replaying the previous step's output.

### CI gate: refuse to save a bundle with structural errors

Use exit code 2 as the gate. Warnings do not count:

```yaml
- name: Validate genealogy bundle
  run: axgf validate archive/family.axgf
```

`actions/setup-node` etc. treat exit 1 and 2 identically as failures, so
the job goes red for `CYCLE_DETECTED` but stays green for
`SCHEMA_VALIDATION_FAILED` (a warning). If you *want* to fail on warnings
too, add your own predicate against `--json` output.

### Chaining without touching disk

Every subcommand accepts `-` for its input, so bundles can flow through
a script entirely in memory:

```bash
axgf convert-gedcom tree.ged --json \
  | jq -c .data.bundle \
  | axgf dedup - --json \
  | jq -c .data.bundle \
  | axgf export - -o tree.axgf
```

---

## See also

- [`API.md`]API.md — the same operations from Rust.
- [`../README.md`]../README.md — project overview and installation matrix.
- [`../SETUP.md`]../SETUP.md — local dev, feature flags, regression suite.