feat(ui): importanceScore relevance sort + phase badges in NewsPanel#2608
feat(ui): importanceScore relevance sort + phase badges in NewsPanel#2608
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR wires up three new proto scoring fields ( Key changes:
Issues found:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Proto as ProtoNewsItem (wire)
participant DL as data-loader.ts<br/>protoItemToNewsItem()
participant NI as NewsItem (client)
participant NP as NewsPanel<br/>renderFlat()
participant UI as DOM
Proto->>DL: importanceScore, corroborationCount, storyPhase
Note over DL: Type-cast IIFE<br/>filters STORY_PHASE_UNSPECIFIED<br/>⚠ falsy check drops score=0
DL->>NI: importanceScore?, corroborationCount?, storyPhase?
NI->>NP: items[]
Note over NP: sortMode === 'relevance'?<br/>Sort: importanceScore desc<br/>→ threat level desc<br/>→ recency desc
NP->>UI: Sorted HTML list
Note over UI: storyPhase=BREAKING → pulsing orange badge<br/>storyPhase=DEVELOPING → yellow badge
Reviews (1): Last reviewed commit: "revert(gen): restore service_client.ts t..." | Re-trigger Greptile |
src/app/data-loader.ts
Outdated
| ...(ext.importanceScore ? { importanceScore: ext.importanceScore } : {}), | ||
| ...(ext.corroborationCount ? { corroborationCount: ext.corroborationCount } : {}), |
There was a problem hiding this comment.
Falsy check silently drops zero-valued scores
Both importanceScore and corroborationCount use a truthiness check (ext.field ? ...), which means a valid value of 0 is treated as absent and dropped from the mapped NewsItem.
For importanceScore, the downstream sort comparator uses a.importanceScore ?? 0, so the ordering is the same whether the field is 0 or undefined — but the distinction between "scored at 0" and "unscored" is lost.
For corroborationCount, this is more meaningful: 0 corroborations is semantically different from "corroboration not computed", and a future consumer could make wrong decisions based on the missing value.
| ...(ext.importanceScore ? { importanceScore: ext.importanceScore } : {}), | |
| ...(ext.corroborationCount ? { corroborationCount: ext.corroborationCount } : {}), | |
| ...(ext.importanceScore !== undefined ? { importanceScore: ext.importanceScore } : {}), | |
| ...(ext.corroborationCount !== undefined ? { corroborationCount: ext.corroborationCount } : {}), |
src/types/index.ts
Outdated
| source: 'keyword' | 'ml' | 'llm'; | ||
| } | ||
|
|
||
| export type StoryPhase = 'STORY_PHASE_UNSPECIFIED' | 'STORY_PHASE_BREAKING' | 'STORY_PHASE_DEVELOPING' | 'STORY_PHASE_SUSTAINED' | 'STORY_PHASE_FADING'; |
There was a problem hiding this comment.
STORY_PHASE_UNSPECIFIED included in type but never stored on NewsItem
StoryPhase includes 'STORY_PHASE_UNSPECIFIED' as a valid member. However, data-loader.ts explicitly filters this value out before assigning storyPhase to a NewsItem, meaning a NewsItem will never actually carry STORY_PHASE_UNSPECIFIED at runtime. The type on the storyPhase field of NewsItem therefore misleads future callers into thinking they need to handle that sentinel.
Consider narrowing the field type to exclude the sentinel:
| export type StoryPhase = 'STORY_PHASE_UNSPECIFIED' | 'STORY_PHASE_BREAKING' | 'STORY_PHASE_DEVELOPING' | 'STORY_PHASE_SUSTAINED' | 'STORY_PHASE_FADING'; | |
| export type StoryPhase = 'STORY_PHASE_UNSPECIFIED' | 'STORY_PHASE_BREAKING' | 'STORY_PHASE_DEVELOPING' | 'STORY_PHASE_SUSTAINED' | 'STORY_PHASE_FADING'; | |
| export type ActiveStoryPhase = Exclude<StoryPhase, 'STORY_PHASE_UNSPECIFIED'>; |
Then use ActiveStoryPhase for the storyPhase field on NewsItem (line 112), keeping the full StoryPhase union available for proto-layer casting in data-loader.ts.
…dges All other PR changes (types, data-loader cast, NewsPanel) are now in main via E3 (#2620) and E1 (#2621). Only the CSS for .phase-badge.breaking/.developing/.sustained was missing. Class names corrected to match NewsPanel.ts output (phase-badge + modifier vs the original phase-breaking/phase-developing selectors).
9d78894 to
828277e
Compare
Summary
src/styles/main.css: adds.phase-badge,.phase-badge.breaking(pulsing orange),.phase-badge.developing(yellow),.phase-badge.sustained(slate) CSS classes used by NewsPanel.All other feature work landed in main via prerequisite PRs:
importanceScore+corroborationCounttypes → PR feat(scoring): composite importance score + story tracking infrastructure #2604 (merged)storyMeta?: StoryMetaonNewsItem,StoryPhaseclient type → PR feat(e3): story persistence tracking #2620 E3 (merged)data-loader.tsstoryMeta mapping withPROTO_TO_CLIENT_PHASE→ PR feat(e3): story persistence tracking #2620 E3 (merged)NewsPanel.tsrelevance sort + phase badge rendering (item.storyMeta?.phase) → PR feat(e3): story persistence tracking #2620 E3 (merged)This PR was rebased onto main after those merges; the CSS was the only missing piece.
Test plan
npm run typecheckpassesnpm run lintpasses (no errors)Post-Deploy Monitoring & Validation
🤖 Generated with Claude Sonnet 4.6 via Claude Code + Compound Engineering v2.49.0
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com