From 8dd7141f200bb38420c860457737c9e0b0039ee0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 21 Aug 2026 12:50:57 +0000 Subject: [PATCH] docs(CES-70): mark ZIP import shipped on main; M2 closed PR #25 is merged. Delivery plan, specs, and the CES-70 prompt now match that: M2 (export + import) is closed and CES-71 is next. Co-authored-by: JMNofziger --- client/README.md | 2 +- docs/product/README.md | 2 +- docs/product/delivery-plan-v1.md | 36 +++++++++++++---------- docs/product/prompts/ces-70-import.md | 26 ++++++++-------- docs/product/ux/UX_IMPLEMENTATION_GAPS.md | 4 +-- docs/product/ux/cestovni-views.md | 2 +- docs/specs/ARCHITECTURE.md | 2 +- docs/specs/README.md | 2 +- docs/specs/export-import.md | 6 ++-- docs/specs/export-v1.md | 2 +- docs/specs/sync-protocol.md | 2 +- tests/export/README.md | 2 +- tests/import/README.md | 3 +- 13 files changed, 49 insertions(+), 42 deletions(-) diff --git a/client/README.md b/client/README.md index bfd3048..398bf2b 100644 --- a/client/README.md +++ b/client/README.md @@ -1,7 +1,7 @@ # Cestovni — mobile client **Stack:** Flutter + Drift (ADR 003). -**Milestones:** M0 closed (CES-36, CES-37). **Android M1 closed** on `main` (`bb1d5d5`, 2026-08-16) — CES-38 math, CES-39 Log/History/vehicles, CES-57/65 prefs, CES-66 Metrics, CES-67 Maint, CES-40 photos. **CES-41 export** on `main`. **CES-70 import** implemented (Settings → Import data, `client/lib/import/`); spec tests green on PR #25, not on `main`. See [`docs/product/delivery-plan-v1.md`](../docs/product/delivery-plan-v1.md). +**Milestones:** M0 closed (CES-36, CES-37). **Android M1 closed** on `main` (`bb1d5d5`, 2026-08-16) — CES-38 math, CES-39 Log/History/vehicles, CES-57/65 prefs, CES-66 Metrics, CES-67 Maint, CES-40 photos. **CES-41 export** and **CES-70 import** on `main` (Settings → Import data, `client/lib/import/`). See [`docs/product/delivery-plan-v1.md`](../docs/product/delivery-plan-v1.md). ## Quick start diff --git a/docs/product/README.md b/docs/product/README.md index 54662ed..6a33bd5 100644 --- a/docs/product/README.md +++ b/docs/product/README.md @@ -24,7 +24,7 @@ Store listing skeleton, privacy policy outline, in-app Data & privacy bullets: ` ## Delivery (Phase 3 / Stage 5) -Active engineering breakdown, milestone spine, per-vertical backlog with `Spec:` paths, test strategy, and **repo progress**: `[delivery-plan-v1.md](delivery-plan-v1.md)` (RYG checklist is source of truth). Linear epic **[CES-35](https://linear.app/personal-interests-llc/issue/CES-35)**. **M0 closed** (**CES-36** / **CES-37**). **Android M1 closed** on `main` (`bb1d5d5`, 2026-08-16): **CES-38** / **CES-39** / **CES-57** / **CES-65** / **CES-66** / **CES-67** / **CES-40** **Done**. **CES-41** export shipped. In flight: **[CES-70](https://linear.app/personal-interests-llc/issue/CES-70)** ZIP import — prompt [`prompts/ces-70-import.md`](prompts/ces-70-import.md); spec tests green on PR #25, not on `main`. Prerequisites **CES-53**–**CES-56** **Done**. Linear MCP/API: [`../linear/mcp-setup.md`](../linear/mcp-setup.md). +Active engineering breakdown, milestone spine, per-vertical backlog with `Spec:` paths, test strategy, and **repo progress**: `[delivery-plan-v1.md](delivery-plan-v1.md)` (RYG checklist is source of truth). Linear epic **[CES-35](https://linear.app/personal-interests-llc/issue/CES-35)**. **M0 closed** (**CES-36** / **CES-37**). **Android M1 closed** on `main` (`bb1d5d5`, 2026-08-16): **CES-38** / **CES-39** / **CES-57** / **CES-65** / **CES-66** / **CES-67** / **CES-40** **Done**. **M2 closed:** **CES-41** export + **CES-70** import on `main` (PR #25). Next coding: **[CES-71](https://linear.app/personal-interests-llc/issue/CES-71)** cadence rename. Ops: **[CES-63](https://linear.app/personal-interests-llc/issue/CES-63)**. Prerequisites **CES-53**–**CES-56** **Done**. Linear MCP/API: [`../linear/mcp-setup.md`](../linear/mcp-setup.md). ## Specs diff --git a/docs/product/delivery-plan-v1.md b/docs/product/delivery-plan-v1.md index be29678..abcbda6 100644 --- a/docs/product/delivery-plan-v1.md +++ b/docs/product/delivery-plan-v1.md @@ -12,20 +12,24 @@ Stage 5 exit (copied from workflow): **running build with test strategy tied to ## Current focus -**In flight:** **[CES-70](https://linear.app/personal-interests-llc/issue/CES-70) ZIP import** — implementation + spec tests on draft [PR #25](https://github.com/JMNofziger/cestovni/pull/25); **not on `main` yet**. Product locks resolved 2026-08-16: mode is **replace**, cross-account imports stay a warning. **Do not start M3 (CES-42–45)** unless product redirects. Parallel ops: **[CES-63](https://linear.app/personal-interests-llc/issue/CES-63)** iPhone install-doc + T1; **[CES-68](https://linear.app/personal-interests-llc/issue/CES-68)** Android APK anytime for demo distribution. +**Shipped:** **[CES-70](https://linear.app/personal-interests-llc/issue/CES-70) ZIP import** is on `main` ([PR #25](https://github.com/JMNofziger/cestovni/pull/25), merge `7670226`). Replace-mode Settings → **Import data**; spec § Test expectations green. **M2 (export + import) is closed.** + +**In flight (ops):** **[CES-63](https://linear.app/personal-interests-llc/issue/CES-63)** iPhone install-doc + T1. **[CES-68](https://linear.app/personal-interests-llc/issue/CES-68)** Android APK anytime for demo distribution. + +**Next coding:** **[CES-71](https://linear.app/personal-interests-llc/issue/CES-71)** `cadence_km` → `cadence_m` — **unblocked**. Import must keep accepting `cadence_km` (schema 1) **and** `cadence_m` (schema 2). **M3 (CES-42–45)** is eligible now that import shipped; do **not** start it until product says so. | Track | Issue | Why now | Done when | | ----- | ----- | ------- | --------- | -| **A — M2 import** | **[CES-70](https://linear.app/personal-interests-llc/issue/CES-70)** | CES-41 export shipped; import is the matching restore path | Replace-mode restore of vehicles / fill-ups / maint / settings; photos never imported; spec § Test expectations green | +| **A — cadence rename** | **[CES-71](https://linear.app/personal-interests-llc/issue/CES-71)** | CES-70 shipped; column name still lies | Migration `0004`; export/import accept old + new header | | **B — M-dist (ops)** | **[CES-63](https://linear.app/personal-interests-llc/issue/CES-63)** | CI deploy live; install doc + phone T1 still open | [`install-ios.md`](install-ios.md) finalized; iPhone T1 on **CES-62** | -**Shipped this cycle:** **CES-41** (Export ZIP — Settings → Export data, STORE ZIP, photos excluded). Earlier: **CES-67** (Maintenance) and **CES-40** (receipt photos). **M3** stays on the spine after import, not before. +**Shipped this cycle:** **CES-70** (ZIP import, replace) and **CES-41** (Export ZIP). Earlier: **CES-67** (Maintenance) and **CES-40** (receipt photos). **Prompts (executed):** [`prompts/ces-67-maintenance.md`](prompts/ces-67-maintenance.md) · [`prompts/ces-40-photo-pipeline.md`](prompts/ces-40-photo-pipeline.md) · [`prompts/ces-41-export.md`](prompts/ces-41-export.md) · [`prompts/ces-70-import.md`](prompts/ces-70-import.md) -**Spec:** [`../specs/export-import.md`](../specs/export-import.md) — **Complete (v1)**, replace mode locked. Do **not** start M3 (CES-42–45) until product redirects. +**Spec:** [`../specs/export-import.md`](../specs/export-import.md) — **Complete (v1)**, on `main`. CES-71 is the next client vertical. -**Manual checklist (CES-40):** [`ces-40-manual-test.md`](ces-40-manual-test.md) — needs a physical Android device with a camera; not runnable in CI or on the Cloud VM. CES-41 device timing (10k rows / 30 s) rides along with **[CES-68](https://linear.app/personal-interests-llc/issue/CES-68)**. +**Manual checklist (CES-40):** [`ces-40-manual-test.md`](ces-40-manual-test.md) — needs a physical Android device with a camera; not runnable in CI or on the Cloud VM. CES-41/CES-70 device timing (10k rows / 30 s) rides along with **[CES-68](https://linear.app/personal-interests-llc/issue/CES-68)**. --- @@ -37,7 +41,7 @@ Ship in dependency order. Each milestone is a coherent Cursor execution; vertica flowchart LR M0[M0 Bootstrap + DB] M1[M1 Local logging + math] - M2[M2 Export] + M2[M2 Export + import] M3[M3 Backup + restore] M4[M4 Telemetry + CI gate] M5[M5 Hardening + rollback tooling] @@ -53,7 +57,7 @@ flowchart LR - **M0 — Bootstrap + DB:** mobile client shell + client DB schema + migrations. Unblocks everything. **Repo status:** client app + Drift `schema_version` **3** (post-CES-57 `default_vehicle_id`; prior CES-53 maintenance migration at v2) + tests + fixtures landed; see `client/README.md`. - **M1 — Local logging + math:** fill-up/vehicle UI + consumption math + photo pipeline. Offline app is usable end-to-end without a server. -- **M2 — Export:** streaming ZIP + manifest + CSVs. Lets users get their data out before backup exists. +- **M2 — Export + import:** streaming ZIP + manifest + CSVs out; replace-mode ZIP restore in. Lets users get data out and move it between devices before a server exists. **Closed on `main`** (CES-41 + CES-70). - **M3 — Backup + restore:** server Postgres + RLS + API + outbox + restore + dead-letter UX. Closes the ADR 002 loop. - **M4 — Telemetry + CI gate:** `ci/telemetry-gate.`* + client wiring once SDK landed (ADR 004). Can start in parallel with M1 once M0 ships. - **M5 — Hardening:** migration rollback tooling + end-to-end integration tests + Stage 5 exit verification. @@ -96,7 +100,7 @@ Rollup mirrors milestones **M0→M5** and verticals **CES-36..CES-47** ([epic CE ### M1 — Local logging + math -- 🟩 **M1 rollup (closed 2026-08-16)** — offline logging usable end-to-end without a server. Log / History / vehicles / settings / metrics / Maint / photos on `main` (`bb1d5d5`). **CES-41 export shipped** (M2). **CES-70 import** is the remaining M2 work (tests green on PR #25, not on `main`) — not M3. +- 🟩 **M1 rollup (closed 2026-08-16)** — offline logging usable end-to-end without a server. Log / History / vehicles / settings / metrics / Maint / photos on `main` (`bb1d5d5`). **CES-41 export + CES-70 import shipped** (M2 closed). Next client coding is **CES-71**. - 🟩 **CES-38 — Consumption math + golden tests** — **Done in repo on `main`** (2026-05): `client/lib/consumption/`, auto-discovery runner over 20 `tests/math/fixtures/`, module-purity test, validation wired in Log/History save paths. Follow-ups: **CES-51** / **CES-52** (non-blocking). - 🟩 **CES-39 — Fill-up + vehicle UI (core)** — **Done** (repo 2026-05, Linear 2026-07-17): repos + vehicle CRUD + Log/History UI; 121+ tests. Out of scope → **CES-65** / **CES-66** / **CES-67** / **CES-40**. - 🟩 **CES-57 — Settings prefs + default vehicle** — **Done** (PR #9, Linear Done). Display wiring closed as **CES-65**. @@ -108,13 +112,13 @@ Rollup mirrors milestones **M0→M5** and verticals **CES-36..CES-47** ([epic CE ### M2 — Export + import -- 🟨 **M2 rollup** — on-device ZIP export shipped; import implemented with spec tests green on PR #25 (not on `main`). +- 🟩 **M2 rollup (closed 2026-08-21)** — on-device ZIP export + replace-mode import on `main`. - 🟩 **CES-41 — Export ZIP** — **Done in repo** (`client/lib/export/`, Settings → Export data). STORE ZIP (no `archive` write path), A1 headers, `photos_in_export: false`, streaming test over 1 000 lazy rows. Device 10k timing deferred to CES-68. Tests: `client/test/export/` + pointer [`tests/export/README.md`](../../tests/export/README.md). - - 🟨 **CES-70 — ZIP import** — **implemented; spec tests green on PR #25, not on `main`.** `client/lib/import/` (`zip_read` · `csv_parse` · `validate` · `plan` · `apply` · `import_service`) + Settings → **Import data**. **Replace** semantics per [`export-import.md`](../specs/export-import.md). Tests: `client/test/import/` mapped in [`tests/import/README.md`](../../tests/import/README.md). **Outstanding before this goes 🟩:** merge to `main`. + - 🟩 **CES-70 — ZIP import** — **Done on `main`** ([PR #25](https://github.com/JMNofziger/cestovni/pull/25), merge `7670226`). `client/lib/import/` (`zip_read` · `csv_parse` · `validate` · `plan` · `apply` · `import_service`) + Settings → **Import data**. **Replace** semantics per [`export-import.md`](../specs/export-import.md). Tests: `client/test/import/` mapped in [`tests/import/README.md`](../../tests/import/README.md). Spec 1–17 + GitHub CI 5/5. CES-71 unblocked. ### M3 — Backup + restore - - 🟥 **M3 rollup** — ADR 002 + `sync-protocol` closed in running code + tests. *(Fill-up outbox gate slice + `server/dev-sync-stub/` already on `main`. Remaining: real Postgres/RLS, production API, vehicles/settings/maint enqueue, restore UX. **Do not start until CES-70 is on `main` or product redirects.**)* + - 🟥 **M3 rollup** — ADR 002 + `sync-protocol` closed in running code + tests. *(Fill-up outbox gate slice + `server/dev-sync-stub/` already on `main`. Remaining: real Postgres/RLS, production API, vehicles/settings/maint enqueue, restore UX. **CES-70 gate cleared.** Start when product says; next client coding is CES-71.)* - 🟥 **CES-42 — Server Postgres + RLS migrations** — `[tests/rls/](../../tests/rls/)`, `[ci/rls-regression.yml](../../ci/rls-regression.yml)`. - 🟥 **CES-43 — Server API + auth** — contract tests `[tests/contract/](../../tests/contract/)` (managed + self-host). Dev stub on `main` is **not** this ticket. - 🟥 **CES-44 — Backup / outbox (client)** — fill-up enqueue/flush **gate slice on `main`**; vehicles/settings/maint still do not enqueue. Depends on CES-37 + real CES-43. @@ -166,7 +170,7 @@ PWA-lite gate passed 2026-05-29; **Phase 1+2 merged to `main`** (PR #3 + PR #4). Epic: **[CES-35 Delivery v1](https://linear.app/personal-interests-llc/issue/CES-35/delivery-v1-stage-5-engineering-breakdown)**. Every row below is a child of that epic with a literal `Spec:` line and `blockedBy` relations matching this table. -| # | Linear | Vertical | Milestone | `Spec:` | Depends on | Effort | Repo status (2026-04-22, post-CES-53) | +| # | Linear | Vertical | Milestone | `Spec:` | Depends on | Effort | Repo status (2026-08-21) | | --- | ---------------------------------------------------------------- | ---------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------ | -------------------------------------------------------------------------------------------------------- | | 1 | [CES-36](https://linear.app/personal-interests-llc/issue/CES-36) | Mobile client bootstrap | M0 | `docs/specs/adr/003-mobile-stack.md` | — | medium | **In repo** — `client/`, `ci/client-build.yml`, telemetry check 2 active | | 2 | [CES-37](https://linear.app/personal-interests-llc/issue/CES-37) | Client DB schema + migrations | M0 | `docs/specs/data-model.md` + `docs/specs/si-units.md` | CES-36 | medium | **In repo** — `client/lib/db/`, `client/test/db/`, `tests/client-db/fixtures/` | @@ -174,6 +178,7 @@ Epic: **[CES-35 Delivery v1](https://linear.app/personal-interests-llc/issue/CES | 4 | [CES-39](https://linear.app/personal-interests-llc/issue/CES-39) | Fill-up + vehicle UI (core logging) | M1 | `docs/specs/data-model.md` + `docs/product/PRODUCT_BRIEF.md` + `docs/product/ux/cestovni-views.md` + `docs/product/ux/DATA_CONTRACTS.md` + `docs/product/ux/DELIVERY_ACCEPTANCE.md` + `docs/product/ux/UX_IMPLEMENTATION_GAPS.md` | CES-37, CES-38 (CES-53–CES-56 **Done** in repo) | high | **Done in repo on `main`** (2026-05) — repos + vehicle CRUD (phases 1–2) + Log/History UI (phase 3); 121+ widget/DB tests. Out of CES-39 scope: Metrics/Maint tabs, photo (**CES-40**), Log/History prefs *display* follow-on (post-CES-57) | | 5 | [CES-40](https://linear.app/personal-interests-llc/issue/CES-40) | Photo pipeline implementation | M1 | `docs/specs/photo-pipeline.md` | CES-37 | medium | **Done** (2026-08-16, PR #18) — `client/lib/photos/` + Log attach UI; isolate decode; 47 tests in `client/test/photos/` + `log_page_photos_test.dart`. Manual device checklist: [`ces-40-manual-test.md`](ces-40-manual-test.md) (not yet run). | | 6 | [CES-41](https://linear.app/personal-interests-llc/issue/CES-41) | Export ZIP | M2 | `docs/specs/export-v1.md` | CES-37 | medium | **Done in repo** — `client/lib/export/`, Settings Export, `client/test/export/`. Prompt [`prompts/ces-41-export.md`](prompts/ces-41-export.md). | +| 6b | [CES-70](https://linear.app/personal-interests-llc/issue/CES-70) | ZIP import (replace) | M2 | `docs/specs/export-import.md` | CES-41 | medium | **Done on `main`** (PR #25) — `client/lib/import/` + Settings Import; 17 spec cases in `client/test/import/`. Prompt [`prompts/ces-70-import.md`](prompts/ces-70-import.md). | | 7 | [CES-42](https://linear.app/personal-interests-llc/issue/CES-42) | Server Postgres + RLS migrations | M3 | `docs/specs/data-model.md` + `docs/specs/adr/001-backend-api-boundary.md` | — | medium | — | | 8 | [CES-43](https://linear.app/personal-interests-llc/issue/CES-43) | Server API + auth | M3 | `docs/specs/adr/001-backend-api-boundary.md` + `docs/specs/sync-protocol.md` | CES-42 | high | **Backlog** — `server/dev-sync-stub/` (fill-ups only) is **not** CES-43. | | 9 | [CES-44](https://linear.app/personal-interests-llc/issue/CES-44) | Backup / outbox (client) | M3 | `docs/specs/adr/002-backup-sync-layer.md` + `docs/specs/sync-protocol.md` | CES-37, CES-43 | high | **Gate slice on `main`** (fill-up enqueue/flush). Remaining: vehicles/settings/maint + real API. | @@ -199,6 +204,7 @@ Epic: **[CES-35 Delivery v1](https://linear.app/personal-interests-llc/issue/CES | API contract | [ADR 001](../specs/adr/001-backend-api-boundary.md), `[sync-protocol.md](../specs/sync-protocol.md)` | Contract tests against managed + self-host | `[tests/contract/](../../tests/contract/)` | | Backup / restore | `[sync-protocol.md](../specs/sync-protocol.md)`, [ADR 002](../specs/adr/002-backup-sync-layer.md) | Integration (client+server) | `tests/backup/` (to land in M3) | | Export shape | `[export-v1.md](../specs/export-v1.md)` | Fixture-driven ZIP assembly + manifest assertions | `client/test/export/` (pointer [`tests/export/README.md`](../../tests/export/README.md)) | +| Import / replace | `[export-import.md](../specs/export-import.md)` | Golden round-trip + reject + replace apply | `client/test/import/` (pointer [`tests/import/README.md`](../../tests/import/README.md)) — **Done on `main`** (PR #25) | | Telemetry drift | `[telemetry-allowlist.md](../specs/telemetry-allowlist.md)` + `[telemetry-events.v1.yaml](../specs/telemetry-events.v1.yaml)` | YAML + client-source scanner in CI | `[ci/telemetry-gate.py](../../ci/telemetry-gate.py)` + `[ci/telemetry-gate.yml](../../ci/telemetry-gate.yml)` | | Migration rollback | `[TBD-migration-rollback.md](../specs/TBD-migration-rollback.md)` (stub) | Down-migration fixtures | `tests/migrations/` (to land in M5) | @@ -210,8 +216,8 @@ Epic: **[CES-35 Delivery v1](https://linear.app/personal-interests-llc/issue/CES Leading emoji tracks **exit** state (independent of per-vertical RYG above, but should converge at stage close). - 🟩 Every vertical above has a Linear issue with a `Spec:` line. *(CES-35 epic + CES-36..CES-47; M0 follow-ups CES-48/49/50 created 2026-04-22, linked to their downstream verticals via `blocks`.)* -- 🟨 M0 + M1 land: offline app runs, fill-up works end-to-end, golden math tests green. *(**M0 closed**. **M1 closed** — CES-38/39/57 + CES-65/66/67 + CES-40 all Done. **CES-41 export shipped.** **Next:** CES-70 import — see [Current focus](#current-focus).)* -- 🟨 M2 lands: ZIP export round-trips for a representative fixture. *(CES-41 **Done**. CES-70 import + spec tests green on PR #25; still 🟨 until merge.)* +- 🟩 M0 + M1 land: offline app runs, fill-up works end-to-end, golden math tests green. *(**M0 closed**. **M1 closed** — CES-38/39/57 + CES-65/66/67 + CES-40 all Done.)* +- 🟩 M2 lands: ZIP export round-trips for a representative fixture, and replace-mode import restores it. *(CES-41 **Done**. CES-70 **Done on `main`**, PR #25.)* - 🟥 M3 lands: backup/restore passes `tests/contract/` + integration fixtures; RLS regression green. - 🟥 M4 lands: `ci/telemetry-gate.`* green; client emits only allow-listed events. - 🟥 M5 lands: migration rollback spec real (not stub); rollback tooling proven against fixture. @@ -234,5 +240,5 @@ When every exit bullet above is 🟩, Stage 5 exit is met — flip workflow perc - `[launch-copy-v1.md](launch-copy-v1.md)` — Stage 4 copy; feeds Stage 6. - `[../specs/platform-compliance-v1.md](../specs/platform-compliance-v1.md)` — compliance posture already signed off. -*Last updated: 2026-08-21 — CES-70 ZIP import implemented; spec § Test expectations green on PR #25, not on `main`.* +*Last updated: 2026-08-21 — CES-70 ZIP import merged to `main` (PR #25). M2 closed. CES-71 unblocked. M3 eligible; wait for product.* diff --git a/docs/product/prompts/ces-70-import.md b/docs/product/prompts/ces-70-import.md index 10167a8..c717400 100644 --- a/docs/product/prompts/ces-70-import.md +++ b/docs/product/prompts/ces-70-import.md @@ -1,11 +1,11 @@ # Cursor execution prompt — CES-70 ZIP import -> **Status: IMPLEMENTED + TESTS GREEN** (2026-08-21). Mode is `replace`. Code and `client/test/import/` are on draft [PR #25](https://github.com/JMNofziger/cestovni/pull/25). -> **Do not re-implement.** Remaining work is merge to `main`. Device timing stays CES-68. -> Linear **[CES-70](https://linear.app/personal-interests-llc/issue/CES-70)** — **In Progress** until this is on `main`. GitHub automation will flip it Done on merge; that is correct only after merge. -> Do **not** pick up M3 (CES-42–45), CES-51, CES-71, or PWA-lite unless the user explicitly redirects. **Do not unblock CES-71** until import is on `main`. +> **Status: SHIPPED on `main`** (2026-08-21, [PR #25](https://github.com/JMNofziger/cestovni/pull/25), merge `7670226`). Mode is `replace`. +> **Do not re-implement.** Device timing stays CES-68. +> Linear **[CES-70](https://linear.app/personal-interests-llc/issue/CES-70)** — **Done**. **[CES-71](https://linear.app/personal-interests-llc/issue/CES-71)** is unblocked. +> Do **not** pick up M3 (CES-42–45) or CES-51 unless the user explicitly redirects. Next coding is CES-71. -**Branch:** `cursor/ces-70-zip-import-40e4` +**Branch:** `main` (merged via `cursor/ces-70-zip-import-40e4`) **Spec (normative):** [`docs/specs/export-import.md`](../../specs/export-import.md) --- @@ -14,12 +14,12 @@ | Item | State | |------|-------| -| Last coding | **CES-70** import + spec tests — **not on `main`** | +| Last coding | **CES-70** import — **on `main`** (PR #25) | | M1 | **Closed.** | -| M2 | CES-41 on `main`. CES-70 tests green on PR #25 | -| Parallel (do not do here) | CES-63 · CES-68 · M3 · CES-71 | +| M2 | **Closed.** CES-41 + CES-70 on `main` | +| Parallel (do not do here) | CES-63 · CES-68 · M3 | -**Next:** merge PR #25. Do **not** cut a second implementation branch off stale spec history. +**Next:** CES-71 cadence rename. Do **not** re-open CES-70 implementation. --- ## Goal @@ -79,7 +79,7 @@ Validate → `DELETE` children before parents (`maintenance_events` → `fill_up --- -## Scope (in) — code done; tests remaining +## Scope (in) — shipped 1. ✅ `client/lib/import/` split per spec § Suggested layout — pure `csv_parse` / `validate` / `plan`, Drift only in `apply`, IO only in `import_service`. 2. ✅ `client/lib/import/zip_read.dart` (central-directory reader; injected inflate). @@ -121,8 +121,8 @@ Validate → `DELETE` children before parents (`maintenance_events` → `fill_up - [x] Module-purity + streaming tests present (device timing deferred to CES-68 per export A4) - [x] Header-constant drift test (import expected set *is* the export constant set) - [x] `flutter analyze` + `flutter test --no-pub` + `python3 ci/telemetry-gate.py` green -- [ ] CES-71 unblocked — **only after this is on `main`** -- [ ] Linear CES-70 Done + closeout comment — **not before merge to `main`** +- [x] CES-71 unblocked (import is on `main`) +- [x] Linear CES-70 Done + closeout comment ## Implementation status (2026-08-21) @@ -133,6 +133,6 @@ Validate → `DELETE` children before parents (`maintenance_events` → `fill_up 5. **Errors.** 17 `ImportErrorCode` values + 6 `ImportWarningCode` values. Validation happens before the txn; apply is one Drift transaction (`E_TXN_FAILED` on failure). 6. **Tests.** `client/test/import/` — 27 cases covering spec items 1–17. Pointer: [`tests/import/README.md`](../../../tests/import/README.md). Full suite: `flutter analyze` clean, `flutter test --no-pub` **303 passed / 1 skipped** (E2E), telemetry-gate PASS. 7. **Limits.** Device timing deferred to CES-68. Keyword is English-only. Pre-M3 every user with fill-ups has a non-empty outbox — keep "queued changes discarded" quiet. -8. **PR / Linear.** Draft [PR #25](https://github.com/JMNofziger/cestovni/pull/25). CES-70 **In Progress** until merge. CES-71 **Backlog**. +8. **PR / Linear.** Merged [PR #25](https://github.com/JMNofziger/cestovni/pull/25). CES-70 **Done**. CES-71 **unblocked** (Backlog until product starts it). Tag: `CES-70 — ZIP import`. diff --git a/docs/product/ux/UX_IMPLEMENTATION_GAPS.md b/docs/product/ux/UX_IMPLEMENTATION_GAPS.md index b6c041d..6940868 100644 --- a/docs/product/ux/UX_IMPLEMENTATION_GAPS.md +++ b/docs/product/ux/UX_IMPLEMENTATION_GAPS.md @@ -2,9 +2,9 @@ **Purpose:** Track documentation and product gaps discovered before M1 UI execution so they do not leak into implementation as silent contradictions. -**Gate (closed):** Critical-gap rows **Done** (repo + Linear). **CES-39 Done** (2026-07-17). **CES-65 + CES-66 Done** (repo + Linear 2026-07-22). **CES-67 Done** (2026-08-15). **CES-40 Done** (receipt photos, 2026-08-15) — **M1 verticals all closed.** **CES-41** export shipped. Next spine item: import (**CES-70**, M2) — tests green on PR #25, not on `main`. +**Gate (closed):** Critical-gap rows **Done** (repo + Linear). **CES-39 Done** (2026-07-17). **CES-65 + CES-66 Done** (repo + Linear 2026-07-22). **CES-67 Done** (2026-08-15). **CES-40 Done** (receipt photos, 2026-08-15) — **M1 verticals all closed.** **CES-41** export and **CES-70** import shipped (M2 closed). Next client coding: **CES-71** cadence rename. -**Last reviewed:** 2026-08-21 (CES-70 spec tests green on PR #25) +**Last reviewed:** 2026-08-21 (CES-70 merged to `main`, PR #25) --- diff --git a/docs/product/ux/cestovni-views.md b/docs/product/ux/cestovni-views.md index a0d524f..d1ed946 100644 --- a/docs/product/ux/cestovni-views.md +++ b/docs/product/ux/cestovni-views.md @@ -159,7 +159,7 @@ Screenshot: `screenshots/dark-midnight/settings.png` - Preferences currently implemented in `settings_page.dart` (distance/volume/currency/timezone). - Vehicle CRUD is implemented across `vehicle_list_page.dart`, `vehicle_detail_page.dart`, and `vehicle_form_page.dart`. - Export is **shipped (CES-41)** — Settings → Backup → **Export data** (`client/lib/export/`, photos excluded). -- Import is **implemented (CES-70)** — Settings → Backup → **Import data** (`client/lib/import/` + `import_data_section.dart`), replace mode, typed `REPLACE` when local history is non-empty. Spec tests green on PR #25; not on `main` yet. Destructive reset remains Later. +- Import is **shipped (CES-70)** — Settings → Backup → **Import data** (`client/lib/import/` + `import_data_section.dart`), replace mode, typed `REPLACE` when local history is non-empty. On `main` (PR #25). Destructive reset remains Later. **Scope gate** diff --git a/docs/specs/ARCHITECTURE.md b/docs/specs/ARCHITECTURE.md index 2767538..c7e73d2 100644 --- a/docs/specs/ARCHITECTURE.md +++ b/docs/specs/ARCHITECTURE.md @@ -77,7 +77,7 @@ Parent **CES-22**; children **CES-26–CES-32** (see [`README.md`](README.md)). ## Implementation status (Stage 5) -- **M0 (2026-04-18):** Mobile shell + local Drift database on `main` — repository root [`client/`](../../client/). Matches **ADR 003** (Flutter + Drift) and **`data-model.md`** client tables (`schema_version` **3** as of 2026-06-30: `0001_init` + `0002_add_maintenance_events_category_shop` + `0003_settings_default_vehicle_id`; fresh `onCreate` uses `m.createAll()`). **CES-41 export** is on `main` (`client/lib/export/`). **CES-70 import** is implemented (`client/lib/import/`) with spec tests green on PR #25. Not yet: production server backup (M3), Sentry wiring (M4). CI: [`ci/client-build.yml`](../../ci/client-build.yml); telemetry allow-list gate includes a **Dart source scan** for literal `Telemetry.emit` event names ([`ci/telemetry-gate.py`](../../ci/telemetry-gate.py)). +- **M0 (2026-04-18):** Mobile shell + local Drift database on `main` — repository root [`client/`](../../client/). Matches **ADR 003** (Flutter + Drift) and **`data-model.md`** client tables (`schema_version` **3** as of 2026-06-30: `0001_init` + `0002_add_maintenance_events_category_shop` + `0003_settings_default_vehicle_id`; fresh `onCreate` uses `m.createAll()`). **CES-41 export** and **CES-70 import** are on `main` (`client/lib/export/`, `client/lib/import/`). Not yet: production server backup (M3), Sentry wiring (M4). CI: [`ci/client-build.yml`](../../ci/client-build.yml); telemetry allow-list gate includes a **Dart source scan** for literal `Telemetry.emit` event names ([`ci/telemetry-gate.py`](../../ci/telemetry-gate.py)). ## Related compliance / ops diff --git a/docs/specs/README.md b/docs/specs/README.md index df30d2c..11256c2 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -19,7 +19,7 @@ Each Linear issue should include a **`Spec:`** line pointing to the relevant fil | [`consumption-math.md`](consumption-math.md) | Math, segments, fill-up flags | CES-26 | Complete (v1) | | [`si-units.md`](si-units.md) | Canonical INT storage + conversions | CES-27 | Complete (v1) | | [`export-v1.md`](export-v1.md) | ZIP / CSV export contract | CES-28 | Complete (v1) | -| [`export-import.md`](export-import.md) | Device-to-device ZIP import (zero-server) | CES-70 | Complete (v1) — tests green on PR #25; not on `main` | +| [`export-import.md`](export-import.md) | Device-to-device ZIP import (zero-server) | CES-70 | Complete (v1) — on `main` (PR #25) | | [`telemetry-allowlist.md`](telemetry-allowlist.md) | Crash + product event allow-list | CES-29 | Complete (v1) | | [`telemetry-events.v1.yaml`](telemetry-events.v1.yaml) | Machine-readable event catalogue | CES-29 | Complete (v1) | | [`photo-pipeline.md`](photo-pipeline.md) | Ephemeral photos / deferred entry | CES-30 | Complete (v1) | diff --git a/docs/specs/export-import.md b/docs/specs/export-import.md index ae6ecb7..04dde49 100644 --- a/docs/specs/export-import.md +++ b/docs/specs/export-import.md @@ -1,9 +1,9 @@ # Spec: Export ZIP import (device-to-device, zero-server) -**Status:** **Complete (v1) — implemented, tests green on PR #25, not on `main` yet.** All product locks resolved 2026-08-16 (see [§ Product decisions](#product-decisions-locked-2026-08-16)). Mode is **replace**. Code lives in `client/lib/import/` + Settings → **Import data**; the 17 cases in [§ Test expectations](#test-expectations) are in `client/test/import/`. CES-71 stays blocked until this is on `main`. +**Status:** **Complete (v1) — on `main`** ([PR #25](https://github.com/JMNofziger/cestovni/pull/25), merge `7670226`). All product locks resolved 2026-08-16 (see [§ Product decisions](#product-decisions-locked-2026-08-16)). Mode is **replace**. Code lives in `client/lib/import/` + Settings → **Import data**; the 17 cases in [§ Test expectations](#test-expectations) are in `client/test/import/`. CES-71 is unblocked. **Linear:** [CES-70](https://linear.app/personal-interests-llc/issue/CES-70) **Depends on:** [CES-41](https://linear.app/personal-interests-llc/issue/CES-41) export ([PR #21](https://github.com/JMNofziger/cestovni/pull/21), `client/lib/export/`) -**Blocks:** [CES-71](https://linear.app/personal-interests-llc/issue/CES-71) (`cadence_km` → `cadence_m` rename) +**Blocks:** [CES-71](https://linear.app/personal-interests-llc/issue/CES-71) (`cadence_km` → `cadence_m` rename) — **unblocked** (this shipped) **Reads:** [`export-v1.md`](export-v1.md) § v1 amendments · [`data-model.md`](data-model.md) · [`si-units.md`](si-units.md) · [`photo-pipeline.md`](photo-pipeline.md) · [`sync-protocol.md`](sync-protocol.md) § v1.x roadmap > **Contract source.** The authoritative CSV shape is the **shipped export code**, not the 2026-04 body of `export-v1.md`. Header strings live in `client/lib/export/headers.dart`; cell formatting lives in `client/lib/export/snapshot.dart` + `derived.dart`. Import MUST import those same header constants rather than re-declaring them, so export and import cannot drift. @@ -22,7 +22,7 @@ Let a user move their structured history between two devices with **zero server* | | Current | Done when CES-70 is 🟩 | |---|---|---| | Export | Settings → **Export data** writes a STORE ZIP: `manifest.json`, `README_export.txt`, five CSVs | unchanged | -| Import | Settings → **Import data** (`client/lib/import/` + `import_data_section.dart`) replaces local history from a ZIP the app produced. **Not on `main` yet.** Tests live in `client/test/import/` (spec § Test expectations; green on PR #25). | Same code on `main` | +| Import | Settings → **Import data** (`client/lib/import/` + `import_data_section.dart`) replaces local history from a ZIP the app produced. **On `main`** (PR #25). Tests live in `client/test/import/` (spec § Test expectations). | Same (shipped) | | Mode | **Replace** — destructive restore, typed `REPLACE` when there is data to lose. Merge is not built. | unchanged | | Photos | never exported (`photos_in_export: false`) | never imported; a photo-shaped ZIP **fails closed** | | `row_version` | client never writes it; CSV cells empty, manifest `null` | imported rows keep `row_version = NULL` (never synced) | diff --git a/docs/specs/export-v1.md b/docs/specs/export-v1.md index 77b00a3..20c8c57 100644 --- a/docs/specs/export-v1.md +++ b/docs/specs/export-v1.md @@ -216,7 +216,7 @@ Both targets are validated by `tests/export/` fixtures. ## Non-goals (v1) - **No incremental / diff exports.** Every export is the full structured state. -- **No re-import tool in-app (v1) — historical.** This non-goal was deferred, not permanent. In-app restore is [CES-70](https://linear.app/personal-interests-llc/issue/CES-70): Settings → **Import data**, specified in [`export-import.md`](export-import.md) (replace mode). Implementation + spec tests are on [PR #25](https://github.com/JMNofziger/cestovni/pull/25); not on `main` yet. Merge / live multi-device sync remains a v1.x item in [`sync-protocol.md`](sync-protocol.md#v1x-roadmap-pointer-only--tbd-in-this-pass). +- **No re-import tool in-app (v1) — historical.** This non-goal was deferred, not permanent. In-app restore is [CES-70](https://linear.app/personal-interests-llc/issue/CES-70): Settings → **Import data**, specified in [`export-import.md`](export-import.md) (replace mode), **on `main`** ([PR #25](https://github.com/JMNofziger/cestovni/pull/25)). Merge / live multi-device sync remains a v1.x item in [`sync-protocol.md`](sync-protocol.md#v1x-roadmap-pointer-only--tbd-in-this-pass). - **No photos.** Period. - **No signed / encrypted ZIP.** User-managed; they can encrypt after export if they want. diff --git a/docs/specs/sync-protocol.md b/docs/specs/sync-protocol.md index 1e15102..1d2c26d 100644 --- a/docs/specs/sync-protocol.md +++ b/docs/specs/sync-protocol.md @@ -211,7 +211,7 @@ Dead-letter is a **signal** that something is wrong — either a client bug or a - **Push channels.** Options: long-poll on `/changes`, server-sent events, WebSocket. Decision deferred to when real-time UX requirements are set. - **Conflict UX.** How to surface (if ever) a rejected amendment to the user. Deferred with the above. - **Re-evaluation of managed sync runtime.** Gate defined in ADR 002 revisit gates. -- **Device-to-device import from a self-produced export ZIP** ([CES-70](https://linear.app/personal-interests-llc/issue/CES-70)) — specified in [`export-import.md`](export-import.md) (replace, not merge; no outbox enqueue; `row_version` stays `NULL`). Implementation is in `client/lib/import/` + Settings → **Import data**. Spec tests green on [PR #25](https://github.com/JMNofziger/cestovni/pull/25); merge to `main` outstanding. This is **not** live multi-device sync: merge rules, tombstones, and conflict UX remain unspecified and must not be inferred from replace-mode restore. +- **Device-to-device import from a self-produced export ZIP** ([CES-70](https://linear.app/personal-interests-llc/issue/CES-70)) — specified in [`export-import.md`](export-import.md) (replace, not merge; no outbox enqueue; `row_version` stays `NULL`). Implementation is in `client/lib/import/` + Settings → **Import data**, **on `main`** ([PR #25](https://github.com/JMNofziger/cestovni/pull/25)). This is **not** live multi-device sync: merge rules, tombstones, and conflict UX remain unspecified and must not be inferred from replace-mode restore. ## References diff --git a/tests/export/README.md b/tests/export/README.md index 2a12c0d..a548aa4 100644 --- a/tests/export/README.md +++ b/tests/export/README.md @@ -16,7 +16,7 @@ them up without a second runner: **Not in CI (per `export-v1.md` § A4):** 10 000-row / 30 s / 10 MB device timing. That pass moves to [CES-68](https://linear.app/personal-interests-llc/issue/CES-68). -**Import (CES-70):** code is in `client/lib/import/`; tests are **not written**. Pointer: [`../import/README.md`](../import/README.md). +**Import (CES-70):** `client/lib/import/` + spec cases in `client/test/import/` — **on `main`** (PR #25). Pointer: [`../import/README.md`](../import/README.md). Run them with: diff --git a/tests/import/README.md b/tests/import/README.md index ed0beb2..76c440f 100644 --- a/tests/import/README.md +++ b/tests/import/README.md @@ -1,7 +1,8 @@ # tests/import — pointer `docs/specs/export-import.md` § Test expectations. Cases live in the -Flutter client so `flutter test` picks them up without a second runner: +Flutter client so `flutter test` picks them up without a second runner. +**On `main`** (PR #25). | Spec expectation | Implementation | |------------------|----------------|