Implements #35.
- importLedger migrates prior schema versions (v0 bare-array legacy
exports and the v1 envelope) and fails safely on future versions,
malformed JSON, wrong-product envelopes, and oversized files with a
new 2 MiB MAX_IMPORT_BYTES guard applied before parsing.
- exportLedger normalizes entries through sanitizeEntry so confirmed
values and bounded provenance round-trip while smuggled secrets and
unknown fields never enter the portable file.
- Entries may carry a whitelisted provenance origin ('user' or
'ai-suggestion'); mergeVisualSuggestion records 'ai-suggestion' only
when a suggestion is actually applied, keeping nonvisual fields
user-owned.
- App import now merges into the existing ledger instead of replacing
it, so a failed or partial import can never silently drop
user-owned records.
- Service-worker shell cache bumped to v6 (per base-path namespace)
so installed PWAs receive the migration code; old v5 caches are
purged on activation.
- New tests/ledger-portability.acceptance.mjs browser gate covers
export round trip, merge import, safe-failure surfacing, root vs
/timmy-staging storage isolation, and Delete Everything for both
namespaces; wired into package.json test:portability and CI quality.yml.
Deterministic medical safety unchanged: urgent-flag detection, red-flag
copy, and chat escalation paths are untouched; all fixtures synthetic.