Financial Model Validator
Purpose
- Validates
/fm buildoutputs after every build operation - Checks format completeness — expected files exist and are non-empty
- Runs metric sanity checks — no negative ARR, reasonable LTV:CAC ratios, cash runway logic
- Verifies internal reconciliation — P&L net income consistent with cash flow changes
- Flags missing or anomalous months in time series data
- Reports validation results to the invoking persona agent
Trigger
Event Type: Post /fm build execution
Source: /fm build <scenario> --format <format> command completion
Blocking: Yes (validation must pass before outputs are delivered to user)
Timeout: 15 seconds
Trigger Condition: Any /fm build command that generates output files
Behavior
When Triggered
-
Receives build output context:
- Scenario ID and name
- Format(s) built (xlsx, formula, json, csv, all)
- Output file paths
- Build summary metrics (breakeven, M60 ARR, M60 cash, peak burn)
-
Format Completeness Check:
- Verify each expected output file exists
- Verify file size > 0 bytes
- For XLSX: verify expected sheet count (10 for static, 10 for formula)
- For JSON: verify valid JSON with required top-level keys (metadata, summary, monthly_data)
- For CSV: verify header row and expected column count
-
Metric Sanity Checks:
Metric Valid Range Flag If M60 ARR > $0 Negative or zero Breakeven Month M1-M60 > M60 (never breaks even) Peak Monthly Burn < $0 Positive (never burns cash) LTV:CAC (enterprise) 1.0x - 20.0x < 1.0x or > 20.0x LTV:CAC (individual) 0.5x - 10.0x < 0.5x or > 10.0x Customer Count (M60) > 0 Zero or negative Cash Position (M60) Any Warn if negative Expense Ratios Sum < 100% at scale > 100% -
Reconciliation Checks:
- Revenue = sum(tier_customers x tier_price x 12) within 1%
- Cash flow: opening + inflows - outflows = closing within $1
- Customer math: starting + new - churned = ending (exact)
- Month-over-month continuity: no gaps in time series
-
Result Classification:
Result Action PASS All checks pass — deliver outputs to user WARN Sanity check flags present — deliver with warnings FAIL Reconciliation error or missing output — block delivery, report error
Output Format
VALIDATION: financial-model-validator
Scenario: {scenario_id}
Format: {format}
Status: PASS | WARN | FAIL
Format Check: ✅ 4/4 files present
Sanity Check: ✅ 8/8 metrics in range
Reconciliation: ✅ 4/4 balances match
Warnings: (if any)
- M60 cash position is negative (-$234,000) — review burn rate
Errors: (if any)
- Revenue reconciliation gap: 2.3% (threshold: 1%)
Configuration
| Parameter | Default | Description |
|---|---|---|
revenue_tolerance | 0.01 (1%) | Max revenue reconciliation gap |
cash_tolerance | 1.0 ($1) | Max cash balance discrepancy |
warn_negative_cash | true | Warn on negative M60 cash |
block_on_reconciliation | true | FAIL on reconciliation errors |
Implementation
def validate_fm_build(scenario_id, format, output_dir):
"""Post-build validation for /fm outputs."""
results = {"format": [], "sanity": [], "reconciliation": []}
# 1. Format completeness
expected_files = get_expected_files(scenario_id, format)
for f in expected_files:
path = os.path.join(output_dir, f)
if not os.path.exists(path):
results["format"].append(("FAIL", f"Missing: {f}"))
elif os.path.getsize(path) == 0:
results["format"].append(("FAIL", f"Empty: {f}"))
else:
results["format"].append(("PASS", f"OK: {f}"))
# 2. Sanity checks (from JSON summary)
if "json" in format or format == "all":
summary = load_json_summary(scenario_id, output_dir)
check_metric_ranges(summary, results)
# 3. Reconciliation (from CSV monthly data)
if "csv" in format or format == "all":
monthly = load_csv_data(scenario_id, output_dir)
check_reconciliation(monthly, results)
return classify_results(results)
Related
- Engine:
coditect_fm.py(ADR-177) - Command:
/fm(financial-model.md) - Skill:
financial-model-personas/SKILL.md - Agents:
cfo.md,fpa-analyst.md,finance-controller.md,financial-analyst.md - Workflow:
fm-orchestration-workflow.md
Track: N.6.13.4.1 ADR: ADR-177 (Database-Driven Financial Model Engine)