`, time horizon, italicized `rationale_quote`, and a "▶ MM:SS" button calling `playerRef.current?.seekTo(video_timestamp_seconds)`.
+ - Transcript tab: scrollable list of segments; clicking a segment seeks the iframe to `Math.floor(start)`.
+ - Raw JSON tab: `{JSON.stringify(detail.analysis, null, 2)}`.
+- Disable Transcript tab if `transcript_available` is false.
+
+- [ ] **Step 1**: Implement.
+
+- [ ] **Step 2**: Build verification.
+
+- [ ] **Step 3**: Commit
+
+```bash
+git add dashboard/src/pages/meetKevin/VideoDetail.tsx
+git -c user.email=me@viktorbarzin.me commit -m "feat(dashboard): Meet Kevin video detail page (tabs + iframe + deep-links)"
+```
+
+---
+
+### Task 15: Stocks list + drill-down
+
+**Files:**
+- Create: `dashboard/src/pages/meetKevin/Stocks.tsx`
+- Create: `dashboard/src/pages/meetKevin/StockDetail.tsx`
+
+**`Stocks.tsx`:**
+- `useQuery` calls `meetKevinApi.listStocks` with 60s refetch.
+- Renders a table: Symbol (link to `/meet-kevin/stocks/`), Mentions, Last seen, Latest (ActionChip + percent), Avg conviction (ConvictionBar).
+- Empty state: "No analyses yet" card.
+
+**`StockDetail.tsx`:**
+- `useParams<{symbol: string}>()` uppercased.
+- Two `useQuery`s: `getStock(sym)` and `getStockTimeline(sym, "day")`.
+- Layout: large `$SYMBOL` header + back link, line chart of `net_action_score` over time (recharts), then chronological mention list. Each mention card links to its source `/meet-kevin/videos/`, shows date, ActionChip, time horizon, ConvictionBar, percent, video title, and italicized `rationale_quote`.
+
+- [ ] **Step 1**: Implement both pages.
+
+- [ ] **Step 2**: Build verification.
+
+- [ ] **Step 3**: Commit
+
+```bash
+git add dashboard/src/pages/meetKevin/Stocks.tsx dashboard/src/pages/meetKevin/StockDetail.tsx
+git -c user.email=me@viktorbarzin.me commit -m "feat(dashboard): Meet Kevin stocks list + per-ticker drill-down"
+```
+
+---
+
+### Task 16: Wire routes + sidebar
+
+**Files:**
+- Modify: `dashboard/src/App.tsx` — import the 5 page components and add 5 routes inside the protected `` block, BEFORE the catch-all redirect:
+
+```
+} />
+} />
+} />
+} />
+} />
+```
+
+- Modify: `dashboard/src/components/Layout.tsx` — append to `navItems`:
+
+```
+{ to: '/meet-kevin', label: 'Meet Kevin', icon: 'M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z' },
+```
+
+(Heroicon outline play-circle path — matches the video theme.)
+
+- [ ] **Step 1**: Apply both edits.
+
+- [ ] **Step 2**: Build verification + verify nav link renders.
+
+- [ ] **Step 3**: Commit
+
+```bash
+git add dashboard/src/App.tsx dashboard/src/components/Layout.tsx
+git -c user.email=me@viktorbarzin.me commit -m "feat(dashboard): wire Meet Kevin routes + sidebar entry"
+```
+
+---
+
+### Task 17: Manual QA against docker-compose
+
+**Verification only — no commit (unless smoke notes are added).**
+
+- [ ] **Step 1**: Boot infra: `docker compose up -d postgres redis`, then `python -m alembic upgrade head`.
+
+- [ ] **Step 2**: Start api-gateway with `TRADING_DEV_MODE_AUTH_BYPASS=true python -m services.api_gateway.main &`.
+
+- [ ] **Step 3**: Insert one fake row set via psql so the UI has data to render (one `kevin_videos` analyzed, one `kevin_analyses`, one `kevin_stock_mentions` for NVDA with conviction 0.82). Use the `INSERT … RETURNING id \\gset` pattern with psql.
+
+- [ ] **Step 4**: Start the dashboard: `cd dashboard && VITE_DEV_MODE=true VITE_API_BASE_URL=http://localhost:8000/api npm run dev`.
+
+- [ ] **Step 5**: Walkthrough at `http://localhost:5173/meet-kevin`:
+ - Home renders latest video card + NVDA in top conviction list
+ - `/meet-kevin/videos` shows the fake row
+ - `/meet-kevin/videos/` opens the iframe; the NVDA ticker card's "▶" deep-link calls seekTo on the player
+ - `/meet-kevin/stocks` shows NVDA
+ - `/meet-kevin/stocks/NVDA` shows the timeline + mention card
+
+- [ ] **Step 6**: Cleanup: `docker compose down`; kill the api-gateway process.
+
+---
+
+## Phase 5 — Container & dependencies
+
+### Task 18: Add `meet_kevin` extras + Dockerfile
+
+**Files:**
+- Modify: `pyproject.toml` — under `[project.optional-dependencies]` add:
+
+```
+meet_kevin = ["yt-dlp>=2026.04", "feedparser>=6.0", "anthropic>=0.40", "httpx>=0.27"]
+```
+
+- Modify: `docker/Dockerfile.service` — update the `uv pip install --system --no-cache-dir ".[api,news,sentiment,trading,backtester]"` line to also include `meet_kevin`:
+
+```
+uv pip install --system --no-cache-dir ".[api,news,sentiment,trading,backtester,meet_kevin]" && \\
+```
+
+- [ ] **Step 1**: Apply both edits.
+
+- [ ] **Step 2**: Sanity-check locally: `pip install -e ".[meet_kevin]"` (should resolve cleanly).
+
+- [ ] **Step 3**: Commit
+
+```bash
+git add pyproject.toml docker/Dockerfile.service
+git -c user.email=me@viktorbarzin.me commit -m "feat: add meet_kevin extras (yt-dlp, feedparser, anthropic)"
+```
+
+---
+
+### Task 19: Push → CI build
+
+- [ ] **Step 1**: `git push origin master`
+
+- [ ] **Step 2**: Verify Woodpecker picked up the latest pipeline (response of `GET /api/repos/viktor/trading/pipelines` shows `running` or `pending` matching the head commit).
+
+- [ ] **Step 3**: Wait for pipeline transition to terminal state via an `until` loop polling Woodpecker. **Do not pre-sleep** — let the condition drive cadence (~30s checks). Expected: `success`.
+
+- [ ] **Step 4**: Confirm DockerHub got a fresh tag matching the head SHA short-form (`hub.docker.com/v2/repositories/viktorbarzin/trading-bot-service/tags`).
+
+---
+
+## Phase 6 — K8s revival
+
+### Task 20: Vault secrets
+
+- [ ] **Step 1**: `vault login -method=oidc`
+
+- [ ] **Step 2**: Patch `secret/trading-bot`:
+
+```bash
+read -rs ANTHROPIC_KEY; echo
+vault kv patch secret/trading-bot \
+ anthropic_api_key="$ANTHROPIC_KEY" \
+ meet_kevin_channel_id="UCUvvj5lwue7PspotMDjk5UA"
+unset ANTHROPIC_KEY
+```
+
+- [ ] **Step 3**: Verify with `vault kv get -field=meet_kevin_channel_id secret/trading-bot`.
+
+---
+
+### Task 21: Uncomment + edit `infra/stacks/trading-bot/main.tf`
+
+**Workspace:** this happens in `/home/wizard/code/infra/`, NOT the trading repo. **Required action**: claim presence for the infra stack before applying (per `/home/wizard/code/CLAUDE.md` "Agent Presence" rule):
+
+```bash
+~/code/scripts/presence claim stack:trading-bot --purpose "Phase 6 revival — uncomment stack + add meet-kevin-watcher container"
+```
+
+- [ ] **Step 1**: Remove the outer `/* … */` block-comment (lines ~2 and ~631).
+
+- [ ] **Step 2**: In the `external_secret` resource's `template.data` and `data = […]`, add 2 keys: `TRADING_ANTHROPIC_API_KEY` ← `anthropic_api_key`, and `TRADING_MEET_KEVIN_CHANNEL_ID` ← `meet_kevin_channel_id`. Both reference `key = "trading-bot"` (the existing Vault path).
+
+- [ ] **Step 3**: In `locals.common_env`, add:
+
+```
+TRADING_MEET_KEVIN_POLL_INTERVAL_SECONDS = "10800"
+TRADING_MEET_KEVIN_DAILY_COST_CAP_USD = "5"
+TRADING_MEET_KEVIN_LLM_MODEL = "claude-sonnet-4-6"
+TRADING_MEET_KEVIN_PROMPT_VERSION = "v1"
+```
+
+- [ ] **Step 4**: Delete the three worker containers from the `trading-bot-workers` Pod spec: `news-fetcher`, `sentiment-analyzer`, `trade-executor`.
+
+- [ ] **Step 5**: Add the new `meet-kevin-watcher` container to the same Pod spec (after `market-data`):
+
+```hcl
+container {
+ name = "meet-kevin-watcher"
+ image = "viktorbarzin/trading-bot-service:latest"
+ image_pull_policy = "Always"
+ command = ["python", "-m", "services.meet_kevin_watcher.main"]
+ dynamic "env" {
+ for_each = local.common_env
+ content {
+ name = env.key
+ value = env.value
+ }
+ }
+ env { name = "TRADING_OTEL_METRICS_PORT"; value = "9097" }
+ env_from { secret_ref { name = "trading-bot-secrets" } }
+ env_from { secret_ref { name = "trading-bot-db-creds" } }
+ resources {
+ requests = { cpu = "10m", memory = "128Mi" }
+ limits = { memory = "256Mi" }
+ }
+}
+```
+
+- [ ] **Step 6**: Final container order is signal-generator [0], learning-engine [1], market-data [2], meet-kevin-watcher [3]. Update `lifecycle.ignore_changes` to only ignore image on container indices 0–3 plus `dns_config` (drop the index-4 and index-5 lines that were there for the 6-container layout).
+
+- [ ] **Step 7**: `scripts/tg plan` — verify the plan creates the namespace + secrets + Deployments + Service + Ingress, with the new container in the workers pod.
+
+- [ ] **Step 8**: `scripts/tg apply --non-interactive` (expect `Apply complete!`).
+
+- [ ] **Step 9**: Verify K8s:
+
+```bash
+kubectl get ns trading-bot
+kubectl -n trading-bot get pods,deploy,svc,ingress
+kubectl -n trading-bot get pod -l app=trading-bot-workers -o jsonpath='{.items[0].spec.containers[*].name}'
+# expected: signal-generator learning-engine market-data meet-kevin-watcher
+```
+
+- [ ] **Step 10**: Commit + push the infra change
+
+```bash
+git -C /home/wizard/code/infra add stacks/trading-bot/main.tf
+git -C /home/wizard/code/infra -c user.email=me@viktorbarzin.me \
+ commit -m "trading-bot: revive K8s stack + add meet-kevin-watcher; remove 3 disabled workers"
+git -C /home/wizard/code/infra push
+```
+
+- [ ] **Step 11**: `~/code/scripts/presence release stack:trading-bot`.
+
+---
+
+### Task 22: Production smoke test
+
+- [ ] **Step 1**: Tail watcher logs
+
+```bash
+kubectl -n trading-bot logs -l app=trading-bot-workers -c meet-kevin-watcher --tail=200 -f
+```
+
+Expected within ~60s: `Starting meet-kevin-watcher`, then `RSS poll: N new videos` (likely up to 15 from first-run backfill).
+
+- [ ] **Step 2**: Poll until at least one video reaches `status='analyzed'` (use an `until [ $(SELECT count(*)) -ge 1 ]` loop with sleep 30s — no pre-sleep).
+
+- [ ] **Step 3**: Hit the API: `curl -sk https://trading.viktorbarzin.me/api/meet-kevin/health` (with appropriate auth cookie) — expect `counts_by_status.analyzed >= 1`, `daily_cost_usd > 0`.
+
+- [ ] **Step 4**: Open `https://trading.viktorbarzin.me/meet-kevin` in a browser and verify the home + videos + stocks pages render with real Meet Kevin data.
+
+---
+
+## Self-review (run inline before publishing)
+
+- [x] **Spec coverage**: each design-doc section maps to one or more tasks:
+ - Channel + seed → Task 3
+ - Data model (5 tables) → Tasks 2, 3
+ - Pipeline 3 stages → Tasks 5, 6, 7, 8
+ - LLM output schema → Tasks 1, 7
+ - Daily cost cap → Task 8 (logic) + Task 9 (`/health` surface)
+ - 11 API endpoints → Task 9
+ - 5 dashboard pages → Tasks 12, 13, 14, 15
+ - Container changes → Task 18
+ - Vault secrets → Task 20
+ - K8s revival (Terraform edit) → Task 21
+ - Observability metrics → Task 8 main loop (`setup_telemetry` + counters)
+ - Failure modes → Tasks 5-8 (per-stage retry / status logic) + Task 9 (`reprocess` endpoint)
+ - First-run backfill (~$1.50 burst) → Tasks 8 + 22 (covered by the daily cost cap)
+
+- [x] **Placeholder scan**: no TBD / "implement later" / "similar to Task N". Where a section references "see spec", the spec section is named explicitly and the spec is committed at `ab382af` (immutable for this plan).
+
+- [x] **Type consistency**: schema/class/enum/table names match across tasks (`MeetKevinAnalysis`, `KevinVideo`, `kevin_stock_mentions`, `TickerAction.SELL`, env prefix `TRADING_MEET_KEVIN_*`, metrics port `9097`).
+
+---
+
+## Execution
+
+Two options:
+
+1. **Subagent-Driven (recommended)** — fresh subagent per task with two-stage review between tasks. Uses `superpowers:subagent-driven-development`.
+2. **Inline execution** — work through tasks in this session with periodic checkpoints. Uses `superpowers:executing-plans`.