docs(d21): stamp DELIVERED, D-record, real-data verification, test count 363

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/client-detail-redesign
Thomas Joise 4 days ago
parent 51023f9ec4
commit 13d2b5fabb

@ -43,7 +43,7 @@ distilled to what applies to HQ. A change that breaks one does not land.
| Append-only audit | Every mutation calls `writeAudit(db, userId, action, entity, id, before, after)` in the **same transaction** as the change. The audit log is append-only — never update or delete rows. Previews and the public share route write nothing. | `audit.ts` | | Append-only audit | Every mutation calls `writeAudit(db, userId, action, entity, id, before, after)` in the **same transaction** as the change. The audit log is append-only — never update or delete rows. Previews and the public share route write nothing. | `audit.ts` |
| Client id is the future tenant id | `client.id` is a client-generated UUIDv7 that becomes `tenant_id` when the cloud tier stands up — same rows, promoted once. Never re-key clients. | `repos-clients.ts` | | Client id is the future tenant id | `client.id` is a client-generated UUIDv7 that becomes `tenant_id` when the cloud tier stands up — same rows, promoted once. Never re-key clients. | `repos-clients.ts` |
| No silent caps | Every list that can grow paginates or warns; never silently truncate. | reports, directory queries | | No silent caps | Every list that can grow paginates or warns; never silently truncate. | reports, directory queries |
| Strict TS, green before landing | Strict TypeScript everywhere; `npm run typecheck` and `npm test` both clean before anything lands. Money math is locked by tests — a rounding change without a test change does not land. | `tsc`, vitest (346 tests) | | Strict TS, green before landing | Strict TypeScript everywhere; `npm run typecheck` and `npm test` both clean before anything lands. Money math is locked by tests — a rounding change without a test change does not land. | `tsc`, vitest (363 tests) |
## Running it ## Running it
@ -66,7 +66,7 @@ cd apps/hq-web && npm run dev
- Postgres integration tests: `HQ_PG_TEST_URL=postgres://hq:hq_dev@localhost:5432/hq_test npm test` - Postgres integration tests: `HQ_PG_TEST_URL=postgres://hq:hq_dev@localhost:5432/hq_test npm test`
(wipes that database each run — never point it at real data). (wipes that database each run — never point it at real data).
- Health check: `GET /api/health``{ ok: true }`. - Health check: `GET /api/health``{ ok: true }`.
- Verified state: `npm install` + typecheck clean, 346 tests pass. Exercise real flows end - Verified state: `npm install` + typecheck clean, 363 tests pass. Exercise real flows end
to end before claiming done. to end before claiming done.
## Docs (kept in `docs/`) ## Docs (kept in `docs/`)

@ -332,9 +332,14 @@ Two real-data facts to carry forward: the export has NO state/GSTIN column (all
CGST/SGST at the company state) and one client's OS field holds a plaintext Linux root 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). password (move it into an encrypted field + rotate at cutover).
## D21 — Module-defined field schema (config on the module) — DESIGNED, queued after D20 import ## 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 Spec `superpowers/specs/2026-07-17-module-field-schema-design.md`. Each module declares its
own operational fields (`module.field_spec` JSON); per-client values organised by that own operational fields (`module.field_spec` JSON: FieldDef key/label/type/options/required);
schema; a generalised encrypted secret map replaces the single portal password. Founder per-client values live on `client_module` as `field_values` (non-secret, type-validated
mandate: the app, modules and client↔module mapping stay flexible — a new module or a against the owning module's spec) + `secrets_enc` (one AES-GCM blob over a {key:plaintext}
changed field needs no code and no migration. Implementation follows the real-data cutover. 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.

@ -1,10 +1,20 @@
# HQ Console — Module-Defined Field Schema (D21) # HQ Console — Module-Defined Field Schema (D21)
> **STATUS: DESIGNED 2026-07-17, implementation queued AFTER the D20 trial import** > **STATUS: DELIVERED 2026-07-17** (built additively over D20; suite 363 green). Approach:
> (founder timing call). Approach: *config on the module* (the simplest of the three > *config on the module* (the simplest of the three weighed). North star (founder): the app,
> weighed). North star (founder): the app, the modules, and the **client↔module mapping** > the modules, and the **client↔module mapping** must stay flexible — a new module or a
> must stay flexible — a new module or a changed field must never need a code change or a > changed field must never need a code change or a DB migration.
> DB migration. >
> **Verified on real data** (re-import into local Postgres): RTGS declares 13 fields, SMS 6,
> MobileApp 1; 53/53 RTGS links and 102/105 SMS links carry structured field values (IP, IP
> code, partner bank, IFSC, QR, SMS balance, reseller, alert types…) instead of a free-form
> blob. Owner defines a module's fields on the Modules page; every client on that module
> shows the form automatically; secret fields encrypt with an audited reveal.
>
> **Built additively** (deliberate, for the confirm-after-build gate): D20's login trio
> (provider/username/portal-password) stays in its own columns/card; D21 adds the
> module-specific fields on top. Retiring the D20 columns into field_spec is a later cleanup
> once the direction is confirmed.
## 1. The problem ## 1. The problem

Loading…
Cancel
Save