Summary
FutilityTracker in src/keeper/futility.ts holds its state in a Map that dies with the
process. A restarted daemon retries every contract once before it can begin backing off again.
That was a deliberate choice — a restart is often itself the fix, and forgetting is the right
default — but it has a failure mode worth closing: a supervisor restarting a crash-looping daemon
defeats the protection entirely, and the contract that keeps costing money is paid for on every
restart.
Acceptance Criteria
Tech Stack
TypeScript 5.7 on Node 22+ (CI runs 22 and 24), ESM with moduleResolution: "NodeNext" — relative
imports need the .js extension. @stellar/stellar-sdk is pinned to an exact version; verify RPC
API shape against the installed copy, not against documentation. Tests are Vitest under test/ and
run without a network. npm run format:check, npm run lint, npm run build and npm test must
all be clean.
Summary
FutilityTrackerinsrc/keeper/futility.tsholds its state in aMapthat dies with theprocess. A restarted daemon retries every contract once before it can begin backing off again.
That was a deliberate choice — a restart is often itself the fix, and forgetting is the right
default — but it has a failure mode worth closing: a supervisor restarting a crash-looping daemon
defeats the protection entirely, and the contract that keeps costing money is paid for on every
restart.
Acceptance Criteria
delete.
rather than trusted, so a contract fixed while the daemon was down is retried.
off — rather than crashing.
Tech Stack
TypeScript 5.7 on Node 22+ (CI runs 22 and 24), ESM with
moduleResolution: "NodeNext"— relativeimports need the
.jsextension.@stellar/stellar-sdkis pinned to an exact version; verify RPCAPI shape against the installed copy, not against documentation. Tests are Vitest under
test/andrun without a network.
npm run format:check,npm run lint,npm run buildandnpm testmustall be clean.