iDempiere 2.0 — the model, learning from the best¶
Status: DRAFT / SPEC, internal working concept, far-off, grail-gated. "iDempiere 2.0" is an internal name for a lineage, not a shipped product name (see §Guardrails — trademark). This doc owns the model / engine layer. The UI / renderer layer is owned by IDEMPIERE_RENDERER_SPEC.md — this doc defers all rendering to it. Grounding: HolyGrail.md · ERP.md · DistributedERP.md · ERPMaker.md · AnyAppMaker.md.
What it is — and what it is NOT¶
iDempiere 2.0 is what iDempiere reached for and could not get to from inside Java: the engine
as data, not code. iDempiere 1.0 proved the idea that an ERP can be metadata-driven (the
Application Dictionary, AD_Rule/AD_Val_Rule); it could not complete it because the
deterministic core was welded to the JVM / OSGi / trx / side-effects (HolyGrail.md).
2.0 finishes the idea by re-basing that core onto a signed op-log and a small verb set.
| iDempiere 2.0 IS | iDempiere 2.0 is NOT |
|---|---|
| the thin, general core: 5-table bridge + verbs + signed op-log; state = the fold | iDempiere's 925-table schema, v2, with more tables bolted on |
| an abstraction that iDempiere 1.0 is one instance of | a fork or successor of the iDempiere codebase |
| derived from the domain (double-entry 1494, the universal document lifecycle) | derived by copying any ERP's source or schema |
| a lingua franca other ERPs map onto (the pivot) | a cage every ERP must distort itself to fit |
The single invariant: keep the core thin and general. Every feature pressure tests it. The day the core stops being "verbs + a thin bridge + a log" and becomes "a big schema," 2.0 has rotted back into 1.0's bloat — the exact thing it exists to delete.
The method — how it learns without copying¶
The discipline that makes "learn from the best" safe and honest, agreed across the design sessions:
- Model the domain from first principles — as we did with BIM. The source of truth is the domain (accounting, the document state machine), not a competitor's model.
- Competitor = oracle + gap-checklist, never source. A foreign ERP reveals a gap ("we have no analytic accounting") and supplies the executed-rows oracle to verify against (ERP.md §0.12, §0.17). We then model the general primitive ourselves and diff against their output. We learn behaviour, never copy expression (idea/method is free; source is not — and copying GPL/LGPL source would contaminate the MIT corpus).
- Absorb, selective, generalize. When a foreign concept is more general than ours, fold it in and our old form becomes a special case. When it is ERP-specific cruft, quarantine it as an adapter detail. The diff-oracle protects correctness; only this discipline protects parsimony.
- BI is a fold projection, not a feature to copy (see §BI below).
- Clean-room identity as well as code (see §Guardrails).
The core model (the thin pivot)¶
Recap of the invariant core (full detail in ERP.md / DistributedERP.md):
- State = a fold over a signed, append-only op-log. No mutable scalar of record; the
authoritative number is derived (
QtyOnHand= Σ movements; a balance = Σ journal lines). - The 5-table bridge —
documents/document_lines/journal+containers/items— the minimal universal shape every ERP folds into. - A closed verb set (
GUARD/GENERATE/MATCH,completeOrder/createShipment/createInvoice/allocate/post…, ERP.md §0.14) — behaviour as data over a trusted alphabet, not arbitrary code. This is also what makes AI authoring and the marketplace sandboxable (AnyAppMaker.md). - The signed hash-chain (W-CHAIN / W-SIGN) — tamper-evident, replayable, reversible (reversal = appending the inverse op-group), period-close = balance-brought-forward checkpoint.
- Rules as data (ERP.md §0.5, §0.10) — per-cell decision tables, not Rete/DSL; the host language (JS) is the rule language; editing a rule edits the running system (the grail, ERP.md §0.4, gated behind E3/E4).
What it learns from the best — the synthesis¶
Each row: the idea worth taking, its generalized form in 2.0, and how it is verified. Concept absorbed, never code.
From Odoo¶
- Polished view types (form / list / kanban / pivot / graph / calendar) → in 2.0 these are fold projections, not a separate BI engine (§BI). Renderer concern → defer to RENDERER_SPEC.
- Line-level reconciliation → adopt as the general allocation primitive; iDempiere's
header allocation (
C_AllocationHdr) becomes a projection of it. (The §0.17/§0.19 matcher finding — generalize, don't special-case.) - Analytic accounting (cost/profit dimensions orthogonal to the chart of accounts) → a genuinely
general idea: journal lines carry orthogonal dimension tags; reporting folds by any dimension.
Absorb as a general tagging primitive on
journal. - App modularity / "install an app" → the OOTB bundle / genome model (AnyAppMaker.md).
- Studio-style low-code customization → in 2.0 this IS the grail (edit rules live), not a bolt-on.
From ERPNext / Frappe¶
- DocType — everything is metadata → the cleanest external proof of the engine-as-data thesis; validates the dictionary/descriptor approach (and makes ERPNext the easiest future spoke).
- Minimal lifecycle (
docstatus∈ draft / submitted / cancelled) → confirms the transition table generalizes: a 3-state machine is a degenerate case of the DocAction corpus. - "Everything is a document" uniformity → reinforces the documents/lines/journal core.
From SAP (the asymptote — learn the rigor, copy nothing)¶
- The document principle / everything posts → the ledger-first discipline; the fold is the GL.
- Document flow (the linked-document chain view) → 2.0 already has this for free: it is the op-log lineage graph (Glassbowl renders it; Time Machine plays it).
- Org-structure modelling (company code / plant / storage) → generalize to an org+location hierarchy — which meets BIM's spatial location, the isolation primitive of the distributed model (DistributedERP.md §2). Where atoms have a location, contention dissolves.
From the world at large¶
- Git → the doctrine's own analogy: signed, hash-chained, forkable, host-disposable. 2.0's op-log is git for ERP transactions (DistributedERP.md §0).
- Event sourcing / CQRS → named prior art for "state = fold over an event log; read projections separate from the write log." 2.0 is this, with signing + a closed verb set added.
- Double-entry bookkeeping (1494) → the original "the fact is a fold." The deepest first principle.
- Local-first software / CRDTs → offline-first, sync-at-business-time (LocalFirstPriorArt.md).
- The spreadsheet → the most-adopted business tool ever; its lesson is direct manipulation + immediate recalculation + the user is the programmer. The grail + "make the job as play" channel this.
- LLVM / SQLite / the web platform → the positioning: be infrastructure / a grammar others build verticals on, not an app.
BI as a fold projection — the gift¶
Odoo's real value-add beyond the ERP core is its presentation/BI layer. In 2.0 that is not a
feature to reproduce — it falls out of the architecture, because state is already a GROUP BY
over the ledger:
- a pivot is a fold projection; a dashboard is a saved fold; a kanban is a fold grouped by a status column; a report is a fold with a filter.
The data is already ledger-shaped for aggregation — arguably more natural for BI than a mutable-row
store that bolts BI on top. Seeds exist (ad_charts.js, the Glassbowl data-bars + gravity ranking).
We derive views from the fold; we do not copy widgets. (Rendering of these views → RENDERER_SPEC.)
The pivot architecture — migration Switzerland¶
2.0 as lingua franca: every ERP maps bidirectionally to the thin core via one model dictionary each. Consequences:
- N dictionaries, not N² — iDempiere↔2.0, Odoo↔2.0, ERPNext↔2.0 — never every pair directly.
- An exit path / portability layer — iDempiere→2.0→Odoo and back; you are the neutral migrator, not "replace your ERP." Far less threatening to communities (ERPMaker.md migration row).
- Honest limit: reading out (extract) is proven; writing back in to a live foreign ERP is a
separate write-adapter, and round-trips are verified with named loss, not lossless — the five
structural divergences (
build/erp/odoo_fold.log) bound it. A migration bridge, not a transparent sync.
The validation stack — iDempiere's 4 tiers, mapped to our layers (extract, don't invent)¶
iDempiere already separated business-rule enforcement into a 4-tier stack orchestrated by
ModelValidationEngine (docValidate → factsValidate → OSGi event). This is the template for our layer
map — it tells us which of the "284 callouts/rules" belongs in which layer, so the port is a triage, not one
flat bucket. (Sources: iDempiere ModelValidator
+ ModelValidationEngine javadoc.
Clean-room: learn the tier boundaries, extract the rule effects from the AD, never copy their Java.)
| iDempiere tier | What it does | Our layer (ENGINE_CONTRACT / UI_OVERLAY_GOVERNANCE) |
|---|---|---|
Callout (CalloutEngine, field-level, 6-param) |
UI field-change reactions (pick BPartner → fill address/price list) | UI Validation concern-overlay — keyed per field, in front of the seam. The only tier that is a UI overlay |
| ModelValidator.modelChange (row: new/change/delete) | row invariants on save | kernel verb guard, behind the seam (a write(ctx,ops) precondition) |
| ModelValidator.docValidate (doc actions: prepare/complete/void) | document-state-machine rules | op-group atomicity + DocAction (WfMC-on-op-log, ERP_KERNEL_BUILD) |
FactsValidator.factsValidate(AcctSchema, facts[], PO) + ACCT_FACTS_VALIDATE |
validate accounting facts per acct-schema before posting | posting-fold layer — confirms C_AcctSchema is a first-class posting input (one doc posts per schema) |
Consequence (the planning lever): the callout-port backlog (ENGINE_FULL_ERP_ISSUES.md
§I-C) is sorted by this tier first — only the field-level CalloutEngine rules land in the UI overlay; the
other three tiers are engine concerns owned by the engine/posting lanes, not a single "callout" session.
The logic-admission model — how ALL of Odoo/ERPNext/SAP logic enters¶
What's actually admitted today → ERP Coverage Matrix — this model's surfaces enumerated against the live engine: 0 covered / 12 partial / 28 gap of 40. The static-data layers (lists, mandatory/updateable flags) are partially in; the logic-expression layer (DisplayLogic/ReadOnlyLogic/MandatoryLogic) and the security layer have no evaluator at all yet.
The question "can we house every ERP's logic?" has a wrong framing: generalize vs Drools-style DIY vs variants + config. They are not alternatives — ERP logic stratifies into six layers, and each layer has its own admission rule. The layers get progressively harder to reduce to data; the art is knowing which is which so you neither under-generalize (per-ERP cruft) nor over-promise ("everything becomes data").
| Layer | What it is | Admitted as | Generalizes? |
|---|---|---|---|
| L0 Structure | entities / fields | AD / DocType (metadata) | ✅ solved — ERPNext DocType is the external proof |
| L1 Lifecycle | state machines | the transition table (data) | ✅ — the gap to promote from engine into AD (every migration squeezes here) |
| L2 Guards | preconditions / validations | predicates + decision tables (data) | ✅ (§0.14 GUARD) |
| L3 Determination | "which account / price / tax?" | config lookup tables (data, host glue §13.1) | ✅ |
| L4 Effects | ship / invoice / allocate / match / post | the closed verb set (§0.14) | ✅ proven — Odoo+iDempiere O2C/P2P fold, newVerbs=[] (build/erp/odoo_fold.log) |
| L5 Computation | pricing math, tax, derived fields | pure expressions (data) | ✅ |
| L6 Algorithms | MRP, scheduling, SAP condition-technique pricing | constrained plugin (variants + config) | ⚠️ the honest tail — genuine code, caged |
The one invariant that unifies all three instincts: logic enters only as data, or as ops in the closed verb alphabet — never as free code. That single rule is simultaneously what makes the model generalizable (L1–L5 are data), DIY-able (you edit the data), AI/vibe-codable (the grammar is closed and therefore verifiable), and safe (op-log replay + sandbox). It is the membrane every source ERP's logic must cross.
On Drools — keep its idea, drop its engine (the result is stronger than Drools)¶
HolyGrail.md (ERP.md §0.5/§0.9) already rejected Rete/inference. The clean split: - Drools' good idea — rules as editable data / decision tables → yes, that is L2/L5; expose exactly this. - Drools' engine — stateful forward-chaining inference, unrestricted agenda → no: no op-log (so no replay/undo/audit — "the safety Drools never had"), firing order fights deterministic replay, and unrestricted actions are not sandboxable for a marketplace or for AI authoring.
Keep the decision-table/expression DIY, drop the inference engine: decision tables + pure expressions over a closed
verb alphabet, op-logged → editable live, replayable, reversible, sandboxable. That is L1/L2/L5 made live — the
grail (§RULE-EDIT, HolyGrail.md).
Variants + config + vibe coding — the L6 tail, caged¶
Genuine algorithms (MRP, SAP pricing procedures) cannot be decision-tabled into pure data. There variants + config-pick-which is the correct shape — but the plugin may only emit ops in the closed alphabet. That constraint is precisely what Odoo/SAP lack (their algorithms get unrestricted DB access); it is what makes ours composable, swappable, and auditable (AnyAppMaker.md). Vibe coding fits because the grammar is closed: free-code generation is unverifiable; grammar-constrained generation is diff-oracle-checkable — replay the AI-authored rules against the source's recorded oracle and prove equivalence to the cent.
The honest bound (don't over-promise): the claim is NOT "all ERP logic becomes data." It is — all logic is admitted through one safe seam (data, or a verb-emitting plugin), so it is composable, swappable, replayable, and auditable. L0–L5 (the bulk; proven for the transactional core) generalize to data; L6 stays code but is caged. That is stronger and more defensible than "everything generalizes," and it is the line that keeps the MIT moat honest.
AD behavioral-gap corollary (what to metadata-ize next, surfaced 2026-06-05): AD won the entity-metadata battle but left the behavioral conventions hardcoded — and behavior is where ERPs differ. Promote these conventions into AD data, each as a more general primitive: (1) lifecycle — DocStatus/DocAction is a column convention, not a declarative per-doctype state machine (L1); (2) polymorphic reference —
AD_Table_ID + Record_IDexists as a 2-column convention (42 columns) but is not a first-classAD_Referencetype, unlike Odoomany2one_reference/ ERPNext Dynamic Link; (3) reactive computed fields — only imperative Callouts today, no declarative@depends-style dependency graph (L5); (4) cross-cutting mixins — no declarable chatter/activities/followers/attach-to-anything layer (the op-log already gives audit + document-flow for free); (5) open dimensional tags on journal lines, generalizing Fact_Acct's fixed-arity accounting dimensions.
Boundaries — what this doc does NOT cover¶
- UI / rendering → IDEMPIERE_RENDERER_SPEC.md (other session).
- The engine↔UI interface → ENGINE_CONTRACT.md — the single seam
(
read/dispatch/manifest/verbs/verify+ role context); engine-owned, renderer-referenced. - The live write-loop / editable rules reflowing records → grail, gated behind E3/E4 (HolyGrail.md).
- Full feature breadth parity → a campaign, one diff-oracle per cell (ERP.md §0.17); 2.0 ships the durable, ownable core + an exit path, not a clone of any ERP's full app suite.
- Per-customer migration (their data + Z-customizations) → per-engagement; the method generalizes, the customizations do not come free.
Guardrails (doctrine, not preference)¶
- Keep the pivot thin — generalize, don't accrete. Each absorbed gap must reveal a more general primitive (fold it in) or be quarantined as an adapter detail. No per-ERP cruft in the core.
- Competitor = oracle + checklist, not source. Clean-room: learn behaviour, never copy code. Idea/method is free; expression (their source/schema-as-written) is not — and copying copyleft source would contaminate the MIT moat.
- Clean identity too. "iDempiere 2.0" is an internal concept name; ship under our own mark. Reference other ERPs only in factual interop language ("migrates from / compatible with"), never adopt their logos. Get IP counsel before any branding that touches another project's name/logo.
- BI is a projection, not a feature. Derive views from the fold; don't reproduce widgets.
- Witness everything. No parity claim without a
§-logged diff-oracle to the cent. Under-promise exactness; lead with witnessed flows.
Status: DRAFT v0.1, 2026-06-02 — model/engine synthesis. Sibling (UI): IDEMPIERE_RENDERER_SPEC.md (in progress, other session). Every "extract" claim here is witnessed in a dated log or marked aspirational; nothing is asserted that the source data does not support. Grounding: HolyGrail.md · ERP.md §0.4/§0.5/§0.10/§0.12/§0.14/§0.17/§0.19 · DistributedERP.md §0/§2 · LocalFirstPriorArt.md · ERPMaker.md · AnyAppMaker.md · build/erp/odoo_fold.log.