A system where a functional Business Analyst authors the complete application specification — screens, fields, validations, workflows, actions, schedules — and a deterministic generator produces the backend, database, API integrations and frontend wiring. AI is used only where it is safe: one-time construction of the platform itself, and visual UI generation via Stitch.
wallet.debit, notify.send, approval.request, provider.call, status.transition…). Developers publish new verbs once; the BA reuses them forever. BA owns composition; devs own primitives.Four producers, one application. The tags below are used throughout this document: BA-authored Deterministic generator AI (Stitch) Dev-owned primitive
| Output | Contents | Notes |
|---|---|---|
| Backend (complete) | NestJS service per app: REST endpoints per entity/screen action, request validation from spec rules, authentication, module- and field-level RBAC guards, service layer invoking verb primitives, workflow-engine integration, audit logging on every mutation, CSV import/export where flagged. Per-entity CRUD defaults (always generated): pagination (capped), sorting, filtering, Postgres full-text search, bulk upsert with per-row error reporting, bulk deactivate, soft delete ( deleted_at; hard delete only where spec says so), idempotency keys on all money-touching endpoints, rate limiting per route class.Observability (always generated): structured JSON logs (pino) with end-to-end request IDs, OpenTelemetry traces, Prometheus metrics, health/readiness endpoints. | Generated code is never hand-edited. Custom logic enters only via extension points (§8). Redis policy: queues (BullMQ), sessions, OTPs, rate-limit counters and idempotency keys by default. Read caching is opt-in per entity via a spec flag (cache-aside, TTL) for change-tolerant data only (masters, catalogs, dashboards) — never wallet balances or ledger. Postgres is always the single source of truth; no write-behind. |
| Database (complete) | PostgreSQL schema, constraints & indexes, spec-diff-driven migrations, destructive-change detection (blocked without explicit confirmation), seed/master data packaged from the studio (GST rates, state codes, categories, compliance catalogs), numbering series (sequential GST invoice numbers). | Migrations run staging-first via the publish gate. |
| Crons / scheduled jobs | Every on-schedule trigger in the spec becomes a queued job: nightly settlement, compliance reminders, KYC document expiry, abandoned-cart flags, low-balance alerts. | BullMQ + Redis; retries, idempotency keys and dead-letter queues generated by default. |
| External API integration services | Adapter-pattern scaffolds per provider: interface, config/credential slots, request/response mapping stubs, webhook receivers, callback verification, retry policy, sandbox/production switching. | Adapter internals are dev-written (provider quirks are not spec-able); everything around them is generated. |
| FE API integration layer | Typed API clients + React Query hooks per endpoint, auth/session handling, error & loading conventions, and a binding manifest: screen → fields → validations → actions → API calls, consumed when wiring Stitch-generated UI. | This is the contract that lets AI-generated UI stay a thin skin over deterministic behaviour. |
| Generated tests | One test per validation rule, per legal/illegal status transition, per workflow path; API contract tests per endpoint. Every test carries the SRS requirement ID from the spec (srs_ref). | Requirement → spec → code → test traceability for client sign-off (fixed-price insurance). |
Every requirement type the BA provides, where it is stored, and what he sees on screen. Everything below auto-syncs to YAML in git (the canonical spec, §6) — the BA never touches YAML directly.
| # | What the BA provides | Stored in | UI shown to the BA |
|---|---|---|---|
| 1 | Entities & fields (data dictionary) | Grist → YAML | Editable spreadsheet grid |
| 2 | Field validations (required, format, range, cross-field) | Grist (linked to fields) → YAML | Editable grid — field/operator/value via dropdowns |
| 3 | Enums & dropdown option lists | Grist → YAML | Editable grid |
| 4 | Form screens — sections, fields, visibility rules, buttons | Grist (Screens + Screen-Fields tables) → YAML | Editable table + live screen preview with fake data |
| 5 | List screens — columns, filters, search, sort, row/bulk actions | Grist → YAML | Config form + live list preview |
| 6 | Status lifecycles — statuses, transitions, who can transition | YAML (authored on canvas) | State diagram editor (boxes & arrows) |
| 7 | Workflows & on-click action chains | YAML (authored on canvas) | Drag-drop flow canvas with verb palette (§13) |
| 8 | Schedules — when timed flows run (nightly settlement, reminders) | YAML (trigger config) | Simple form — frequency/time dropdowns on the flow's trigger node |
| 9 | Decision tables — commission slabs, tier pricing, eligibility | Grist → YAML | Condition → outcome grid (Excel-style) |
| 10 | Roles & permissions (module / screen / field level) | Grist → YAML | Matrix grid with checkboxes |
| 11 | Menus & navigation per role | Grist → YAML | Tree editor |
| 12 | Notification templates (SMS / email / push / in-app) | Git (via studio editor) | Rich-text editor + merge-field dropdowns + preview |
| 13 | Document / PDF templates (GST invoice, statements, contracts) | Git (via studio editor) | Template editor + live PDF preview |
| 14 | Numbering series (invoice no., order no.) | Grist → YAML | Small settings form |
| 15 | Report definitions — dataset, filters, group-bys, columns, chart | Grist → YAML | Structured form + sample output preview |
| 16 | Master / seed data (GST rates, state codes, categories, compliance catalog) | Grist → packaged with app | Editable spreadsheet grid |
| 17 | Import/export flags per entity (CSV bulk upload) | Grist → YAML | Checkbox on the entity row |
| 18 | SRS traceability refs (srs_ref per item) | Grist column → YAML | Dropdown column on every grid/table |
One web application, several surfaces — each matched to the shape of what is being authored. All surfaces write to the same spec and obey P1 (dropdowns only).
| Surface | What the BA authors | UI shape | Base (all OSS) |
|---|---|---|---|
| Data grid | Entities, fields, enums, field-level validations, permission matrices, master/seed data | Spreadsheet with linked-record dropdowns; bulk entry | Grist (self-hosted) or NocoDB, synced to YAML |
| Logical screen designer | Form screens: sections, fields (picked), conditional visibility, validations, buttons/actions | Structured builder + live preview with fake data | Custom (React) + Faker |
| List screen designer | List/table screens: columns, filters, search, sort, bulk & row actions, export flags | Configuration form + live preview | Custom (React) — mostly config, low effort |
| Status lifecycle editor | Statuses, allowed transitions, who may transition, what fires on transition | Small state diagram (states + arrows) | React Flow |
| Flow canvas | Workflows & action chains from the verb palette; branches, approvals, error/compensation paths | Drag-drop node canvas; every node configured via dropdown forms | React Flow (custom palette) |
| Decision tables | Commission slabs, tier pricing, eligibility/applicability rules | Condition → outcome grid (Excel-native mental model) | Grid with conventions |
| Template editors | Email/SMS/push templates; printable documents (GST invoice, statements, contracts) with merge-field pickers and numbering series | Rich text + merge-field dropdowns; PDF preview | Custom + pdfmake/Puppeteer |
| Navigation & RBAC | Menu tree per role; screen/module access | Tree editor + matrix grid | Custom (trivial) |
| Report definitions | Dataset (entity + joins), filters, group-bys, aggregates, columns, chart type, export | Structured form (tabular v1; visual composer later) | Custom |
Two roles, strict separation: the BA authors content and publishes to SIT; the Technical PM (TPM) reviews and promotes to UAT and Prod. The BA cannot promote; the TPM cannot edit the spec — every production change passes both pairs of eyes, and git records who published and who promoted each version.
emart-spec-v13) → CI generates code + migration plan + tests and auto-deploys to the SIT URL. The BA verifies his changes in the actual running application. SIT redeploys on every publish — it may run several versions ahead of Prod.Environments: each is a fully running copy at its own URL (e.g. emart-sit.… / emart-uat.… / emart.…) with its own database, seed data and provider credentials (sandbox in SIT/UAT, live in Prod). The environment selector in the studio is a viewer: see which version each stage runs, browse it read-only, compare diffs. Rollback = repoint a stage to an earlier tag.
No parallel branches (v1): an urgent Prod fix is a small fast-tracked publish + promotion, not a spec branch — YAML branching/merging is deferred deliberately.
emart-spec-v13) with a human-readable message referencing the SRS ID. The BA never touches git — the studio commits behind the publish button.credit_limit added to Retailer; flow 'Confirm Order' gained 2 steps" — rendered by diffing the YAML trees, never raw YAML. Doubles as change-request evidence under the SOW's change-control clause.error.raise, insufficient-balance branches, decision-table rejections) — every message template written by the BA.Every workflow starts from exactly one of four triggers — this covers the full SRS surface (settlements, reminders, expiries, webhooks):
| Trigger | Example from Omara scope | Generated as |
|---|---|---|
on-action (user clicks) | "Confirm order", "Approve KYC", "Initiate payout" | API endpoint → workflow run |
on-record-change | Order status becomes Delivered → release commission entry | DB-event hook → workflow run |
on-schedule | Nightly settlement run; compliance calendar reminders; KYC doc expiry | Cron job (BullMQ repeatable) |
on-event (external) | PG webhook: payment success/failure; provider callback | Webhook receiver → verified → workflow run |
Domain engines are not spec-expressible and are hand-built once, then exposed to the BA as verbs:
Generated code calls custom hooks only at named extension points (e.g. custom/onOrderConfirm.ts if present). Regeneration is therefore always safe. When the BA needs a missing capability, they raise a "request new verb" item; a dev ships the primitive once and it joins the palette.
Constraint honoured: no paid licences anywhere. Free cloud tiers only where noted.
| Concern | Choice | Licence / cost |
|---|---|---|
| BA data grid | Grist (self-hosted) — fallback: NocoDB / Baserow OSS | Apache-2.0 / self-host free |
| Flow canvas & lifecycle editor | React Flow (custom node palette) | MIT |
| Spec storage & review | YAML in git (GitHub free) + JSON Schema / AJV validation | Free / MIT |
| Generator | Node.js + TypeScript; templates via Handlebars/EJS, AST work via ts-morph | MIT |
| Web frontend target | Next.js (also used for the six static brand sites via SSG) | MIT |
| Mobile target (Phase 2) | React Native + Expo — deferred; spec stays platform-neutral, shares generated API clients | MIT |
| Backend target | NestJS (on Express, Node.js runtime) — final | MIT |
| Database + ORM/migrations | PostgreSQL + Drizzle (or Prisma) with generated spec-diff migrations — final (MongoDB rejected: ledger atomicity, constraints, report joins, spec-diff migrations all favour relational) | OSS; Neon free tier already in use |
| Observability | pino logs + OpenTelemetry + Prometheus/Grafana/Loki | MIT / Apache-2.0 / AGPL (self-host free) |
| Workflow runtime | Custom interpreter (XState-informed) executing workflow spec | MIT deps |
| Jobs / crons / queues | BullMQ + Redis | MIT / OSS |
| Auth | better-auth or Keycloak (if SSO needed) | MIT / Apache-2.0 |
| Frontend data layer | React Query + generated typed clients (openapi-typescript) | MIT |
| Visual UI | Stitch (AI) from logical screen spec + design system | Free |
| PDF / documents | pdfmake or Puppeteer print pipeline | MIT / Apache-2.0 |
| Fake data for previews | Faker.js | MIT |
| Notifications | Nodemailer + client-procured SMS/email gateways (SOW puts gateway costs on client); Novu OSS if a hub is wanted | MIT / OSS |
| Tests | Vitest + Supertest (API), Playwright (E2E) | MIT / Apache-2.0 |
| CI | GitHub Actions free tier | Free |
srs_ref (e.g. EM-OM-004) mapping to the client's SRS requirement IDs.Deferred deliberately: React Native renderer, visual dashboard composer, AI spec-drafting copilot (add only after the BA is fluent in manual authoring).
| Decision | Locked choice | Rationale |
|---|---|---|
| BA grid tool | Grist (self-hosted) | Strongest linked-record dropdowns + API for YAML sync. NocoDB is the fallback only if a 1-day spike surfaces a blocker. |
| Backend flavour | NestJS (on Express) | Structure, guards and DI make it the friendlier deterministic-generation target; consistency beats raw speed here. |
| Full stack (final) | Next.js · NestJS · PostgreSQL; React Native + Expo deferred to Phase 2 | "MERN with the M upgraded" — all-TypeScript, Node runtime, React everywhere; Postgres over MongoDB for ledger integrity, constraints and reporting. Redis = queues + ephemera by default, read cache opt-in per entity. |
| Workflow runtime | Custom interpreter with XState semantics (guards, compensation) | Full control over execution; keeps the BA-facing spec vocabulary small instead of inheriting a library's model. |
| Verb palette v1 | Drafted — see §13 | Derived from the Omara SRS module lists; to be finalised in a dedicated review session with the BA. |
| Repo layout | One platform repo + one generated repo per client app | IP boundary matches the contract: generated output transfers to the client, the platform (studio + generator + primitives) remains ours. |
The ~40 building blocks the BA snaps together on the flow canvas. Every verb is configured through dropdowns only (P1), declares its outcome branches explicitly (e.g. success / failure / pending), and is either generated behaviour or a wrapper over a dev-owned engine (§8). Anything not on this list is, by definition, a "request new verb" item.
| Category | Verb | What it does · BA configures |
|---|---|---|
| Data & records | record.create | Create a record · entity, field values (picked/mapped) |
record.update | Update fields on a record · entity, record ref, field values | |
record.delete | Soft/hard delete · entity, record ref | |
record.find | Look up records for later steps · entity, filter conditions | |
field.set | Set a computed value on the current record · field, expression/source | |
counter.next | Next value from a numbering series (GST invoice no., order no.) · series | |
| Status & flow control | status.transition | Move record to a new status — validated against its lifecycle (§5) · target status |
branch.condition | If/else on field values · condition builder (field, operator, value) | |
branch.decisionTable | Evaluate a decision table (commission slab, eligibility) and branch/use outcome · table | |
wait.delay | Pause the flow · duration | |
wait.forEvent | Pause until an external event (payment callback) · event, timeout branch | |
error.raise | Stop with a business error · message template | |
flow.compensate | Reversal steps that run if a later step fails (ledger reversal on provider failure) | |
| People & approvals | approval.request | Send an approval task; branches approved / rejected · role, context screen |
task.assign | Create a manual work item (KYC review queue) · role/queue, due rule | |
user.invite | Create/invite a user · role, notify template | |
role.assign | Grant/revoke a role · role | |
| Wallet & money engine-backed | wallet.checkBalance | Branches sufficient / insufficient · wallet, amount source |
wallet.debit | Atomic ledger debit · wallet, amount, narration template | |
wallet.credit | Atomic ledger credit · wallet, amount, narration template | |
wallet.hold | Place a hold (pending txn) · wallet, amount | |
wallet.release | Release a hold · hold ref | |
wallet.reverse | Reverse an earlier entry · entry ref | |
commission.record | Write a commission ledger entry (report-only per SOW) · decision table, parties | |
payment.collect | Collect via payment gateway; branches paid / failed / pending · gateway, amount | |
payment.refund | Refund a collection · payment ref, amount | |
payout.initiate | Queue a settlement payout entry · beneficiary, amount source | |
| Providers & external engine-backed | provider.call | Call an upstream API via its adapter; branches success / failure / pending · provider, operation, field mapping |
provider.checkStatus | Re-query a transaction's status · txn ref | |
api.call | Generic REST call to a dev-registered endpoint · endpoint, mapping | |
webhook.emit | Notify an external system · registered webhook, payload mapping | |
| Communication & documents | notify.send | Send via template · template, channel (SMS/email/push/in-app), recipient |
otp.send | Send an OTP · channel, template | |
otp.verify | Branches valid / invalid / expired | |
document.generate | Render a PDF from a template (invoice, statement, contract) · template, numbering series | |
document.requestUpload | Ask a user to upload a document (KYC) · doc type, expiry rule | |
file.export | CSV/Excel export of a dataset · report/list definition | |
file.import | Bulk import with column mapping (catalog upload) · entity, mapping, error handling | |
| Inventory & commerce | inventory.reserve | Reserve stock for an order · warehouse rule, line items |
inventory.release | Release a reservation · reservation ref | |
inventory.adjust | Stock adjustment with reason · item, quantity, reason list |