Milestone 3 Completion — Trigger Hardening and Domain Migration
Kernel Milestone: 3 of 6 — Completion patch Previous: Milestone 3 — Monetary Domain Migration (v5.17.0) Next: Milestone 4 — Provenance
System Impact
-------------
Ledger integrity: strengthened
Ledger mutation: none — no posted records modified financially
Tenant isolation: unchanged
Financial immutability: strengthened — immutability triggers now function correctly
State machine: unchanged
Breaking changes: none
Replay determinism: preserved
Invariant Change
----------------
Financial immutability triggers are now unconditionally executable regardless
of session search_path. Transaction VAT components and director/dividend
amounts are now stored under the canonical monetary domain.
Why This Matters
v5.17.0 established the monetary domain and corrected all application-layer arithmetic paths. During the final domain promotion migrations for transaction VAT components, two trigger paths sharing the same schema-qualification defect were discovered in the financial immutability enforcement layer: both referenced tables without schema qualification. PostgreSQL trigger functions execute without inheriting the session search path, which caused these guards to fail at runtime with a relation resolution error rather than the intended invariant exception. Both defects are corrected here.
Without these fixes, any application path that issued an update or delete against transaction lines associated with locked journals would fail with an opaque database error rather than the intended TMADD 5.0 rejection. The immutability enforcement was present but non-functional on these two paths.
This release also completes the final two domain promotions deferred from v5.17.0: transaction VAT components and director loan / dividend amounts are now stored under the canonical pence domain, eliminating the last remaining primitive-typed monetary columns in the financial kernel.
Immutability Trigger Fixes
Schema qualification defect — update path
A trigger guarding updates to transaction lines belonging to locked opening balance journals referenced the parent transactions table without schema qualification. Because trigger functions do not inherit the application session search path, PostgreSQL could not resolve the unqualified reference and the trigger aborted with a relation resolution error instead of enforcing the TMADD 5.0 Axiom 5 rejection.
Corrected to use the fully qualified reference. The guard now executes deterministically regardless of session configuration.
Enforced by: database trigger — Class M. Proof: migration_077.
Schema qualification defect — delete path
An identical defect existed in the corresponding delete-path trigger. Same root cause, same correction.
Enforced by: database trigger — Class M. Proof: migration_077b.
Domain Promotions
Transaction VAT components
The VAT amount column on transaction lines was a nullable primitive integer. Three-hundred and thirty existing rows carried a NULL value representing zero VAT — a tri-state representation (NULL / zero / non-zero) that is incompatible with the canonical monetary domain, which admits only integer values.
All NULL values were normalised to zero — a semantically neutral transformation, as NULL VAT and zero VAT are financially identical. The column was then promoted to the canonical monetary domain and made non-nullable, collapsing the tri-state to a binary (zero / non-zero). Triggers were temporarily disabled within the migration transaction to permit this schema normalisation and re-enabled immediately after the update. This is the only legitimate trigger bypass in a migration — it does not weaken the immutability guarantee for any application-layer write path.
Enforced by: domain type constraint — Class M. Proof: migration_078.
Director loan and dividend amounts
Director loan transaction amounts and dividend amounts were stored as 32-bit integers, creating an overflow risk for values above approximately £21,000 — a realistic threshold for director salary and dividend workflows. Both columns have been widened to the canonical 64-bit monetary domain, eliminating the overflow risk with no data rewrite required.
Enforced by: domain type constraint — Class M. Proof: migration_079.
Threat Closure
| Threat | Status Before | Status After | Enforcement Layer |
|---|---|---|---|
| Immutability trigger failing on locked-journal update path | Open — runtime error instead of invariant rejection | Closed (Class M) | Database trigger, schema-qualified |
| Immutability trigger failing on locked-journal delete path | Open — runtime error instead of invariant rejection | Closed (Class M) | Database trigger, schema-qualified |
| Tri-state NULL / zero / non-zero VAT representation | Open — domain inconsistency | Closed (Class M) | Domain type constraint, NOT NULL |
| 32-bit overflow on director loan and dividend amounts | Open — silent truncation above ~£21k | Closed (Class M) | Domain type widening to 64-bit |
Kernel Status
| Milestone | Description | Status |
|---|---|---|
| M1 | Tenant Isolation | Complete |
| M2 | Financial Immutability and State Machines | Complete |
| M3 | Monetary Domain Migration | Complete |
| M4 | Provenance | Pending |
| M5 | Schema-Derived Categorical Boundary | Pending |
| M6 | Append-Only Cryptographic Ledger | Pending |
M4 (Provenance) begins with a fully canonical monetary schema and a correctly functioning immutability enforcement layer.