Files
jarvis/docs/KICKOFF_PROMPT.md
Robin Kutesa 27d0e22767 Initial commit: Jarvis iOS app
SwiftUI client for a self-hosted RSS news-correlation platform: signal feed,
story detail, article reader, feed manager, and LAN⇄Tailscale connectivity.
Project generated from project.yml via XcodeGen.

Includes CI build matrix (macOS 14/15 × Debug/Release), issue templates,
backlog, and API/backend handoff docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 18:04:59 +03:00

148 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Jarvis iOS — Claude Code Kickoff Prompt
Paste this entire prompt into Claude Code to begin.
---
## Prompt
You are building **Jarvis** — a SwiftUI iOS app that connects to a self-hosted RSS news correlation platform and displays stories ranked by signal score.
Read `CLAUDE.md` before writing a single line of code. It contains every architecture decision already made. Do not deviate from it.
---
## Your job
Build the remaining SwiftUI views. The networking layer, models, stores, and onboarding screen are already written. You are building the UI on top of them.
### Views to build
1. **SignalFeedView** — the home screen
- Wordmark `Jarvis` with `J` in #FF5C00, rest white, weight 800, kerning -2
- Live/Offline connection indicator (driven by `WebSocketManager.shared.connectionState`)
- Sync bar: "Synced X min ago · N cached" when live, "Last synced X min ago" when offline
- Topic filter pills: All · Finance · Tech · Politics · Africa (calls `store.setTopic()`)
- Column headers: Story / Signal ↓
- List of `StoryRowView` sorted by `signalScore` descending
- Pull to refresh calls `store.loadStories(refresh: true)`
- Infinite scroll: call `store.loadMore()` when last row appears
- Tap row → navigate to `StoryDetailView`
2. **StoryRowView** — one row in the signal feed
- Left edge stripe: 3pt wide, color opacity tied to signal score (97 = #FF5C00, fades to #1a1a1a at 0)
- Story title, dimmed proportionally to score (97 = white, 20 = #444)
- Signal score number, same fade behaviour
- Source count, topic, time ago in monospaced font
- Source chips (first 2 named, rest "+N")
- Small green dot if story is fully cached offline
3. **StoryDetailView** — full story
- Back button "Signal feed"
- Category label + signal score pill (e.g. "Finance · 97 signal")
- Large headline, weight 800
- Source chips
- "All N articles cached · available offline" line if cached
- Consensus block: orange left border, dark orange background
- Conflicting block: dark red left border, dark red background (only if `conflict != nil`)
- Coverage timeline: chronological list of `TimelineEntry`, first item has orange node
- Tapping a timeline entry → navigate to `ArticleReaderView`
4. **ArticleReaderView** — full article
- Back button showing parent story headline (truncated to 30 chars)
- Source pill in #FF5C00, timestamp in monospaced
- Cached badge inline (green, only if article is in SwiftData)
- Large headline weight 800
- Hero image placeholder (AsyncImage with grey fallback)
- Body text, #4a4a4a, size 16, line spacing 1.65
- Divider then "More from this cluster" section
- 23 other articles from same story as tappable rows
5. **FeedManagerView** — feed health and management
- Wordmark `Jarvis` header + "+ Add feed" button
- Platform connection card: server URL, live/offline dot (from `WebSocketManager`)
- Search bar to filter feed list
- Two sections: "Healthy" and "Needs attention"
- Each row: RSS icon, feed name, poll interval + article count, health dot
- Health dot: green (#2a5a2a) = active, amber (#AA6600) = failing with retry countdown, red (#5a1a1a) = dead
- Swipe to delete calls `APIClient.shared.deleteFeed(id:)`
- "+ Add feed" sheet: URL input + name field + confirm button
---
## Design rules — non-negotiable
- Background: `Color.black` / `#000000` everywhere
- Accent: `Color("KisaniOrange")` = `#FF5C00`
- Surface cards: `#111111`, `#1A1A1A`
- All screens: `.preferredColorScheme(.dark)`
- Font weight: 800 for headlines, 700 for labels, 400 for body — never use 600
- Monospaced font for: timestamps, signal scores, server URLs, feed metadata
- No gradients. No shadows. Flat surfaces only.
- Minimum tap target: 44pt
- Signal score stripe and text both fade: score 80100 = full orange, 5079 = dimmed orange, 2049 = dark brown, 019 = near invisible
---
## Data flow rules — non-negotiable
- Views never call `APIClient` directly — always go through `StoryStore` or a dedicated `@StateObject` ViewModel
- Signal scores are server-computed — never calculate or modify them in the UI
- WebSocket events trigger REST refetches — the store handles this already
- SwiftData (`CachedStory`, `CachedArticle`) is the offline source — query it when the server is unreachable
- Use `@EnvironmentObject var store: StoryStore` in views that need stories
- Use `@EnvironmentObject var ws: WebSocketManager` for connection state
---
## Xcode project setup
1. Create new Xcode project: iOS App, named **Jarvis**, Swift, SwiftUI
2. Set minimum deployment target: **iOS 17.0** (required for SwiftData)
3. Add all provided `.swift` files to the project
4. In `Assets.xcassets` add a new Color Set named `KisaniOrange`:
- Any / Dark: `#FF5C00`
5. Add `Assets.xcassets` Color Set `AppBackground`: `#000000`
6. The SwiftData model container is already configured in `JarvisApp.swift`
---
## File structure to produce
```
Jarvis/
Views/
Home/
SignalFeedView.swift
StoryRowView.swift
Story/
StoryDetailView.swift
Reader/
ArticleReaderView.swift
Feeds/
FeedManagerView.swift
AddFeedSheet.swift
Shared/
SignalStripe.swift ← reusable 3pt stripe component
SourceChips.swift ← reusable chips row
CachedBadge.swift ← reusable green cached indicator
ConnectionBanner.swift ← offline warning banner
```
---
## Start here
1. Read `CLAUDE.md`
2. Create the Xcode project and add all existing files
3. Build `SignalStripe.swift`, `SourceChips.swift`, `CachedBadge.swift` — shared components used by multiple views
4. Build `StoryRowView.swift`
5. Build `SignalFeedView.swift`
6. Build `StoryDetailView.swift`
7. Build `ArticleReaderView.swift`
8. Build `FeedManagerView.swift` + `AddFeedSheet.swift`
9. Run on simulator, fix layout issues, verify dark mode renders correctly
10. Confirm WebSocket connection state drives the live/offline indicator on the home screen
Do not skip steps. Do not simplify the signal stripe fade logic. Do not use placeholder colours — every hex value is specified above.