This is a submission for the GitHub Finish-Up-A-Thon Challenge
The Bug That Doubled Real Trades
On May 21, my live trading bot generated six duplicate execution attempts in one session.
SafeAgent blocked all six.
Without the guard:
- one duplicated a $1,350 sell
- another doubled a TQQQ position
- total duplicate transaction exposure: $3,653
That session changed how I think about AI agents, retries, and execution guarantees.
What I Built
SafeAgent is an exactly-once execution guard for AI agents and SaaS applications. It prevents duplicate payments, emails, trades, and webhook processing when retries fire after a timeout or crash.
- Live endpoint: https://safeagent-production.up.railway.app
- GitHub: https://github.com/azender1/SafeAgent
-
PyPI:
pip install safeagent-exec-guard
The Comeback Story
How it actually started
Six months ago I was building two things at once: PeerPlay — a patented P2P wagering exchange for skill-based video game tournaments (USPTO provisional 63/914,036) — and a live QQQ/TQQQ momentum trading bot running on Alpaca Markets.
Both hit the same bug. Contest verification agent times out, retries, settlement fires twice. Bot order fills, confirmation drops, retry fires, doubled position. Same failure mode. Different domain.
Different models pushed me toward very different architectures during development. Some were fast but overconfident. The most useful moments came when a model explained why an approach was broken before I implemented it.
That's part of why SafeAgent sat unfinished. Not just time — wrong turns that burned momentum.
Why local idempotency fails
Early versions used a local SQLite guard. It worked until it didn't:
- workers restart and the in-memory state is gone
- containers reschedule and replay from the last checkpoint
- retries land on a different machine entirely
Exactly-once semantics require a durable coordination boundary outside the worker itself. That's what the hosted /claim endpoint provides — the claim lives on the server, not in the process.
Where the project was
I published the original article in April after extracting the pattern from both projects. That was the before state:
- Local SQLite guard only
- Basic
/claimendpoint - Trading bot integration example
-
/auditmarked "coming soon" - No SaaS coverage
- No external integrations
What I finished
1. /audit endpoint — now live
Was implemented in code, never deployed, never documented. Now it's live:
curl "https://safeagent-production.up.railway.app/audit?agent_id=bot-1&status=COMMITTED"
Full claim history, filterable by agent_id, action, status, and timestamp range. Every claim, every SKIP, every duplicate blocked — with timestamps.
2. SaaS coverage — Stripe, webhooks, email
The original README read like a trading tool. SafeAgent solves the same problem for any SaaS. Stripe, GitHub, and Twilio all guarantee at-least-once webhook delivery. SafeAgent turns at-least-once into exactly-once:
def handle_stripe_webhook(event):
r = requests.post(
"https://safeagent-production.up.railway.app/claim",
json={
"agent_id": "saas-webhooks",
"action_type": "stripe_event",
"scope": event["id"]
}
)
if r.json()["status"] == "SKIP":
return {"ok": True}
provision_subscription(event)
3. WisePick integration
WisePick shipped a full adapter, replay demo, and integration docs. The integration splits routing from execution — WisePick answers what and which provider, SafeAgent answers whether this already ran. The decision_id is intentionally excluded from the request_id derivation so retries that mint a new routing decision still hit the same execution slot and return SKIP.
4. CrewAI hosted backend
PR crewAIInc/crewAI#5822 adds pluggable idempotency backends. I shipped a hosted SafeAgentCacheBackend that implements the interface — cross-machine, crash-safe, no local SQLite required.
5. Production proof — May 21
| Time | Event | Blocked |
|---|---|---|
| 0942 ET | duplicate buy TQQQ qty=6 | $452 |
| 0947 ET | duplicate add TQQQ qty=6 | $452 |
| 0949 ET | duplicate sell TQQQ qty=12 | $902 |
| 1000 ET | duplicate entry TQQQ qty=6 | $454 |
| 1014 ET | duplicate sell TQQQ qty=18 | $1,350 |
| 1106 ET | duplicate SQQQ add | $43 |
The session also surfaced a gap: the exit side has no guard. When a SQQQ exit failed with 422 Unprocessable Entity, the bot logged ENTRY BLOCKED for three hours from a phantom position that didn't exist. That failure mode is now documented and is the next spec item — not buried, in the README.
Demo
- Live: https://safeagent-production.up.railway.app/audit
- Session data: https://gist.github.com/azender1/b9112b6519c935df4a75cb05cd250e26
- GitHub: https://github.com/azender1/SafeAgent
My Experience with AI
Claude has been in every meaningful step: the two-phase claim architecture, the SaaS integration examples, the WisePick README section, every GitHub PR comment before I posted it.
The most useful thing it does isn't writing code — it's telling me when an approach is wrong before I build it.
The model that's most confident isn't always the most correct. The one that says "this approach is broken because X" is worth more than the one that says "here's how to build the broken thing faster."
Top comments (2)
The exit-side gap mentioned at the end now has a fault-injection test suite thanks to w2jmoe — github.com/w2jmoe/WisePick/blob/ma.... Building the production implementation against it this week.
Great write-up! Integrating SafeAgent with WisePick was seamless and has significantly hardened our agentic workflows. Your approach to splitting routing from execution is spot on! 🤗✨️