Repository navigation
Releases: ldamasio/robson
Release list
v2.0.0 introduction of governed execution architecture
This release marks the beginning of Robson v2.
Robson transitions from an execution-focused system into a governed execution runtime. The core change is not additional automation, but the introduction of an explicit execution layer that binds decision, risk, approval, audit, and recovery into a single controlled lifecycle.
This change introduces what was previously missing: the execution glue.
Key shifts:
- Execution becomes a governed lifecycle through QueryEngine
- Risk acts as a blocking gate, not advisory logic
- Operator approval becomes a formal state transition
- EventLog, projections, and recovery are tightened into a coherent path
- Execution is treated as a first-class runtime concern
Robson does not generate signals. It governs what happens after a trading decision exists.
For architectural context:
https://rbxsystems.ch/blog/2026-04-05-robson-v2-finds-its-missing-execution-glue
Release 0.101
Features
- Migrate from Create React App (CRA) to Vite
- Add multi tenant architecture
- Add authentication and refreshing tokens
Bug fixes
- Fix DJANGO_SETTINGS_MODULE env var - Fix DJANGO_SETTINGS_MODULE env var
Release 0.1
v2.5.0
● Robson v2.5.0 Release Notes
Release Date: 2026-05-09
Type: Minor Release (Feature + Security Enhancement)
Status: Production Ready
🎯 Overview
Robson v2.5.0 introduces Policy I3 (Reverse Reconciliation Invariant), completing the symmetric position lifecycle protection system. This
release addresses a critical asymmetric reconciliation gap where robsond could maintain a position in Active state locally while the
corresponding exchange position had already been closed (via liquidation, manual close, or externally-resident insurance stop fill).
Key Achievement: Zero stale-Active positions can now persist undetected in production.
✨ New Features
Policy I3 — Reverse Reconciliation Invariant
Symmetry restored: The runtime now enforces invariants in both directions:
Direction: Exchange has, Robson does not
Invariant: I1/I2 (UNTRACKED)
Detection: Foreign position on operated account
Action: Close at market (UNTRACKED_ON_EXCHANGE)
────────────────────────────────────────
Direction: Robson Active, Exchange does not
Invariant: I3 (stale-Active)
Detection: Local lifecycle drift after external close
Action: Gather evidence, close locally (ReconciledMissingOnExchange)
Implementation: TD-2026-05-05-001 Slices 0–5A
Startup Stale-Active Gate (Fail-Closed)
New behavior: When robsond starts, it now checks all local Active positions against the exchange before entering the control loop.
- Stale-Active detected → Daemon refuses to start (exit code 78)
- Log severity: CRITICAL per affected position
- Operator path: Follow runbook docs/runbooks/td-2026-05-05-001-stale-active-recovery.md
Configuration:
Default: abort (fail-closed)
ROBSON_RECONCILIATION_ON_STARTUP_STALE_ACTIVE=abort
Grace period for periodic worker (not used by startup gate)
ROBSON_RECONCILIATION_MISSING_GRACE_SECS=60 # default
Rationale: Capital safety > delayed detection. Operator must explicitly resolve any drift before production restart.
Evidence-Based Reconciliation
Every reconciliation-close event carries provable exchange evidence in strict priority order:
- OrderFillRecord — Specific exchange order (e.g., resident insurance_stop_id) confirmed FILLED
- UserTradeRecord — Per-symbol trade history when no candidate order id is known
- AccountSnapshot — Two consecutive snapshots proving position is zeroed (rejected in v2.5)
- Estimated — Last resort conservative exit price (rejected in v2.5)
v2.5 scope (Slice 5A): Only OrderFillRecord and UserTradeRecord auto-close positions. AccountSnapshot and Estimated are explicitly rejected
to prevent invented PnL.
Domain type: New ClosureEvidence enum in Event::PositionClosed
Active-Only Enforcement
Safe auto-close scope:
- Active positions → Auto-close after grace period + second observation
- Entering positions → Detected, logged, NOT closed (placement latency protection)
- Exiting positions → Detected, logged, NOT closed (legitimate exit in-flight)
New events:
- DaemonEvent::ReconciliationStaleNonActiveDetected — Entering/Exiting drift warning
- DaemonEvent::ReconciliationStaleActiveUnresolved — No unambiguous real evidence available
Grace Period + Second Observation
False-positive protection:
- First observation → Exchange snapshot does not contain expected (symbol, side). Record first_observed_missing_at, no action.
- Grace period → Default 60s (configurable via ROBSON_RECONCILIATION_MISSING_GRACE_SECS)
- Second consecutive observation → Confirm drift, invoke close path
Idempotency: Reconciliation runs every 60s; all paths are safe to re-run.
🐛 Bug Fixes
Monthly Halt Validation Gate
Fixed: Fall back to in-memory monthly state when pg_pool is unwired, preventing false-negative halt enforcement.
Commit: d7b3098
📚 Documentation
New Documents
- TD-2026-05-05-001 — Implementation guide (Slices 0–6, v2.5 ships 0–5A)
- Policy I3 — Formal invariant definition
- Runbook: Stale-Active Recovery — Operator recovery when startup gate aborts (Slice 5A)
Updated Documents
- ADR-0022 — Amendment 2026-05-08: I3 added as symmetric counterpart to I1/I2
🔧 API Changes
New Exit Reason
pub enum ExitReason {
// ... existing variants
ReconciledMissingOnExchange, // NEW in v2.5
}
Usage: Appears in Event::PositionClosed when a position is closed via reverse reconciliation.
New Event Field
pub struct PositionClosed {
// ... existing fields
pub closure_evidence: ClosureEvidence, // NEW in v2.5
}
Backward compatibility: #[serde(default)] — legacy events deserialize to ClosureEvidence::RealFill default.
⚙️ Configuration
New Environment Variables
Variable: ROBSON_RECONCILIATION_MISSING_GRACE_SECS
Type: u64
Default: 60
Description: Grace period before missing-position triggers close attempt (periodic worker only)
────────────────────────────────────────
Variable: ROBSON_RECONCILIATION_ON_STARTUP_STALE_ACTIVE
Type: abort
Default: abort
Description: Startup gate policy (v2.5: only abort accepted)
🚨 Breaking Changes
None. All changes are backward-compatible:
- New events/fields use #[serde(default)]
- Existing behavior unchanged for normal exit paths
- New reconciliation logic is additive
📦 Upgrade Notes
From v2.4.x to v2.5.0
- No schema migration required — EventLog and projections are backward-compatible
- Review startup behavior:
- If upgrading a running production instance, ensure all local Active positions have exchange counterparts before restart
- First restart will run the startup gate; any stale-Active → exit 78 → follow runbook - Alerting:
- Add CRITICAL log filter for Startup gate: Robson-Active position absent from exchange
- Monitor new Prometheus counter: robson_reconciliation_stale_active_total - Runbook drill (recommended):
- Exercise docs/runbooks/td-2026-05-05-001-stale-active-recovery.md on testnet
- Verify operator access to Binance Futures account and evidence collection endpoints
Environment Variables
No action required — new variables have safe defaults. To customize:
Optional: adjust grace period (default 60s is recommended)
ROBSON_RECONCILIATION_MISSING_GRACE_SECS=120
Startup gate policy (only 'abort' accepted in v2.5)
ROBSON_RECONCILIATION_ON_STARTUP_STALE_ACTIVE=abort
🧪 Testing
Test Coverage
- 210 unit tests (lib)
- 244 integration tests (13 ignored, require DATABASE_URL)
- New test coverage:
- Startup gate: 5 tests (clean, stale-Active abort, no mutation, Entering/Exiting skip)
- Evidence rejection: 2 tests (AccountSnapshot, Estimated)
- Symmetric loop: 2 tests (Entering/Exiting skip without close)
Validation
- ✅ All tests pass with cargo test -p robsond --all-features
- ✅ Clippy clean (warnings: dead code in ExecutionAttempt, non-blocking)
- ✅ cargo +nightly fmt --all --check clean
🔮 Future Work (Slice 5B — Follow-up Release)
Not included in v2.5:
- StartupStaleActivePolicy::AutoReconcile — Opt-in auto-reconcile at startup with real evidence
- robson-cli reconcile-close — Operator-driven close command for manual recovery
- AccountSnapshot and Estimated evidence support (with explicit operator approval)
Tracking: Implementation guide marks Slice 5B as "NEXT"
👥 Contributors
- Leandro Damasio (@ldamasio) — Implementation, testing, documentation
- Claude Sonnet 4.6 (@Anthropic) — Co-authored commits, architectural review
📎 References
- Pull Request: #59 — fix(robsond): close Robson-Active positions missing on exchange
- Technical Debt: TD-2026-05-05-001 (Slices 0–5A resolved)
- Policy: UNTRACKED-POSITION-RECONCILIATION.md §I3
- ADR: ADR-0022 (Robson-Authored Position Invariant)
🎊 Acknowledgments
This release completes the symmetric position lifecycle protection system first outlined in ADR-0022 (2026-04-18). The fail-closed startup
gate design prioritizes capital safety over operational convenience — the correct trade-off for production trading systems.
Robson v2.5.0 is production-ready.
Full Changelog: v2.4.0...v2.5.0