Local multi-project library — architecture and backlog
Status and boundary
NOT SHIPPING IN PR #118. The current beta intentionally owns one active
local project. This document defines a later local-first library without adding
cloud, accounts, analytics, client-management claims, or network access.
Implementation is tracked by issue #119.
The feature should make shop testing safer by preventing a new placement from
silently replacing the previous one. It is not a client CRM and must not invite
artists to store unnecessary personal or medical information.
Product goals
- list multiple TatMapper projects stored only in app-owned local storage;
- create, open, rename, duplicate, archive, restore, and delete projects;
- show a locally generated thumbnail and useful updated/calibration status;
- keep one immutable project ID per project;
- preserve current media ownership, atomic save, serialized writes, migrations,
and cleanup guarantees;
- make destructive actions explicit and recoverable where the platform allows;
- continue working completely offline.
Proposed data layout
Application Support/
settings.json
library/
index.json
projects/
<immutable-project-id>/
project.json
media/
placement-original.*
placement-working.*
design-original.*
design-working.*
thumbnail.webp
exports/
temporary/
project.json remains authoritative for project state. library/index.json is
a rebuildable summary index, written atomically, containing no geometry:
- project ID;
- display name;
- created/updated/last-opened dates;
- archived flag;
- current workflow step;
- thumbnail relative path;
- calibration method/status;
- unresolved save/import warning flag.
All indexed media paths are project-relative. TatMapper must refuse deletion of
any path outside its validated application-support project root.
Migration
- Detect the existing single project on first library-enabled launch.
- Validate its JSON and all TatMapper-owned media before moving.
- Create a new immutable directory using the existing project ID.
- Atomically persist the migrated project and index.
- Delete the legacy record/files only after both writes verify.
- If migration fails, leave the original project untouched and offer retry.
- Rebuild a missing/corrupt index by scanning valid project directories; never
infer a project from arbitrary external media.
Library experience
- Home shows New placement as the primary action and Projects nearby.
- Cards show project name, thumbnail, last updated, step, and calibration label.
- Search/filter stays local; useful initial filters are Active and Archived.
- Opening a project acquires one save coordinator for that project.
- Rename changes only display metadata, never the immutable directory/ID.
- Duplicate copies validated owned media into a new directory before publishing
the new index entry.
- Archive removes a project from the default active list without deleting it.
- Delete shows owned media count/size, requires confirmation, closes active file
handles, removes the complete owned directory, and verifies removal.
- A failed save or migration keeps the user on-screen with Retry/Discard; it
never silently navigates away.
- The synthetic sample remains ephemeral and never appears in the library.
Concurrency and integrity
- only one persistence write per project may be active;
- index updates are serialized separately and always use atomic replace;
- latest state wins; an older async save can never overwrite a newer state;
- duplication and migration use staging directories removed by startup cleanup;
- startup cleanup removes only recognized TatMapper staging names that are not
referenced by a valid active operation;
- schema versions for project and index migrate independently;
- a per-project edit lease prevents two in-app screens from editing one project
simultaneously;
- background/close flushes project state, then the index summary.
Privacy and export
No client name, contact field, consent record, appointment record, body/health
category, cloud backup, or automatic photo-library export is introduced. An
artist may use a neutral project name. Sharing remains an explicit OS share
action. Device backup behavior must be documented per platform before shipping.
Suggested branch and PR
- issue: Local multi-project library and safe single-project migration;
- branch:
feat/local-project-library;
- base: the reviewed/merged Android-first artist-experience stack at the time
implementation begins;
- one draft PR covering storage/index/migration/library UI only;
- no flash scanning, AR, direct printer integration, cloud, or CRM work.
Acceptance criteria
- existing single project migrates without loss and is reloadable;
- migration failure preserves the original;
- new/rename/duplicate/archive/restore/delete work offline;
- duplicate projects have independent media and immutable IDs;
- deleting one project cannot delete external or another project’s files;
- corrupt index rebuild preserves every valid project;
- sample use creates no library entry or orphan media;
- rapid edits and switching projects persist the latest state deterministically;
- failed flush blocks navigation and offers retry/discard;
- large-text, TalkBack, compact phone, unfolded phone, and tablet layouts pass;
- clean install, upgrade migration, force-stop/restart, low-storage, and
interrupted-copy device tests pass;
- Android minified APK/AAB and iOS no-sign builds remain green;
- no network, tracking, monetization, or newly unjustified plugin appears.
Test plan
- unit: index/project schemas, path validation, sort/filter, migrations,
staging cleanup, duplicate/delete ownership, atomic recovery;
- widget: empty/library/error/archive/delete/large-text states and semantics;
- integration: upgrade a legacy project, switch projects during debounced saves,
process kill/restart, duplicate, delete, rebuild a corrupt index;
- physical device: large media, low storage, background during copy, app update,
fold/unfold, TalkBack/VoiceOver, and on-disk deletion verification.