docs(33): capture phase context
This commit is contained in:
125
.planning/phases/33-currency-system/33-CONTEXT.md
Normal file
125
.planning/phases/33-currency-system/33-CONTEXT.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Phase 33: Currency System - Context
|
||||
|
||||
**Gathered:** 2026-04-13
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Replace the placeholder currency symbol swap with a real market-aware pricing system. Users select their market (tied to currency), see market-specific UVP/MSRP prices, community "what I paid" data filtered by locale, and approximate conversions as a labeled fallback when local prices don't exist. Includes community price submissions, candidate research prices, and purchase date tracking.
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Data Model & Source Currency
|
||||
- **D-01:** Prices are market-specific, NOT simple exchange rate conversions. A €2,000 bike in Germany may be £2,200 in the UK and $3,100 in the US — these are independent market prices
|
||||
- **D-02:** Catalog items (globalItems) can have multiple market prices — UVP/MSRP stored per market/currency. Start with EU/DE prices as the primary market
|
||||
- **D-03:** Personal items store "what I paid" in the user's currency, auto-tagged with their market
|
||||
- **D-04:** Community price data is locale-tagged — German users see aggregate data from German submissions, UK users from UK submissions
|
||||
- **D-05:** Price submissions tied to collection ownership — you can only report a price for items you have in your collection (you actually bought it). Captured automatically when adding to collection
|
||||
- **D-06:** Candidate items in research threads can also have a "price I found it for" field — research-quality price data during comparison, valuable even before purchase
|
||||
- **D-07:** Both "what I paid" and "price I found it for" include a date field (when bought / when found) for temporal context and data aging
|
||||
|
||||
### Conversion Strategy
|
||||
- **D-08:** Exchange rates sourced from ECB via frankfurter.app — free, daily updates, no API key, covers EUR/USD/GBP/JPY/CAD/AUD and ~30 more
|
||||
- **D-09:** Server-side conversion — server fetches rates daily, caches them, returns converted prices in API responses. MCP/API consumers also get conversion
|
||||
- **D-10:** Conversion is a FALLBACK, not the default — when a local market price exists, show that. Only convert when no local price is available
|
||||
- **D-11:** Converted prices are always clearly labeled as approximate — never presented as real market prices
|
||||
|
||||
### User Experience & Display
|
||||
- **D-12:** Currency picker = market picker. Selecting EUR implies EU market, GBP implies UK market, USD implies US market. Simplifies settings — one choice drives both currency display and market data filtering
|
||||
- **D-13:** Auto-suggestion on first visit based on browser locale and IP geolocation. User can change anytime in settings
|
||||
- **D-14:** Converted prices use dual display format: `€2,000 (~£1,720)` — source price prominent, converted in parentheses. Makes it clear what's real vs. approximate
|
||||
- **D-15:** Global setting to auto-activate conversion (show converted prices by default) OR per-price toggle. User controls whether they see conversions automatically
|
||||
- **D-16:** Existing currency picker in settings page evolves to be the market/currency selector with the auto-suggestion behavior
|
||||
|
||||
### Catalog & Sharing Implications
|
||||
- **D-17:** Catalog detail page: user's market UVP shown prominently + community average for their market. Collapsible "Other markets" section shows prices from other regions
|
||||
- **D-18:** Shared setups (card level): show viewer's market MSRP if available, otherwise converted price
|
||||
- **D-19:** Shared setups (detail level): full breakdown — owner's actual price, MSRP per market, community averages, conversion info
|
||||
- **D-20:** Comparison tables (thread candidates): normalize all candidates to user's currency for apples-to-apples comparison. Converted prices marked with ~. Users can add their own researched price via "price I found it for"
|
||||
- **D-21:** Community price aggregation shows per-market stats: "Users in DE typically pay €1,600 (12 reports)"
|
||||
|
||||
### Claude's Discretion
|
||||
- Rate caching strategy (how long to cache, fallback when ECB is unreachable)
|
||||
- Schema design for market prices table (separate table vs. JSONB on globalItems)
|
||||
- Aggregation queries for community price stats (median vs. average, minimum report count threshold)
|
||||
- How to handle the transition from the current simple `priceCents` integer to the richer model
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
No external specs — requirements fully captured in decisions above.
|
||||
|
||||
### Existing Implementation (to be replaced/extended)
|
||||
- `src/client/lib/formatters.ts` — Current `formatPrice()` with symbol-only swap, `Currency` type (lines 24-45)
|
||||
- `src/client/hooks/useCurrency.ts` — Current `useCurrency()` hook reading from settings (6 currencies)
|
||||
- `src/client/hooks/useFormatters.ts` — `useFormatters()` hook composing weight + price formatters
|
||||
- `src/client/routes/settings.tsx` — Currency pill picker UI (lines 254-280)
|
||||
- `src/db/schema.ts` — `priceCents: integer` on items, candidates, globalItems
|
||||
- `src/shared/schemas.ts` — Zod schemas with `priceCents` fields
|
||||
- `src/server/services/setup.service.ts` — Setup totals computed via SQL SUM on price_cents
|
||||
- `src/server/services/discovery.service.ts` — Discovery feed price queries
|
||||
- `src/client/components/ComparisonTable.tsx` — Candidate price comparison display
|
||||
- `src/client/lib/impactDeltas.ts` — Price delta calculations for setup impact preview
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `useFormatters()` hook: Central price formatting — all price displays go through this. Extend rather than replace
|
||||
- `useCurrency()` hook: Already reads currency preference from settings DB — extend to also imply market
|
||||
- Settings page currency picker: Existing UI to evolve into market/currency selector
|
||||
- `formatPrice()`: Needs to support dual display format and conversion annotations
|
||||
|
||||
### Established Patterns
|
||||
- Prices stored as integer cents (`priceCents`) throughout the codebase (items, candidates, globalItems, setup aggregates)
|
||||
- COALESCE merge for reference items — global base + personal overlay. Currency data needs to work with this pattern
|
||||
- SQL aggregates for setup totals — computed on read, not stored. Currency conversion needs to integrate with these queries
|
||||
- Settings stored via `useSetting()` hook / settings table — currency/market preference fits this pattern
|
||||
|
||||
### Integration Points
|
||||
- `src/db/schema.ts`: New market prices table, modify items/candidates for source currency + date fields
|
||||
- `src/server/services/`: New currency service for rate fetching, caching, conversion
|
||||
- `src/server/services/setup.service.ts`: Setup total queries need currency-aware aggregation
|
||||
- `src/server/services/discovery.service.ts`: Feed prices need market-awareness
|
||||
- `src/client/lib/formatters.ts`: Dual display format, conversion labeling
|
||||
- `src/client/hooks/useCurrency.ts`: Evolve to market-aware hook
|
||||
- `src/client/routes/settings.tsx`: Market/currency selector with auto-suggestion
|
||||
- `src/server/mcp/`: MCP tools need currency-aware price responses
|
||||
- `src/client/components/ComparisonTable.tsx`: Normalized currency display
|
||||
- `src/client/routes/global-items/$globalItemId.tsx`: Market prices + community data display
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- The existing currency picker (pill toggle in settings) becomes the market selector — same UI pattern but with market implications
|
||||
- Auto-suggestion uses browser locale first, IP geolocation as fallback — suggest on first visit, respect manual override
|
||||
- Community price display: "Users in DE typically pay €1,600 (12 reports)" — locale-filtered aggregation
|
||||
- Candidate "price I found it for" is research-quality data — valuable even pre-purchase, should be treated as community data too
|
||||
- Purchase date on price submissions enables data aging — prices from years ago are less relevant
|
||||
- Primary market is EU/DE — start seeding UVP data for European manufacturers
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
None — discussion stayed within phase scope.
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 33-currency-system*
|
||||
*Context gathered: 2026-04-13*
|
||||
138
.planning/phases/33-currency-system/33-DISCUSSION-LOG.md
Normal file
138
.planning/phases/33-currency-system/33-DISCUSSION-LOG.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Phase 33: Currency System - Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-04-13
|
||||
**Phase:** 33-Currency System
|
||||
**Areas discussed:** Data model & source currency, Conversion strategy, User experience & display, Catalog & sharing implications
|
||||
|
||||
---
|
||||
|
||||
## Data Model & Source Currency
|
||||
|
||||
### Core pricing model
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Per-item source currency | Add priceCurrency column alongside priceCents | |
|
||||
| Per-user base currency only | All prices assumed in user's currency | |
|
||||
| Everything stored as USD | Normalize to USD on entry | |
|
||||
| Market-specific pricing | Prices are market-specific, not converted. Different markets have different UVP/MSRP | ✓ |
|
||||
|
||||
**User's choice:** Market-specific pricing — not just exchange rate conversion. A €2,000 bike in Germany may be £2,200 in the UK because that's the UK market price, not a conversion.
|
||||
**Notes:** User emphasized that German UVP (MSRP) can be completely different from UK or US retail prices. This is a market reality, not a conversion problem.
|
||||
|
||||
### Community price data
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| On catalog detail page | "Report your price" button | |
|
||||
| During add-to-collection | Capture when adding item | |
|
||||
| Both (tied to ownership) | Only report for items you own, auto-captured from collection | ✓ |
|
||||
|
||||
**User's choice:** Both, but tied to collection ownership — you can only report prices for items you have. Prevents duplicates.
|
||||
**Notes:** User also identified that candidate "price I found it for" is research-quality data. Purchase date should be tracked for temporal context.
|
||||
|
||||
### Scope check
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Foundation layer | UVP display + conversion, community data later | |
|
||||
| Full system | Everything: market prices, community submissions, conversion | ✓ |
|
||||
| Let's scope it together | Walk through each piece | |
|
||||
|
||||
**User's choice:** Full system in Phase 33.
|
||||
|
||||
---
|
||||
|
||||
## Conversion Strategy
|
||||
|
||||
### Rate source
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Free API (ECB/frankfurter.app) | Daily rates, no API key, ~30 currencies | ✓ |
|
||||
| Paid API (Open Exchange Rates) | More currencies, intraday, ~$12/mo | |
|
||||
| You decide | Claude picks | |
|
||||
|
||||
**User's choice:** Free API — ECB via frankfurter.app
|
||||
|
||||
### Conversion location
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Server-side | Server fetches rates, returns converted prices | ✓ |
|
||||
| Client-side | Client converts locally | |
|
||||
| You decide | Claude picks | |
|
||||
|
||||
**User's choice:** Server-side
|
||||
|
||||
---
|
||||
|
||||
## User Experience & Display
|
||||
|
||||
### Market/locale detection
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Manual setting only | User picks market in settings | |
|
||||
| Auto-detect + manual override | Browser locale / IP, changeable | |
|
||||
| Tied to currency choice | Currency = market, with auto-suggestion | ✓ |
|
||||
|
||||
**User's choice:** Currency tied to market (EUR=EU, GBP=UK, USD=US) with auto-suggestion from browser locale and IP geolocation. Best of both worlds.
|
||||
|
||||
### Converted price display
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Prefix with ~ and muted style | ~£1,720 with tooltip | |
|
||||
| Dual display | €2,000 (~£1,720) — source prominent, converted in parens | ✓ |
|
||||
| You decide | Claude picks | |
|
||||
|
||||
**User's choice:** Dual display
|
||||
|
||||
---
|
||||
|
||||
## Catalog & Sharing Implications
|
||||
|
||||
### Catalog detail page
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| User's market first, others expandable | Local UVP + community avg prominent, other markets collapsible | ✓ |
|
||||
| All markets in a table | Price table showing all markets at once | |
|
||||
| You decide | Claude picks | |
|
||||
|
||||
**User's choice:** User's market first, others expandable
|
||||
|
||||
### Shared setup prices
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Owner's original prices | Show in owner's currency with conversion toggle | |
|
||||
| Viewer's market prices | Auto-convert to viewer's currency | |
|
||||
| Layered disclosure | Card: viewer's market MSRP. Detail: full breakdown including owner's price | ✓ |
|
||||
|
||||
**User's choice:** Layered — card shows viewer's market MSRP, detail page shows full breakdown (owner's price, all market MSRPs, community averages).
|
||||
|
||||
### Comparison table currencies
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Normalize to user's currency | All candidates in viewer's currency, marked as approximate | ✓ |
|
||||
| Source currency with tooltip | Each in source currency, hover for conversion | |
|
||||
| You decide | Claude picks | |
|
||||
|
||||
**User's choice:** Normalize — mixed currencies (EUR, USD, JPY, TRY) are useless for comparison. Rough converted price gives direction even if not exact. Users can add their own researched "price I found it for."
|
||||
|
||||
## Claude's Discretion
|
||||
|
||||
- Rate caching strategy
|
||||
- Schema design for market prices table
|
||||
- Community price aggregation approach
|
||||
- Transition from current simple priceCents model
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
None — discussion stayed within phase scope.
|
||||
Reference in New Issue
Block a user