# 06 — Open Decisions (founder calls) Each has a recommendation; none is blocking to *start* Phase 0, all must be locked to *exit* it. ## D1 — Product name & positioning vs "SiMS Classic" New name, or "SiMS" with a generation marker? **Recommendation:** keep the SiMS brand (it carries your existing base's trust), market the rebuild as *SiMS Next* or *SiMS 2* — the migration pitch is easier when it's "your SiMS, reborn" rather than a stranger. ## D2 — POS shell ✅ REVISED (2026-07-09): web app first Founder call: **the product is a web app.** POS and back office are browser apps served by the store-server on LAN (later the same apps served from cloud). Zero installs at counters — the Oracle Forms deployment model, modernized. Consequences: - **Hardware routes through the store-server**: web POS → HTTP → store-server → ESC/POS over TCP 9100 (printers + drawer). Scanners are keyboard-wedge (browser-native). Weighing scales: Web Serial API (Chrome/Edge) or a store-server serial bridge — spike item. - **Electron shell is kept as an optional kiosk wrapper** (~100 lines, already built) for counters that need true offline billing or locked-down kiosk mode; same web bundle inside. - **Browser key conflicts** (F11 fullscreen, F12 devtools): keymaps are DB config — the web preset leans on Numpad+ for cash-close and Chrome kiosk mode at counters. - Offline story for browser POS (PWA + IndexedDB bill queue) is a Phase-2 hardening item; v1 counters depend on the store-server over LAN, exactly like Classic depends on the Oracle server today. ## D3 — Sync: buy (PowerSync / ElectricSQL) vs build (custom outbox) **Recommendation:** spike both in Phase 0; prefer buying the transport if it handles per-tenant partial sync cleanly. The domain rules (immutable documents, per-counter series, derived stock) are ours regardless. ## D4 — GSP vendor for e-Invoice / e-Way ClearTax vs Masters India vs others. Decide on: API reliability, per-document pricing at your volume, sandbox quality. **Recommendation:** shortlist two, integrate behind our own interface so switching is a config change. ## D5 — Pricing tiers ✅ RESOLVED (direction) Founder call (2026-07-08): prices aren't fixed yet and must be **addable/changeable in the DB** so they reflect in the app without a release. Design adopted: `plan`, `plan_feature`, and `price_book` tables (see [07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md) §2) — new plans and ₹ amounts are rows, the license token is a signed snapshot of them. Actual ₹ amounts get decided whenever you're ready; nothing blocks on it. ## D6 — Accounting depth at launch Full books (P&L/BS) in-app from Phase 2, or ledgers + **Tally export** first? **Recommendation:** ledgers + Tally export in Phase 2, full statements later. Most shops' CAs live in Tally; export buys goodwill for a fraction of the effort. Classic parity data should confirm how many customers actually used full accounting. ## D7 — Store Hub (LAN sync box) as requirement or option Multi-counter stores need counters to see shared stock/bills when internet is down. **Recommendation:** ship Store Hub as an optional role of the main billing PC (a checkbox, not a separate device) from Phase 2. ## D8 — Android POS terminals (Sunmi-style) in scope? ✅ RESOLVED Settled by the market-architecture brainstorm (08-MARKET-ARCHITECTURES.md, in the Store repo): every player winning high-throughput counters is desktop + local-first; Android wins micro-merchants and mobility. **Decision:** v1 = Windows desktop POS; Phase 3–4 = Android *companion* app (line-busting, stock-take, approvals) sharing the billing-engine package; full Android POS only if we later go down-market deliberately. D2 (Electron) stands. ## D9 — Purchase Inbox channels at launch Email-in + manual upload first; WhatsApp-forward intake later (needs WhatsApp Business API setup). **Recommendation:** email + upload in Phase 3, WhatsApp intake fast-follow. ## D10 — Cloud region & provider Any major provider with a Mumbai region works. Deciding inputs: managed-Postgres quality, egress pricing, your team's familiarity. **Recommendation:** pick boring; revisit never. ## D13 — Support ticketing: build vs integrate (raised by 11-ADMIN-SUPPORT-CONSOLE.md) The diagnostic bundle, error-code playbooks, and Store 360° are ours; the ticket queue itself is commodity. **Recommendation:** integrate a boring helpdesk in Phase 2; build only if the integration fights the error-code workflow. ## D14 — Cloud staging ✅ RESOLVED: local-only first, cloud layered on later Founder call (2026-07-09): v1 runs fully local (counters + in-store Oracle 12c per D12), then the cloud tier is added and switched on per store. Accepted consequences: owner app, Purchase Inbox, WhatsApp, central backup, silent auto-update, and HQ fleet features all wait for the cloud workstream. Guardrail (non-negotiable): client UUIDs + dormant outbox rows are written from the first local build, so cloud enablement is switch-on, not migration. See the staging note atop 04-ROADMAP.md (in the Store repo). ## D11 — Existing team & hiring plan (partially answered) Founder input (2026-07-08): a support team exists for Classic and continues; the product principle is a **stable frontend** with changes flowing from backend/DB, handled by the team — adopted as the config-driven design in [07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md). Still needed to calibrate the roadmap: builder headcount and skill mix for the new stack. ## D12 — Local DB engine ✅ RESOLVED (for now): Oracle 12c stays at the store tier Founder call (2026-07-09): the in-store DB remains **Oracle 12c** — it is already deployed shared infrastructure with multiple other systems running on it, so for the existing base the license and operations cost is sunk. Analysis and the adapted design are in [07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md) §3. **Consequences accepted with this call:** - **D3 resolves to "build"**: off-the-shelf sync engines (PowerSync/ElectricSQL) speak Postgres/SQLite only — sync is a custom outbox, with outbox tables living in Oracle and a store-side sync agent draining them to the cloud. - **Counters still carry a local SQLite buffer** (offline bill queue + item-search cache) so a counter survives store-server or LAN death — the "counter never stops" goal is independent of the store engine. - Backend connects via node-oracledb **thin mode** (no Oracle client install needed; supports 12.1+). - **New code stays engine-portable**: repository/DAL layer, portable SQL by default, Oracle-only features only with a written justification — "for now" must stay cheap to revisit. **Revisit triggers (any one reopens this decision):** onboarding NEW customers who don't already have Oracle in-store (license cost is no longer sunk); the other systems sharing the DB migrate off 12c or upgrade (19c/23ai); a security/compliance audit flags the unpatched engine; or sync friction in Phase 0–1 proves materially costlier than an engine port. Risk register entries in 07 §3 remain on record. ## D15 — Internal HQ ops console: build now ✅ RESOLVED (2026-07-09; split to its own repo 2026-07-16) Founder call (2026-07-09): an **internal tool for running our own ~300-client business** — quotations/proforma/invoices in minutes, recurring billing with auto/manual email reminders, per-module payment + AMC tracking, per-client AWS usage & cost, and the full client interaction memory (installation, training, visits, calls, meetings, sales outcomes) in a system instead of heads. **Decision:** start the [doc-11 HQ Console](11-ADMIN-SUPPORT-CONSOLE.md) **early**, as `apps/hq` (built in the shared monorepo at the time; **split into its own repo `C:/SiMS/hq` on 2026-07-16**) — spec in [14-SPEC-HQ-CONSOLE.md](14-SPEC-HQ-CONSOLE.md). Cloud-hosted **vendor-side** (D14 untouched — the store product stays local); SQLite behind portable repositories with S3 backup; **Gmail API** sending from the company mailbox; automated AWS cost attribution; **full replacement of the internal Oracle APEX app** — one-time verified migration, then APEX goes read-only, no double entry. Reuses `@sims/billing-engine` (our invoices are GST documents), `@sims/domain` (paise money, FY, doc series), `@sims/ui`, and the `@sims/auth` patterns. **Alternatives rejected:** fully separate codebase (rebuilds the engine/UI/patterns and builds doc-11 twice); off-the-shelf CRM (no module-level tracking against our catalog, no AWS attribution, no path to the fleet console). **Consequences accepted:** - This shipped as **two deployables with different release rhythms**. On **2026-07-16 they were split into two independent repos** (`C:/SiMS/hq` and `C:/SiMS/store`) — separate histories, deploy scripts, and versioning; the shared packages were forked into each. - Doc-11's phase mapping **splits**: the ops slice ships early (HQ-1…3 in [14-SPEC-HQ-CONSOLE.md](14-SPEC-HQ-CONSOLE.md) §6); the fleet half is unchanged and waits for the cloud tier per D14. - **One repo, two runtime-independent applications** (founder re-confirmed 2026-07-10 after weighing a full split): separate servers, DBs, builds and deploys; only source packages are shared. Guardrails: a bundle-isolation check in the test suite (store bundle contains no HQ code and vice versa), per-app version tags, and the rule that **HQ's DB is the cloud tier's nucleus** — the tenant registry is born from HQ's client table (same UUIDs, promoted once), never built beside it. **Postgres locked as the production engine (founder, 2026-07-10)** — swap runs as its own task after HQ-2, before the real-data import, so live data never migrates engines; SQLite stays the dev/test engine behind the same repositories. Physical repo split stays a cheap, reversible option if practice ever demands it. ## D16 — Quote-to-close funnel slice (2026-07-17) Founder-approved design in `superpowers/specs/2026-07-17-quote-to-close-funnel-design.md`; decisions locked with it: - **Employee foundation rides `staff_user`.** The Employee entity is **backed by the existing `staff_user` table** — renaming it would churn `session.staff_id`, `interaction.staff_id` and audit entity names for zero gain; "Employee" lives in the repo/API/UI vocabulary only. Roles widened to `owner | manager | staff` via a guarded, transactional table rebuild (SQLite cannot ALTER a CHECK); the enum is validated in the repo. Session hardening rode along: `verifySession` requires `active=1`, and deactivation / password reset purge sessions in the same transaction. - **Chase-list over kanban (option B).** The pipeline is **one cross-client ranked list** — most-overdue on top, colour bands, an explicit next action per row, `All | Mine | Overdue | Lost` filters — not a kanban board. - **Derived pipeline stages, zero new pipeline storage.** Stage (Enquiry / New Project / Quoted-Waiting / Won / Lost) is **never stored** — computed from `client.status` + the client's latest live quotation; age anchors on the first `sent` document event. The only new column anywhere in the slice is `client.owner_id` (a bare lead has no creator to derive an owner from — A4). - **Escalating owner+client reminders on the dated `reminder_schedule` table.** New sendable `quote_followup` rule kind: the queued reminder row **is** the owning employee's nudge (owner derived `doc_id → document.created_by`), and its send **is** the client email with a public share link. Cadence **and** message text are dated config rows (rule 3) resolved by `resolveSchedule` — defaults 3/7/14 (quote) and 7/15/30 (invoice) ship as seeded rows plus code-constant fallbacks, so a cadence change is a new dated row, never a release. Catch-up fires only the highest crossed milestone; accept / lose / convert / cancel dismisses open nudges per-row (audited) in the same transaction; previews resolve shares read-only — minting happens only in the send path (60-day expiry, live links reused). - **Convert recompute + duplicate-invoice guard (F3/F4).** PROFORMA → INVOICE rebuilds the carried lines through `computeBill` on the **invoice's own `doc_date`** (a tax invoice uses the rate that is law on its issue date), and `convertDocument` rejects any source with a live (non-cancelled) forward child — one sale, one invoice number. - **Proforma supersede matrix.** Corrections stay firewalled by document type: a draft PI is simply re-drafted; an issued, unallocated PI may cancel (number stays consumed) and be recreated as a `ref_doc_id`-linked supersede; an issued INVOICE is **never** recreated — cancel-if-unpaid or credit note only. - **Assumptions A1–A7 (defaulted, flagged in the spec):** A1 module roster balances support and revenue; A2 employees are a plain table (no org chart); A3 document owner = `created_by` for v1 (no `document.owner_id` until a handoff feature is real); A4 `client.owner_id` is the one genuinely new owner column; A5 notify-all text is owner-typed ad-hoc, not dated config; A6 roster visible to all staff, notify-all owner/manager only; A7 invoice-overdue anchors on `doc_date` (no due-date column exists). **Recorded deviation from the funnel spec's blanket pagination rule (§5 / rule 8):** `GET /employees` returns the full set with `total` and **no `page`/`pageSize`** — console users are a small bounded set (single-digit headcount), the count is displayed, and nothing is silently truncated, which is the intent of rule 8. If the team ever grows past a screenful, paginate it like every other list. **Delivery state (2026-07-17, final):** all ten phases landed. Phases 1–6 (employee foundation + management surface, owner plumbing, dated schedule, pipeline chase-list, escalating quote follow-up) plus the F3/F4 convert hardening, then 7–10 in the same day's second pass: one-click `POST /documents/:id/convert-and-send` (guards → one txn convert+issue → post-commit email), proforma `POST /documents/:id/supersede`, invoice `dN` milestone escalation (d7/d15/d30 from the dated schedule, F7 highest-only catch-up), and the module → client roster (`GET /modules/:id/clients` + CSV export + owner/manager Notify-all with per-recipient audit). Suite at 293 tests / 57 files. ## D17 — HQ console visual redesign: warm ops-console (2026-07-17) Founder-approved spec in `superpowers/specs/2026-07-17-hq-console-redesign-design.md` (delivered same day; adversarial multi-agent review + fixes recorded there). Decisions locked with it: - **Direction: "Direction A v2 discipline wearing the H-04 warm palette."** Synthesised from the two most recent design artifacts (the chosen Claude-Design "Direction A v2" system and the H-04 Client 360 mockup): warm paper neutrals (`#FAFAF7`/`#E7E5DE`/ `#1A1A17`), flat border-defined surfaces, shadows only on overlays. - **Teal is the default accent; primary buttons are ink, not accent.** Accent colour is reserved for *state* (active nav, chips, links, focus). The 6 previous accents stay switchable; dark mode is warm-tinted with explicit per-accent dark values. - **Mono/sans split.** JetBrains Mono for machine data (codes, doc numbers, GSTINs, ids); Inter with tabular numerals for money. Uppercase micro-labels never below 11px (WCAG). - **Ctrl-K command palette over more nav.** Client search + pages + actions in one keyboard-first overlay (generic `CommandPalette` in `@sims/ui`; AND-term matcher). - **Client 360 = record header + KPI row + relationship-pulse ribbon + deep-linkable tabs** — the ribbon plots documents / payments / interactions / AMC on one 12-month axis from data the page already loads (no chart lib, no new endpoints). - **UI-only slice.** No backend/schema changes; `@sims/ui` stays generic (no router or icon deps — `lucide-react` lives in `apps/hq-web` only). Known deferred: server-side "outstanding ₹" figure for the Client 360 KPI row (client-side paise math is banned). ## D18 — Go-live cluster: due dates, ops pages, shell & profile, client support data ✅ DELIVERED (2026-07-17) Founder-approved slice (spec `superpowers/specs/2026-07-17-golive-cluster-design.md`), built in ten phases the same day. The calls that matter going forward: - **Invoice due dates = terms default + per-document override.** `billing.payment_terms_days` (seeded 15) is the operational default; the *dated* fact is the `due_date` stamped on each invoice at issue (covers manual issue, convert-and-send and recurring generation in one place). Overdue reminders anchor `COALESCE(due_date, doc_date)`; the chase mail says "was due on X and is now N day(s) overdue"; legacy invoices keep the old anchor. - **Client support data (APEX parity):** `anydesk`/`os`/`district`/`sector` on the client; district+sector filter the book. The DB password is AES-256-GCM at rest (`HQ_SECRET_KEY`, loud refusal without it), NEVER in payloads (only `hasDbPassword`), write and reveal are owner/manager-gated with one audit row per reveal. - **Roster single-write-path:** the module→clients roster edits (kind/edition/renewal/ status, assign, unassign) reuse `updateClientModule`/`assignModule` — the same audited functions Client 360 uses, so the two views cannot disagree. `ClientModulePatch` gained kind/edition with allowedKinds validation. - **Quotation sectioning:** QUOTATION prints one titled section per module (name + what's-included + price; print page-break per module); INVOICE/PI/CN/RECEIPT keep the billing grid. Rendered in the ONE `documentHtml`, preserving preview↔PDF fidelity. - **Self-service profile:** `/me` routes are strictly self-scoped (role/email untouchable); changing your own password requires the current one and kills every OTHER session. - **My Day:** staff dashboards are always scoped to their own book server-side; owner/manager toggle Mine/Everyone (`?mine=1`). Sidebar collapses to an icon rail. - **APEX import UI:** owner-only `/import` wraps the existing stage/verify/commit — commit stays locked while any staged row carries a problem. Delivery was followed the same day by an independent review of the full slice diff: 8 findings (4 major — convert-and-send click bubbling on the register, importer not mapping the WS-F support fields, Pipeline still on the old plain convert, missing client-book filter UI; 4 minor — typed contact roles absent, dashboard/aging still anchored on doc_date, non-atomic dbPassword PATCH, audit-outside-txn on three new write paths) — all confirmed and fixed in one pass (`fix(review)` commit). Suite at 345 tests / 66 files after the review pass. ## D19 — Postgres switch executed (D15 delivered) ✅ DELIVERED (2026-07-17) The engine slice queued after D18, done BEFORE the real APEX cutover so production data is born in Postgres and nothing is ever migrated. Spec: `superpowers/specs/2026-07-17-postgres-switch-design.md`. The calls that matter: - **One async DB interface, two engines.** Every repo/route/scheduler/test awaits `db.all/get/run/exec/transaction`. better-sqlite3 stays for dev + the fast suite (`SqliteDb`, statement-cached, manual BEGIN/SAVEPOINT); Postgres (`PgDb`, node-pg Pool) is selected by `DATABASE_URL`. Repos keep `?` placeholders and portable SQL; the pg adapter rewrites `?`→`$n`, pins transactions to one pooled client via AsyncLocalStorage (nested = savepoints), and parses int8/numeric to JS numbers (paise stay integer math). - **Postgres schema choices:** bigint for every paise column (int4 overflows at ₹2.15 crore), 0/1 flags stay INTEGER (repos say `active=1` on both engines), JSON stays TEXT (node-pg auto-parses jsonb — would break JSON.parse at the repo edge), dates stay TEXT ISO. Numbered migrations + schema_migrations runner (migrations-pg.ts), applied atomically; the reminder rule_kind CHECK is born in its final widened form. - **Proof:** default suite 346/346 on SQLite (zero behavior change); 5-test pg integration suite (opt-in `HQ_PG_TEST_URL`, wipes its target) green on local PostgreSQL 17 — migrations-from-zero, series numbering + due-date stamp, paisa-exact payment→paid, ON CONFLICT exactly-once reminders, savepoint rollback, HTTP boot smoke. - **Known nuance before go-live:** LIKE is case-insensitive on SQLite, case-sensitive on Postgres — client search will match case-sensitively in prod until repos switch to `LOWER(...) LIKE LOWER(?)`. - Local dev Postgres: PostgreSQL 17 Windows service, superuser `postgres`, role `hq`, databases `hq` (runtime) + `hq_test` (integration suite). Prod = RDS at go-live, same `DATABASE_URL` mechanism. ## D20 — HQ hosting target: hq.simssoftware.com on the company AWS box (2026-07-17) Founder call. The console goes live at **`https://hq.simssoftware.com`**: - **DNS stays at GoDaddy** (where simssoftware.com lives) — one `A` record `hq` → the EC2 Elastic IP. No nameserver moves, no Route 53 migration; the rest of the domain (www, mail) is untouched. - **The existing company EC2 hosts it** as its own section (`/opt/sims-hq`), beside whatever else the box serves — no new instance for v1. TLS via Caddy (or a server block in the box's existing nginx), pm2 from the repo root, only 80/443 public. - **Sequencing: database first, then the server section.** Provision RDS Postgres, set `DATABASE_URL` (D19's switch), boot + migrate, run the APEX cutover into Postgres — and only then wire DNS/TLS and hand the URL to staff. Production data is born in Postgres; the SQLite dev DB is never migrated. - Runbook updated with the exact configs (GoDaddy record, Caddyfile/nginx block, RDS sizing + security-group shape, pm2 invocation): `DEPLOY-HQ.md` §0 and §7. The founder has hosted this way before — the runbook now carries the fiddly configuration detail so setup on the box is a checklist, not archaeology. ## D20 — APEX parity: ticket desk, per-service data, branches, full importer ✅ DELIVERED (2026-07-17) Driven by the real APEX export (`C:\SiMS\hq\Apex`, 25 tables). A 46-agent profiling+audit swarm analysed the actual data before the importer was trusted. Spec: `superpowers/specs/2026-07-17-apex-parity-design.md`. The calls that matter: - **Ticket desk is in scope** (reversing the original non-goal): 4,567 APEX tickets are the team's daily workbench. New `ticket` table (team-visible; "mine" is a filter not a gate), status open/in_progress/waiting/closed/dropped, honest pagination, kind suggestions from history. Client 360 gains a Tickets tab. - **client_module carries per-service operational data** (provider/username/password_enc/ details JSON/remark) — the portal password encrypted + reveal audited, exactly like the client DB password. **NOTE: D21 restructures this into a module-defined field schema.** - **client_branch** table (minimal; APEX ho_branch_list parity). - **Five modules** (SMS, RTGS, WhatsApp, Mobile App, ATM) seeded from client YES/NO flags; ticket + roster module pickers read the live catalog, so a sixth module needs no code. - **Importer v2** (`import-apex-full.ts`, 9 files) survived the swarm's real-data findings: bills are LINE ITEMS grouped by INVOICE_NO across three GST eras; 46 raw ticket statuses pattern-mapped; OLD_ID re-key chains collapsed; APEX test client (SID 329) + orphan refs skipped with counted notes; plaintext credentials encrypted at commit (refuses without HQ_SECRET_KEY). Trial import into local Postgres verified — see the spec banner for the reconciled counts (273 clients / 150 docs = ₹33.17L / 1,826 tickets / INVOICE seed 10252). Two real-data facts to carry forward: the export has NO state/GSTIN column (all docs split CGST/SGST at the company state) and one client's OS field holds a plaintext Linux root password (move it into an encrypted field + rotate at cutover). ## D21 — Module-defined field schema (config on the module) ✅ DELIVERED (2026-07-17) Spec `superpowers/specs/2026-07-17-module-field-schema-design.md`. Each module declares its own operational fields (`module.field_spec` JSON: FieldDef key/label/type/options/required); per-client values live on `client_module` as `field_values` (non-secret, type-validated against the owning module's spec) + `secrets_enc` (one AES-GCM blob over a {key:plaintext} map, N secrets, reveal audited). Owner defines a module's fields on the Modules page; the Client 360 service card renders the form automatically; a new module or field needs NO code and NO migration — the founder's flexibility mandate, delivered. Built **additively** over D20 (login trio keeps its columns; retiring them into field_spec is a later cleanup after the direction is confirmed). Verified on the real re-import: RTGS 13 fields / SMS 6 / MobileApp 1, 53/53 RTGS + 102/105 SMS links carrying structured values. Suite 363 green.