diff --git a/plans/012-dashboard-order-limits-declutter.md b/plans/012-dashboard-order-limits-declutter.md index 3851ab6..86eb461 100644 --- a/plans/012-dashboard-order-limits-declutter.md +++ b/plans/012-dashboard-order-limits-declutter.md @@ -93,10 +93,9 @@ and removes the dead columns. ## Scope **In scope** (the only files you should modify): -- `api/static/dashboard.html` -- `storage/projections.py` (ONLY `query_order_lifecycle_snapshot`, only to pass - through fields that already exist in the event payloads) -- `tests/unit/api/` (extend an existing test file if a projection changes) +- `api/static/dashboard.html` (after the 2026-07-17 Step-3 revision this is + the ONLY file that changes) +- `tests/unit/api/` (only if a Python change sneaks back in — it should not) **Out of scope** (do NOT touch): - `api/static/admin.html`, anything under `archive/` @@ -139,18 +138,36 @@ the Symbol cell. Update the `table([...])` header array to match. ### Step 3: Order Lifecycle — split "Price" into Limit / Stop / Fill and drop the order-id column -- If `query_order_lifecycle_snapshot` in `storage/projections.py` does not - already emit `limit_price`, `stop_price`, `filled_avg_price`, and `type`, - add them as pass-throughs from the payload (same pattern as the existing - fields in that function). -- Change the table columns to: `Symbol, Side, Type, Qty, Limit, Stop, Fill, - Status, Updated`. Drop the `Order` (truncated order-id) column — move the - full `order_id` into a `title` attribute on the Symbol cell so it is - hover-recoverable, not gone. -- Coerce string prices (`+o.limit_price` etc.) before `fmt`. +> **REVISED 2026-07-17** after an executor STOP: the lifecycle EVENTS +> (`order_submitted`/`order_filled`, see `query_order_lifecycle_snapshot`, +> `storage/projections.py:280`) never captured `limit_price`/`stop_price`/ +> `type` — there is nothing to pass through, and adding fields to the +> emitters is an out-of-scope write-path change. The rich fields live in the +> BROKER snapshot the dashboard already receives as `d.orders` +> (`account_orders_updated`, flattened in `alpaca_paper/sync.py:135-153`), +> and the join key exists: `alpaca_paper/execution.py:116` sends the internal +> `order_id` as the broker `client_order_id` (`request.order_id[:48]`; the +> ids are 36-char uuids so the truncation never bites). Enrich CLIENT-SIDE — +> do NOT modify `storage/projections.py` at all. -**Verify**: `python3 -m pytest tests/unit/api/ -q` behaves identically to a -pre-change stash run; `node --check` → exit 0. +- In `render(d)` next to the leg maps from Step 1, build + `brokerByClientId = {}`: scan `d.orders` snapshots and map + `o.client_order_id` → `{type: o.type, limit: +o.limit_price || null, + stop: +o.stop_price || null, fill: +o.filled_avg_price || null}` (first + snapshot wins, same idiom as `stopBy`). +- In the Order Lifecycle rows, look up `brokerByClientId[o.order_id]`; change + the table columns to: `Symbol, Side, Type, Qty, Limit, Stop, Fill, Status, + Updated`. `Fill` falls back to the event-sourced `o.fill_price` when the + broker join misses. Old lifecycle rows without a broker match render + em-dashes for Type/Limit/Stop — acceptable (the broker snapshot covers the + recent orders the panel shows). +- Drop the `Order` (truncated order-id) column — move the full `order_id` + into a `title` attribute on the Symbol cell so it is hover-recoverable, + not gone. + +**Verify**: `node --check` → exit 0. (No Python change in this step — the +pytest stash comparison is unnecessary unless some other step touched +`storage/projections.py`.) ### Step 4: Approval queue — drop the dead "Mode" column, show explicit numbers