# RPL-1 — the marketable-limit market-replace is the one order path outside the safety chokepoint

status: open · raised 2026-08-29 (external design review finding 5 + maintainer verification) · class: execution-safety (standing-covenant violation) · judgement: **YES** · effort ~0.5–1 pd · horizon: live on momentum + meanrev every rebalance with an unfilled limit; P low (minutes-wide, BUY-only window)

---

**Plain-language summary for an owner reading one paragraph.** When a
touch-priced limit order doesn't fully fill within the fill window,
`_resolve_unfilled_limits` cancels it and re-submits the remainder as a
market order. That replacement re-checks only the manual/fleet halt — not the
daily-loss breaker or any per-order reject that materialized *during* the
minutes-long wait — and it submits via `broker.submit_market_order`
directly. That makes it the single order path in the codebase that bypasses
`pipeline.execute_orders`, which the shop's own covenant
(`memory/execution-safety-layer`, 2026-06-06) names THE fail-closed
chokepoint: *"never add an order path that bypasses it."* A second facet
shares the site: `remaining = shares − filled_qty` reads `filled_qty`
BEFORE the cancel; fills landing between the read and the cancel taking
effect over-buy the remainder (check-then-act race).

## Mechanism — verified at HEAD (external review, re-verified by the maintainer)

- `src/thales/execution/daily.py` `_resolve_unfilled_limits`: top-of-function
  re-check is `manual_halt_engaged` only; the replacement goes out via
  `broker.submit_market_order(..., client_order_id=…-mr)` with no
  `evaluate_safety` pass over the remainder batch.
- `filled_qty` is read from `get_order_status` before `cancel_order`; the
  remainder is sized from that pre-cancel read.
- Reachability: `config/settings.yaml` (momentum) and `config/meanrev.yaml`
  both run `order_type: "marketable_limit"`; only vrp is `market`.
- Credit where due (also verified): the surrounding code is careful —
  unknown status → leave the resting limit untouched (fail closed, 2026-07-18
  fix); failed cancel → no replacement. The gap is exactly the missing
  full-gate re-check and the stale read.

## Why P is low (and why it still matters)

The trigger needs a >10% intraday move or a fresh reject to materialize
inside a ≤5-minute fill window, and only bites a BUY remainder — a
de-risking SELL remainder *should* complete. But the site is live on both
equity sleeves on every rebalance, and it is a covenant violation
independent of P: the gate's guarantee is only as good as the paths that go
through it.

## Fix shape (PROPOSE-only — this row does not implement it)

1. Route the replacement through the full gate: run `evaluate_safety` over
   the remainder batch (fresh account snapshot) before any market replace;
   a HALT verdict cancels-without-replacing (the fail-closed arm already
   exists for the manual-halt case).
2. Re-read `filled_qty` AFTER the cancel is confirmed, size the remainder
   from that.

## Test design + kill criterion (pre-registered, negative-controlled)

1. Stub broker whose account crosses the −10% daily-loss line between the
   initial submit and the resolve pass, with an unfilled BUY limit: the
   market replacement must NOT be submitted (deny-asserted — red today).
2. Race test: the stubbed order reports more `filled_qty` on the post-cancel
   read than the pre-cancel read; the replacement must be sized from the
   post-cancel value (red today).
3. De-risking SELL remainder with the breaker tripped: replacement still
   goes out (the exemption must survive the rewiring).

**Kill criterion:** all three green post-fix; revert the gate re-route and
test 1 reds; revert the re-read and test 2 reds. If routing through the gate
proves impossible without submitting a second time through
`execute_orders`'s idempotency machinery, that is a finding about the
chokepoint's shape — record it, don't work around it.

## Scheduling note

Deliberately NOT built in the 2026-08-29 review-response PR: rewiring
order-path plumbing the weekend before a selection day + owner absence is
worse risk management than the defect (minutes-wide, BUY-only exposure).
First unhurried week.
