diff --git a/plans/260620-coin-sell-insufficient-message/phase-01-update-sell-message-and-tests.md b/plans/260620-coin-sell-insufficient-message/phase-01-update-sell-message-and-tests.md new file mode 100644 index 0000000..596c4a8 --- /dev/null +++ b/plans/260620-coin-sell-insufficient-message/phase-01-update-sell-message-and-tests.md @@ -0,0 +1,68 @@ +--- +phase: 1 +title: Update sell message and tests +status: completed +priority: P2 +dependencies: [] +effort: 30-45m +--- + +# Phase 1: Update sell message and tests + +## Overview + +Improve `/coin_sell` insufficient-holdings replies so users see the failed sell amount and available-to-sell value in USD, with coin quantity and current price as supporting context. + +## Requirements + +- Functional: If user has some holdings but less than requested USD sell amount, reply: + - `Not enough to sell .` + - `Available to sell: ( @ ).` + - `Try or less.` +- Functional: If user has zero holdings, reply: + - `No available to sell.` + - `Try /coin_buy first.` +- Functional: Keep successful sell response unchanged. +- Non-functional: Preserve current portfolio mutation semantics and command argument contract. + +## Architecture + +`handleSell` already resolves the USD amount, fetches price, computes required coin quantity, and receives held quantity from `Portfolio.DeductAsset` on failure. Reuse that held quantity to format a user-facing reply through a small helper or localized branch inside `handleSell`. + +No new storage, price, command registration, or module boundary changes. + +## Related Code Files + +- Modify: `internal/modules/coin/handlers.go` — format improved insufficient sell reply. +- Modify: `internal/modules/coin/handlers_test.go` — assert partial-holding and zero-holding wording. +- Delete: none. +- Create: none. + +## Implementation Steps + +1. Add or inline a small formatter for insufficient sell replies. +2. In `handleSell`, keep `held` from `DeductAsset`. +3. If `held` normalizes to zero, return the zero-holdings message. +4. Otherwise compute `availableUSD := held * price.USD` and return the three-line available-to-sell message. +5. Update `TestHandleSellInsufficientCoin` to assert the zero-holdings copy. +6. Update `TestHandleSellInsufficientCoinWithHoldings` to assert requested USD, available USD, coin quantity, price, and retry hint; keep mutation assertion. +7. Run focused and full tests. + +## Success Criteria + +- [x] Partial-holdings insufficient sell message uses requested USD as primary value. +- [x] Partial-holdings insufficient sell message includes available-to-sell USD, coin quantity, and price. +- [x] Zero-holdings insufficient sell message does not show `$0.00 (0 ETH @ price)`. +- [x] Failed sell leaves portfolio unchanged. +- [x] `go test ./internal/modules/coin -count=1` passes. +- [x] `go test ./... -count=1` passes. + +## Risk Assessment + +- Risk: message includes USD cash-like wording and remains ambiguous. Mitigation: use `Available to sell`, not `have`. +- Risk: floating point precision leaks into text. Mitigation: use existing `FormatUSD` and `FormatCoinQty`. +- Risk: tests become too brittle around full text. Mitigation: assert meaningful substrings, not every newline. + +## Unresolved Questions + +None. diff --git a/plans/260620-coin-sell-insufficient-message/plan.md b/plans/260620-coin-sell-insufficient-message/plan.md new file mode 100644 index 0000000..9b43812 --- /dev/null +++ b/plans/260620-coin-sell-insufficient-message/plan.md @@ -0,0 +1,66 @@ +--- +title: Improve coin sell insufficient message +description: >- + Make /coin_sell insufficient-holdings replies explain available-to-sell USD + value, with coin quantity and price as context. +status: completed +priority: P2 +branch: main +tags: + - bugfix + - backend + - ux +blockedBy: [] +blocks: [] +created: '2026-06-20T02:12:55.097Z' +createdBy: 'ck:plan' +source: skill +--- + +# Improve coin sell insufficient message + +## Overview + +`/coin_sell ` sells a USD amount of crypto. When holdings are insufficient, the reply must stay in the user's USD framing while making clear the limiting balance is coin holdings, not USD cash. + +Recommended UX: + +```text +Not enough BTC to sell $600.00. +Available to sell: $500.00 (0.01 BTC @ $50,000.00). +Try $500.00 or less. +``` + +Zero holdings should use a clearer special case: + +```text +No ETH available to sell. +Try /coin_buy ETH first. +``` + +## Phases + +| Phase | Name | Status | +|-------|------|--------| +| 1 | [Update sell message and tests](./phase-01-update-sell-message-and-tests.md) | Completed | + +## Dependencies + +- No blocking active plan. Existing coin module plan `plans/260612-1005-coin-module-price-fallback/plan.md` is completed and only provides context. + +## Scope + +- In scope: sell insufficient-holdings message in `internal/modules/coin/handlers.go`; regression tests in `internal/modules/coin/handlers_test.go`. +- Out of scope: changing command arguments, portfolio math, price providers, command registration, storage schema, buy wording, docs. + +## Success Criteria + +- `/coin_sell 600 BTC` after holding only `$500` worth of BTC says not enough BTC to sell `$600`, shows `$500` available, includes `0.01 BTC @ $50,000.00`, and suggests `$500.00 or less`. +- `/coin_sell 10 ETH` with zero ETH says no ETH available to sell and suggests buying ETH first. +- Failed sell still does not mutate portfolio. +- Existing successful sell response remains unchanged. +- `go test ./internal/modules/coin -count=1` and `go test ./... -count=1` pass. + +## Unresolved Questions + +None.