10 KiB
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. Appshq,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 realdata/hq.db(225 KB) when launched from the repo root (the DB path resolves against cwd — see README).C:/SiMS/store— appspos,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 correctSIMS_DB_KEYto 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.tsimports./taxand four types from@sims/domain(BillLine,BillTotals,Paise,IsoDate) — all in files HQ keeps.tax.tsimports onlyIsoDate.documents.tsimports only frommoneyandbusiness-day.pin.tsimports onlynode:crypto— no@simsdeps.uihas no@simsdeps at all (react+ its owntheme).- Nothing HQ retains imports
tenancy,masters,batch, oroutbox. The reverse edges (batch → business-day,masters → money) are one-way and drop cleanly. @sims/printingis declared inapps/hq-web/package.jsonand aliased in itsvite.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:
node_modulesremoved andnpm installrun freshnpm run typecheckpassesnpm testpasses- 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
projecttable, 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 |