# Vaults, handover, and surviving your own death

Where the work lives so that an ephemeral agent session, a reset container, and a human on a
laptop can all collaborate on it without losing anything.

The store here is `sgit` — a zero-knowledge encrypted, git-like versioned folder synced to a
server that never sees plaintext. Any equivalent works; what matters is the **split** and the
**contracts**, not the tool.

---

## 1. Two stores, not one

```
code vault                              data vault
├── the extension source                ├── captures/      verbatim exports, one per run
├── tests                               ├── derived/       one small stable file per run
├── tools/ (deriver, builders)          ├── contacts/      one file per entity
├── notes/NOTES.md   numbered lessons   ├── app-data.json  everything merged, built
├── guides/          these documents    ├── index.html     the viewer app
└── README.md        changelog          ├── INDEX.md       one row per capture run
                                        └── HANDOVER.md    the contract, for a stranger
```

**Why they are separate**, in order of how much each reason matters:

1. **Different sensitivity.** The data vault holds verbatim payloads with real names and real
   identifiers. The code vault holds no personal data and can be mirrored to a public
   repository. One store would mean the most restrictive rule governs everything.
2. **Different lifecycles.** Code changes many times a day; captures arrive once every few
   days and are never edited afterwards.
3. **Different sizes.** A run is 50–120 MB. Mixing that with source makes every clone of the
   source expensive, and this matters more than it sounds when the agent's container is
   ephemeral and re-clones from scratch.
4. **Different failure modes.** Corrupting a capture is unrecoverable — it is evidence.
   Corrupting code is a revert.

The code vault carries an explicit **publication rule by folder**, stated at the top of its
README: which directories may be mirrored publicly and which never leave. That rule is written
down rather than remembered, because the answer is not obvious for every folder and the cost of
getting it wrong is asymmetric.

---

## 2. The deriver is the seam between them

The one tool that spans both vaults lives in the **code** vault and writes into the **data**
vault:

```
node ../code-vault/tools/derive.mjs  captures/<run>.full.json  derived/<run>.derived.json
node tools/build-app.mjs        # every derived file → one merged dataset + the viewer
node tools/build-contacts.mjs   # every derived file → one file per entity
```

Three rules that make this safe:

- **Captures are read-only.** Nothing ever writes into `captures/`. It is evidence.
- **Everything else is regenerable.** `derived/`, the merged dataset, the contact files, the
  viewer's inlined data — all rebuilt from captures by running two commands. If a parser
  improves, re-derive everything; it takes seconds and retroactively improves every past run.
- **The derived format is versioned and refused when unknown.** Builders exit non-zero on an
  old version rather than merging partially. A v1 file has no event log, and silently reading
  one understates the dataset without saying so.

That last rule is what makes improving a parser a *safe* operation instead of a scary one.

---

## 3. The documents that make a stranger productive

Four, each with a job. Together they are how an agent with no memory of the project becomes
useful in ten minutes.

### `HANDOVER.md` — the contract

Written for a stranger, updated as part of the work rather than as a farewell. Sections:

- What this is, and the boundaries (what the tool will and will not do)
- **The data contract** — every field of the derived format, what `null` means for each
- What is settled, with the evidence
- What is still open, with what would settle it
- **What we have been wrong about** — see §5
- Provenance, and why absence means nothing

The "why absence means nothing" section is the one people skip and shouldn't. A dataset built
from observation needs a paragraph explaining that a missing edge means "not captured", and
where the coverage tables are that prove it.

### `INDEX.md` — one row per capture run

| Date | Build | Records | Surfaces | full? | What it settled |
|---|---|---|---|---|---|

The "what it settled" column is the valuable one, and it is written the day the run is
processed. Not "what was captured" — what question it *answered*, including negative results.
Some of the most useful rows read *"zero of X captured despite 180 observed"*, which is how a
ten-day-old silent failure was eventually diagnosed.

Rows are corrected in place when later evidence overturns them (§5), never rewritten.

### `NOTES.md` — numbered, append-only lessons

Thirty-three entries in five weeks. Each is a mistake or a discovery with the reasoning intact,
and each has a stable number that gets cited **from code comments**:

```js
// The chunks are not a bag — they are one tree cut into numbered pieces. See NOTES #31.
```

That citation is the mechanism. A future reader hitting surprising code gets the full reasoning
in one hop, permanently, instead of it living in a chat log nobody will search. Write the note
when the lesson is fresh; half of ours would not exist if deferred to the end of a session.

### `README.md` — the changelog as narrative

Every version bump gets an entry explaining **why**, with measured evidence in it. Not "added
X" but "X was added because a run showed Y, measured at Z". This is the spine that connects a
line of code to the run that caused it.

---

## 4. The session loop

```
session start │  clone both vaults            (a fresh container has nothing)
              │  read HANDOVER, INDEX, recent NOTES
              ▼
    work      │  human hands over a capture; agent derives, analyses, ships code
              │  commit + push AT EVERY MEANINGFUL UNIT, not at the end
              ▼
 session end  │  push both vaults; INDEX row written; NOTES entry written
              │  (the session does not get to choose when it ends)
```

**Commit and push at every meaningful unit.** This project survived a container reset that
destroyed every local file mid-task and lost nothing, because the last push had been minutes
earlier. The habit costs seconds; the alternative costs a day.

The one thing that did *not* survive the reset was **access**: the credentials to reach the
stores were in the destroyed container. Design so that is the only thing you ever need to ask
the human for on restart, and expect to ask for it.

Two practical notes from that recovery:

- After re-cloning, **verify you are on the same lineage** before pushing — compare the HEAD
  commit id against what the human's clone reports. Branch labels can differ between clones
  while the history is identical; the commit id is the thing that settles it.
- Have a fallback delivery path. If a push lands somewhere the human's clone cannot pull, hand
  them the changed files directly with their paths preserved. Do not leave a session's work
  reachable only through infrastructure that just proved unreliable.

---

## 5. Corrections stay where the mistake was

The rule across every document in both vaults: **a falsified claim is never edited away.** It is
struck through or annotated in place, with the correction beside it and a pointer to the note
that explains it.

```markdown
| 2026-08-26 | v0.13.0 | … | **CORRECTED — the original text of this row is kept below,
struck through, because the mistake is the useful part.** … See NOTES #30.
~~Confirms the finding on a real, non-truncated sample: …~~ |
```

Three reasons, and they compound:

1. A confident wrong claim left in a document is a trap for the next reader — including the
   next session of the same agent, which will read it as settled fact. Deleting it removes the
   trap but also removes the warning.
2. The *reasoning* that produced the error is reusable in a way the error is not.
3. It calibrates everything else. A document with visible corrections is one whose uncorrected
   parts you can trust.

The handover doc keeps a running numbered list titled **"What we have been wrong about — read
before trusting older notes"**. Five entries so far. It is the first thing a new session should
read, and it is the section that most reliably prevents a repeat.

---

## 6. Per-entity files, and structural guarantees

One pattern worth stealing wholesale. Where a merged dataset is one document for one reader, an
entity record is the opposite shape: read one at a time, accumulating slowly over months,
diffable, greppable, syncable. So: **one JSON file per entity**, plus an index for anything that
wants the list without reading two hundred files.

Two design decisions did the real work.

**Membership is structural, not a filter.** An entity file exists only for something seen on a
specific authoritative surface — for us, the list the target itself renders of first-degree
connections. Visiting a profile *enriches* an existing record and can never create one.

That is much stronger than a `degree: 1` field, because a field has to be trusted and a
provenance rule does not. Profiles get opened for all sorts of reasons; "appeared on the list
the target rendered for you" is a fact about a page, and it makes the constraint impossible to
violate by accident.

**Fields remember being different.** The target holds only the current value: today's job title
replaces yesterday's and yesterday's is gone. Capturing across runs is the only way to see
someone change job — so a changed value appends rather than overwrites:

```jsonc
"headline": {
  "value": "Current title",
  "firstSeen": "2026-09-01T11:36:25Z",
  "lastSeen":  "2026-09-08T15:40:02Z",
  "history": [ { "value": "Previous title", "firstSeen": "…", "lastSeen": "…" } ]
}
```

And: **nothing is deleted on absence.** A run that did not open the list is silence, not a
disconnection. This is the same rule as "report coverage, not just results" (guide 03 §9),
applied to time instead of surfaces.

---

## 7. The viewer, and why it ships as one file

The data vault contains a self-contained `index.html` that renders the whole dataset —
charts, tables, coverage. It fetches the merged dataset from the vault and **falls back to an
inlined copy** built into the file, so it renders standalone when opened from anywhere.

Worth the constraint. It means the human can look at the data without a server, a build step or
a network, and it means the agent can screenshot it headlessly for verification. Building a
viewer that requires infrastructure is how a dataset ends up unexamined.

Two things it does that any such viewer should:

- **Never draws a missing value as zero.** Absent numbers render as `—` and are excluded from
  charts, with a caption saying how many were excluded and why.
- **Has a coverage tab.** Per-surface, per-run: what was captured, when, and what has never been
  looked at. The tab that tells you the dataset is incomplete is the one that makes the rest
  trustworthy.

---

## 8. A checklist for starting one of these

- [ ] Two stores: code (publishable, small, fast-moving) and data (private, large, append-only).
- [ ] A written publication rule, per folder, in the code store's README.
- [ ] `captures/` is read-only evidence; everything else is regenerable from it.
- [ ] A versioned derived format that builders **refuse** when unknown.
- [ ] `HANDOVER.md` written for a stranger, including "what we have been wrong about".
- [ ] `INDEX.md` with one row per run and a "what it settled" column.
- [ ] `NOTES.md`, numbered, append-only, cited from code comments.
- [ ] A changelog that says *why*, with measurements.
- [ ] Commit and push at every meaningful unit.
- [ ] Corrections annotated in place, never edited away.
- [ ] Coverage reported everywhere, so "no edge" and "never looked" stay distinguishable.
