An approval engine that knows what not to do — shipped for a public-sector field-safety operation.
Forms, multi-step approvals, training, and audit trails — not as a demo UI, but as a production system with explicit capability boundaries and delivery discipline you can verify.
Modular monolith · React + FastAPI · versioned JSON workflows
Approval correctness + migration without silent mock drift
Contract APIs · automated gates · multi-layer acceptance
A real operational loop — not a document tool.
[Enterprise Client] — a large public-sector organization in construction / field-safety operations — needed the full compliance loop to run in software: people, projects, forms, approvals, training, exams, and records, with real accountability.
Off-the-shelf tools didn’t fit. A conventional multi-person agency build meant timeline and cost the program couldn’t justify.
Constraint: correctness over cleverness. An approval that “usually works” is a liability. The interesting engineering is what the system refuses to pretend it can do — and how migration from mock to real API was forced to be honest.
Modular monolith on purpose.
ADR decision: do not split into microservices for this domain. One deployable backend, clear module boundaries, a versioned JSON workflow engine that freezes a definition snapshot at submit time, dual-layer RBAC, and a file-bound license model for deployment control.
One backend, many modules, one audit trail. Complexity lives in workflow rules and permissions — not in a service mesh.
Five decisions, with non-goals made explicit.
Refuse BPMN; own a lightweight JSON DSL
Approvals needed multi-step routing, withdraw/reject, and audit — but not a full BPM suite with designers, timers, and event gateways.
Camunda / Flowable (heavy for this domain) · Temporal-class orchestrators (wrong problem) · Lightweight JSON definitions
Custom versioned JSON workflow DSL. Capability boundary documented in writing — including upgrade triggers for when a real BPM engine would become justified. Countersign/or-sign reserved in schema, not fake-shipped.
No visual process designer. You get a reviewable definition and tests that match the product.
Reject / withdraw rules over “magic recall”
Real operators need to pull back mistakes without corrupting history or inventing admin backdoors.
Unbounded admin rewrite · Soft “recall anytime” · Explicit reject + gated withdraw + resume rules
Explicit reject rules, three-gate withdraw, and resume-from-breakpoint after reject — history stays explainable.
More state transitions to design and test; fewer silent history rewrites.
Unassigned roles fail closed
A step with no assignee is either a stuck process or a security hole if “someone admin” rubber-stamps it.
Auto-assign to admin · Silently skip approval nodes · Fail closed + operational repair path
Soft-block at start when required assignees are missing; at runtime, CC-only nodes may skip, approval nodes wait for repair; admins cannot casually proxy-approve away the gap.
Operations must keep role staffing healthy. The system will not paper over governance failures.
Strangler migration with three hard fences
Replacing mock APIs with real ones is where demos lie — pages keep “working” while data never hits production storage.
Freeze API contracts before swapping implementations.
In real mode, any remaining mock call throws immediately.
Ratchet mock-import whitelist toward zero with a CI script.
Migration is louder and earlier — which is the point. Silent mock drift cannot ship.
Delivery discipline as a product surface
“Looks done” and “proven done” are different states — especially on compliance workflows.
Automated tests → API probes → independent strict acceptance → log verification
Multi-layer acceptance on real closed loops. When acceptance language was broader than actual scope, it was corrected in writing.
Slower green-check theater; higher trust when you say shipped.
What you can publish without leaking the client.
The workflow engine documents what it will not do (heavy BPMN constructs, visual designers, event-gateway zoo) and the conditions under which a mature engine should be reconsidered. Non-goals are part of the architecture, not a footnote.
Real mode does not “prefer” real APIs — remaining mock usage throws. A ratchet script keeps mock imports from creeping back. Contract text freezes the surface area before implementations move.
- · Live production client system — solo delivery
- · Modular monolith · React + FastAPI · SQLite WAL
- · Versioned JSON approval engine with frozen submit snapshots
- · 85 workflow / form templates across staged complexity bands
- · 100+ contract API surface · dual-layer RBAC with page permission guards
- · 50 sprint directories of delivery record · 254 commits over ~6 months
Client identity stays at [Enterprise Client]. No project names, geographies, people, or UI screenshots.
Delivery you can audit — not just admire.
The transferable proof is not “we drew a workflow.” It’s an approval engine with explicit non-goals, a migration path that cannot silently stay on mocks, and acceptance discipline that corrects itself.
If you need a production operations platform — permissions, multi-step approvals, training, audit — built by one AI-native senior engineer end to end, this is that bar.