Early-stage modular infrastructure for M&A workflows: deal pipeline, due diligence tracking, risk register, decision log and post-merger integration, built on TypeScript and PostgreSQL.
Status: early-stage / experimental — data model and domain core; API and admin panel are on the roadmap.
ma-workflow-engine is the data and domain foundation we are building at Quantum Orbit Labs for software that manages merger and acquisition processes end to end.
The repository currently contains three things: a complete PostgreSQL data model, a typed TypeScript domain core (deal stage state machine, risk scoring and due-diligence checklist calculations) and unit tests with a runnable synthetic example. There is no HTTP API, persistence layer or user interface yet. Those are on the roadmap, and we prefer to say so rather than scaffold placeholders.
This software models workflow state. It does not provide financial or legal advice.
M&A processes fragment across documents, people, decisions, financial data and tasks. Spreadsheets, mail threads and data rooms each hold a slice of the truth. Stage changes happen implicitly, diligence progress is estimated rather than measured, and decisions and their rationale are reconstructed after the fact.
We think software should turn this into a trackable system: explicit deal stages with guarded transitions, a due-diligence checklist with measurable progress, a risk register with consistent scoring, a decision log that records what was decided and why, and an append-only audit history underneath all of it.
- A relational data model (
db/schema.sql) covering the whole process: deals, target companies, document requests, document metadata, due-diligence checklist items, risks, decisions, ownership entries, financial metrics, integration tasks and an audit log. - A typed domain core in strict TypeScript:
src/domain/dealStages.ts— deal stage state machine with an explicit transition map and guard reasons.src/domain/riskScoring.ts— likelihood times impact scoring with configurable severity bands.src/domain/checklist.ts— due-diligence progress and completion calculations.
- Unit tests for the domain core and a runnable example with synthetic data.
Not included yet: HTTP API, persistence layer, authentication, user interface. See Status and roadmap.
Four functional modules operate over one shared PostgreSQL model; every material change is meant to land in the append-only audit log.
The domain core is deliberately pure. Functions take rows in and return values out, with no I/O, so the same logic can later serve an API, background jobs and a user interface without change. The database stores state; the domain layer owns the rules for how state may change.
A deal moves through a fixed pipeline, one stage at a time:
sourcing -> screening -> indicative_offer -> due_diligence -> negotiation -> signing -> closing -> integration
Two special stages sit outside the pipeline:
on_hold— reachable from any stage before integration. The engine records the held-from stage and only allows resuming exactly there, or dropping the deal.dropped— reachable from any stage before integration; terminal.
integration is also terminal. Transitions go through transition(deal, target), which returns a result object instead of throwing: either the updated deal or a machine-readable code (transition_not_allowed, resume_stage_mismatch and others) with a human-readable reason.
Risks carry a likelihood and an impact, each an integer from 1 to 5. The score is their product; severity bands are defined in src/config.ts and can be overridden per call:
| Severity | Score range |
|---|---|
| low | 1 to 4 |
| moderate | 5 to 9 |
| high | 10 to 15 |
| critical | 16 to 25 |
Severity is always derived, never stored, so changing the bands does not require a data migration.
Due-diligence checklist items count toward completion unless marked not_applicable. Progress is completed over applicable items, and a checklist is complete when every applicable item is. An empty checklist is treated as vacuously complete, so callers should require a populated checklist before relying on that signal.
Eleven tables for PostgreSQL 14 or later, with UUID primary keys, native enum types, foreign keys and check constraints. Apply to an empty database:
createdb ma_workflow
psql ma_workflow -f db/schema.sql
| Table | Purpose |
|---|---|
deals |
One row per acquisition process; current stage plus held-from stage |
target_companies |
Companies under evaluation |
document_requests |
Information request list per deal |
documents |
Document metadata and content hash; file bodies stay in external storage |
dd_checklist_items |
Due-diligence work items per category |
risks |
Risk register; likelihood and impact stored, severity derived |
decisions |
Decision log: outcome, rationale, deciding role |
ownership_entries |
Cap table over time |
financial_metrics |
Reported, normalized or forecast figures per company and period |
integration_tasks |
Post-merger plan by workstream, with task dependencies |
audit_log |
Append-only history of every material change |
updated_at columns are maintained by a shared trigger. Stage transition rules are deliberately not enforced in SQL; the domain layer owns them.
Requires Node.js 22 or later. PostgreSQL 14 or later is only needed to apply the schema.
git clone https://github.com/issmailturann/ma-workflow-engine.git
cd ma-workflow-engine
npm ci
npm test
The package is not published to npm yet; consume it from source.
import { assessRisk, checklistProgress, transition } from './src/index.js';
// deal is a Deal row loaded from your store.
const result = transition(deal, 'due_diligence');
if (result.ok) {
console.log(`now in ${result.deal.stage}`);
} else {
console.log(`${result.code}: ${result.reason}`);
}
const { score, severity } = assessRisk(4, 4); // score 16, severity "critical"
const progress = checklistProgress(items); // ratio, counts and completion flagA complete walk-through with synthetic data:
npx tsx examples/deal-lifecycle.ts
- The engine currently exposes no network surface: it is a schema plus pure functions.
audit_logis designed append-only. In managed environments, grant only INSERT and SELECT on it.documentsstores metadata and a SHA-256 content hash only; file bodies never enter the database.- Role-based access control is on the roadmap and arrives together with the API layer. Until then, database privileges are the control plane.
- All example and test data is synthetic.
- To report a vulnerability, see SECURITY.md.
Status: early-stage / experimental — data model and domain core; API and admin panel are on the roadmap.
The data model and domain core are implemented and tested. Interfaces may still change without notice while we validate the model against real workflows.
Planned, in rough order:
- REST/GraphQL API over the data model
- React admin panel
- Role-based access control
- Document metadata ingestion
- AI abstraction layer for document summarization
See CONTRIBUTING.md. Short version: open an issue first, branch from main, keep pull requests small and make npm run lint && npm test && npm run build pass.
MIT — see LICENSE. Copyright (c) 2026 Quantum Orbit Labs.