Versioned export.exportLedgerBackup writes schema version 2 with the confirmed logs and the training contribution receipts (including withdrawn tombstones). Both are written through an allowlist, so unknown keys and credentials are never copied out of the device.
Migration on import.importLedgerBackup accepts version 1 and 2. Version 1 entries are upgraded, the notice names the version the file came from, and logs that predate provenance keep provenance: null rather than being given a source they never recorded.
Fail closed. A file from a newer Timmy is refused with an update instruction; malformed JSON, non-Timmy files, oversized files and over-limit entry counts are refused before anything is replaced. A refused import leaves the ledger unchanged.
Provenance on the record. A log saved from a confirmed or corrected AI suggestion records source, the suggested visible fields, the answering model, and confirmedAt. The entry sheet states it in the app's own words.
Shell cache v5 -> v6 so installed clients receive the changed app.js / src/domain.js.
Evidence
tests/portability.test.js — 7 unit cases (export version + provenance, v1 upgrade notice, receipt/tombstone round trip with secret stripping, future-version refusal, malformed/non-Timmy refusal, oversized/over-limit refusal, unreadable-receipt notice).
tests/portability.acceptance.mjs — real browser against the real server: v1 backup upgraded with the version on screen, v3 file refused with the ledger byte-identical, provenance recorded by an actual confirm-and-save, export at version 2, and a full round trip.
tests/release-demo.test.js — new gate for the four on-camera captions and the portable_backup_restore release key.
scripts/record_release_demo.mjs — new scenes record the real export file, import a fictional v1 backup, refuse a v3 backup, and reopen a restored log showing its AI provenance. All claims are asserted in the browser before the caption; the run recorded 69/69 captions with pixel proof.
scripts/build_release.py — requires those captions and runs npm run test:portability.
Boundary
No change to the medical/non-diagnostic boundary, no inference exposure, no model weights, no real medical images (synthetic Type 4 fixture and fictional logs only), no production services touched. Not merged — RC approval stays human.
Test results
npm test 215/216 on this branch. The one failure, tests/staging-health.test.js «malformed and escaping base paths are rejected at startup», is a pre-existing timing flake: it fails the same way on unmodified main under the full parallel suite (206/207) and passes standalone on both branches.
Closes #35
## What changed
- **Versioned export.** `exportLedgerBackup` writes schema version 2 with the confirmed logs *and* the training contribution receipts (including withdrawn tombstones). Both are written through an allowlist, so unknown keys and credentials are never copied out of the device.
- **Migration on import.** `importLedgerBackup` accepts version 1 and 2. Version 1 entries are upgraded, the notice names the version the file came from, and logs that predate provenance keep `provenance: null` rather than being given a source they never recorded.
- **Fail closed.** A file from a newer Timmy is refused with an update instruction; malformed JSON, non-Timmy files, oversized files and over-limit entry counts are refused before anything is replaced. A refused import leaves the ledger unchanged.
- **Provenance on the record.** A log saved from a confirmed or corrected AI suggestion records `source`, the suggested visible fields, the answering model, and `confirmedAt`. The entry sheet states it in the app's own words.
- **Shell cache v5 -> v6** so installed clients receive the changed `app.js` / `src/domain.js`.
## Evidence
- `tests/portability.test.js` — 7 unit cases (export version + provenance, v1 upgrade notice, receipt/tombstone round trip with secret stripping, future-version refusal, malformed/non-Timmy refusal, oversized/over-limit refusal, unreadable-receipt notice).
- `tests/portability.acceptance.mjs` — real browser against the real server: v1 backup upgraded with the version on screen, v3 file refused with the ledger byte-identical, provenance recorded by an actual confirm-and-save, export at version 2, and a full round trip.
- `tests/release-demo.test.js` — new gate for the four on-camera captions and the `portable_backup_restore` release key.
- `scripts/record_release_demo.mjs` — new scenes record the real export file, import a fictional v1 backup, refuse a v3 backup, and reopen a restored log showing its AI provenance. All claims are asserted in the browser before the caption; the run recorded 69/69 captions with pixel proof.
- `scripts/build_release.py` — requires those captions and runs `npm run test:portability`.
## Boundary
No change to the medical/non-diagnostic boundary, no inference exposure, no model weights, no real medical images (synthetic Type 4 fixture and fictional logs only), no production services touched. Not merged — RC approval stays human.
## Test results
`npm test` 215/216 on this branch. The one failure, `tests/staging-health.test.js` «malformed and escaping base paths are rejected at startup», is a pre-existing timing flake: it fails the same way on unmodified `main` under the full parallel suite (206/207) and passes standalone on both branches.
A backup is the person's own record and has to keep working as the AI fields evolve.
- Export stamps schema version 2 and carries the confirmed logs plus the training
contribution receipts, including withdrawn tombstones; both are written through an
allowlist so unknown keys and credentials never leave the device.
- Import accepts version 1 and 2. A version 1 file is upgraded, the notice names the
version it came from, and entries that predate provenance stay null instead of being
given a source they never recorded.
- A file from a newer Timmy is refused with an update instruction, malformed, oversized
and over-limit files fail closed, and a refused import leaves the ledger unchanged.
- A log saved from a confirmed or corrected AI suggestion records its provenance
(source, suggested visible fields, model, confirmedAt) and the entry sheet states it.
- Service worker shell cache v5 -> v6 so installed clients actually receive the change.
Evidence: tests/portability.test.js (7 unit cases), tests/portability.acceptance.mjs
(real browser: v1 upgrade, future refusal with unchanged ledger, provenance recorded,
export v2, round trip), release demo scenes with on-camera caption proof, and the
release builder gate portable_backup_restore.
Refs #35
Reviewed the full 16-file diff: export/import go through allowlists, provenance is bounded to source/suggested visible fields/model/confirmedAt, v1 files keep provenance: null instead of inventing a source, newer-version and oversized/malformed files fail before replacement, and the demo asserts each measured claim before its caption (version-2 export, v1 upgrade notice, ledger bytes unchanged after refusal, restored provenance line). CI quality gate is green on 2094a07. Approving.
Reviewed the full 16-file diff: export/import go through allowlists, provenance is bounded to source/suggested visible fields/model/confirmedAt, v1 files keep `provenance: null` instead of inventing a source, newer-version and oversized/malformed files fail before replacement, and the demo asserts each measured claim before its caption (version-2 export, v1 upgrade notice, ledger bytes unchanged after refusal, restored provenance line). CI quality gate is green on 2094a07. Approving.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #35
What changed
exportLedgerBackupwrites schema version 2 with the confirmed logs and the training contribution receipts (including withdrawn tombstones). Both are written through an allowlist, so unknown keys and credentials are never copied out of the device.importLedgerBackupaccepts version 1 and 2. Version 1 entries are upgraded, the notice names the version the file came from, and logs that predate provenance keepprovenance: nullrather than being given a source they never recorded.source, the suggested visible fields, the answering model, andconfirmedAt. The entry sheet states it in the app's own words.app.js/src/domain.js.Evidence
tests/portability.test.js— 7 unit cases (export version + provenance, v1 upgrade notice, receipt/tombstone round trip with secret stripping, future-version refusal, malformed/non-Timmy refusal, oversized/over-limit refusal, unreadable-receipt notice).tests/portability.acceptance.mjs— real browser against the real server: v1 backup upgraded with the version on screen, v3 file refused with the ledger byte-identical, provenance recorded by an actual confirm-and-save, export at version 2, and a full round trip.tests/release-demo.test.js— new gate for the four on-camera captions and theportable_backup_restorerelease key.scripts/record_release_demo.mjs— new scenes record the real export file, import a fictional v1 backup, refuse a v3 backup, and reopen a restored log showing its AI provenance. All claims are asserted in the browser before the caption; the run recorded 69/69 captions with pixel proof.scripts/build_release.py— requires those captions and runsnpm run test:portability.Boundary
No change to the medical/non-diagnostic boundary, no inference exposure, no model weights, no real medical images (synthetic Type 4 fixture and fictional logs only), no production services touched. Not merged — RC approval stays human.
Test results
npm test215/216 on this branch. The one failure,tests/staging-health.test.js«malformed and escaping base paths are rejected at startup», is a pre-existing timing flake: it fails the same way on unmodifiedmainunder the full parallel suite (206/207) and passes standalone on both branches.Reviewed the full 16-file diff: export/import go through allowlists, provenance is bounded to source/suggested visible fields/model/confirmedAt, v1 files keep
provenance: nullinstead of inventing a source, newer-version and oversized/malformed files fail before replacement, and the demo asserts each measured claim before its caption (version-2 export, v1 upgrade notice, ledger bytes unchanged after refusal, restored provenance line). CI quality gate is green on2094a07. Approving.