City-card fallback reads now stop using stale METAR or official observations as strong live anchors. A stale observation no longer forces high/low revisions, and both backend and browser AI cache keys include the observation fingerprint so updated report times, receipt times, temperatures, or stale status invalidate old AI text.
Constraint: Cached city AI reads must not survive a material observation update
Rejected: Let stale METAR trigger observed-break revisions | stale reports can be older than the active temperature path
Confidence: high
Scope-risk: moderate
Tested: pytest tests/test_web_observability.py -q
Tested: npm run build
The prior label said the weather decision layer had no market price connected, which could be read as a broken market integration. The card now says weather-first read with market prices shown separately.
Constraint: Market price layer is already rendered below the weather decision band
Rejected: Keep 'no market price input' | accurate internally but misleading in user-facing Chinese copy
Confidence: high
Scope-risk: narrow
Tested: npm run build
Not-tested: Visual screenshot review
City AI can return a partially streamed JSON string when the provider truncates output. The fallback previously kept an unfinished clause such as '但TAF显示', which made the forecast explanation look broken even though earlier evidence was usable.
Constraint: Provider JSON can be truncated after useful fields have already streamed
Rejected: Drop all partial AI text | would lose valid METAR interpretation already returned before truncation
Confidence: high
Scope-risk: narrow
Tested: pytest city AI truncation regression tests
Tested: npm run build
Not-tested: Live DeepSeek provider response