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.
306 lines
12 KiB
Markdown
306 lines
12 KiB
Markdown
# Implementation Turn: quote lifecycle truth and execution explanation
|
|
|
|
Status: open
|
|
Opened: 2026-04-09
|
|
|
|
## Goal
|
|
Replace ambiguous quote and decision wording with a truthful per-quote lifecycle that tells the operator exactly why a quote was filtered, rejected, blocked, submitted, failed, not filled, or completed.
|
|
|
|
## Selected backlog items
|
|
- [I021] Quote lifecycle truth and execution explanation: replace ambiguous dashboard verdicts with a per-quote state machine that shows exactly why a quote was filtered, rejected, blocked, submitted, not filled, or executed, with durable reason codes and operator-facing traceability.
|
|
|
|
## Design rules
|
|
- Treat quote lifecycle as product truth, not UI decoration.
|
|
- Strategy verdict is not the final operator answer.
|
|
- Prefer one explicit lifecycle derivation path shared by backend and dashboard over ad hoc page-specific wording.
|
|
- Do not invent downstream certainty where durable evidence is absent.
|
|
- Remove `Actionable` completely from operator-facing copy.
|
|
- Do not use stronger operator words than the durable evidence supports.
|
|
- Fix semantic bugs by changing both the code and the tests that encoded the wrong assumption.
|
|
|
|
## Problem statement for this turn
|
|
The current dashboard still forces operators to infer too much:
|
|
- `Actionable` does not say whether a command was emitted or submitted
|
|
- the Strategy page mixes strategy and execution truth
|
|
- executor-rejected rows are not clearly distinguishable from strategy-rejected rows
|
|
- quote ids are truncated and awkward to use during debugging
|
|
|
|
The repo already stores enough of the real lifecycle to do better:
|
|
- quote id
|
|
- decision id
|
|
- emitted command id
|
|
- execution result status and result code
|
|
|
|
The recent submission-versus-trade bug showed the broader prevention gap:
|
|
- wrong semantics were encoded in backend query names
|
|
- the dashboard rendered stronger claims than the evidence supported
|
|
- tests asserted the wrong meaning instead of protecting the truth
|
|
|
|
The turn therefore needs to improve:
|
|
- lifecycle derivation
|
|
- durable reason mapping
|
|
- recent-row rendering
|
|
- trace affordances
|
|
|
|
## Lifecycle model for this turn
|
|
Implement one repo-owned lifecycle derivation for recent rows, using durable evidence in this order:
|
|
|
|
1. Quote observed
|
|
2. Strategy evaluated
|
|
3. Command emitted or not emitted
|
|
4. Executor result observed or absent
|
|
5. Venue downstream outcome when available
|
|
|
|
The first mandatory states are:
|
|
- `Filtered`
|
|
- `Rejected`
|
|
- `Blocked`
|
|
- `Submitted`
|
|
- `Failed`
|
|
- `Awaiting outcome`
|
|
- `Completed`
|
|
|
|
These states must become the repo-owned evidence vocabulary for operator surfaces and summaries.
|
|
|
|
Suggested meanings:
|
|
- `Filtered`
|
|
quote never entered the active trade path or was excluded before strategy decision
|
|
- `Rejected`
|
|
strategy evaluated the quote and decided not to trade
|
|
- `Blocked`
|
|
strategy approved or emitted a command, but execution did not proceed due to control state or another repo-owned gate
|
|
- `Submitted`
|
|
executor accepted the command and successfully submitted a quote response
|
|
- `Failed`
|
|
execution submission failed technically
|
|
- `Awaiting outcome`
|
|
submitted to venue, but no later durable terminal venue outcome exists yet
|
|
- `Completed`
|
|
durable evidence shows the trade completed successfully
|
|
|
|
Do not show states we cannot support yet for a given row.
|
|
|
|
## Reason-code model
|
|
For each lifecycle state, map durable payload fields to a small operator reason taxonomy.
|
|
|
|
Examples:
|
|
- strategy reason codes:
|
|
- unsupported_pair
|
|
- below_edge_threshold
|
|
- inventory_unavailable
|
|
- stale_reference_price
|
|
- executor reason codes:
|
|
- executor_disarmed
|
|
- executor_paused
|
|
- submission_failed
|
|
- quote_response_ok
|
|
- downstream outcome reasons if available:
|
|
- expired
|
|
- not_filled
|
|
- completed
|
|
|
|
If the exact reason is missing:
|
|
- expose `reason_unknown`
|
|
- keep the row truthful instead of synthesizing an explanation
|
|
|
|
## Semantic guardrails for this turn
|
|
|
|
### 1. Ban overloaded certainty words unless evidence justifies them
|
|
Review and remove or rename operator-facing and backend terms such as:
|
|
- `successfulTradeCount`
|
|
- `lastSuccessfulTradeAt`
|
|
- `loadSuccessfulTradesPage`
|
|
- `trade_asset_changes`
|
|
- any UI label implying trade completion, realized asset movement, or PnL attribution from mere submission evidence
|
|
|
|
Allowed wording must be tied to the strongest durable evidence actually present.
|
|
|
|
### 2. Encode semantic invariants in code and tests
|
|
Add explicit checks and regression coverage for:
|
|
- `submitted != completed`
|
|
- `submitted != realized asset delta`
|
|
- executor blocking != strategy rejection
|
|
- no UI label may claim trade completion from submission-only evidence
|
|
|
|
Negative tests are required, not just positive-path tests.
|
|
|
|
## Backend changes
|
|
|
|
### 1. Add a lifecycle derivation helper
|
|
Create or extend a backend module that derives quote lifecycle from:
|
|
- recent trade decisions
|
|
- recent execution results
|
|
- later terminal records only where they are real
|
|
- any available quote-status or venue result surfaces
|
|
|
|
It should emit a normalized row object with:
|
|
- `quote_id`
|
|
- `decision_id`
|
|
- `command_id`
|
|
- `pair`
|
|
- `direction`
|
|
- `lifecycle_state`
|
|
- `lifecycle_label`
|
|
- `reason_code`
|
|
- `reason_text`
|
|
- timestamps for the latest known stage
|
|
- stage details for tooltips or drilldown
|
|
|
|
### 2. Join decision and execution truth explicitly
|
|
The backend should no longer leave the frontend to infer execution from isolated tables.
|
|
|
|
For each recent quote/decision row:
|
|
- attach the matching execution result by `command_id`, `decision_id`, or `quote_id`
|
|
- attach terminal completion or non-fill evidence only where it is genuinely available
|
|
- expose whether the row is strategy-only, strategy-plus-command, or strategy-plus-execution
|
|
|
|
As part of this phase, rename misleading backend aggregation helpers and payload fields where practical so code meaning matches evidence meaning.
|
|
|
|
### 3. Preserve operator drilldown identifiers
|
|
Ensure the bootstrap payload exposes:
|
|
- full quote id
|
|
- full decision id
|
|
- full command id
|
|
|
|
Avoid requiring the frontend to reconstruct or guess identifiers from formatted strings.
|
|
|
|
## Dashboard changes
|
|
|
|
### 4. Remove forbidden language
|
|
Remove `Actionable` from:
|
|
- Strategy page tables
|
|
- any lifecycle badge or verdict cell
|
|
- any supporting labels or legends
|
|
|
|
Replace it with explicit state labels driven by lifecycle derivation.
|
|
|
|
Also remove or rename any remaining wording that presents submission evidence as trade completion or realized asset movement.
|
|
|
|
### 5. Make recent rows self-explanatory
|
|
For each row, render:
|
|
- primary lifecycle state
|
|
- secondary reason text
|
|
- quote id with copy action
|
|
- command id if emitted
|
|
- timestamps
|
|
|
|
The operator should be able to scan rows and answer:
|
|
- why no trade happened
|
|
- whether the system tried to trade
|
|
- whether failure was strategic, operational, or downstream
|
|
|
|
### 6. Add trace affordances
|
|
At minimum:
|
|
- copy button for quote id
|
|
- avoid over-truncating ids without recovery path
|
|
- show linked ids in a dedicated trace column or expanded detail panel
|
|
|
|
If the row layout gets crowded, prefer an expandable detail tray over hiding identifiers.
|
|
|
|
## Page-level application
|
|
|
|
### Strategy page
|
|
This page should become the primary recent quote-decision-execution lifecycle surface.
|
|
|
|
It should show:
|
|
- the latest recent rows for the active pair
|
|
- lifecycle state rather than strategy-only verdict
|
|
- explicit explanation text
|
|
|
|
If a strategy-only summary remains, it must be visually separate from per-quote lifecycle truth.
|
|
|
|
### Related quote surfaces
|
|
Inspect quote and system surfaces for similar ambiguity and align the wording if they expose the same concepts.
|
|
|
|
Do not let one page say `Submitted` while another page still says `Actionable` for the same row.
|
|
Do not let one page say `trade` while another page only has `submitted` evidence for the same row.
|
|
|
|
## Data and state edge cases
|
|
- Strategy decision exists, no command emitted:
|
|
render as `Rejected` with strategy reason
|
|
- Command emitted, no execution result yet:
|
|
render as `Blocked` or `Awaiting executor` only if that distinction is durably supportable; otherwise use a truthful pending label
|
|
- Execution result `executor_disarmed`:
|
|
render as `Blocked` with reason `executor disarmed`
|
|
- Execution result `submission_failed`:
|
|
render as `Failed`
|
|
- Execution result `submitted`:
|
|
render as `Submitted` or `Awaiting outcome`
|
|
- Successful trade summary exists but no explicit per-quote completion event:
|
|
only promote to `Completed` where the durable linkage is real
|
|
- Submission evidence appears in profitability or summary widgets:
|
|
rename and constrain those widgets so they do not imply realized trade truth
|
|
|
|
## Concrete implementation order
|
|
|
|
### Phase 1. Define lifecycle derivation
|
|
- inspect current durable decision and execution payloads
|
|
- write the normalized lifecycle state mapping
|
|
- define forbidden and allowed operator labels
|
|
- list misleading backend and UI names that must be changed
|
|
- define the semantic invariant tests up front
|
|
|
|
### Phase 2. Implement backend aggregation
|
|
- derive unified recent lifecycle rows
|
|
- expose full identifiers and reason codes
|
|
- keep old consumers working until the frontend is switched
|
|
- rename misleading submission-as-trade helpers and summary fields where touched
|
|
|
|
### Phase 3. Update Strategy page rendering
|
|
- replace verdict column with lifecycle state
|
|
- add reason text
|
|
- add quote-id copy affordance
|
|
- surface command id and execution state where relevant
|
|
|
|
### Phase 4. Tighten wording and consistency
|
|
- remove `Actionable`
|
|
- align supporting labels
|
|
- ensure blocked vs rejected vs submitted are clearly distinct
|
|
- ensure submitted vs completed vs realized asset movement are clearly distinct
|
|
|
|
### Phase 5. Validate with live recent rows
|
|
- verify a row rejected due to executor disarmed renders as blocked with reason
|
|
- verify a submitted row renders as submitted
|
|
- verify quote ids can be copied and used for tracing
|
|
- verify no submission-only row is rendered as a trade, completion, or realized asset delta
|
|
|
|
## Test plan
|
|
- unit tests for lifecycle derivation from:
|
|
- strategy-rejected rows
|
|
- executor-disarmed rows
|
|
- submission-failed rows
|
|
- submitted rows
|
|
- unit tests for semantic invariants:
|
|
- submitted rows must not be counted as completed trades
|
|
- submission-only rows must not render as asset deltas
|
|
- dashboard bootstrap tests for:
|
|
- forbidden `Actionable` removal
|
|
- explicit lifecycle labels
|
|
- reason text rendering
|
|
- identifier exposure
|
|
- dashboard summary tests for renamed or narrowed submission metrics
|
|
- frontend component tests if needed for copy affordance or row rendering logic
|
|
|
|
No lifecycle ambiguity fix is complete without a regression test proving the old ambiguous wording or overclaim cannot return.
|
|
|
|
## Validation checklist against the proof
|
|
- `Actionable` no longer appears
|
|
- strategy approval is visibly distinct from execution submission
|
|
- recent blocked rows explain why they did not trade
|
|
- recent submitted rows show that they were submitted
|
|
- quote ids are directly usable from the dashboard
|
|
- submission-only evidence is no longer rendered as trade completion or asset delta truth
|
|
|
|
## Failure modes to plan for
|
|
- the backend joins rows incorrectly and attributes the wrong execution result
|
|
- the UI uses softer wording than the backend lifecycle state
|
|
- older rows lack enough evidence and the UI pretends certainty
|
|
- ids are still truncated without a copy or expand path
|
|
- misleading legacy names remain in place and create new semantic drift later
|
|
|
|
## Truth review checklist for this turn
|
|
For every operator-facing label, metric, table, or badge touched in this proof:
|
|
- what exact durable table or event backs it?
|
|
- what is the strongest claim the evidence supports?
|
|
- what wording would overclaim certainty?
|
|
- what negative regression test locks that boundary in?
|