Problem Statement
After the authoritative chain-sync settlement implementation (ADR 0005), PUT trades that are resolved as ASSIGNED on-chain cause holding lot cards to appear in the UI. These ASSIGNED-derived lot cards have no edit or delete controls — the edit button is gated behind a check that only passes for manually-entered HOLDING trades, and no delete button exists on any holding card at all.
This leaves the user unable to manage holding cards that were created via assignment. Concretely: some ASSIGNED lots appeared for positions the user has since sold on the spot market. With no delete control, these phantom lots are stuck in the UI indefinitely.
Additionally, when a chain-imported trade is deleted, its txHash must remain in `hw_synced_v1` so chain sync does not re-import it on the next sync. This behaviour is currently incidental (deleteTrade does not touch the synced set) but undocumented.
Solution
Add an edit button and a delete button to every holding lot card, regardless of whether the underlying lot was opened by a manual HOLDING trade or by an ASSIGNED PUT outcome. Both buttons act on the source trade record for the lot. Deletion retains the txHash in the synced set so chain sync cannot re-import the deleted trade.
Document the txHash retention behaviour as ADR 0006 so future developers understand the invariant.
User Stories
- As a wheel trader, I want an edit button on every holding card, so that I can correct the cost basis, size, or notes of any lot regardless of how it was opened.
- As a wheel trader, I want a delete button on every holding card, so that I can remove phantom holdings that appeared from incorrect or stale chain-sync assignments.
- As a wheel trader, I want a deleted chain-imported trade to stay excluded from future syncs, so that deleting a phantom holding does not result in it reappearing after the next chain sync.
- As a wheel trader, I want the delete action to show a toast confirmation, so that I know the deletion succeeded.
- As a wheel trader, I want the edit modal to open on the underlying PUT trade when editing an ASSIGNED-derived lot card, so that I can correct the strike (cost basis), size, date, or notes on the originating trade.
- As a wheel trader, I want the edit and delete controls to appear consistently for all lot types, so that I do not need to know or remember which lots are manual versus chain-imported.
Implementation Decisions
-
Edit button gate removed: The edit button on holding cards is currently only rendered when the underlying trade is type === 'HOLDING'. This gate should be removed so the edit button appears for all lot types. The edit modal already supports all fields relevant to PUT trades (ef-date, ef-strike, ef-size, ef-premium, ef-outcome, ef-notes).
-
Delete button added to holding cards: No holding card currently has a delete button. A delete button should be added alongside the edit button on every card, calling deleteTrade on lot.tradeIds[0] (the source trade ID of the lot).
-
Delete behaviour for ASSIGNED-derived lots: Deleting the source PUT trade is the correct action (option A). Reverting to EXPIRED would introduce a false record. The premium history is acceptable collateral loss — it is the user's explicit choice to remove the record.
-
txHash retention invariant: deleteTrade must not remove the deleted trade's txHash from hw_synced_v1. This is already the current behaviour (deleteTrade does not touch the synced set), but it must be preserved as an explicit invariant and documented in a new ADR (0006). The synced set is the sole guard against re-import; removing a txHash from it on delete would cause re-import on the next chain sync.
-
Edge case acknowledged, out of scope: If the user deletes every chain-imported trade, the boot migration in 17-boot.js will detect no chain trades and clear the synced set, causing full re-import on next boot. This edge case is not addressed in this PRD — the user will always have other chain-imported trades remaining.
-
ADR 0006: Document that deleteTrade intentionally leaves txHashes in hw_synced_v1. Cover: the invariant, the reason (re-import prevention), and the edge case of the boot migration.
-
Modules changed:
06-render-table.js — remove isManualHolding gate; add delete button to holding card HTML
docs/adr/0006-txhash-retention-on-delete.md — new ADR
Testing Decisions
- Good tests verify external behaviour, not implementation details: assert on what the user sees (button presence) and what the data looks like after an action (trade removed, synced set unchanged), not which internal function was called.
- Unit tests are not warranted for this change — it is a UI rendering adjustment plus a prose ADR. The logic (deleteTrade, synced set) already exists and is unchanged.
- Integration tests (jsdom,
test/integration/) could assert that after deleteTrade(id) is called on a chain-imported trade, the trade is absent from trades and the txHash is still present in hw_synced_v1. Prior art: test/integration/chain-sync-outcomes.test.js drives globals and asserts on localStorage.
- Whether to write the integration test is left to the implementer's judgment — the behaviour is already correct, so the test value is in locking it as an explicit invariant.
Out of Scope
- A new EXITED outcome for recording a voluntary spot sale at a known price. The user sold at prices they no longer recall; a proper EXITED outcome with P&L tracking is a future enhancement.
- A revert-to-EXPIRED option on ASSIGNED trades (would introduce a false history record).
- UI to view or filter lots by origin type (manual vs chain-imported).
- Any change to the cloud sync path (
/api/sync) — synced set management is local-only.
Further Notes
- The txHash retention invariant is subtle: it appears to be a bug (deleteTrade seems to forget to clean up) but is actually correct behaviour. The ADR exists to prevent a future developer from "fixing" it.
- The boot migration at
17-boot.js lines 10–17 is a pre-existing footgun for the full-delete edge case. It was written to recover from an old bug where chain trades were not persisted. It should be noted in ADR 0006 but not changed in this PR.
Problem Statement
After the authoritative chain-sync settlement implementation (ADR 0005), PUT trades that are resolved as ASSIGNED on-chain cause holding lot cards to appear in the UI. These ASSIGNED-derived lot cards have no edit or delete controls — the edit button is gated behind a check that only passes for manually-entered HOLDING trades, and no delete button exists on any holding card at all.
This leaves the user unable to manage holding cards that were created via assignment. Concretely: some ASSIGNED lots appeared for positions the user has since sold on the spot market. With no delete control, these phantom lots are stuck in the UI indefinitely.
Additionally, when a chain-imported trade is deleted, its txHash must remain in `hw_synced_v1` so chain sync does not re-import it on the next sync. This behaviour is currently incidental (deleteTrade does not touch the synced set) but undocumented.
Solution
Add an edit button and a delete button to every holding lot card, regardless of whether the underlying lot was opened by a manual HOLDING trade or by an ASSIGNED PUT outcome. Both buttons act on the source trade record for the lot. Deletion retains the txHash in the synced set so chain sync cannot re-import the deleted trade.
Document the txHash retention behaviour as ADR 0006 so future developers understand the invariant.
User Stories
Implementation Decisions
Edit button gate removed: The edit button on holding cards is currently only rendered when the underlying trade is
type === 'HOLDING'. This gate should be removed so the edit button appears for all lot types. The edit modal already supports all fields relevant to PUT trades (ef-date,ef-strike,ef-size,ef-premium,ef-outcome,ef-notes).Delete button added to holding cards: No holding card currently has a delete button. A delete button should be added alongside the edit button on every card, calling
deleteTradeonlot.tradeIds[0](the source trade ID of the lot).Delete behaviour for ASSIGNED-derived lots: Deleting the source PUT trade is the correct action (option A). Reverting to EXPIRED would introduce a false record. The premium history is acceptable collateral loss — it is the user's explicit choice to remove the record.
txHash retention invariant:
deleteTrademust not remove the deleted trade's txHash fromhw_synced_v1. This is already the current behaviour (deleteTrade does not touch the synced set), but it must be preserved as an explicit invariant and documented in a new ADR (0006). The synced set is the sole guard against re-import; removing a txHash from it on delete would cause re-import on the next chain sync.Edge case acknowledged, out of scope: If the user deletes every chain-imported trade, the boot migration in
17-boot.jswill detect no chain trades and clear the synced set, causing full re-import on next boot. This edge case is not addressed in this PRD — the user will always have other chain-imported trades remaining.ADR 0006: Document that
deleteTradeintentionally leaves txHashes inhw_synced_v1. Cover: the invariant, the reason (re-import prevention), and the edge case of the boot migration.Modules changed:
06-render-table.js— removeisManualHoldinggate; add delete button to holding card HTMLdocs/adr/0006-txhash-retention-on-delete.md— new ADRTesting Decisions
test/integration/) could assert that afterdeleteTrade(id)is called on a chain-imported trade, the trade is absent fromtradesand the txHash is still present inhw_synced_v1. Prior art:test/integration/chain-sync-outcomes.test.jsdrives globals and asserts on localStorage.Out of Scope
/api/sync) — synced set management is local-only.Further Notes
17-boot.jslines 10–17 is a pre-existing footgun for the full-delete edge case. It was written to recover from an old bug where chain trades were not persisted. It should be noted in ADR 0006 but not changed in this PR.