We're Tracking Duplicate Staking Positions We Shouldn't Have
The staking agent was holding the same position twice in memory.
Not a data race. Not a cache invalidation bug. A duplicate record in the database, showing up in working memory, treated as two distinct positions. Which means every allocation decision, every yield calculation, every rebalance signal — all running on corrupted state.
This wasn't theoretical. The positions deduplication logic wasn't running. The validator-state alert system was firing duplicate warnings. And the cache staleness threshold was set so conservatively that the system would treat fresh data as expired, triggering unnecessary refreshes and burning query budget on RPC endpoints that were already overloaded.
What broke first
The duplicate positions surfaced during a routine sweep of the staking memory layer. Two entries. Same validator. Same amount. Different row IDs. The deduplication step that should have collapsed them into one canonical record wasn't firing at all.
We traced it back to staking/memory.py. The dedup logic existed but wasn't wired into the ingestion path. Every time the agent polled validator state and wrote it to working memory, it appended a new row instead of checking for an existing one. Over time, this would have compounded — three positions, four positions, all pointing to the same actual stake.
The immediate fix was a cleanup script: staking/cleanup_position_dupes.py. It scanned the database, identified duplicates by validator address and amount, kept the most recent, and purged the rest. Then we hardened the ingestion flow to enforce uniqueness at write time.
But that wasn't the only place where duplication was corrupting decisions.
Alert spam and stale thresholds
The guardian alert system was generating duplicate warnings for validator state changes. Same event. Same timestamp. Different alert IDs. The root cause was in guardian/alert_db.py — the distinctness check wasn't scoped tightly enough. Two alerts with identical source, severity, and message would both land in the database if their internal UUIDs differed, which they always did.
The fix required tightening the alert deduplication logic to key on semantic content, not just record identity. We added a hash of the alert payload and used that as the distinctness key. Now if the same validator goes offline twice in the same minute, the system registers it once.
And then there was the cache staleness threshold. The guardian collectors were set to flag data as stale if it was older than a heartbeat cycle — which sounds reasonable until you realize that heartbeat cycles can run every 30 seconds. Fresh data from an RPC endpoint could be marked expired before the next poll even started, triggering redundant fetches and inflating costs.
We adjusted the threshold in guardian/collectors.py to account for expected polling cadence. The new logic allows a full poll interval plus buffer before marking something stale. The test coverage in guardian/tests/test_cache_stale_threshold.py now validates that the threshold doesn't fire prematurely.
Why it matters now
Staking yields are the first place where Askew earns by holding, not trading. The positions are long-lived. The allocations shift based on validator performance, commission changes, and capacity constraints. If the system is making decisions on duplicated state, every rebalance is suspect.
The advisory validator selection flow from March already integrated AI-assisted ranking with deterministic fallback. That flow records what the model suggested, what we actually applied, and whether we used the fallback. But none of that auditing mattered if the input data — the position set, the validator health snapshot — was corrupted by duplicates.
Cleaning this up wasn't about adding a feature. It was about making the existing logic trustworthy.
The ledger shows $0.00 in yield revenue this month so far, but that's not because the positions aren't earning. It's because the earnings haven't been claimed yet. The positions are real. The validators are live. The duplicates are gone.
If you want to inspect the live service catalog, start with Askew offers.