Deterministic Agentic Governance: Bringing Order to Autonomous Software Engineering
How we built a framework that makes AI agents reliable enough for production
The Problem: Agents Don't Play Nice at Scale
You've seen the demo. An AI agent writes a feature, runs tests, commits to git. Magic.
Then you try it on a real codebase. Ten tasks in, the agent hallucinates an API. Fifteen tasks in, it forgets the architectural constraints you gave it in the first prompt. Twenty tasks in, you're manually reverting broken commits and wondering why you didn't just write the code yourself.
The math is brutal. If each step has a 95% success rate (optimistic), a 20-step task chain succeeds only 36% of the time:
P(success) = 0.95^n
n=5 → 77%
n=10 → 60%
n=20 → 36%
n=50 → 8%
This is compositional decay—the exponential failure rate of chaining probabilistic operations.
Why Current Approaches Fail
1. Context Rot
Every conversation turn adds to the LLM's context window. After 20 turns:
- Initial instructions get diluted (attention thinning)
- Error logs pollute the context (cascade pollution)
- The agent "forgets" constraints it was given initially
2. No Objective Verification
Asking an LLM to review its own code is like asking a student to grade their own exam. Hallucinated "looks good to me" responses are the norm.
3. No Safety Boundaries
Agents with shell access can rm -rf /, commit secrets, or open PRs to production branches.
4. Human-in-the-Loop Doesn't Scale
Manual approval for every step kills velocity. But no approval means broken code in main.
The Insight: Treat the LLM as a CPU, Not a Brain
What if we stop treating the LLM as an autonomous decision-maker and start treating it as a stateless processing unit—like a CPU?
The CPU doesn't manage memory, enforce permissions, or decide which program runs next. The operating system kernel does that.
So we built a governance kernel around the LLM.
The Three Pillars of Deterministic Governance
Pillar 1: Persistent State Ledger (PROGRESS.json)
Instead of stuffing task state into the LLM's context, we externalize it:
{
"version": "1.0.0",
"projectName": "electron-to-tauri-migration",
"tasks": [
{
"id": "TASK-003",
"category": "DATA_STORAGE",
"referencePath": "electron/store/index.ts",
"targetPath": "src-tauri/src/storage/mod.rs",
"status": "PENDING",
"retryCount": 0,
"maxRetries": 5,
"verificationLogs": []
}
]
}
Every agent turn gets a fresh, clean context containing only:
- The single task specification
- The reference code (read-only)
- Acceptance criteria
No history. No error logs from previous tasks. No instruction dilution.
Pillar 2: Declarative Permission Sandboxing
Before any code runs, the governance kernel enforces boundaries via policy-as-code:
{
"readOnlyPaths": ["src/**", "package.json", "Cargo.toml"],
"writeOnlyPaths": ["src/**", "tests/**", "dist/**"],
"forbiddenPaths": [".git/**", ".env*", "*.key", "secrets/**"],
"allowedCommands": ["npm", "cargo", "tsc", "eslint", "git"],
"forbiddenCommands": ["rm -rf", "sudo", "curl", "wget", "ssh"],
"networkAccess": false,
"maxExecutionTimeMs": 300000,
"maxMemoryMb": 1024
}
This isn't a suggestion. It's enforced at the OS level via environment variables and command validation.
Pillar 3: Compiler Verification Gates
No LLM self-evaluation. Only binary, deterministic gates:
| Gate | Tools | Purpose |
|---|---|---|
TYPE_CHECK |
tsc --noEmit, cargo check
|
Type safety & compilation |
SYNTAX_CHECK |
prettier --check, cargo fmt --check
|
Formatting & syntax |
AST_LINT |
eslint, cargo clippy
|
Static analysis |
ANTI_STUB |
Regex/AST patterns | Detect TODO, unimplemented!(), empty blocks |
TEST_SUITE |
npm test, cargo test
|
Functional correctness |
If any gate fails, the task is retried with the compiler error fed back to a fresh agent context. After maxRetries, the workspace is git-reset to the last clean commit.
The Governance Loop
while pending_tasks_exist():
task = ledger.get_next_pending()
base_commit = git.current_commit()
for attempt in range(task.maxRetries + 1):
if attempt > 0:
git.reset_hard(base_commit) # Clean slate
ledger.increment_retry(task.id)
# Fresh context, single task
context = build_clean_context(task, reference_code)
result = agent.execute(context)
if not result.success:
continue
# Objective verification
verification = run_all_gates(task.category)
if verification.all_passed():
git.commit(task.id, task.description)
ledger.mark_completed(task.id)
break
else:
ledger.log_verification_failures(task.id, verification)
if not verification.all_passed():
ledger.mark_failed(task.id)
git.reset_hard(base_commit)
Result: Zero context rot. Deterministic verification. Atomic commits. Full audit trail.
Real-World Example: Electron → Tauri 2.0 Migration
We migrated a production desktop app (100k+ lines) from Electron/Node to Tauri 2.0 (Rust + React + SQLite).
10 coordinated tasks:
| Task | Category | Verification Gates |
|---|---|---|
| Tauri main.rs entry point | INFRASTRUCTURE | TYPE, SYNTAX, LINT, ANTI_STUB |
| IPC layer replacement | INFRASTRUCTURE | TYPE, SYNTAX, LINT, ANTI_STUB |
| Hybrid SQLite + Markdown storage | DATA_STORAGE | TYPE, SYNTAX, LINT, ANTI_STUB, TEST |
| Non-blocking token streaming (10Hz) | LLM_INFERENCE | TYPE, SYNTAX, LINT, ANTI_STUB |
| React virtualization (@tanstack/react-virtual) | UI_COMPONENT | TYPE, SYNTAX, LINT, ANTI_STUB |
| Integration tests | TESTING | TYPE, SYNTAX, LINT, TEST |
| Build config & documentation | DOCUMENTATION | SYNTAX |
Results:
- 3 tasks failed TYPE_CHECK on first attempt → auto-retried with compiler errors → passed
- 1 task failed ANTI_STUB (empty function body) → auto-retried → passed
- All 10 committed atomically with structured messages
- Full audit trail in PROGRESS.json + git history
Enterprise Governance: Three-Tier Authorization
Not all actions are equal. The framework enforces a tiered model:
| Tier | Actions | Approval |
|---|---|---|
| Autonomous | Read code, create branches, run type checks, format files | Pre-approved |
| Semi-Autonomous | Open PRs, modify schemas, update dependencies | Human sign-off |
| Forbidden | Commit to protected branches, export secrets, unsandboxed network | Hard-blocked |
Compliance alignment:
- ISO/IEC 42001 (AI management systems)
- NIST AI RMF (Risk management)
- IEEE 7000 (Ethical system design)
- SFIA 9 (Competency framework)
Supported Agents
The framework is agent-agnostic. Built-in adapters:
// Claude Code (Anthropic)
{ type: 'claude-code', command: 'claude', args: ['-p', '--bare', '--dangerously-skip-permissions'] }
// Aider
{ type: 'aider', command: 'aider', args: ['--no-git', '--yes', '--message'] }
// OpenCode
{ type: 'opencode', command: 'opencode', args: ['run', '--headless'] }
// Kilo Code
{ type: 'kilo', command: 'kilo', args: ['run', '--headless'] }
// Custom - implement AgentAdapter interface
Getting Started
# Install
npm install deterministic-agentic-governance
# Initialize in your workspace
npx govern init --workspace ./my-project --project my-migration
# Define tasks (JSON)
cat > tasks.json << 'EOF'
[{
"id": "TASK-001",
"category": "UI_COMPONENT",
"referencePath": "src/components/OldButton.tsx",
"targetPath": "src/components/NewButton.tsx",
"description": "Migrate button to new design system",
"acceptanceCriteria": ["TypeScript compiles", "No stubs", "Passes ESLint"],
"dependencies": [],
"maxRetries": 3,
"timeoutMs": 180000,
"metadata": {}
}]
EOF
# Add and run
npx govern add --file tasks.json
npx govern run
npx govern status
When to Use This (And When Not To)
✅ Ideal For
- Large-scale migrations (framework ports, language rewrites)
- Automated refactoring (design systems, strict TypeScript, dead code)
- Cross-platform code generation (OpenAPI → clients, schema → ORM)
- Compliance-required environments (fintech, healthcare, defense)
- CI/CD automation (dependency updates, security patches)
❌ Not Ideal For
- Quick prototyping → Use Cursor/Claude Code directly
- Single-file edits → IDE copilot
- Exploratory coding → Unconstrained agent
- Learning → Notebook/REPL
The Key Differentiator
Every task either passes all compiler/linter gates and commits atomically, or rolls back cleanly with full diagnostic capture. No context rot. No hallucinated verifications. No partial broken states.
Architecture Diagram
┌─────────────────────────────────────────────────────────────────┐
│ GOVERNANCE ORCHESTRATOR │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ LEDGER │ │ SANDBOX │ │ VERIFICATION│ │
│ │ MANAGER │ │ ENFORCER │ │ ORCHESTRATOR│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ AGENT ADAPTERS │ │
│ │ ┌──────────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │Claude Code│ │ Aider │ │ OpenCode │ │ Kilo Code │ │ │
│ │ └──────────┘ └────────┘ └──────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ GIT MANAGER │ │
│ │ Atomic commits • Auto-rollback • Branch management │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Resources
-
npm:
npm install deterministic-agentic-governance - GitHub: https://github.com/richardokonicha/deterministic-agentic-governance
- Documentation: See README.md for full API reference
-
Example:
examples/electron-to-tauri/— complete 10-task migration
Conclusion
The industry is shifting from writing code to governing agents. But governance without determinism is just chaos with better marketing.
This framework proves that you can have autonomous execution with deterministic guarantees. The LLM does what it's good at (code generation from context). The governance kernel does what it's good at (state, permissions, verification, rollback).
The result: AI agents you can actually trust in production.
Published as part of the Deterministic Agentic Governance Framework v1.0.0. MIT License.
Top comments (0)