Archive implementation turn: quote lifecycle truth and execution explanation

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.
This commit is contained in:
philipp 2026-04-09 18:07:40 +02:00
parent 715a0aec50
commit bd72e355b1
5 changed files with 513 additions and 502 deletions

View file

@ -12,6 +12,7 @@ Legacy note:
- 2026-04-07: `operator-dashboard-foundation-funds-desk-and-operator-controls` closed with status `passed`. A real operator dashboard shipped with authenticated REST and WebSocket surfaces, truthful funded-capital profitability, live quote and trade visibility, and safe operator controls over the BTC/EURe system.
- 2026-04-08: `cow-protocol-intent-based-venue-integration` closed with status `paused`. The CoW venue integration turn is paused after cluster investigation showed a real NEAR Intents ingest outage exposed a larger observability gap: the dashboard did not escalate stale quote flow or disconnected websocket clients, so runtime health and alerting need to be strengthened before adding a second venue.
- 2026-04-08: `runtime-health-sentinel-alert-routing-and-anomaly-detection` closed with status `passed`. Runtime health is now sentinel-owned, stale truth no longer renders healthy, alert delivery and safe containment exist, and deployment automation rolls all repo-owned services from push.
- 2026-04-09: `quote-lifecycle-truth-and-execution-explanation` closed with status `passed`. Live operator surfaces now derive quote lifecycle from durable evidence, distinguish strategy rejection from executor blocking and submission, expose copyable quote traces, and no longer overclaim submission as completion or asset movement.
## Research Turns
## Planning Events

View file

@ -1,306 +1,5 @@
# Implementation Turn: quote lifecycle truth and execution explanation
# Implementation Turn
Status: open
Opened: 2026-04-09
Status: idle
## 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?
No approved implementation turn is active yet.

201
PROOF.md
View file

@ -1,200 +1,5 @@
# Implementation Proof: quote lifecycle truth and execution explanation
# Implementation Proof
Status: open
Opened: 2026-04-09
Status: idle
## Target outcome
This turn proves that `unrip` can explain every quote outcome in operator-visible, durable terms instead of forcing the operator to infer meaning from ambiguous labels.
The concrete target is the live NEAR Intents BTC/EURe system:
- each quote row must expose its current lifecycle state
- each non-trade outcome must expose the decisive reason code
- execution submission must be distinguishable from strategy approval
- blocked, rejected, submitted, failed, and not-filled paths must be visibly different
- quote identifiers must be directly usable by operators for tracing and support
- operator-facing labels must not overclaim beyond the durable evidence actually stored
## Why this is a meaningful architecture test
The current operator surface still fails a core thesis requirement:
- the Strategy page shows `Actionable`, which does not tell the operator whether a trade was actually submitted
- an operator looking at one quote cannot answer, at a glance, why it did or did not become a trade
- quote ids are hidden behind truncation with no direct copy affordance
- execution truth exists durably in PostgreSQL, but the UI does not surface the lifecycle coherently
That is not just a copy problem. It is an observability gap in the trading product itself. If the system cannot explain a quote outcome precisely, execution is outrunning observability.
The immediate trigger for this turn is a real semantic failure:
- the dashboard treated `trade_execution_results.status = submitted` as a successful trade
- recent submitted quote terms were rendered as if they were realized asset deltas
- tests passed because that wrong assumption had been encoded into the test suite itself
This turn must therefore fix both the UI and the conditions that allowed the mistake through.
## Hypothesis
`unrip` becomes more trustworthy if quote handling is modeled and rendered as an explicit lifecycle instead of a single strategy verdict:
- strategy evaluation is only one stage in the lifecycle
- executor acceptance or rejection must be first-class state
- venue submission and downstream outcome must be represented separately
- durable reason codes must drive operator labels
- forbidden labels that collapse multiple meanings, especially `Actionable`, must be removed
The turn passes only if an operator can inspect a quote and immediately understand whether it was filtered, rejected, blocked, submitted, failed, awaiting venue outcome, not filled, or completed, and why.
## Scope
- [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.
- Cover the live path from:
- raw or normalized quote observation
- strategy decision
- execute-trade command emission
- executor result
- solver relay or venue outcome where available
- Update the dashboard surfaces that currently expose quote or strategy truth:
- Strategy page
- any quote or recent-decision tables tied to the active pair
- quote-id presentation and operator trace affordances
## Assumptions
- The existing durable stores already contain enough information for at least the current live path through strategy decision and executor result.
- Some downstream venue-outcome states may still be partially fake or unavailable for older rows; if so, the UI must say that plainly rather than implying more certainty.
- The immediate turn should prioritize truthful lifecycle explanation over broader analytics such as markout or long-window outcome attribution.
- The prevention strategy must be implemented in repo code and tests rather than left to reviewer judgment alone.
## Turn-shaping rules
- `Actionable` is forbidden as an operator-facing state or label.
- Operator-facing labels must not overstate event certainty.
- Terms such as `trade`, `success`, `filled`, `completed`, `profit`, and `asset delta` are forbidden unless backed by a durable event explicitly representing that fact.
- Do not add a second analytics product. Stay focused on per-quote lifecycle truth for the live active pair.
- Do not invent lifecycle states that cannot be backed by durable repo-owned evidence.
- If a state transition is inferred rather than durably observed, the UI must make that distinction explicit.
- Prefer a small, explicit state machine over a long list of loosely related badges.
## Non-goals
- No new venue integrations.
- No broad historical markout analytics turn.
- No new execution automation or risk widening.
- No redesign of the entire dashboard visual system beyond what is needed to make the quote lifecycle understandable.
## Required operator behavior
### Lifecycle truth
For each visible quote or decision row, the operator must be able to identify the current lifecycle state from a bounded set such as:
- `Filtered`
- `Rejected by strategy`
- `Blocked before submit`
- `Submission failed`
- `Submitted`
- `Awaiting venue outcome`
- `Not filled` or equivalent final non-fill state, if durable evidence exists
- `Completed` or equivalent successful terminal state, if durable evidence exists
Exact labels may vary, but they must be specific and mutually meaningful.
The repo must adopt a hard evidence-state vocabulary for this turn. At minimum:
- `observed`
- `evaluated`
- `command_emitted`
- `rejected`
- `blocked`
- `submitted`
- `failed`
- `awaiting_outcome`
- `completed`
No operator surface may collapse these into softer or stronger claims.
### Reason truth
Each non-terminal or terminal non-trade state must expose a clear decisive reason, such as:
- unsupported pair
- below edge threshold
- inventory unavailable
- executor disarmed
- executor paused
- submission failed
- venue timeout
- quote expired
If the decisive reason is not known, the surface must say that plainly instead of inventing confidence.
### Traceability
For each quote row the operator must be able to access:
- quote id in a non-hidden, copyable form
- decision id where present
- command id where present
- execution result where present
- pair and direction
The operator must be able to reason from a single row without manually cross-correlating multiple pages.
### UI language
The UI must not render `Actionable`.
Any replacement label must answer a concrete operator question, such as:
- did strategy approve this?
- was a command emitted?
- was the command blocked?
- was it submitted?
- did it fail?
### Semantic invariants
The implementation and tests must enforce at least these invariants:
- `submitted` is not `completed`
- `submitted` is not a realized asset delta
- executor-side blocking is not strategy rejection
- stronger labels must not be rendered from weaker evidence
These invariants are proof-critical, not optional cleanup.
## Definition of done
- `Actionable` is removed from operator-facing dashboard surfaces.
- A durable quote lifecycle model exists in repo-owned code and is used by the dashboard.
- At least the current live quote path through strategy decision and executor result is rendered coherently per quote.
- The operator can tell, from one row, why a recent quote did or did not turn into a submitted trade.
- Quote ids are copyable and clearly visible enough for tracing.
- overloaded backend and UI names that imply stronger certainty than the evidence supports are removed or renamed
- Regression tests cover at least:
- strategy-approved but executor-disarmed rows
- submitted rows
- forbidden ambiguous label removal
- forbidden semantic overclaims such as treating `submitted` as `completed`
For this turn to close with status `passed`, the specific operator question:
`Why did this quote not trade?`
must be answerable directly from the dashboard for recent rows without needing manual database inspection.
## Validation evidence required
- direct UI or bootstrap evidence that recent rows show explicit lifecycle states instead of `Actionable`
- direct evidence that a strategy-approved but executor-disarmed row renders as blocked or rejected with reason
- direct evidence that a submitted row renders as submitted
- direct evidence that quote ids are directly usable for tracing
- automated test evidence for lifecycle derivation and dashboard rendering
- automated test evidence for negative semantic invariants, especially `submitted != completed`
## Failure conditions
- `Actionable` still appears in the dashboard
- the operator still cannot distinguish strategy approval from execution submission
- non-trade rows still lack a decisive reason
- quote ids remain hidden or non-copyable
- lifecycle labels are only cosmetic and not backed by durable repo-owned state
- the repo still uses `trade` or `asset delta` language for mere submission evidence
- tests still encode the old overclaiming semantics
## Current real before this turn
- strategy decisions are stored durably
- execution results are stored durably
- command ids, decision ids, and quote ids already exist in the durable path
- the operator dashboard already serves recent decisions and execution-adjacent state
## Deliberately not built by this proof
- full venue settlement attribution for all historic trades
- generalized quote analytics beyond lifecycle explanation
- multi-venue lifecycle harmonization
## Prevention requirements for this proof
- Add a truth-review checklist to the implementation work:
- what exact durable table or event backs this label?
- what is the strongest claim the evidence supports?
- what would make this wording false?
- what negative regression test prevents that overclaim from returning?
- Separate lifecycle derivation from summary metrics so summaries are computed from lifecycle states rather than raw convenience queries.
No approved implementation proof is active yet.

View file

@ -0,0 +1,306 @@
# 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?

View file

@ -0,0 +1,200 @@
# Implementation Proof: quote lifecycle truth and execution explanation
Status: open
Opened: 2026-04-09
## Target outcome
This turn proves that `unrip` can explain every quote outcome in operator-visible, durable terms instead of forcing the operator to infer meaning from ambiguous labels.
The concrete target is the live NEAR Intents BTC/EURe system:
- each quote row must expose its current lifecycle state
- each non-trade outcome must expose the decisive reason code
- execution submission must be distinguishable from strategy approval
- blocked, rejected, submitted, failed, and not-filled paths must be visibly different
- quote identifiers must be directly usable by operators for tracing and support
- operator-facing labels must not overclaim beyond the durable evidence actually stored
## Why this is a meaningful architecture test
The current operator surface still fails a core thesis requirement:
- the Strategy page shows `Actionable`, which does not tell the operator whether a trade was actually submitted
- an operator looking at one quote cannot answer, at a glance, why it did or did not become a trade
- quote ids are hidden behind truncation with no direct copy affordance
- execution truth exists durably in PostgreSQL, but the UI does not surface the lifecycle coherently
That is not just a copy problem. It is an observability gap in the trading product itself. If the system cannot explain a quote outcome precisely, execution is outrunning observability.
The immediate trigger for this turn is a real semantic failure:
- the dashboard treated `trade_execution_results.status = submitted` as a successful trade
- recent submitted quote terms were rendered as if they were realized asset deltas
- tests passed because that wrong assumption had been encoded into the test suite itself
This turn must therefore fix both the UI and the conditions that allowed the mistake through.
## Hypothesis
`unrip` becomes more trustworthy if quote handling is modeled and rendered as an explicit lifecycle instead of a single strategy verdict:
- strategy evaluation is only one stage in the lifecycle
- executor acceptance or rejection must be first-class state
- venue submission and downstream outcome must be represented separately
- durable reason codes must drive operator labels
- forbidden labels that collapse multiple meanings, especially `Actionable`, must be removed
The turn passes only if an operator can inspect a quote and immediately understand whether it was filtered, rejected, blocked, submitted, failed, awaiting venue outcome, not filled, or completed, and why.
## Scope
- [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.
- Cover the live path from:
- raw or normalized quote observation
- strategy decision
- execute-trade command emission
- executor result
- solver relay or venue outcome where available
- Update the dashboard surfaces that currently expose quote or strategy truth:
- Strategy page
- any quote or recent-decision tables tied to the active pair
- quote-id presentation and operator trace affordances
## Assumptions
- The existing durable stores already contain enough information for at least the current live path through strategy decision and executor result.
- Some downstream venue-outcome states may still be partially fake or unavailable for older rows; if so, the UI must say that plainly rather than implying more certainty.
- The immediate turn should prioritize truthful lifecycle explanation over broader analytics such as markout or long-window outcome attribution.
- The prevention strategy must be implemented in repo code and tests rather than left to reviewer judgment alone.
## Turn-shaping rules
- `Actionable` is forbidden as an operator-facing state or label.
- Operator-facing labels must not overstate event certainty.
- Terms such as `trade`, `success`, `filled`, `completed`, `profit`, and `asset delta` are forbidden unless backed by a durable event explicitly representing that fact.
- Do not add a second analytics product. Stay focused on per-quote lifecycle truth for the live active pair.
- Do not invent lifecycle states that cannot be backed by durable repo-owned evidence.
- If a state transition is inferred rather than durably observed, the UI must make that distinction explicit.
- Prefer a small, explicit state machine over a long list of loosely related badges.
## Non-goals
- No new venue integrations.
- No broad historical markout analytics turn.
- No new execution automation or risk widening.
- No redesign of the entire dashboard visual system beyond what is needed to make the quote lifecycle understandable.
## Required operator behavior
### Lifecycle truth
For each visible quote or decision row, the operator must be able to identify the current lifecycle state from a bounded set such as:
- `Filtered`
- `Rejected by strategy`
- `Blocked before submit`
- `Submission failed`
- `Submitted`
- `Awaiting venue outcome`
- `Not filled` or equivalent final non-fill state, if durable evidence exists
- `Completed` or equivalent successful terminal state, if durable evidence exists
Exact labels may vary, but they must be specific and mutually meaningful.
The repo must adopt a hard evidence-state vocabulary for this turn. At minimum:
- `observed`
- `evaluated`
- `command_emitted`
- `rejected`
- `blocked`
- `submitted`
- `failed`
- `awaiting_outcome`
- `completed`
No operator surface may collapse these into softer or stronger claims.
### Reason truth
Each non-terminal or terminal non-trade state must expose a clear decisive reason, such as:
- unsupported pair
- below edge threshold
- inventory unavailable
- executor disarmed
- executor paused
- submission failed
- venue timeout
- quote expired
If the decisive reason is not known, the surface must say that plainly instead of inventing confidence.
### Traceability
For each quote row the operator must be able to access:
- quote id in a non-hidden, copyable form
- decision id where present
- command id where present
- execution result where present
- pair and direction
The operator must be able to reason from a single row without manually cross-correlating multiple pages.
### UI language
The UI must not render `Actionable`.
Any replacement label must answer a concrete operator question, such as:
- did strategy approve this?
- was a command emitted?
- was the command blocked?
- was it submitted?
- did it fail?
### Semantic invariants
The implementation and tests must enforce at least these invariants:
- `submitted` is not `completed`
- `submitted` is not a realized asset delta
- executor-side blocking is not strategy rejection
- stronger labels must not be rendered from weaker evidence
These invariants are proof-critical, not optional cleanup.
## Definition of done
- `Actionable` is removed from operator-facing dashboard surfaces.
- A durable quote lifecycle model exists in repo-owned code and is used by the dashboard.
- At least the current live quote path through strategy decision and executor result is rendered coherently per quote.
- The operator can tell, from one row, why a recent quote did or did not turn into a submitted trade.
- Quote ids are copyable and clearly visible enough for tracing.
- overloaded backend and UI names that imply stronger certainty than the evidence supports are removed or renamed
- Regression tests cover at least:
- strategy-approved but executor-disarmed rows
- submitted rows
- forbidden ambiguous label removal
- forbidden semantic overclaims such as treating `submitted` as `completed`
For this turn to close with status `passed`, the specific operator question:
`Why did this quote not trade?`
must be answerable directly from the dashboard for recent rows without needing manual database inspection.
## Validation evidence required
- direct UI or bootstrap evidence that recent rows show explicit lifecycle states instead of `Actionable`
- direct evidence that a strategy-approved but executor-disarmed row renders as blocked or rejected with reason
- direct evidence that a submitted row renders as submitted
- direct evidence that quote ids are directly usable for tracing
- automated test evidence for lifecycle derivation and dashboard rendering
- automated test evidence for negative semantic invariants, especially `submitted != completed`
## Failure conditions
- `Actionable` still appears in the dashboard
- the operator still cannot distinguish strategy approval from execution submission
- non-trade rows still lack a decisive reason
- quote ids remain hidden or non-copyable
- lifecycle labels are only cosmetic and not backed by durable repo-owned state
- the repo still uses `trade` or `asset delta` language for mere submission evidence
- tests still encode the old overclaiming semantics
## Current real before this turn
- strategy decisions are stored durably
- execution results are stored durably
- command ids, decision ids, and quote ids already exist in the durable path
- the operator dashboard already serves recent decisions and execution-adjacent state
## Deliberately not built by this proof
- full venue settlement attribution for all historic trades
- generalized quote analytics beyond lifecycle explanation
- multi-venue lifecycle harmonization
## Prevention requirements for this proof
- Add a truth-review checklist to the implementation work:
- what exact durable table or event backs this label?
- what is the strongest claim the evidence supports?
- what would make this wording false?
- what negative regression test prevents that overclaim from returning?
- Separate lifecycle derivation from summary metrics so summaries are computed from lifecycle states rather than raw convenience queries.