Phase 0 — foundation fixes (architecture view)

This page describes how the four Phase 0 tasks fit together at the system level. For per-task technical detail see the technical finance docs. For staff-facing flow see the business finance doc.

What Phase 0 changes (and doesn't)

Phase 0 strengthens the existing GL layer without adding new pipeline stages. The pipeline before Phase 0 was:

Trigger → journal-entries.service.create() → GL + hash chain

After Phase 0 it is still that — but the GL now has the AAOIFI account skeleton it needs for Phases 1–3, can reverse entries cleanly, and is independently verified nightly.

Component map

Invariants Phase 0 introduces / preserves

  1. One shared hash function. Both the poster (journal-entries.service.ts) and the verifier (hash-chain-verifier.service.ts) call computeJournalHash. Drift between poster and verifier is structurally impossible.
  2. Reversal is the only allowed mutation. _markAsReversed is the single private path that updates a posted entry, and it touches only metadata.reversedBy. All other mutations are forbidden.
  3. Append-only ordering. Hash chain is over (createdAt ASC, id ASC). Lines within an entry hash in id ASC order. Any verifier or future migration that reorders breaks the chain.
  4. Idempotent seeds. AAOIFI seed and per-fund auto-provision can be replayed safely. Idempotency is by 4-digit code (sub-groups) and by name key (per-fund custom accounts).
  5. Audit assurance is server-authoritative. The audit UI no longer makes correctness claims from the client; it shows the result of a backend run.

Dependencies Phase 1 has on Phase 0

Phase 1 blockDepends on
1.A subledgersAAOIFI control accounts (6101, 6102, 7001, 7002); per-fund control accounts (6001*)
1.B posting/ShariahSourceModule extension (so engine emits typed values); _markAsReversed stays the only mutation
1.C NAVPer-fund NAV adjustment accounts (6002*); FX gain/loss accounts (6301, 6302)
1.D handlersReversal API (for cancelling a mispostied trade); all of the above

Failure modes Phase 0 explicitly handles

  • Missing parent group during AAOIFI seed → reported in errors, seed continues
  • Per-fund account collision during fund creation → non-fatal; fund row remains valid, operator can re-run
  • Legacy entries with hashedAt: null → surfaced under distinct legacy_unhashed_timestamp category, not flagged as tampering
  • Reverse of a reversal → 400; cycles are blocked
  • Concurrent reverse of same original → second caller gets 409 because metadata update is in the same transaction

What's left for later phases to wire up

  • A system-actor convention so the nightly verifier can post notifications via the existing notification module (currently it only logs at error level).
  • A scheduled rebaselinerebaselineHashChain() exists but is admin-only manual today; future ops may want a guarded automation.
  • An exhaustive SourceModule handler registry in Phase 1.B that compile-fails on missing handlers.