TASK: Phase 0b — the minimum-premium floor in contract selection. The
      2026-09-01 findings' own priority #1, still unimplemented. Mirrors the
      expiry floor exactly: Spec 05 amendment, one config key, one selection
      site, wall-clock test.

## Established context

- Findings 2026-09-01 §3 row 1 and the 2026-09-02 win/loss analysis §6:
  one selection rule addresses three measured defects at once —
  (a) the flat ~₹89 round-trip cost falls under 2% of position capital,
  (b) the atr_derived stop (1.25 × atr_14 × delta) becomes < 10% of
  premium, so the plan's own stop fires before any percentage backstop —
  the invariant spike_exit asserted about itself and lost,
  (c) trailing_stop.v3's +1.0R activation drops from +28–44% of premium to
  a single-digit percentage, so the mechanism that produced EVERY win in
  the window can actually arm.
- The precedent to copy is the expiry floor (shipped 09-01):
  threshold.option_min_days_to_expiry — schema.py:68 default, :117 type,
  :163 range (0.0, 30.0); read via resolved.py; enforced at ONE selection
  site in api/v2_frame_builder (_select_expiry, ~:445-469); guarded by
  test_option_expiry_floor.py including a wall-clock-never-consulted test.
- This is NOT an RR-2 optimisation: no appeal to outcomes is made. The
  number falls out of the cost model and the stop model's own geometry,
  and the amendment must present it that way.

## IMPORTANT FINDING — the value is DERIVED, not chosen

Do not copy "45–65" or "66.9" from the documents — the two figures disagree
because they used different cost assumptions. Derive the floor in the
amendment from the repo's own current facts, showing the arithmetic:

  1. cost constraint: round_trip_cost ≤ 2% × (premium × lot_size), using
     the experiments/costmodel.py figures and the NIFTY lot from the
     contract-quantity owner — solve for premium;
  2. stop constraint: 1.25 × atr_14 × delta < 10% × premium at the ratified
     delta (0.5) across the OBSERVED atr_14 range in the evidence rows —
     state the range you measured and the premium it implies;
  3. the floor = the binding (larger) constraint, rounded up to a clean
     number; the schema range generous (e.g. 0.0–500.0), 0.0 meaning "no
     floor" so the key can be disabled without a code change.
  State all three numbers in the amendment. If costmodel's spread term
  (RATES.verified=False) moves the answer materially, say so — the F-2
  caveat carries forward.

## Verify the premises

1. The ONE strike/contract selection site in v2_frame_builder (the
   expiry floor's neighbour — likely _option_chain_feature or the strike
   pick it feeds). If selection happens in MORE than one place, STOP and
   report the sites before writing anything.
2. Billing interplay: when the floor refuses every candidate contract, the
   analysis must follow the same path as today's geometry refusal —
   scan_accounting decides billability once at analysis end; verify a
   floor refusal yields NOT billable (no actionable trade), costs no scan
   credit, and writes an evidence row with a distinct rejection_reason
   (e.g. option_premium_below_floor) so zero-trade days stay readable.
3. The three lockstep config artifacts (Spec 05 §DB config keys fenced
   block + schema.py + dormant seed SQL) — the v2-config-schema-sync gate
   compares values; all three change together, as the atr_derived flip did.
4. Nothing else reads the premium at selection time in a way the floor
   would shadow (grep for existing premium-based refusals).

## Do NOT change

The stop model, the geometry gate, every exit policy, trail activation ·
the expiry floor · confidence/threshold anything · the billing decision
site (scan_accounting) — the floor changes what reaches it, never how it
decides · replay/ (the corpus must keep seeing unfloored history; the
floor binds LIVE selection — verify replay does not route through the
same selection site, and STOP if it does, because silently flooring
replay would corrupt Phase 1's measurement).

## Implementation

1. Spec 05 amendment (dated): the key, the derivation arithmetic, the
   default, the range, the 0.0-disables convention.
2. schema.py + seed SQL in lockstep: threshold.option_min_premium_points,
   type float, range, default = the derived value.
3. resolved.py: the read + the DECLARED_READ_BY entry (api.v2_frame_builder).
4. The one selection site: candidates below the floor are skipped exactly
   as too-near expiries are; when none survives, the analysis refuses with
   the distinct rejection_reason.
5. Evidence: the refusal reason lands in ai_auto_scan_evidence like
   option_stop_inside_noise does today.

## Tests

- Selection: fixture chain where the floor excludes some/all candidates —
  correct pick, correct refusal.
- Wall clock never consulted (mirror the expiry floor's test).
- Billing: floored-out analysis is not billable, costs nothing.
- Config: the sync gate green (three artifacts agree).
Then the FULL suite:

    bash backend/scripts/ci/run_all.sh

All 13 gates green.

## Verification report

A. The derivation (three numbers, the binding constraint, the chosen
default)  B. Selection-site verdict (one site, or the STOP list)
C. Replay-isolation verdict (premise: replay unaffected)  D. Billing proof
E. Evidence row example with the new rejection_reason  F. Spec/schema/seed
diff  G. 13 gates  H. Premises that did not match  I. Unknowns

CRITICAL: One selection site, one derived number with its arithmetic shown,
zero behaviour change anywhere except which contracts are eligible. If
replay shares the selection path, STOP — that finding changes the design.
