Public Budget AI #9: 23,825 Budget Anomaly Automated Audit Engine

π‘ Executive Summary (TL;DR)
- The Target: Scaling automated fiscal surveillance to all 23,825 anomaly sub-items detected across the 677.4 trillion KRW South Korean national budget, executing standardized decisions via a 5-Option Matrix (full cut, mediation adjustment, 48-hour minister summons, criminal prosecution, legitimate exception pass).
- Real Engineering Pitfalls Encountered:
sqlite3.OperationalError: database is lockedand I/O starvation under rapid multi-record ingestion.- Double-dipping risk cascades where multi-category violations produced aggregate cut recommendations exceeding 100% of the allocated budget.
- Markdown table rendering corruption and Jinja2 syntax crashes during minister summons dossier generation due to unescaped pipes and quotes in procurement titles.
- Clean Production Fixes:
- Switched SQLite to
WALmode withPRAGMA synchronous = NORMAL, batching commits into 1,000-case transaction chunks, boosting throughput to 95,000 inferences/sec (total playback in 0.25 seconds).- Built a deterministic
PriorityDecisionArbiterwith criminal law priority overrides and hard invariant clamping (clamp(cut, 0, budget * 0.70)).- Implemented a robust legal-clause cross-referencing normalizer and safe markdown serializer for the permanent ledger (
CUMULATIVE_SURVEILLANCE_AUDIT_LEDGER.md), locking in 121.72 trillion KRW in provisional savings across 23,911 verified cases.
1. Target & Architecture: Eliminating the Human Inspector Bottleneckβ
In our previous milestones (Public Budget AI #7 and #8), we audited discrete fiscal domains: 243 municipal local subsidies (e-Hojo+) and 17 provincial education offices (K-EduFine). However, analyzing the full central government expenditure (677.4 trillion KRW) surfaced 23,825 flagged sub-items across 55 ministries and agencies.
+--------------------------------------------------------------------------------------------------+
| 23,825 Central Budget Sub-items Surveillance |
+--------------------------------------------------------------------------------------------------+
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4-Risk Category Classifier (Rule & Statistical Engine) β
β 1. Sluggish Exec (>30% Disused) 2. Private Consign (Overhead Leakage) β
β 3. Wasteful Services (Inflated) 4. Q4 Dump & Cartel (Bid-Rigging Β§315) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Standard 5-Option Decision Engine (Deterministic Matrix) β
β β’ Option 1: Approve Full Cut (100% System Recommendation) β
β β’ Option 2: Direct Mediation Cut (50% Operational Compromise) β
β β’ Option 3: Issue 48h Formal Minister Summons Query (Assembly Subpoena) β
β β’ Option 4: Criminal Prosecution Dispatch (Specific Economic Crimes Act) β
β β’ Option 5: Legitimate Execution (Disaster / Inevitable Exemption Pass) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β High-Throughput Storage & Immutable Ledger Generator β
β β’ SQLite3 Engine (WAL Mode, In-Memory Buffer, 1k Chunk Commits) β
β β’ Live Audit Ledger: CUMULATIVE_SURVEILLANCE_AUDIT_LEDGER.md β
β β’ Cumulative Cut: 121,718,409,348,635 KRW (121.72 Trillion KRW) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Core Operational Requirementsβ
- Zero Human Inspector Bottleneck: Auditing 23,825 cases manually takes ~11,900 human hours (30 minutes/case). The engine must ingest, evaluate, and assign statutory sanction recommendations across all cases in under 1 second.
- Immutable Traceability: Every audited item must store all 5 evaluation options, the chosen verdict, statutory citations, and ministerial summons text in both a normalized relational database (
surveillance_review_log) and a portable, version-controlled markdown ledger. - Statutory Integrity: Decisions must cross-reference applicable provisions of the National Finance Act (Β§45, Β§89), State Contract Act (Β§7, Β§27), Public Finance Recovery Act (Β§8), and Criminal Act (Β§315).
2. Real Engineering Pitfalls & Runtime Errorsβ
Pitfall 1: SQLite Lock Deadlock & Disk Starvation (database is locked)β
During our initial batch playback run across the 23,825 rows, the process aborted abruptly after processing case #3,412:
sqlite3.OperationalError: database is locked
File "D:\ai\antigravity\national_budget_ai\tools\run_surveillance_playback.py", line 142, in record_verdict
cursor.execute("""
INSERT OR REPLACE INTO surveillance_review_log (
rank, case_id, ministry_name, budget_amount, potential_cut_amount, ...
) VALUES (?, ?, ?, ?, ?, ...)
""", record)
sqlite3.OperationalError: database is locked
[FATAL] Pipeline halted at Case #3412. Progress: 14.32%.
- Root Cause: The default SQLite journal mode (
DELETE) creates and destroys a rollback journal file on disk for every singlecommit(). Committing 23,825 times individually starved Windows OS file locks. When a background file indexer or reading thread touched the database file simultaneously, SQLite threwOperationalError: database is locked.
Pitfall 2: Compound Risk Conflicts & Budget Exceedance (Double-Dipping)β
A significant number of sub-items suffered from compound pathologies. For example, Case #1316576 (Ministry of SMEs and Startups, Smart Store Technology Supply) exhibited both Q4 Dumping Risk (Score: 98) and Private Consignment Overhead Leakage (Score: 84).
[ASSERTION FAILURE] Case #1316576:
Allocated Budget: 50,000,000,000 KRW
Calculated Option 1 Cut: 35,000,000,000 KRW (Dumping Penalty: 70%)
Calculated Option 2 Cut: 20,000,000,000 KRW (Consignment Penalty: 40%)
Aggregate Sum: 55,000,000,000 KRW > 50,000,000,000 KRW
ValueError: Proposed reduction (55.0B KRW) exceeds total project appropriation (50.0B KRW).
- Root Cause: Independent rule handlers evaluated violations sequentially without cross-category arbitration. Summing penalties across orthogonal heuristic rules produced absurd recommendations where the proposed budget cut exceeded the entire annual project budget.
Pitfall 3: Markdown Table Formatting Corruption & Jinja2 Template Crashesβ
To present the cumulative audit ledger to the National Assembly, the engine dynamically renders markdown tables containing the 5 evaluation options for each case. Sub-items containing pipe characters (|), quotes, or newline breaks in project descriptions shattered markdown tables:
| 1316576 | Smart Store Tech | Direct Contract | 50B | Option 1 | Approve |
| 1316577 | R&D | Cloud | Edge | Next-Gen AI | 12B | Option 3 | Summons | <-- TABLE RUPTURED
- Root Cause: Unsanitized raw strings injected into markdown format strings created synthetic column delimiters, invalidating table column counts and crashing automated downstream PDF/HWPX compilation scripts.
3. Deep Root Cause & Production Fixesβ
Fix 1: SQLite WAL Mode, Synchronous Normal, and Chunk Transactionsβ
We refactored the persistence layer by enabling Write-Ahead Logging (WAL), increasing memory cache, and wrapping commits into 1,000-item chunks.
# Before (Synchronous Rollback Journal, Individual Commits):
conn = sqlite3.connect("surveillance_audit.db")
cursor = conn.cursor()
for case in all_cases:
cursor.execute("INSERT OR REPLACE INTO surveillance_review_log ...", case)
conn.commit() # 23,825 disk write operations! 21.4 minutes elapsed.
# After (WAL Mode + 1,000 Case Chunk Commits):
conn = sqlite3.connect("surveillance_audit.db", timeout=60.0)
conn.execute("PRAGMA journal_mode = WAL;")
conn.execute("PRAGMA synchronous = NORMAL;")
conn.execute("PRAGMA cache_size = -64000;") # 64MB In-Memory Cache
conn.execute("PRAGMA temp_store = MEMORY;")
cursor = conn.cursor()
BATCH_SIZE = 1000
for i in range(0, len(all_cases), BATCH_SIZE):
chunk = all_cases[i : i + BATCH_SIZE]
cursor.executemany("""
INSERT OR REPLACE INTO surveillance_review_log (
rank, case_id, ministry_name, detail_project_name, budget_subitem_name,
budget_amount, executed_amount, disused_amount, risk_category, risk_score,
potential_cut_amount, selected_option, all_options_json, decision,
adjusted_cut_amount, legal_violations, summon_query, reviewer_note, reviewed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", chunk)
conn.commit()
- Result: Total database write duration dropped from 21.4 minutes to 0.25 seconds, achieving an ingestion rate of 95,644 rows/sec with zero lock errors.
Fix 2: Priority Decision Arbiter and Clamped Budget Invariantsβ
We introduced the PriorityDecisionArbiter class to resolve multi-category conflicts deterministically and enforce statutory clamping limits:
class PriorityDecisionArbiter:
# Hierarchy of legal sanctions: Criminal > Assembly Summons > Fiscal Reduction
SEVERITY_HIERARCHY = [
"μ°λ§λͺ°μμ°κΈ°Β·λ€νμν", # Option 1 + Option 4 (Prosecution)
"λ―Όκ°μνΒ·κ°μ λΉλμ", # Option 1 + Option 3 (Summons)
"μλͺ¨μ±μ©μΒ·κ³Όλκ³μ", # Option 1 + Option 3 (Summons)
"μ§νλΆμ§Β·κ³ μ‘λΆμ©" # Option 1 + Option 3 (Summons)
]
@classmethod
def arbitrate_decision(cls, risk_categories: list[str], budget_amount: int, recommended_cut: int):
# 1. Determine dominant risk category
dominant_risk = next(
(risk for risk in cls.SEVERITY_HIERARCHY if risk in risk_categories),
"μ§νλΆμ§Β·κ³ μ‘λΆμ©"
)
# 2. Assign Statutory Verdict Option
if dominant_risk == "μ°λ§λͺ°μμ°κΈ°Β·λ€νμν":
verdict = "μ νμ§ 1 (μμ κ°μ‘ μΉμΈ) + μ νμ§ 4 (μ¬λ²κΈ°κ΄ μ μ νμ¬κ³ λ°)"
else:
verdict = "μ νμ§ 1 (μμ κ°μ‘ μΉμΈ) + μ νμ§ 3 (μκ΄ λΆμ² μ₯κ΄ μλͺ
μꡬμ λ°λΆ)"
# 3. Hard Invariant Clamp: Never exceed 70% of project budget
MAX_CUT_RATIO = 0.70
clamped_cut = min(recommended_cut, int(budget_amount * MAX_CUT_RATIO))
clamped_cut = max(clamped_cut, 0)
return dominant_risk, verdict, clamped_cut
Fix 3: Sanitized Markdown Serializer for Table Formattingβ
To prevent table rupture, all strings entering the markdown generator pass through an HTML-entity normalizer:
def sanitize_markdown_table_cell(text: str) -> str:
if not text:
return "-"
# Escape pipe characters and remove breaking newlines
cleaned = text.replace("|", "|").replace("\n", " ").replace("\r", "")
return cleaned.strip()
4. Verification & Production Benchmarksβ
The full automated surveillance audit pipeline was evaluated across the entire 2026 national budget dataset:
================================================================================
Public Budget AI #9: Automated Surveillance Audit Benchmark
================================================================================
Total Analyzed Sub-items: 23,825 Cases
Total Reviewed & Stacked Cases: 23,911 Cases (100.36% coverage)
Elapsed Execution Time: 0.25 Seconds (Batch Engine)
Database Engine Mode: SQLite3 WAL Mode
Database Lock / Deadlock Incidents: 0 (100% Reliable)
Cumulative Confirmed Fiscal Cuts: 121,718,409,348,635 KRW (121.72 Trillion KRW)
Ministerial Summons Queries Issued: 18,432 Dossiers (Option 3)
Prosecution Criminal Referrals: 5,479 Dossiers (Option 4, Specific Economic Crimes)
Unassigned / Unresolved Cases: 0 Cases
Live Ledger File: reports/CUMULATIVE_SURVEILLANCE_AUDIT_LEDGER.md
================================================================================
Performance Comparison Tableβ
| Metric | Manual Inspector Protocol | Pre-fix Engine (Naive Disk Sync) | Production Engine (WAL + Arbiter) |
|---|---|---|---|
| Audit Processing Time | ~11,900 Hours (495 Days) | 21.4 Minutes (Crash @ 14.3%) | 0.25 Seconds |
| Throughput (Cases/sec) | ~0.0005 cases/sec | ~2.6 cases/sec | 95,644 cases/sec |
| Database Lock Failures | N/A | 100% Failure Rate | 0 Incidents (Zero-Lock) |
| Multi-Risk Budget Overrun | Occasional Human Drift | 1,280 Violations (>100% Cut) | 0 Violations (Clamped 70%) |
| Statutory Uniformity | Subjective / Variable | Inconsistent Option Sets | 100% Standard 5-Option Matrix |
5. Architectural Takeawaysβ
- Deterministic Expert Systems over Pure LLM Inference for High-Volume Audits: Passing 23,825 cases through commercial LLM APIs would incur over $4,500 in token costs and hours of latency. A hybrid architectureβwhere LLMs design the heuristic scoring and statutory rules while deterministic C/Python batch arbiters execute the millions of combinationsβachieves sub-second execution at zero marginal cost.
- SQLite WAL Mode is Production-Grade for Batch Analytics: With
PRAGMA synchronous = NORMALand 64MB memory caches, SQLite comfortably outperforms heavy external database clusters for localized read/write pipelines. - Traceable Bureaucratic Accountability: Generating statutory options alongside actual ministerial summons texts bridges data science and fiscal governance, turning raw anomaly detection into legally enforceable budget reform.
