Runtime Invariant Checker - Usage Examples
This document shows how to use the runtime invariant checker in your z0 code.
Basic Usage
Section titled “Basic Usage”Simple Assertions
Section titled “Simple Assertions”import { assertInvariant } from '@/platform/lib/invariants';
function createFact(data: any): Fact { // Assert basic invariants assertInvariant( data.type !== undefined, 'Fact must have a type', 'FACT_TYPE_REQUIRED' );
assertInvariant( data.timestamp > 0, 'Fact timestamp must be positive', 'FACT_TIMESTAMP_POSITIVE', { timestamp: data.timestamp } );
// ... rest of implementation}Using InvariantChecker Methods
Section titled “Using InvariantChecker Methods”import { InvariantChecker } from '@/platform/lib/invariants';
async function appendChargeFact(charge: Fact, outcomeId: string): Promise<Fact> { // Fetch the outcome this charge references const outcome = await getFactById(outcomeId);
// Verify charge traceability invariant InvariantChecker.verifyChargeTraceability(charge, outcome);
// Verify temporal ordering InvariantChecker.verifyChargeTemporalOrdering(charge, outcome);
// Append the fact return await ledger.appendFact(charge);}Integration with EntityLedger
Section titled “Integration with EntityLedger”In appendFact Method
Section titled “In appendFact Method”// In EntityLedger.tsprotected async appendFact(fact: any): Promise<any> { await this.ensureInitialized();
const id = fact.id ?? crypto.randomUUID(); const timestamp = fact.timestamp ?? Date.now(); const completeFact = { ...fact, id, timestamp };
// ✅ VERIFY INVARIANTS BEFORE STORING await InvariantChecker.verifyFactInvariants(completeFact, { entity: this.getEntity(), sourceFact: fact.source_id ? this.getFactById(fact.source_id) : null, });
// Store fact const dataBlob = JSON.stringify(completeFact); this.sql.exec( `INSERT INTO facts (id, type, subtype, timestamp, tenant_id, source_id, entity_id, trace_id, external_source, external_id, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, completeFact.id, completeFact.type, completeFact.subtype ?? null, completeFact.timestamp, completeFact.tenant_id, completeFact.source_id ?? null, completeFact.entity_id ?? null, completeFact.trace_id ?? null, completeFact.external_source ?? null, completeFact.external_id ?? null, dataBlob );
await this.updateCachedState(completeFact); await this.executeHooks(completeFact); await this.scheduleReplication();
return completeFact;}In Config Operations
Section titled “In Config Operations”// In EntityLedger.tsprotected createConfig(config: any): any { const now = Date.now(); const newConfig = { ...config, version: 1, effective_at: now };
// ✅ VERIFY CONFIG INVARIANTS InvariantChecker.verifyConfigInvariants(newConfig, { entity: this.getEntity(), allConfigs: this.getAllConfigs(), // Get all configs to check uniqueness });
this.sql.exec( `INSERT INTO configs (id, version, type, category, name, applies_to, scope, settings, effective_at, superseded_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, newConfig.id, newConfig.version, newConfig.type, newConfig.category, newConfig.name, newConfig.applies_to, newConfig.scope, JSON.stringify(newConfig.settings), newConfig.effective_at, null );
return newConfig;}API Layer Integration
Section titled “API Layer Integration”In Facts API Handler
Section titled “In Facts API Handler”// In src/platform/api/facts.tsimport { InvariantChecker, InvariantViolation } from '@/platform/lib/invariants';
export async function createFact(request: Request, env: Env): Promise<Response> { try { const factData = await request.json();
// Validate basic structure if (!factData.type || !factData.entity_id) { return Response.json({ error: 'Missing required fields' }, { status: 400 }); }
// ✅ VERIFY ECONOMIC INVARIANTS InvariantChecker.verifyEconomicTenantId(factData); InvariantChecker.verifyAmountCurrency(factData); InvariantChecker.verifyMoneyFlow(factData);
// Create the fact const fact = await ledger.appendFact(factData);
return Response.json(fact, { status: 201 }); } catch (error) { // ✅ HANDLE INVARIANT VIOLATIONS if (error instanceof InvariantViolation) { console.error('[Invariant Violation]', { invariant: error.invariant, message: error.message, context: error.context, });
return Response.json( { error: 'Invariant violation', invariant: error.invariant, message: error.message, context: error.context, }, { status: 400 } ); }
throw error; }}Type-Specific Invariant Checks
Section titled “Type-Specific Invariant Checks”Charge Facts
Section titled “Charge Facts”async function createCharge( outcomeId: string, amount: number, currency: string): Promise<Fact> { // Fetch the outcome const outcome = await getFactById(outcomeId);
// Create charge fact const charge: Fact = { id: generateId('fact_'), type: 'charge', subtype: 'incurred', timestamp: Date.now(), tenant_id: outcome.tenant_id, source_id: outcomeId, entity_id: outcome.entity_id, amount, currency, from_entity: outcome.entity_id, data: {}, };
// ✅ VERIFY ALL CHARGE INVARIANTS InvariantChecker.verifyChargeTraceability(charge, outcome); InvariantChecker.verifyChargeTemporalOrdering(charge, outcome); InvariantChecker.verifyEconomicTenantId(charge); InvariantChecker.verifyAmountCurrency(charge); InvariantChecker.verifyMoneyFlow(charge);
return await ledger.appendFact(charge);}Cost Facts
Section titled “Cost Facts”async function recordCost( invocationId: string, amount: number, vendorId: string): Promise<Fact> { const invocation = await getFactById(invocationId);
const cost: Fact = { id: generateId('fact_'), type: 'cost', timestamp: Date.now(), tenant_id: invocation.tenant_id, source_id: invocationId, entity_id: invocation.entity_id, amount, currency: 'USD', to_entity: vendorId, data: { vendor_id: vendorId }, };
// ✅ VERIFY COST INVARIANTS InvariantChecker.verifyCostTraceability(cost, invocation); InvariantChecker.verifyCostTemporalOrdering(cost, invocation);
return await ledger.appendFact(cost);}Decorator Usage
Section titled “Decorator Usage”import { VerifyInvariants } from '@/platform/lib/invariants';
class FactService { @VerifyInvariants((fact: Fact) => { InvariantChecker.verifyEconomicTenantId(fact); InvariantChecker.verifyAmountCurrency(fact); }) async createFact(data: any): Promise<Fact> { // Implementation return await ledger.appendFact(data); }}Testing with Invariants
Section titled “Testing with Invariants”// In testsimport { InvariantChecker, InvariantViolation } from '@/platform/lib/invariants';
describe('Charge Creation', () => { it('should throw InvariantViolation if charge lacks outcome', async () => { const charge = createChargeFixture(entityId, 'nonexistent_outcome', 5000);
await expect(async () => { InvariantChecker.verifyChargeTraceability(charge, null); }).rejects.toThrow(InvariantViolation); });
it('should throw InvariantViolation if outcome timestamp > charge timestamp', async () => { const outcome = createOutcomeFixture(entityId, invocationId); outcome.timestamp = Date.now() + 10000; // Future timestamp
const charge = createChargeFixture(entityId, outcome.id, 5000); charge.timestamp = Date.now();
await expect(async () => { InvariantChecker.verifyChargeTemporalOrdering(charge, outcome); }).rejects.toThrow(InvariantViolation); });});Conditional Invariant Checking
Section titled “Conditional Invariant Checking”You can enable/disable invariant checking based on environment:
// In env.ts or configexport const ENABLE_INVARIANT_CHECKS = process.env.NODE_ENV !== 'production';
// Wrapper functionexport function checkInvariant(fn: () => void): void { if (ENABLE_INVARIANT_CHECKS) { fn(); }}
// UsagecheckInvariant(() => { InvariantChecker.verifyChargeTraceability(charge, outcome);});Logging Invariant Violations
Section titled “Logging Invariant Violations”import { InvariantViolation } from '@/platform/lib/invariants';
try { InvariantChecker.verifyFactInvariants(fact, { entity, sourceFact });} catch (error) { if (error instanceof InvariantViolation) { // Log to observability system console.error('[INVARIANT_VIOLATION]', { timestamp: Date.now(), invariant: error.invariant, message: error.message, context: error.context, stack: error.stack, });
// Send to monitoring (e.g., Sentry, Datadog) // monitoringService.captureException(error);
// Optionally re-throw or handle gracefully throw error; }}Best Practices
Section titled “Best Practices”- Check Early: Verify invariants as early as possible (at API boundaries, before DB writes)
- Be Specific: Include context in assertions to aid debugging
- Fail Fast: Don’t continue execution if an invariant is violated
- Log Everything: Always log invariant violations for observability
- Test Violations: Write tests that verify invariants are enforced
- Document: Reference the invariant name from INVARIANTS.md in your code
Performance Considerations
Section titled “Performance Considerations”Invariant checking has overhead. Consider:
- Development: Enable all checks
- Staging: Enable all checks
- Production: Enable critical checks only, or use sampling
const CRITICAL_INVARIANTS = ['CHARGE_TRACEABILITY', 'COST_TRACEABILITY', 'FACT_IMMUTABILITY'];
function shouldCheckInvariant(invariant: string): boolean { if (process.env.NODE_ENV === 'development') return true; if (process.env.NODE_ENV === 'staging') return true; return CRITICAL_INVARIANTS.includes(invariant);}Summary
Section titled “Summary”The runtime invariant checker provides:
- ✅ Type-safe assertions for all 7 invariant categories
- ✅ Detailed error messages with context
- ✅ Composite verification methods for common patterns
- ✅ Decorator support for automatic checking
- ✅ Integration points for API, ledger, and business logic
Use it to enforce the formal constraints defined in INVARIANTS.md at runtime.