# VRP decision-fidelity amendment — 2026-07-25

**Status:** AMENDMENT to the VRP pre-registration (spec S2, activated 2026-07-12).
**Effective:** first trading day on or after 2026-07-27.
**Scope:** the sleeve's *implementation* of its manage rule. **No registered
parameter value changes.**

---

## 1. What is NOT changing

`manage_dte: 21`, `profit_take_frac: 0.50`, `width: 1.0`, `min_credit: 0.05`,
`risk_budget_pct: 0.02`, `dte_min/dte_max`, `target_delta` — all unchanged and
still keystone-pinned. The entry rule, the sizing rule, and the safety gate are
untouched.

This matters because the obvious failure mode for an amendment like this is
laundering a parameter change as a "bug fix". The test applied throughout: does
the change make the code do what the *already-registered* value says, or does it
change what we are asking for? Only the former is admitted here.

## 2. Why an amendment was needed

Two defects were found on 2026-07-25 while auditing the 2026-07-24 runner-loss
incident. Both are in the *implementation* of a correctly-registered rule.

### 2.1 The profit trigger could fire on a single bad quote

`manage_decision` closes when `1 - (current_spread_value / entry_credit) >= 0.50`.
`current_spread_value` is `mid(short) - mid(long)`, and its only guard was
`bid <= 0 or ask <= 0 or ask < bid` (`vrp_daily.py`) — no width, staleness, or
sanity check, and no requirement that the reading hold more than once.

**Evidence it actually fired spuriously, from the sleeve's own committed record:**

The cohort opened 2026-07-13 (expiry 2026-08-14, `entry_credit` 0.265, qty 2)
was closed on 2026-07-22 with `run_summary.action = "manage:close"`.

- DTE on 2026-07-22 = 23. `manage_dte` is 21, so the DTE branch **cannot** have
  fired. `dte < 0` would have logged `manage:expired-HALT`, not `manage:close`.
- By elimination the profit branch fired, which required
  `current_spread_value <= 0.50 x 0.265 = 0.1325`.
- The **same run**, moments later, recorded `mid_close_cost: 0.225` in
  `pessimistic_ledger.jsonl` — computed by the *same function*.

Two reads of one spread in one run, ~70% apart, with the decision-side read
landing just under the trigger.

Corroboration that 50% capture was not real: the 2026-07-23 replacement cohort's
−30Δ short strike is 722 at 29 DTE, versus 735 at 32 DTE on 07-13. Same delta,
near-identical DTE ⇒ spot fell ~1.8% (or IV rose). Either move makes an OTM put
credit spread worth **more**, not less. Both direct measurements in the record
put the spread at ~0.225–0.26, i.e. ~85% of the entry credit — the opposite of
50% captured.

Cost of a spurious close: a full unplanned round trip. The pessimistic twin
measures 0.035–0.04/sh of drag per side against a cycle credit of ~0.18–0.27/sh,
so each one burns roughly 30–45% of a cycle's gross credit.

*Caveat, stated plainly:* this is **one** observed instance. A genuine fast-tape
move between the two reads is the innocent explanation — but it does not fit the
direction implied by the strike migration. The mechanism (an unguarded ratio of
two wide-leg mids) is not in doubt regardless of how this single instance is read,
and the remedy is a guard, not a redesign.

### 2.2 The 50% target was really ~36%

`entry_credit` was persisted as `credit_estimate` — the decision-time **mid**.
For a credit you fill at or below mid, so the denominator is overstated
**one-directionally, never understated**.

Current position: mid 0.23 recorded, broker-realized fill **0.18**.

| | registered | actual |
|---|---|---|
| profit trigger | close at 50% of max profit | close at **36.1%** of realized max profit |
| — net of measured close drag | — | **~17%** |
| defined risk / contract | $82 | recorded **$77** (understated $5) |

Sizing and the safety gate are **not** affected — both recompute worst case from
leg strikes (`width x 100`) and refuse to trust the declared field, so contract
count and the 2%-of-equity cap were never exposed. This is a rule-fidelity defect,
not a risk-limit breach.

The pessimistic-twin ledger does not cover this: it is explicitly pure
observability and never feeds back into `entry_credit`. It *measures* the drag;
it did not *correct* the rule.

## 3. The amendment

1. **Sanity band on spread revaluation.** A vertical's cost to close is bounded
   by construction to `[0, width]`. A mid outside that is a broken quote, not an
   observation; it now returns `None`, degrading to the DTE rule — the existing
   fail-closed path. Catches gross corruption only.

2. **Confirmation re-read on the profit branch.** The profit trigger must hold
   across `profit_confirm_reads: 2` observations spaced
   `profit_confirm_delay_seconds: 15`. Failure to confirm ⇒ **hold**. This is the
   house's own fail-closed idiom applied one layer up: sleeve state already
   mutates only on a broker-*confirmed* fill, so a decision worth a round trip
   should likewise survive a second look. The **DTE rule is never gated by this**
   — it is time-based and cannot be faked by a quote. Cost: one extra quote read
   on the rare day the trigger fires.

   Branch detection is derived from DTE arithmetic, not by string-matching the
   reason text, so rewording a log message cannot silently disable it.

3. **Realized-fill reconciliation.** After a broker-confirmed open, `entry_credit`
   is set from the order's realized fill. The decision-time mid is **kept
   alongside** as `entry_credit_mid` — the gap between them is the
   execution-quality series the twin exists to measure. `max_loss_per_contract`
   is recomputed from the realized credit.

   Fail-soft toward the pre-amendment behaviour: no order id, no fill price, a
   broker error, or a fill magnitude outside `(0, width)` all fall back to the
   mid and log. Sign convention is **not** hard-coded off one observation —
   magnitude is unambiguous, and the `(0, width)` band (a credit cannot be zero,
   nor >= width without being arbitrage) rejects a misread more reliably than a
   sign rule.

Both new parameters are keystone-pinned **at introduction, before the amended
rule produced any forward data**. Loosening either silently restores the
single-read trigger.

## 4. Forward-clock reset — and why this is not shopping

`oos_monitor.since` moves **2026-07-13 → 2026-07-27**. The 9 trading days accrued
under the old rule measure a different system and cannot be spliced onto the
amended one.

The distinction from start-date shopping is the entire point, so state it
explicitly:

| start-date shopping | this reset |
|---|---|
| rule unchanged | **rule changed** |
| start chosen *because* the curve looks better after it | start **forced** by the change date; no discretion |
| taken after enough data to see what flatters | taken at day 9 — the earliest possible moment |
| discarded window quietly disappears | discarded window **preserved below** |

Taking it now is also the cheapest it will ever be: at day 100 the same fix would
cost the entire gate, and we would face a standing incentive to leave a known
defect in place. That asymmetry is precisely why it is being done today rather
than "watched for another instance".

**`execution.live_start_date` does NOT move** and stays 2026-07-13. It is a fact
of account history (when the Alpaca account began trading), and the
backfill/reconcile jurisdiction guards filter pre-live broker filler against it;
moving it would re-admit that filler as real equity. Two different clocks,
deliberately.

**The discarded window, preserved (2026-07-13 → 2026-07-24, 9 marks):**

```
2026-07-14  9983.84    2026-07-20  9981.84
2026-07-15  9987.84    2026-07-21  9985.84
2026-07-16  9991.84    2026-07-22  9989.84
2026-07-17  9975.84    2026-07-23  9991.76
                       2026-07-24  9989.59  (backfilled from broker truth)
```

Two cohorts, one full cycle: opened 07-13 at 0.265 mid (qty 2), closed 07-22 at
0.225 mid — the close this amendment argues was spurious. Net +$7.92 over the
window, which is noise on $10k and carries no information either way. Nothing of
value is being discarded; the window is retained here so the claim can be checked.

## 4b. One-time correction of the OPEN cohort

The reconciliation in §3.3 applies to *new* opens. The cohort open at the time of
this amendment (entered 2026-07-23, expiry 2026-08-21) would otherwise be managed
under the defective mid denominator for ~4 more weeks, and the reset clock would
then start on days that still ran the old rule.

So `data/state/vrp/vrp_position.json` is corrected once, from **verified broker
truth** — order `56123d75-0117-4f6c-8f14-0df0f497bfad`, `filled_avg_price -0.18`,
`filled_qty 1.0`, read back via `thales --sleeve vrp portfolio orders --json`:

| field | before | after |
|---|---|---|
| `entry_credit` | 0.23 (mid) | **0.18** (realized) |
| `entry_credit_mid` | — | 0.23 (retained) |
| `max_loss_per_contract` | $77 | **$82** (true defined risk) |
| profit trigger fires at close cost | 0.1150 | **0.0900** |

This is a correction *toward* measured reality, not a fabrication: every value is
read from the broker, and the effect is to make the position **harder** to close
and its recorded risk **larger**. Nothing here is estimated or back-filled from a
model.

## 5. What would falsify the 2.1 diagnosis

If, over the next cohorts, `profit_confirm_reads: 2` **never** vetoes a trigger —
i.e. the confirmation always agrees with the first read — then the 07-22 event was
more likely a genuine fast move than quote noise, and the confirmation is
harmless-but-unnecessary insurance. The veto rate is logged
(`manage:hold-unconfirmed`) and should be reviewed after ~5 cohorts. A veto rate
materially above zero confirms the diagnosis; a rate of exactly zero across many
triggers argues the guard is inert and the single instance was a genuine move.

Either way the guard stays: its cost is one quote read.

## 6. What this amendment does NOT claim

It does not claim the VRP sleeve works. It does not improve any expected return
estimate. It removes two ways the implementation departed from its own registered
rule, and it makes the sleeve exit **later** in both cases — a strictly higher bar
to close. The forward gate remains the sleeve's entire validation, now running on
a rule that matches its registration, from 2026-07-27.
