Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/product/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
36 changes: 21 additions & 15 deletions docs/product/delivery-plan-v1.md

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions docs/product/prompts/ces-70-import.md
Original file line number Diff line number Diff line change
@@ -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)

---
Expand All @@ -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
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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)

Expand All @@ -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`.
4 changes: 2 additions & 2 deletions docs/product/ux/UX_IMPLEMENTATION_GAPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

---

Expand Down
2 changes: 1 addition & 1 deletion docs/product/ux/cestovni-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**

Expand Down
2 changes: 1 addition & 1 deletion docs/specs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/specs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
6 changes: 3 additions & 3 deletions docs/specs/export-import.md
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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) |
Expand Down
2 changes: 1 addition & 1 deletion docs/specs/export-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion docs/specs/sync-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/export/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
3 changes: 2 additions & 1 deletion tests/import/README.md
Original file line number Diff line number Diff line change
@@ -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 |
|------------------|----------------|
Expand Down
Loading