Skip to content

feat(fleet): stranded-cargo fallback frees parked-full traders (v2.9.3)#56

Merged
mabry1985 merged 1 commit into
mainfrom
feat/stranded-cargo-fallback
Jul 5, 2026
Merged

feat(fleet): stranded-cargo fallback frees parked-full traders (v2.9.3)#56
mabry1985 merged 1 commit into
mainfrom
feat/stranded-cargo-fallback

Conversation

@mabry1985

Copy link
Copy Markdown
Member

Closes #55.

Problem

A trader parked full of a good the local market doesn't buy sits idle indefinitely — observed live 2026-07-05: PROTOTRADER-1 sat with 40 AMMONIA_ICE at a non-buying waypoint for ~1 hour, needing two manual A2A rescues. _trade_loop's no-route branch just sleep(15)s, and the engine only detected the strand as a sitrep hint (report()), never acted.

Fix

New _clear_stranded_hold, called only from _trade_loop's no-route branch (so it never pre-empts a real trade run): repositions the ship to the nearest in-system market that buys the stranded good and sells it, accepting sub-margin — freeing the ship beats hoarding dead capital.

Guards (each tested)

  • Never mid-flight (nav.status != IN_TRANSIT).
  • Contract cargo protected — a stuck contract lead falls back to _trade_loop still holding its deliver haul; /my/contracts is the authoritative signal, and an unreadable list protects everything (fail-safe).
  • Buyer must be provably reachableT._good_markets raise (unproven scan) or an empty buyer set leaves the cargo aboard. This sells stranded cargo; it never jettisons (proof-gated destruction stays the mining loop's job).
  • One side-trip per window (cleared flag).

Tests

test_stranded_cargo.py (8): reposition+sell, contract-cargo protected, no-buyer holds, unproven-scan holds, in-transit no-op, unreadable-contracts fail-safe, empty-hold no-op, once-per-window wiring.

Full suite: 338 pass incl. the 8 new; ruff clean. (One unrelated pre-existing failure in test_idle_capital_banking_toward_the_gate_stays_quiet reproduces on clean main in my local env — host idle_capital/gate-buffer logic, not touched here.)

🤖 Generated with Claude Code

Closes #55. A trader parked FULL of a good the local market doesn't buy sat idle
~1h live (2026-07-05, PROTOTRADER-1 with 40 AMMONIA_ICE) — _trade_loop's no-route
branch just slept, and the engine only *detected* the strand as a sitrep hint
(fleet.py report()), never acted on it.

New _clear_stranded_hold, called ONLY from _trade_loop's no-route branch (so it
never pre-empts a real run), repositions the ship to the nearest in-system market
that BUYS the stranded good and sells it, accepting sub-margin — freeing the ship
beats hoarding dead capital.

Guards (each tested): never act mid-flight; NEVER liquidate contract-deliver cargo
(a stuck contract lead reaches _trade_loop still holding its haul — /my/contracts
is the authoritative signal, and an unreadable list protects EVERYTHING, fail-safe);
an unproven buyer scan (T._good_markets raised) or a provably-empty buyer set leaves
the cargo aboard — this SELLS stranded cargo, it never jettisons; at most one
side-trip per window.

8 new tests (test_stranded_cargo.py): reposition+sell, contract-cargo protected,
no-buyer holds, unproven-scan holds, in-transit no-op, unreadable-contracts fail-safe,
empty-hold no-op, and the once-per-window wiring in _trade_loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@protoquinn

protoquinn Bot commented Jul 5, 2026

Copy link
Copy Markdown

👀 Quinn is reviewing — verdict (PASS / WARN / FAIL) + findings to follow.

@mabry1985
mabry1985 merged commit d54cece into main Jul 5, 2026
1 check passed
@mabry1985
mabry1985 deleted the feat/stranded-cargo-fallback branch July 5, 2026 19:24

@protoquinn protoquinn Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Audit — PR #56 | feat(fleet): stranded-cargo fallback frees parked-full traders (v2.9.3)

VERDICT: WARN (CI still settling — test: in_progress; approve-on-green policy promotes this to APPROVED automatically once every check passes terminal-green)


CI Status

  • test: in_progress ⏳

Diff Review (3 files, +290/-1)

  • fleet.py: New _clear_stranded_hold() called from _trade_loop's no-route branch — repositions a parked-full trader to the nearest in-system market that buys the stranded good, sells it sub-margin, then loops to re-check routes. One side-trip per window via cleared flag.
  • protoagent.plugin.yaml: version bump 2.9.2 → 2.9.3.
  • test_stranded_cargo.py: 8 new tests covering reposition+sell, contract-cargo protection, no-buyer hold, unproven-scan hold, in-transit no-op, unreadable-contracts fail-safe, empty-hold no-op, once-per-window wiring.

Observations

  • CLAWPATCH/LOW: Test suite feature mapped as structurally sound — 8 features mapped, 1 finding (test-suite signal), no correctness or security flags raised.
  • LOW: cleared = True is set unconditionally before _clear_stranded_hold() returns — a transient failure (e.g. contracts API blip) won't retry in the same window. This is the documented design ("one side-trip per window") and resets on the next _trade_loop entry; no action needed, just noting.
  • The fail-safe on unreadable /my/contracts (return False → protect everything) plus the "never jettison" guard (empty buyer set leaves cargo aboard) are well-judged. Contract cargo protected via authoritative signal, not heuristic.

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 5, 2026

Copy link
Copy Markdown

Submitted COMMENT review on #56.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stranded-cargo fallback: engine parks a full hauler indefinitely when no reachable market clears min_margin

1 participant