Mobile foundation (Phase 1) + harden opportunity stage validation

Phase 1 mobile foundation (additive, no desktop change): :root mobile vars, a
4-tab bottom nav bar + mobile account/logout popover wired into App, a
bottom-sheet CSS primitive, and .mobile-only/.desktop-only utilities -- all
display:none >=768px. The <BottomSheet> React component + useIsMobile() + the
per-surface 15px type bump are deferred to Phase 2 (first use); light theme to
Phase 6.

Review hardening (fresh-eyes pass on the Phase 0+1 diff): validate stage in
handle_create_opportunity + handle_update_opportunity against PIPELINE_STAGES --
the narrower 4-stage enum makes a stale-client write of a legacy value invisible
to the report ORDER BY CASEs and unsettable from the UI. Use the canonical
pipelineStageLabel in the opportunity detail select; document the intentional
graveyard omission in the existing_investor / staleness helpers.

Tests: stage-validation regression in test_grid_pipeline_link.py + empty
source_row_id guard in test_pipeline_stages_v2.py; 36/36 green, render-smoke green.
This commit is contained in:
Keysat
2026-06-19 13:15:53 -05:00
parent e46dd36517
commit 634fc4260f
6 changed files with 221 additions and 15 deletions
+4 -4
View File
@@ -107,14 +107,14 @@ Subsystem rules live in `docs/guides/` and lazy-load in Claude Code via `.claude
## Current state
_Phase 0 + Phase 1 built; **box + repo live at v0.1.0:94** (`main` ahead by docs/design-only commits since). **The fundraising grid + email capture is the canonical system of record.** Active threads: **mobile-first redesign** (design DONE → scoped + **Phase 0 data layer BUILT 2026-06-19**, deploy pending; mobile foundation next) and **W2 NL query** (live; web "Ask" box outstanding). History: git log + `start9/0.4/startos/versions/`; backlog/debt: `ROADMAP.md` / `EVALUATION.md`._
_Phase 0 + Phase 1 built; **box + repo live at v0.1.0:94** (`main` ahead by docs/design-only commits since). **The fundraising grid + email capture is the canonical system of record.** Active threads: **mobile-first redesign** (design DONE → scoped + **Phase 0 data layer + Phase 1 mobile foundation BUILT 2026-06-19**, deploy pending; Contacts surface next) and **W2 NL query** (live; web "Ask" box outstanding). History: git log + `start9/0.4/startos/versions/`; backlog/debt: `ROADMAP.md` / `EVALUATION.md`._
- **Mobile-first redesign — design phase COMPLETE; implementation not started.** This session ran the `/design` round-trip Phase C/D: distilled the Claude Design cloud output ("Venture-CRM mobile redesign") into the contract — `DESIGN.md` §8 (responsive) + §4 (mobile component states) + §3 (15px scale), tokens `mobile` group + `color.light`, provenance + per-surface interaction reference in `design/_imports/2026-06-19/`. **Light theme adopted as a planned, toggle-gated feature** (dark default). Comps are Claude Design **runtime prototypes** — re-author each surface in React against the real API, not drop-in. Process learnings pushed to `standards/guides/design.md`.
- **Mobile implementation — SCOPED 2026-06-19 (plan in `ROADMAP.md` "Mobile-first implementation").** Key finding: the inline-style→CSS "blocker" is **~114 inline styles across the 4 surfaces + shell** (Grid 70 / Reminders 18 / Contacts 17 / Pipeline 7 / shell 2), **not ~1,300** — the app is already majority class-based (1,861-line `<style>`, 1,088 `className`s, 4 media queries). So it's **divisible per-surface, no upfront sweep**, and splits into two axes: *responsive* (layout→classes, gates mobile) vs *theming* (inline-hex→`var()`, 183 literals, gates light theme). Sequence: **Phase 0** pipeline-stages/flags data layer (standalone) → **Phase 1** shared foundation (tokens/`:root` + shell + bottom-tabs + sheet primitive) → **Phase 2** Contacts (validator) → **Phase 3** Grid (crux) → **Phase 4** Pipeline → **Phase 5** Reminders → **Phase 6** light theme. **Phase 0 is BUILT + tested (2026-06-19, deploy pending)** enum→4 stages + migration `0007` + `existing_investor`/`staleness` injection; the *visible* existing-investor star + staleness recency column + Stale view deferred to Phase 3 (data injected + test-locked now). See ROADMAP for the change set.
- **Mobile implementation — SCOPED 2026-06-19 (plan in `ROADMAP.md` "Mobile-first implementation").** Key finding: the inline-style→CSS "blocker" is **~114 inline styles across the 4 surfaces + shell** (Grid 70 / Reminders 18 / Contacts 17 / Pipeline 7 / shell 2), **not ~1,300** — the app is already majority class-based (1,861-line `<style>`, 1,088 `className`s, 4 media queries). So it's **divisible per-surface, no upfront sweep**, and splits into two axes: *responsive* (layout→classes, gates mobile) vs *theming* (inline-hex→`var()`, 183 literals, gates light theme). Sequence: **Phase 0** pipeline-stages/flags data layer (standalone) → **Phase 1** shared foundation (tokens/`:root` + shell + bottom-tabs + sheet primitive) → **Phase 2** Contacts (validator) → **Phase 3** Grid (crux) → **Phase 4** Pipeline → **Phase 5** Reminders → **Phase 6** light theme. **Phase 0 + Phase 1 are BUILT (2026-06-19, deploy pending).** Phase 0: enum→4 stages + migration `0007` + `existing_investor`/`staleness` injection (visible star/staleness column + Stale view deferred to Phase 3). Phase 1: `:root` mobile vars + `.bottom-tab-bar` (4 tabs wired in `App`) + mobile account popover + `.bottom-sheet` CSS primitive + `.mobile-only`/`.desktop-only` utils — all `display:none` on desktop (zero desktop change); the `<BottomSheet>` React component + `useIsMobile()` + per-surface 15px bump deferred to Phase 2 (first use). See ROADMAP for both change sets.
- **Built, deploy pending:** **drag-reorder grid views** (frontend-only; `moveViewBefore` in `index.html`; persists via autosave → `views_json`; render-smoke green, browser-interaction untested).
- **W2 — NL query (read-only): LIVE** (v93; matched-only fix v94). Local-Qwen translate → curated intents + slot validator (no generic SQL), `POST /api/query/nl`, audited; Matrix Q&A + intake `?`/`@bot` live. Remaining: **in-room human smoke** + **step-4 web "Ask" box**. Guides: `docs/guides/nl-query.md` + matrix-intake.
- **W1 — reminders: LIVE (v93).** Grid-tied tickler (migration `0006`, `/api/reminders`, derived `reminder_status`, `last_activity_at` rollup). Deferred **W1b** = nurture-gap auto-suggested reminders (staleness nudge → Engaged/Diligence).
- **Done & live:** email-proposal Matrix review + `bot` role (v91); grid-driven Pipeline (v88); Matrix intake bot; Gmail capture (DWD) + propose→approve + daily digest; Thesis Workshop + Architect (Claude, dual-approval); outreach drafts. All draft-only.
- **Tests:** **36/36 backend green** (`python3 backend/run_tests.py`; +`test_pipeline_stages_v2.py`), `py_compile` clean, render-smoke green, fresh-DB migrate clean. (Phase 0 code shipped this session.)
- **Next (priority order):** 1) **deploy Phase 0** (s9pk build + install — pipeline-stages/flags data layer; bundle view-reorder, which is also deploy-pending; **authorize first**); 2) **Phase 1mobile foundation** (tokens/`:root` + viewport-gated shell + 4-tab bottom bar + bottom-sheet primitive); 3) mobile surfaces **Contacts → Grid → Pipeline → Reminders** (writes via one-row `log-communication` + pipeline link→stage, never whole-grid PUT; the deferred existing-investor star + staleness recency column + Stale view co-land here); 4) **Phase 6 light theme** (inline-hex→`var()` + `[data-theme]` toggle); 5) **W2 step-4** web Ask box + in-room smoke; 6) **W3** bot grid-mutations behind Matrix gate; 7) **W1b** nurture-gap reminders (target Engaged/Diligence); then P2 debt (reports comms-aggregate soft-delete sweep, `?limit=abc` crash, auth regression test, oversized icon).
- **Open / risks:** **Phase 0 built but not yet deployed** — the `0007` enum migration is a no-op on the live DB (0 opps today, so near-zero remap risk), and the derived `existing_investor`/`staleness` signals are injected + test-locked but not yet *rendered* on desktop (that lands in Phase 3); the mobile UI itself is still unbuilt (Grid is the heavy surface at ~70 inline styles + the two-call stage write path); W2 translation only **happy-path-validated**; **Claude/Architect path still unverified live on the box**; v2.0 reserve-asset spine approved but **not canonical** (needs dual sign-off); doc drift — `crm-overview.md` + `EVALUATION.md` still call `lp_profiles` live.
- **Next (priority order):** 1) **Phase 2 — Contacts surface** (read-only AZ list + segmented tabs + search → full-screen detail; lands the `<BottomSheet>` component + `useIsMobile()` + 15px bump — the list→detail→sheet validator before the Grid); 2) **Phase 3Grid** (crux; card list + view-picker sheet + per-field edit sheets + existing-investor star/staleness rendering; writes via one-row `log-communication` + pipeline link→stage, never whole-grid PUT); 3) **Phase 4 Pipeline → Phase 5 Reminders**; 4) **Phase 6 light theme** (inline-hex→`var()` + `[data-theme]` toggle); 5) **deploy** the accumulated Phase 0 + Phase 1 (+ view-reorder) in one s9pk build (**authorize first**); 6) **W2 step-4** web Ask box + in-room smoke; 7) **W3** bot grid-mutations behind Matrix gate; 8) **W1b** nurture-gap reminders (target Engaged/Diligence); then P2 debt (reports comms-aggregate soft-delete sweep, `?limit=abc` crash, auth regression test, oversized icon).
- **Open / risks:** **Phase 0 built but not yet deployed** — the `0007` enum migration is a no-op on the live DB (0 opps today, so near-zero remap risk), and the derived `existing_investor`/`staleness` signals are injected + test-locked but not yet *rendered* on desktop (that lands in Phase 3). **Phase 1 mobile shell/nav is built but browser-untested** (render-smoke only — the bottom tab bar shows authenticated + <768px; verify on a real phone, like view-reorder); the four mobile *surfaces* are still unbuilt (Grid is the heavy one at ~70 inline styles + the two-call stage write path). W2 translation only **happy-path-validated**; **Claude/Architect path still unverified live on the box**; v2.0 reserve-asset spine approved but **not canonical** (needs dual sign-off); doc drift — `crm-overview.md` + `EVALUATION.md` still call `lp_profiles` live.