cocottetech/@platform/codebase/@features/ai-copilot/docs/export-build.flow.md
natalie 1b719e1fd7 chore(bootstrap): initial V4 commit
Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac
working tree at ~/Code/@projects/@cocottetech/. node_modules and build
artifacts excluded via .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:11:41 -07:00

11 KiB
Raw Blame History

Export-build flow

End-to-end: from "give me everything" through downloaded tar.gz on Quinn's disk. Pairs with brief V §V1 (Quinn-side full export).

Voice register throughout: plain (voice §V2c). This is operational and legally consequential — no metaphor, exact nouns, exact counts.

Triggers (two entry points)

# Entry Confirmation Latency target
1 Settings → S8 Privacy & data → "Build an export" Multi-step picker (scope → format → size → confirm) <1s to landing
2 Voice: "Hey copilot, export everything" ai-copilot opens the same picker pre-filled with V1a defaults; Quinn taps confirm <2s including STT

Both converge on the same export-build-dispatch action. The picker is the same component either way; voice trigger just pre-seeds defaults.

Step 1 — scope picker (9 sections)

Landing card, plain register. Renders the V1a table as togglable rows. All "✓ on" by default per V1a; required row is non-toggleable.

Build an export. Pick what to include.

  Section                               Default   Toggle
  ──────────────────────────────────────────────────────
  Profile + persona                     ✓ on      required
  All prospects + conversations         ✓ on      [✓]
  Content plans, posts, assets          ✓ on      [✓]
  Audit log (agent_actions)             ✓ on      [✓]
  Journal entries                       ✓ on      [✓]
  Settings + policies                   ✓ on      [✓]
  Tour legs + hotel history             ✓ on      [✓]
  Coop reports you filed                ✓ on      [✓]
  Encrypted attachments                 off       [ ]   (large; decrypts at download)

  Estimated size: 4.7 GB         [ Cancel ]  [ Next: format ]
  • "Profile + persona" cannot be toggled off — it's the export's identity anchor.
  • "Coop reports you filed" row has a sub-line: peer reports about subjects are not included — someone else's data.
  • "Encrypted attachments" sub-line: decrypts client-side on download; otherwise stays encrypted.
  • "Brief/doc content" from V1a is omitted from the user-facing picker (platform artifacts, not Quinn data) — surfaces only behind Advanced.

Step 2 — format picker

Pick a format.

  ( • ) JSON archive (tar.gz)        programmatic re-import, full fidelity
  (   ) PDF report                   human-readable, for legal/regulatory submissions
  (   ) Per-prospect Markdown        scoped to a single prospect — use SAR flow instead

  [ Back ]              [ Next: review ]
  • JSON archive is the default per V1b.
  • Selecting "Per-prospect Markdown" disables Next and surfaces a one-line redirect: that's the SAR flow — see Settings S8 → Field a prospect SAR.
  • PDF runs the same worker; output is a single PDF instead of tar.gz. Size estimate recomputes.

Step 3 — size estimate (live updates)

Bottom-of-card estimate refreshes on every toggle and format change. Computed from current row counts × per-section average byte budget. Displayed as size + estimated build time:

Estimated size: 4.7 GB · build time ~12 minutes.

  • Updates debounce at 200ms to avoid flicker.
  • Encrypted-attachments toggle is the dominant driver — flipping it on can move the estimate from ~50 MB to multi-GB.
  • If the estimate exceeds 10 GB, the card surfaces a sub-line: large export — keep your device on Wi-Fi to download.

Step 4 — worker dispatch

On confirm:

  1. platform.api enqueues a job on the black-resident export-worker queue with scope + format payload.
  2. Worker writes a row in agent_actions with action_type='export_started' and outcome.scope_hash for idempotency.
  3. ai-copilot replies in chat (read-only confirmation, no notification):

    Building your export. About 12 minutes for 4.7 GB. We'll notify when it's ready.

  4. A progress fraction (rows_processed / rows_total) updates in Redis pub/sub keyed by export-job:{job_id}.
  5. Quinn can leave the surface — the job runs server-side regardless of app state.

Step 5 — build progress (visible counter)

While the job runs, Settings → S8 shows a live row:

  Export in progress · 4.7 GB · 43% · ~7 min remaining        [ Cancel ]
  • Counter polls Redis every 2s while the surface is open; closes the channel when Quinn leaves S8.
  • Cancel is allowed up to the bundle-archive step; cancelled jobs write action_type='export_cancelled' and discard partial output.
  • A single in-flight export per user. A second trigger while one is running surfaces: one export already building — wait for it or cancel.

Step 6 — ready notification

When the worker finishes, notifier dispatches a high-stakes push per brief C. Overrides quiet hours — the export expiry timer makes timeliness real.

Your export is ready. Download link expires in 7 days.

  • Fallback hierarchy per degraded-mode.flow.md §M4: iMessage → push → in-app banner.
  • The in-app banner persists in S8 even if push was acknowledged — Quinn can find the download without chasing the notification.
  • Tap deeplinks to the download sheet (Step 7).

Step 7 — download with privacy reminder

Tap → download sheet, plain register:

Your export is ready.

  Size: 4.7 GB
  Format: JSON archive (tar.gz)
  Expires: in 7 days (May 25, 2026)

  This export contains everything in your CocotteAI account, including
  prospect identifiers and tour-leg revenue. Store it somewhere encrypted.

  [ Cancel ]                        [ Download ]
  • Download streams from the worker's output store (MinIO, signed URL with 7-day expiry).
  • Encrypted-attachments section decrypts client-side during download per V1c — bundle bytes leaving MinIO are still encrypted; the iOS/web client decrypts as it writes to disk.
  • After download, sheet updates: Downloaded May 18, 14:09. Link still valid for 6 days. Quinn can re-download until expiry.
  • After 7 days, MinIO object is deleted; signed URL expires; sheet shows: Expired. Build a new export.

Step 8 — audit row writes

Two audit rows for a successful export:

  1. action_type='export_started' on dispatch (Step 4) — captures scope hash, format, estimated size.
  2. action_type='export_built' on worker completion — captures actual size, actual row counts per section, output object key, expiry timestamp.

A third row action_type='export_downloaded' writes on first download tap (Step 7); subsequent re-downloads within the 7-day window do not double-log (one row per export, not per fetch).

All rows preserved per brief I append-only. Visible in audit drawer; surface in daily digest under "things tended to."

Edge cases

  • Worker fails mid-build: worker writes action_type='export_failed' with failure_reason. Chat receipt: Export didn't finish — {reason}. Nothing was sent anywhere. Try again or pick a smaller scope. No partial bundle is exposed; MinIO object discarded.
  • Attachment decryption fails on a subset: worker continues; manifest inside the bundle lists undecryptable items with their original encrypted key references. Chat receipt notes the count: Export ready. 3 attachments couldn't be decrypted and are included encrypted — keys are in your vault.
  • Expiry without download: after 7 days the signed URL expires and the MinIO object is deleted. S8 row updates: Expired May 25 — never downloaded. Build a new one? Audit row action_type='export_expired' writes; no notification (low-stakes, expected outcome).
  • Partial-section error: one section (e.g. tour legs) fails to serialize while others succeed. Worker writes the rest, marks the section as {status: 'error', rows_attempted: N, rows_written: 0} in the bundle manifest, and surfaces in the receipt: Export ready. Tour-leg section failed — everything else is in the bundle. Open audit to retry that section alone.
  • Trigger while kill-switch active (see kill-switch.flow.md): export-build is allowed — it's a read flow, not a dispatch. Banner All specialists paused stays; the worker runs.
  • Trigger while platform.api is in Path C degraded: picker still renders (cached counts); dispatch is queued in SyncEngine and fires on recovery. Quinn sees: Will start building when memory's back.

Privacy reminders (V1d invariants)

The bundle is built with these invariants enforced at the worker, not the UI — UI cannot override:

  • Govt name never in the JSON archive. If KYC artifacts exist anywhere in scope (e.g. TS4Rent verification metadata), the worker writes the rows with govt_name: null and a redacted_reason: 'kyc_vault_separate' marker. KYC vault export is a separate Quinn-typed legal-pretext flow gated behind Advanced + extra confirm.
  • Hotel addresses never in any export shared back to a prospect (the V3 SAR path) or in PDF reports unless Quinn explicitly opts in per-section. The full V1 export to Quinn herself does include addresses (it's her data) — but the worker tags those rows so a downstream re-export to a prospect can't accidentally leak them.
  • Coop peer reports never included — only Quinn's own filed reports. Peer reports about subjects belong to other providers (per brief N).
  • The worker validates these invariants post-serialize before sealing the tar.gz. A violation aborts the export, writes action_type='export_invariant_violation', and surfaces in chat as a plain failure: Export aborted — internal safety check failed. Not your fault. Engineering paged.

Voice / TTS specifics

Voice trigger "Hey copilot, export everything" routes through the same keyword spotter family as kill-switch (fast path, no full LLM intent classifier). ai-copilot's spoken confirmation:

Build an export with the usual sections. Confirm?

Quinn says "yes" → dispatch with V1a defaults + JSON format. Anything else → cancelled with: Holding off. Open S8 if you want to pick sections.

Ready notification's TTS rendering (if voice-output is enabled per S2) is the plain-register sentence verbatim — no metaphor, no flourish:

Your export is ready. Download link expires in seven days.

  • brief V §V1 — design source.
  • brief C — high-stakes notification, quiet-hours override.
  • brief S §S8 — entry point in Privacy & data.
  • brief I — append-only agent_actions rows + digest rendering.
  • degraded-mode.flow.md — fallback hierarchy + Path C behavior during dispatch.
  • voice §V2c — plain register throughout.