Milestones
The plan, and what is actually finished. Update the status here whenever milestone progress changes.
Status: β done Β· π§ in progress Β· β¬ not started
Milestone 1 β Runnable foundation β
Goal: a clean, runnable, documented foundation. No product features.
| Item | Status |
|---|---|
.gitignore protecting secrets and user data |
β |
Project memory (project-memory/) |
β |
Cross-agent instructions (AGENTS.md, per-tool pointers) |
β |
Documentation (README.md, docs/*.md) |
β |
LICENSE (PolyForm Noncommercial), LICENSING.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md, CHANGELOG.md |
β |
project.json + scripts/set_identity.py for project identity |
β |
Session memory (SESSION_PROTOCOL.md, sessions/, scripts/session.py) |
β |
.env.example with safe placeholders |
β |
| Backend: FastAPI, health + config endpoints, settings, event bus | β |
| Backend tests (pytest) | β |
| Frontend: React + TypeScript + Vite single-page workspace | β |
| Light mode default, dark mode toggle | β |
| Frontend tests (Vitest) | β |
| Docker + Docker Compose | β |
| GitHub Actions CI | β |
| GitHub Pages documentation foundation | β |
| Clean initial commit | β |
Deliberately excluded: any importer, any archive browsing, any search, any cloud or sync code, authentication, multi-user support.
Milestone 2 β ChatGPT importer β
Goal: import a ChatGPT export and store it as human-readable files.
| Item | Status |
|---|---|
Importer protocol: detect(), validate(), parse() |
β |
| Registry so core code never imports a provider module | β |
ChatGPT parser, including the branching mapping tree |
β |
Normalisation into Conversation / Message |
β |
| Markdown + JSON metadata on disk, one folder per conversation | β |
| Hostile-input handling: zip slip, zip bombs, size caps, path traversal | β |
archive.imported and conversation.created events |
β |
| Import UI: choose a file, see progress, plain-language result | β |
| Tests for malformed, truncated and hostile input | β |
Deliberately excluded: browsing or reading imported conversations, search, the SQLite schema, attachments and images (recorded as placeholders in the Markdown), abandoned conversation branches.
Not generalised. The adapter interface stays shaped by one real importer. It gets generalised in Milestone 5 against a genuine second case.
Milestone 3 β Archive browser, Markdown and search β
| Item | Status |
|---|---|
| Browse imported conversations | β |
| Render Markdown for reading | β |
| SQLite schema for metadata and indexing | β |
| Full-text search using SQLite FTS5 | β |
| The database is rebuildable from the files on disk | β enforced by a test |
| Rebuild on startup, and on demand | β |
No Elasticsearch or external search infrastructure, as planned.
Deliberately excluded: editing or deleting conversations from the interface, tags, projects, and any per-message structure β the whole Markdown file is rendered rather than parsed back into messages.
Milestone 3.5 β Import ergonomics β
Slotted in after Milestone 3 because getting an export out of ChatGPT turned out to be the real obstacle to using the product, not anything in the code. OpenAIβs own email says the export βmay take a few daysβ β see RESEARCH.md R-004.
| Item | Status |
|---|---|
| Watched inbox folder β drop an export in and it imports itself | β |
| Configurable to a folder you already keep exports in, files left in place | β |
| Honest re-import reporting: new, updated, already there | β |
| Unchanged conversations are not rewritten at all | β |
| Correct guidance: days to prepare, 24h link expiry, one request at a time | β |
scripts/inspect_export.py β structure only, safe to share |
β |
scripts/make_fixture_export.py β large messy synthetic exports |
β |
scripts/browser/chatgpt-export.js β optional fast path, run by the user |
β |
Deliberately excluded: a filesystem watcher (scanning covers it), progress reporting during a long import, and anything that puts a session token inside Mind Archive (D-024).
Milestone 4 β Tags β
| Item | Status |
|---|---|
Tags stored in metadata.json, indexed in SQLite |
β |
| An import never removes a tag | β enforced by a test |
| Add and remove tags on a conversation | β |
| Filter by tag, combined with search | β |
| Tag counts | β |
conversation.tagged event |
β |
Deliberately excluded (D-026): Projects, and the Memory / Document /
Attachment model types originally listed here. Tags plus full-text search
already answer the question people actually have. A schema with no feature
behind it is one nobody has tested against a real need.
Projects are in docs/BACKLOG.md, most likely as a reserved tag namespace
rather than a parallel hierarchy.
Milestone 5 β A second provider, and getting everything back out β
| Item | Status |
|---|---|
| A second real importer (Claude) | β |
The Importer interface generalised against it |
β |
| Shared JSON reading extracted once two callers wanted it | β |
| Export the whole archive as ordinary files | β |
StorageProvider interface |
β¬ deferred to Milestone 6 (D-028) |
What the second provider revealed. ChatGPT and Claude both ship a file
called conversations.json, and detection matched on the filename β so the
ChatGPT importer would have claimed a Claude export and reported it empty. A
latent bug from Milestone 2 that no amount of testing one importer could find.
Detection now inspects shape (D-027).
Why StorageProvider waits. An interface with one implementation is a guess
about the second, and this milestone is the evidence: the Importer interface
only revealed its defect when a real second case arrived. Building
StorageProvider now β with cloud still a milestone away β would repeat the
mistake this milestone just corrected (D-028).
Milestone 6 β Optional cloud storage and synchronisation β¬
SyncProvider, kept separate fromStorageProviderstorage.mode=localΒ·cloudΒ·both- Disabled by default; never silently enabled (see DECISIONS.md D-011)
- Candidate adapters: S3-compatible, WebDAV, self-hosted. Not all at once.
Milestone 7 β Production hardening and public release β¬
- Security review, packaging, release process, public documentation site