You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/docs/superpowers/specs/2026-07-16-hq-store-split-d...

218 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Split SiMS into two independent projects: HQ and Store
**Date:** 2026-07-16
**Status:** DELIVERED 2026-07-16 (executed directly, no separate implementation plan)
## Delivered — outcome
Both repos exist, are independently buildable/testable/runnable, and were red-teamed.
- **`C:/SiMS/hq`** — this repo. Inherited the full history. Apps `hq`, `hq-web`; trimmed
forks of `@sims/domain`, `@sims/auth`, `@sims/billing-engine`, `@sims/ui`. Typecheck
clean, **177 tests pass**. Server boots on :5182 against the real `data/hq.db` (225 KB)
**when launched from the repo root** (the DB path resolves against cwd — see README).
- **`C:/SiMS/store`** — apps `pos`, `store-server`, `backoffice`; full package set.
**Fresh single-commit history with zero HQ bytes** (rebuilt after an audit caught HQ
material in the first init). Typecheck clean, **205 tests pass**. Server boots on :5181.
Its copied dev DB is SQLCipher-encrypted — needs the correct `SIMS_DB_KEY` to open, else
start fresh.
- **`C:/SiMS/Store Software`** — the pre-split combined repo. **Deleted 2026-07-16**: all
contents removed; the empty folder shell could not be unlinked from within the session
that is rooted in it, to be removed by the user after reopening elsewhere.
Decisions held: shared code forked (not shared) so each repo is self-contained and free to
diverge; the `@sims/*` scope and `SIMS_*` env prefix are deliberately kept in both — they
resolve per-repo/per-process and never collide, so renaming them was declined as pure churn.
Transfer completeness verified: all 273 original tracked files land in one repo or the other
(zero orphans); every shared source module is byte-identical across the two.
## Why
`C:/SiMS/Store Software` currently holds two products that are not one product.
**HQ** (`apps/hq`, `apps/hq-web`) is internal: our client book, their projects,
proforma-invoice billing, call tracking, AMC contracts, AWS cost recovery,
payments and reminders. It is the commercial record of the business.
**Store** (`apps/pos`, `apps/store-server`, `apps/backoffice`) is the product we
sell: GST retail management deployed on client premises.
They are independent projects and should be developed, deployed and released as
such:
- **Different audiences.** HQ is internal-only; Store ships to ~300 clients.
- **Different databases.** Two SQLite schemas with no conceptual overlap. HQ:
`client`, `module`, `document`, `interaction`, `amc_contract`, `aws_usage`,
`payment`, `reminder`, `staff_user`. Store: `item`, `batch`, `bill`,
`purchase`, `stock_movement`, `party`, `counter`, `store`, `tenant`. Four
names collide (`audit_log`, `doc_series`, `setting`, `tax_class`) but they are
different definitions in different DBs — shared in appearance only.
- **Different cadences.** Of 75 commits, 60 touch HQ and 2 touch Store. The repo
is already HQ wearing a Store name.
- **Confidentiality.** A single tree means every Store branch, contractor
handout, or repo copy carries our client book and revenue data in its history.
## Decisions
| # | Decision | Choice |
|---|---|---|
| 1 | Split shape | Two separate git repos |
| 2 | Shared code | Fork into each repo, trimmed to what each uses |
| 3 | History | HQ inherits the existing 60 commits; Store starts fresh |
| 4 | Docs | Trim each side to only what that repo needs |
| 5 | Repo name | `C:/SiMS/hq` (keep — matches docs/14, DEPLOY-HQ.md, `@sims/hq`, 60 commits) |
| 6 | Project tracking | Out of scope — split first, build after |
Rejected: a third shared repo and a private npm registry. Both add versioning
overhead to solve a divergence risk the evidence says does not exist (see below).
## Target layout
```
C:/SiMS/
├── hq/ ← current repo, 60 commits preserved
│ ├── apps/{hq, hq-web}
│ ├── packages/{domain, auth, billing-engine, ui} ← trimmed, names kept
│ ├── docs/ ← HQ-only, renumbered
│ ├── DEPLOY-HQ.md
│ └── README.md, STATUS.md ← STATUS.md is new
└── store/ ← fresh git init
├── apps/{pos, store-server, backoffice}
├── packages/{domain, billing-engine, auth, ui, config, scanning, search-core}
├── docs/ ← the remaining 15 docs
└── README.md, STATUS.md, BUILDING.md
```
Each repo gets its own `package.json` (`sims-hq` / `sims-store`), `tsconfig`,
`vitest.config`, `.gitignore`, and `CLAUDE.md`. Neither references the other at
any point — no workspace links, no relative paths across roots, no doc citing a
file it does not contain.
## Shared code: fork, trimmed
HQ's dependency on shared packages is shallow and fully verified — utilities and
a design system, no shared schema or business logic.
**HQ keeps the package names** (`@sims/domain`, `@sims/auth`,
`@sims/billing-engine`, `@sims/ui`) and removes files underneath. Import
specifiers are unchanged, so **no import statement in HQ's app source changes**
not one of `apps/hq`'s 75 files or `apps/hq-web`'s 14. What changes is narrow and
enumerable: the four `index.ts` barrels (drop the removed `export *` lines), and
`apps/hq-web`'s `package.json` + `vite.config.ts` (drop the dead `@sims/printing`
dependency and alias).
| Package | HQ keeps | HQ drops |
|---|---|---|
| `domain` | `ids`, `money`, `business-day`, `doc-series`, `gstin`, `documents` | `tenancy`, `masters`, `batch`, `outbox` |
| `auth` | `pin` | `lockout`, `permissions`, `session` |
| `billing-engine` | `compute`, `tax` | `returns`, `gst-returns` (588 lines) |
| `ui` | all 5 files | — |
| `printing` | — | whole package |
Store's `packages/` is untouched.
### Closure verification (done, 2026-07-16)
Every trim is a clean cut — verified by reading the imports, not by assumption:
- `compute.ts` imports `./tax` and four **types** from `@sims/domain`
(`BillLine`, `BillTotals`, `Paise`, `IsoDate`) — all in files HQ keeps.
- `tax.ts` imports only `IsoDate`.
- `documents.ts` imports only from `money` and `business-day`.
- `pin.ts` imports only `node:crypto` — no `@sims` deps.
- `ui` has no `@sims` deps at all (`react` + its own `theme`).
- Nothing HQ retains imports `tenancy`, `masters`, `batch`, or `outbox`. The
reverse edges (`batch → business-day`, `masters → money`) are one-way and
drop cleanly.
- `@sims/printing` is declared in `apps/hq-web/package.json` and aliased in its
`vite.config.ts`, but **imported by no source file** — a dead dependency.
### Why forking is safe
The divergence risk for forked code is GST computation. It does not apply:
**tax rates are dated DB rows, not code.** `tax_class` rows carry
`effective_from`/`effective_to` and resolve by the bill's business date, with
the Sept-2025 GST 2.0 slab change cited in `tax.ts` as the precedent. HQ reads
its own `tax_class` table; Store reads its own. A rate change is a row insert
per DB, never a code edit.
What remains in `compute.ts` is stable arithmetic — CGST/SGST/IGST split,
rounding. It has no reason to change often, and the two products are entitled to
diverge: HQ invoices software modules, Store bills retail goods.
## History
The current repo becomes `C:/SiMS/hq`: all 60 HQ commits preserved, then one
commit removing Store paths. No `filter-repo` surgery.
`C:/SiMS/store` gets `git init` and one clean initial commit. It loses only 2
commits (one titled `wip`) and gains a guarantee: **zero HQ bytes in its history,
permanently.** This is the confidentiality boundary, and it is only real if the
Store history starts empty.
## Docs
Trimmed per repo — each doc cut to what that side actually needs.
**HQ** (renumbered `00``04`):
| New | From | Treatment |
|---|---|---|
| `00-HQ-CONSOLE-SPEC.md` | `14-SPEC-HQ-CONSOLE.md` | As-is |
| `01-ADMIN-SUPPORT-CONSOLE.md` | `11-ADMIN-SUPPORT-CONSOLE.md` | As-is |
| `02-PROJECT-RULES.md` | `16-PROJECT-RULES.md` | Trimmed: keep money, config-over-code, document rules; drop counter/POS rules |
| `03-DECISIONS.md` | `06-DECISIONS.md` | Extract D15 + HQ-only calls |
| `04-DB-AND-CONFIG.md` | `07-DB-AND-CONFIG.md` | Trimmed: D12 portable-repo pattern, dated tax-class config |
Plus `DEPLOY-HQ.md` as-is, a rewritten `README.md`, and a **new `STATUS.md`**.
**Store**: the remaining 15 docs, with D15/HQ references pruned from
`06-DECISIONS.md` and the HQ rows removed from `README.md`'s doc table.
### HQ has no STATUS.md
The largest app in the repo (75 files, at go-live per `DEPLOY-HQ.md`) has no
build-state doc. `STATUS.md` covers only the Store product. HQ's current state
had to be reconstructed from source and `git log`. Write one during the split,
covering the built surface: clients, modules and price book, documents
(QT/PI/INV/CN) with templates/PDF/share links, interactions, AMC, AWS usage,
payments, recurring plans, reminders with Gmail send and bounce handling,
reports, dashboard, audit, APEX import, scheduler.
## Verification
The split is done when, for each repo independently:
1. `node_modules` removed and `npm install` run fresh
2. `npm run typecheck` passes
3. `npm test` passes
4. No path resolves outside its own root
Typecheck is the real proof of the fork closure — a missed dependency fails
immediately and loudly.
Additionally, confirm the confidentiality boundary holds rather than assuming it:
searching `C:/SiMS/store/.git` for client, AMC, and AWS-cost terms must return
nothing.
## Out of scope
- **Project tracking.** Listed as a core HQ concept but not built — no `project`
table, no repo module, no UI. It is a feature to design and build, not code to
move. Deferred so the split stays mechanical: pure move-and-verify, with no new
feature to confuse a failure with.
- **Conversion reporting.** `client.status` (`lead`/`active`/`dormant`/`lost`)
exists but has no lead-to-won reporting on it. Wanted alongside the existing
conversation log (`interaction`), but after the split.
- **Oracle 12c adapter** (Store, pending Classic DDL) and the **46 wireframe
back-office screens** (Store) are unaffected by this work.
## Risks
| Risk | Mitigation |
|---|---|
| A trimmed file is needed by something unread | Typecheck on a fresh install in each repo catches it immediately |
| Store history retains HQ data | Fresh `git init`, verified by searching `.git` |
| Docs cite files that no longer exist in their repo | Grep each repo's docs for cross-references to removed paths |
| HQ's dead `@sims/printing` alias breaks its Vite build when dropped | Remove from both `package.json` and `vite.config.ts` together; build verifies |