ApprovalLoop — Autonomous Approval Chasing
Most agents wait for a prompt. ApprovalLoop acts when nothing happens.
Inspiration
In enterprise workflows, the biggest source of operational drag isn't always complex decision-making. Often, it's human inaction.
An expense report, purchase order, or access request can be submitted correctly and still remain stuck because the designated approver is traveling, overloaded with meetings, or simply hasn't seen it.
And nobody prompts an AI chatbot because nobody is watching the clock.
We asked:
Why should agents wait for a user prompt when the clock itself can be an authoritative trigger?
That question led to ApprovalLoop, an autonomous approval agent designed for workflows where inaction itself is a signal.
Our core thesis is simple:
Most agents wait for a prompt. ApprovalLoop acts when nothing happens.
What It Does
ApprovalLoop is an autonomous enterprise approval agent that runs as a background workflow on Google Cloud without requiring a human prompt for each action.
The agent follows a bounded execution loop:
1. Wakes autonomously
Google Cloud Scheduler periodically triggers the ApprovalLoop backend.
No user message is required to start the workflow.
2. Observes workflow state
The backend reads open expense approvals from Firestore and determines which reports are still waiting for action.
3. Makes a bounded decision
Eligibility is determined by deterministic workflow rules rather than by the LLM.
For the demo:
- Pending for more than 30 seconds → Nudge
- Nudged for more than 90 seconds → Escalate
Production defaults use longer windows:
- Pending for more than 24 hours → Nudge
- Nudged for more than 72 hours → Escalate
4. Discovers the procedural skill
The reusable approval_escalation skill is loaded at runtime through SkillRegistry.
High-value escalation scenarios can additionally load the skill's Level-2 reference material.
5. Claims the action atomically
Before generating or sending a notification, ApprovalLoop claims the logical action using:
{report_id}:{action_type}
The claim is transactional, preventing duplicate logical actions across repeated scheduler ticks or overlapping processing.
6. Generates contextual language
Google Gemini generates the natural-language wording for the notification through the Google GenAI SDK.
The model is used for communication, not enterprise authority.
7. Validates deterministically
The generated notification is checked against authoritative workflow data.
The four-point safety validator verifies:
- Authorized recipient
- Exact report ID
- Exact monetary amount
- Legal state transition
8. Enforces deterministic policy
Python policy code remains authoritative over:
- Eligibility
- Recipient restrictions
- Financial authorization
- State invariants
- Environment restrictions
- High-value escalation rules
For high-value expenses, the system requires an appropriate senior/admin recipient.
9. Dispatches safely
Authorized notifications are passed through a deterministic notification-provider interface.
The demo uses a simulated notification provider with:
- Provider-side idempotency
- Delivery receipts
- Deterministic behavior
Slack and SMTP adapters are also implemented.
10. Protects human actions
Autonomous processing can race with human activity.
For example, an agent may begin preparing an escalation while an administrator resolves the report.
ApprovalLoop uses conditional state transitions so stale autonomous work cannot overwrite a newer human decision.
11. Records execution
Execution tracing captures the workflow path, while the AgBOM exposes declared runtime dependencies and safety information.
The Core Idea: LLM Proposes. Code Disposes.
The most important architectural boundary in ApprovalLoop is:
LLM proposes. Code disposes.
Gemini generates communication.
It does not control:
- Who receives the notification
- What amount is authoritative
- Whether an approval is eligible
- Whether a financial action is authorized
- Whether workflow state can change
- Whether a notification can be dispatched
Those decisions remain under deterministic application code and transactional infrastructure.
This allows the system to benefit from an LLM's language capabilities without allowing the model to become the source of truth for sensitive enterprise operations.
How We Built It
AI & Agent Layer
- Google Gemini 3.7 Flash
- Google GenAI SDK (
google-genai) - Reusable
approval_escalationprocedural skill - Progressive disclosure through
SkillRegistry
The LLM is intentionally restricted to natural-language generation.
Backend
- FastAPI
- Python 3.10+
- Pydantic v2
- Python
Decimalfor monetary precision - Deterministic eligibility engine
- Deterministic policy engine
- Four-point safety validator
- Transactional state management
- Conditional state transitions
- OpenTelemetry-compatible execution tracing
Frontend
- React 18
- TypeScript
- Vite
- Tailwind CSS
- Lucide Icons
The dashboard is intentionally observational.
The user does not have to click a button to tell the agent what to do. The purpose of the dashboard is to observe what the autonomous system is doing.
Google Cloud
- Cloud Run — serverless backend execution
- Cloud Scheduler — autonomous time-based triggering
- Firestore — workflow state, reports, action claims, registry and durable state
- Secret Manager — supported for production secret injection
The production deployment configures Cloud Scheduler with a one-minute cadence.
Additional Safety
The project also supports optional Google Cloud Model Armor inspection of model inputs and outputs when configured.
The core approval workflow does not depend on the model being trusted with business authority: deterministic validation and policy enforcement remain the final gates.
Demo Configuration
The demo uses intentionally short thresholds so the autonomous behavior can be observed quickly.
| Setting | Demo |
|---|---|
| Nudge threshold | 30 seconds |
| Escalation threshold | 90 seconds after nudge |
| Demo tick cadence | 15 seconds |
Production defaults:
| Setting | Production |
|---|---|
| Nudge threshold | 24 hours |
| Escalation threshold | 72 hours |
| Scheduler cadence | 1 minute |
This allows the same architecture to demonstrate autonomous behavior quickly while retaining realistic production-oriented defaults.
Architecture
Google Cloud Scheduler
│
│ recurring /api/tick
▼
Cloud Run / FastAPI
│
▼
Observe Open Approvals
│
▼
Deterministic Eligibility
(clock + workflow state)
│
▼
Load approval_escalation
procedural skill
│
▼
Atomic Outbox Claim
{report_id}:{action_type}
│
▼
Gemini Wording Draft
│
▼
Deterministic Safety Gate
recipient / ID / amount / state
│
▼
Deterministic Policy
│
┌──────┴──────┐
│ │
BLOCK ALLOW
│ │
▼ ▼
Audit Notification Worker
│
▼
Conditional State Transition
│
▼
Firestore + Trace
Challenges We Faced
The hardest part wasn't getting an LLM to generate a reminder.
The real challenge was:
How do you give an AI agent autonomy without giving the language model authority over sensitive enterprise actions?
A naïve implementation could simply ask an LLM:
"This approval is old. What should I do?"
But that would allow the model to influence business-critical decisions.
We instead established a strict architectural boundary:
LLM proposes. Code disposes.
The model can propose language.
Deterministic code decides whether the action is allowed.
That separation required us to build several safeguards around the model.
Deterministic eligibility
The clock and workflow state—not the LLM—determine whether a nudge or escalation is due.
Transactional action claims
Repeated scheduler ticks must not produce duplicate actions.
The {report_id}:{action_type} key provides an idempotent boundary around each logical action.
Deterministic validation
The generated message is checked against authoritative data before dispatch.
Policy enforcement
Even a perfectly written LLM response cannot bypass authorization or financial policy.
Race-condition protection
The hardest edge case was concurrent human activity.
A human may resolve an approval while the autonomous agent is processing it.
ApprovalLoop therefore checks state before dispatch and performs a conditional state transition at commit time.
If the workflow has changed, the autonomous action becomes stale and is skipped instead of overwriting the human decision.
Accomplishments We're Proud Of
128 Backend Test Functions
The repository contains 128 backend test functions covering areas including:
- Safety validation
- Policy enforcement
- State transitions
- Concurrency behavior
- Runtime behavior
- API behavior
- Agent behavior
- Integration behavior
1,000-Report Deterministic Benchmark
We also executed a deterministic synthetic workload containing 1,000 approval reports.
Observed local run:
- 1,000 synthetic reports
- 350 autonomous nudge actions
- 250 autonomous escalation actions
- 50 admin fallback escalations
- 50 race-condition guards triggered
- 0 duplicate actions on repeated ticks
- 0 invalid state transitions
- 0 unauthorized sends observed
- 0 human prompts required
- 0.87 seconds total local runtime
The benchmark is intentionally synthetic and deterministic.
Its purpose is to validate workflow invariants and safety behavior—not to claim production throughput or distributed concurrency performance.
What We Learned
1. The Clock Can Be an Agent Trigger
Many agentic systems begin with a user message.
ApprovalLoop begins with something different:
the passage of time.
For workflows where inaction itself is meaningful, time can become an authoritative trigger.
That opens a broader class of autonomous systems where agents respond not only to what users say, but also to what users haven't done yet.
2. LLMs Should Not Own Enterprise Authority
Our most important architectural lesson was:
LLM proposes. Code disposes.
Natural-language generation and authorization are fundamentally different responsibilities.
The model is useful precisely because it doesn't need to own the business decision.
3. Race Conditions Are First-Class Problems
An autonomous system operates in an environment that can change while it is processing.
Humans can approve, reject, cancel, or modify a workflow at any time.
Therefore, autonomous state transitions cannot simply assume that the world is unchanged.
Conditional transitions are essential.
4. Autonomy Requires Observability
When a system can act without a human prompt, developers need to understand exactly what happened.
ApprovalLoop therefore captures information about:
- What triggered the execution
- What the agent observed
- Which action became eligible
- Which procedural skill was loaded
- What wording was generated
- Which validations ran
- What policy decision was made
- Whether dispatch succeeded
- Whether the final state transition succeeded or was skipped
Autonomy without observability would be difficult to trust or debug.
What's Next
The current implementation demonstrates the autonomous decision-and-action loop in a controlled environment.
Our next step is to move the architecture toward production enterprise workflows.
Real notification integrations
Expand beyond the deterministic demo provider into real:
- Slack
- Microsoft Teams
- Enterprise workflow systems
Configurable enterprise policies
Allow organizations to configure:
- Approval thresholds
- Escalation windows
- Financial limits
- Recipient rules
- Organizational approval hierarchies
Production observability
Integrate the execution traces with production OpenTelemetry infrastructure and enterprise observability platforms.
Distributed concurrency testing
Move from deterministic local simulation to true distributed-load testing across multiple workers and scheduler invocations.
Human override and audit controls
Provide explicit controls for reviewing, overriding, and auditing autonomous actions in sensitive workflows.
More procedural skills
Extend the same architecture beyond expense approvals to:
- Purchase orders
- IT access requests
- Compliance reviews
- Procurement workflows
- Other time-sensitive enterprise processes
Our Long-Term Goal
ApprovalLoop is based on a simple idea:
The next generation of agents shouldn't only respond to what humans ask. They should understand when the absence of action itself requires a response.
Our goal is to build agents that don't just answer when asked—
but safely act when the workflow requires action.
Built With
- armor
- cloud
- fastapi
- gemini
- genai
- opentelementry
- pydantic
- pytest
- python
- react
- tailwind
- typescript
- vite
Log in or sign up for Devpost to join the conversation.