Proof: Preserve the completed implementation turn and record its outcome in the tracked archive. Assumptions: The archived files capture the relevant planning state for the completed turn. Still fake: Archiving does not validate the work by itself; external evidence still governs whether the result is trustworthy.
8.6 KiB
8.6 KiB
Implementation Turn: Quote lifecycle investigator and missed quote follow-up
Status: open Opened: 2026-06-12
Goal
Improve the dashboard Quote lifecycle view so operators can pin, search, and follow missed or pending quotes from durable evidence even after live rows move on.
Selected Backlog Items
- [I012] Quotes and execution analytics workbench: live quote tape, quote count and volume windows, size-bucket distributions, oracle-deviation views, per-quote decision and execution trace, and matched-only filters.
This turn intentionally implements only the per-quote lifecycle investigation and missed-quote follow-up part of I012. Remaining analytics-workbench work has been re-added to backlog as follow-on scope.
Constraints
- Preserve trading behavior: no strategy, pricing, threshold, notional, inventory, arming, signer, pair enablement, quote skip, or relay policy changes.
- Do not add active pair, edge, notional, latency, response-policy, or retention env vars.
- Do not introduce a new storage system or retention policy.
- Preserve existing lifecycle and executor result rows as readable.
- Use repo workflow for deployment only; no manual kubectl rollout or image patching.
- Add regression tests for every bug fix or behavior contract introduced here.
Current Shape To Inspect
src/operator-dashboard/static/pages/StrategyPage.jsxQuoteLifecycleSectionQuoteLifecycleTableLifecycleDetails- pause/filter/expanded-row behavior
src/operator-dashboard/static/state/dashboardReducer.jsquote_lifecycle.updatedhandling
src/core/operator-dashboard.mjsderiveQuoteLifecycleRowsbuildLiveQuoteLifecycleRowsbuildStrategySummary- lifecycle row normalization/enrichment
src/lib/postgres.mjs- recent quote, decision, command, execution result, and outcome loaders
src/apps/operator-dashboard.mjs- bootstrap data loading
- REST API routes
- WebSocket update path
- Existing dashboard/static tests under
test/.
Implementation Plan
1. Add a durable lifecycle lookup contract
- Add a backend loader that can find lifecycle evidence by one identifier:
quote_iddecision_idcommand_id
- Reuse existing durable tables and normalizers:
swap_demand_eventstrade_decisionsexecute_trade_commandstrade_execution_results- quote outcomes table
- Return the same normalized lifecycle row shape produced by
deriveQuoteLifecycleRows, enriched for UI in the same way as recent rows. - Include lookup metadata:
- requested identifier
- matched identifier type
- found/not found
- lookup timestamp
- latest stage timestamp
- If no row is found, return a structured not-found response instead of a dashboard error.
- Make missing old fields render as
null/Unavailable; do not require new timing fields to exist.
2. Expose a dashboard API endpoint
- Add an authenticated operator-dashboard route for lifecycle lookup, for example:
GET /api/strategy/quote-lifecycle/:identifier
- The endpoint should:
- validate a non-empty identifier
- call the durable lookup loader
- return
{ ok, lifecycle_row, lookup } - return a 404-style payload for not found without crashing the dashboard
- Do not expose secrets or raw relay payloads beyond what existing lifecycle rows already show.
3. Improve frontend state and interaction model
- Replace the current "pause solves investigation" behavior with a selected quote investigator:
- row action: inspect or pin quote
- selected row highlight
- stable investigator panel above the table or directly below controls
- selected quote remains visible even when filters change or live rows move
- Keep pause as table-only:
- paused table keeps row order and row contents stable
- selected investigator can still refresh from durable lookup
- resume table applies latest rolling rows without clearing the selected quote
- Add identifier lookup:
- search input for quote id, decision id, or command id
- loading, found, not-found, and error states
- copyable identifiers remain available
- Add problem-state filters without changing lifecycle data:
- all
- awaiting executor
- approved/no command
- blocked before submit
- submission failed
- quote not found or finished
- strategy rejected
- submitted/awaiting outcome
- Ensure filters do not hide or clear the selected investigator.
4. Make Awaiting executor explain the next step
- In the investigator panel, show a concise stage status:
- quote observed
- strategy decision
- command recorded or missing
- executor result recorded or missing
- relay/outcome status when present
- For
command_emitted/Awaiting executorrows:- show command age using the current clock
- show the command id and command timestamp
- show "waiting for executor result" only when no result is found
- if a later lookup finds an executor result, show the terminal status instead of stale awaiting text
- For relay failures, make
quote_not_found_or_finishedvisible as a first-class reason.
5. Keep live updates and lookup refresh coherent
- When
quote_lifecycle.updatedarrives:- update the rolling table as it does today
- if the selected quote appears in the live payload, refresh the selected panel from that row immediately
- if the selected quote is absent from the live payload, schedule or expose a durable lookup refresh for the selected identifier
- Avoid unbounded request loops:
- debounce lookup refreshes client-side
- do not add new env vars for refresh timing
- keep manual refresh available
- Show whether selected evidence came from:
- current live rows
- durable lookup
- not found
- lookup error
6. Preserve layout quality
- Keep the lifecycle table full-width and scan-friendly.
- Avoid large marketing-style or decorative redesign.
- Keep row heights stable with existing fixed row slots.
- Ensure long quote ids and reason text truncate or wrap intentionally.
- Do not nest cards inside cards; use a panel/detail layout consistent with the existing dashboard.
- Keep old rows with missing fields readable on desktop and mobile widths.
7. Tests
- Add or update core tests for lifecycle lookup and row derivation:
- command-only row classifies as
Awaiting executor - later executor result for the same quote updates the lifecycle state
- old rows missing timing fields normalize safely
- command-only row classifies as
- Add backend API tests or static route tests:
- lookup by quote id
- lookup by decision id
- lookup by command id
- not-found response
- Add frontend/static tests proving:
- Quote lifecycle has a selected/pinned investigator
- pause language is table-specific
- identifier lookup controls exist
- problem-state filters include awaiting executor and quote-not-found/finished
- selected investigation is not represented only by expanded table rows
- Add a regression test for the described bug:
- start with a selected
Awaiting executorquote - update the displayed recent list so that quote is absent
- prove the selected quote detail remains available or can be refreshed from durable lookup
- start with a selected
8. Validation Commands
- Run targeted tests first, likely:
node --test test/operator-dashboard*.test.mjs test/*lifecycle*.test.mjs- add narrower filenames as tests are introduced
- Run full:
npm test
- Build dashboard bundle:
npm run operator-dashboard:build
9. Commit, deploy, and evidence
- Commit implementation with required body:
Proof: ...Assumptions: ...Still fake: ...
- Push only if tests and dashboard build pass.
- Let repo workflow deploy; do not manually patch deployments.
- Collect live evidence after deploy:
- deployments on the new image
- dashboard still receives live lifecycle updates
- durable lookup returns at least one recent quote lifecycle row
- an unresolved or missed quote can be inspected by identifier without relying on it being visible in the rolling table
- selected quote UI does not clear on live row movement
Assumptions
- Existing durable quote, decision, command, execution result, and outcome rows are sufficient to reconstruct a single quote lifecycle.
- The dashboard can add a small authenticated read endpoint without changing service topology.
- Client-side debounce plus manual refresh is enough to keep pinned quote investigation fresh without adding a new response-policy or retention setting.
Still Fake
- Venue-native terminal fill truth remains incomplete where outcome rows only infer not-filled or settlement through existing attribution.
- Fee-aware realized PnL and broader quote analytics workbench views remain out of scope.
- Historical detail that has already been pruned cannot be reconstructed beyond existing rollups.