1
+# PRD: Topic-Specific News Channels for SquadScope
2
+
3
+**Author:** Leela (Lead/Architect)
4
+**Date:** 2026-05-18
5
+**Status:** Draft
6
+**Type:** Feature PRD
7
+**Depends on:** docs/analysis-spec.md, docs/pipeline-validation.md, docs/learning-audit.md, .squad/decisions.md
8
+
9
+---
10
+
11
+## Executive Summary
12
+
13
+SquadScope currently crawls all of GitHub looking for "what's interesting this week." This produces broad but shallow coverage — jack of all topics, master of none. This PRD defines how to generalize SquadScope into a **topic-channel system** where each deployment is configured for a specific domain (e.g., `ai-ml`, `rust`, `security`), producing focused, expert-level weekly digests with calibrated learning per topic.
14
+
15
+The approach is **feature first, not a separate platform.** v1 delivers a single configurable topic per instance (one fork/config per topic). Multi-topic single-instance is deferred to v2.
16
+
17
+---
18
+
19
+## Problem Statement
20
+
21
+### Why topic-specific is better than general
22
+
23
+1. **Signal quality degrades with breadth.** A general GitHub crawl returns repos spanning AI, systems programming, web frameworks, security tools, and student homework. No single editorial voice can meaningfully assess "is this Rust crate important?" and "is this ML paper implementation notable?" in the same breath.
24
+
25
+2. **Learning cannot calibrate across domains.** The learning audit (docs/learning-audit.md) identified that wisdom must flow back into analysis (G7). But shared wisdom across domains produces uncalibrated judgments — a heuristic like "repos with >500 stars/week are always significant" is true in AI/ML but false in niche systems programming.
26
+
27
+3. **GitHub topic filtering is noisy.** Simply adding `topic:rust` to a search query returns thousands of results including tutorials, homework, and abandoned projects. Topic filtering needs a multi-stage pipeline: query → score → filter → analyze.
28
+
29
+4. **Readers want depth, not breadth.** A security professional subscribing to SquadScope doesn't want to scroll past 15 ML repos to find the 3 security tools that matter this week.
30
+
31
+5. **Predictions need domain context.** "This will be important" means something different in each field. A prediction ledger must be per-topic to be meaningful.
32
+
33
+---
34
+
35
+## Goals & Non-Goals
36
+
37
+### Goals
38
+
39
+- **G1:** Define a topic configuration format (`squadscope.topic.yml`) that controls all topic-specific behavior
40
+- **G2:** Namespace all data, content, prompts, RSS, and learning state by topic
41
+- **G3:** Deliver per-topic RSS feeds at `/topics/{topic}/index.xml`
42
+- **G4:** Implement a scoring pipeline for GitHub results (not just keyword filters)
43
+- **G5:** Isolate learning state per topic (wisdom, skills, prediction scores)
44
+- **G6:** Add a prediction ledger that tracks claims vs outcomes per topic
45
+- **G7:** Define topic quality criteria (minimum viable coverage thresholds)
46
+- **G8:** Ship 2 example topic configs: `ai-ml` and `rust`
47
+
48
+### Non-Goals
49
+
50
+- Multi-topic single-instance deployment (v2)
51
+- Topic marketplace or discovery
52
+- User-facing topic configuration UI
53
+- Real-time or daily publishing cadence
54
+- Cross-topic trend correlation
55
+- Non-GitHub data sources per topic (future enhancement)
56
+
57
+---
58
+
59
+## Topic Configuration
60
+
61
+### File: `squadscope.topic.yml`
62
+
63
+Each SquadScope instance has exactly one topic config at the repository root. Unless explicitly normalized by the scorer, `language_boost` keys should match the crawler's raw GitHub language strings exactly (for example `Python`, `Rust`, `Jupyter Notebook`).
64
+
65
+```yaml
66
+# squadscope.topic.yml — defines a single topic channel
67
+topic:
68
+ id: ai-ml # URL-safe identifier
69
+ name: "AI & Machine Learning" # Human-readable name
70
+ description: "Weekly digest of significant AI/ML repositories, frameworks, and research implementations on GitHub"
71
+
72
+# Crawler queries — multiple queries combined for coverage
73
+queries:
74
+ primary:
75
+ - "topic:machine-learning stars:>50 pushed:>{last_week}"
76
+ - "topic:deep-learning stars:>50 pushed:>{last_week}"
77
+ - "topic:artificial-intelligence stars:>30 pushed:>{last_week}"
78
+ - "topic:llm stars:>20 pushed:>{last_week}"
79
+ secondary:
80
+ - "topic:transformers stars:>100 pushed:>{last_week}"
81
+ - "topic:diffusion stars:>30 pushed:>{last_week}"
82
+ - "topic:rag stars:>20 pushed:>{last_week}"
83
+
84
+# Scoring pipeline — repos must pass these filters
85
+scoring:
86
+ min_stars: 20 # Absolute minimum to consider
87
+ min_stars_gained: 10 # Minimum weekly star delta
88
+ max_age_days: 365 # Exclude repos older than this from "new" category
89
+ min_relevance_score: 40 # Repos below this score do not reach analysis
90
+ language_boost: # Keys match raw crawler language values
91
+ Python: 1.2
92
+ Jupyter Notebook: 1.1
93
+ Rust: 1.0
94
+ topic_relevance: # Required topic overlap (at least one must match)
95
+ - machine-learning
96
+ - deep-learning
97
+ - artificial-intelligence
98
+ - neural-network
99
+ - llm
100
+ - nlp
101
+ - computer-vision
102
+ - reinforcement-learning
103
+ noise_topics: # Penalty topics (reduce score)
104
+ - tutorial
105
+ - course
106
+ - awesome-list
107
+ - homework
108
+ noise_name_patterns: # Regex patterns that reduce relevance score
109
+ - "^awesome-"
110
+ - "-tutorial$"
111
+ - "-course$"
112
+
113
+# Quality thresholds — topic must meet these to justify a channel
114
+quality:
115
+ min_repos_per_week: 8 # Minimum repos passing filters weekly
116
+ max_false_positive_rate: 0.25 # Max 25% irrelevant results after scoring
117
+ min_signal_repos: 3 # At least 3 genuinely significant repos per issue
118
+
119
+# Content configuration
120
+content:
121
+ tone: "technical, analytical"
122
+ audience: "ML engineers and researchers"
123
+ emphasis:
124
+ - "Novel architectures and training techniques"
125
+ - "Production-ready frameworks and tools"
126
+ - "Significant performance improvements"
127
+ de_emphasis:
128
+ - "Yet another wrapper around OpenAI API"
129
+ - "Awesome lists and link collections"
130
+ - "Course materials and tutorials"
131
+
132
+# Learning configuration
133
+learning:
134
+ wisdom_file: "topics/ai-ml/wisdom.md"
135
+ skills_dir: "topics/ai-ml/skills/"
136
+ predictions_file: "topics/ai-ml/predictions.jsonl"
137
+ reskill_context:
138
+ - "What ML-specific heuristics should we update?"
139
+ - "Are we over/under-weighting any sub-domain?"
140
+ - "Which prediction categories are we worst at?"
141
+```
142
+
143
+### Second example: `rust`
144
+
145
+```yaml
146
+topic:
147
+ id: rust
148
+ name: "Rust Ecosystem"
149
+ description: "Weekly digest of significant Rust crates, tools, and ecosystem developments"
150
+
151
+queries:
152
+ primary:
153
+ - "language:rust stars:>30 pushed:>{last_week}"
154
+ - "topic:rust stars:>20 pushed:>{last_week}"
155
+ - "topic:rust-lang stars:>20 pushed:>{last_week}"
156
+ secondary:
157
+ - "topic:cargo stars:>50 pushed:>{last_week}"
158
+ - "topic:wasm language:rust stars:>30 pushed:>{last_week}"
159
+
160
+scoring:
161
+ min_stars: 15
162
+ min_stars_gained: 8
163
+ max_age_days: 730
164
+ min_relevance_score: 40
165
+ language_boost:
166
+ Rust: 1.5
167
+ C: 1.0
168
+ topic_relevance:
169
+ - rust
170
+ - rust-lang
171
+ - cargo
172
+ - wasm
173
+ - systems-programming
174
+ - embedded
175
+ noise_topics:
176
+ - tutorial
177
+ - learning-rust
178
+ - rust-exercises
179
+ noise_name_patterns:
180
+ - "^rust-by-example"
181
+ - "-exercises$"
182
+
183
+quality:
184
+ min_repos_per_week: 5
185
+ max_false_positive_rate: 0.30
186
+ min_signal_repos: 2
187
+
188
+content:
189
+ tone: "systems-oriented, precise"
190
+ audience: "Rust developers and systems programmers"
191
+ emphasis:
192
+ - "Crates reaching stability milestones"
193
+ - "Performance and safety innovations"
194
+ - "Ecosystem tooling improvements"
195
+ de_emphasis:
196
+ - "Beginner tutorials"
197
+ - "Reimplementations of existing tools without novel approach"
198
+
199
+learning:
200
+ wisdom_file: "topics/rust/wisdom.md"
201
+ skills_dir: "topics/rust/skills/"
202
+ predictions_file: "topics/rust/predictions.jsonl"
203
+ reskill_context:
204
+ - "Are we calibrated for the Rust ecosystem's smaller scale?"
205
+ - "Which crate categories are we missing?"
206
+```
207
+
208
+---
209
+
210
+## Pipeline Changes
211
+
212
+### Overview
213
+
214
+```
215
+┌─────────────────────────────────────────────────────────────────────────┐
216
+│ Topic-Aware Pipeline (v1) │
217
+├─────────────────────────────────────────────────────────────────────────┤
218
+│ │
219
+│ squadscope.topic.yml │
220
+│ │ │
221
+│ ▼ │
222
+│ ┌───────────┐ query+ ┌───────────┐ scored ┌──────────────┐ │
223
+│ │ Crawler │──────────►│ Scorer │──────────►│ Analyzer │ │
224
+│ └───────────┘ raw JSON └───────────┘ repos └──────────────┘ │
225
+│ │ │
226
+│ ┌───────────┼──────────┐ │
227
+│ ▼ ▼ ▼ │
228
+│ ┌─────────┐ ┌──────────┐ ┌────┐│
229
+│ │ Content │ │Prediction│ │RSS ││
230
+│ │ Page │ │ Ledger │ │Feed││
231
+│ └─────────┘ └──────────┘ └────┘│
232
+│ │
233
+│ Learning loop (per topic): │
234
+│ predictions.jsonl → validate_predictions.py → scorecard → reskill │
235
+│ → updated wisdom.md → injected into next analysis prompt │
236
+│ │
237
+└─────────────────────────────────────────────────────────────────────────┘
238
+```
239
+
240
+### 1. Crawler Changes (`scripts/crawl.py`)
241
+
242
+**Current:** Hardcoded queries in `crawl.py` searching for generic trending repos.
243
+
244
+**Proposed:**
245
+- Read `squadscope.topic.yml` at startup
246
+- Build search queries from `queries.primary` and `queries.secondary`
247
+- Apply `scoring.min_stars` as a pre-filter in the GitHub API query
248
+- Template `{last_week}` in query strings to ISO date of 7 days ago
249
+- Output to `data/raw/{topic_id}/YYYY-WNN.json` (namespaced)
250
+
251
+```python
252
+# Pseudocode for topic-aware crawling
253
+config = load_topic_config("squadscope.topic.yml")
254
+queries = config["queries"]["primary"] + config["queries"]["secondary"]
255
+for q in queries:
256
+ q = q.replace("{last_week}", last_week_iso())
257
+ results = search_github(q)
258
+ all_repos.extend(results)
259
+
260
+# Deduplicate by full_name, then classify into the existing raw payload shape
261
+unique_repos = deduplicate(all_repos)
262
+new_repos, trending_repos = partition_repo_sets(unique_repos)
263
+payload = {
264
+ "week": current_iso_week(),
265
+ "new_repos": new_repos,
266
+ "trending_repos": trending_repos,
267
+ "signals": build_signals(new_repos, trending_repos),
268
+ "metadata": build_metadata(config, queries),
269
+}
270
+write_json(f"data/raw/{config['topic']['id']}/YYYY-WNN.json", payload)
271
+```
272
+
273
+### 2. New: Scoring Pipeline (`scripts/score_repos.py`)
274
+
275
+A new pipeline stage between crawl and analyze. Repos get a **relevance score** (0-100):
276
+
277
+| Factor | Weight | Scoring Logic |
278
+|--------|--------|---------------|
279
+| Topic overlap | 30% | Count of repo topics matching `scoring.topic_relevance` |
280
+| Star momentum | 25% | `stars_gained / min_stars_gained` ratio (capped at 3x) |
281
+| Language match | 15% | Boost from `scoring.language_boost` |
282
+| Noise penalty | -20% | Repos matching `noise_topics` or `noise_name_patterns` |
283
+| Recency | 10% | Days since last push (more recent = higher) |
284
+
285
+**Output:** `data/scored/{topic_id}/YYYY-WNN.json` — same top-level schema as raw (`week`, `new_repos`, `trending_repos`, `signals`, `metadata`), with `relevance_score` added to repo entries and scoring/filter metadata appended under `metadata`. Only repos with `relevance_score >= scoring.min_relevance_score` pass to analysis.
286
+
287
+### 3. Analysis Prompt Changes (`prompts/analyze-weekly.md`)
288
+
289
+**Current:** Static prompt with no topic context or learned state.
290
+
291
+**Proposed:** Topic-aware prompt template with injection points:
292
+
293
+```markdown
294
+# Weekly Analysis: {{TOPIC_NAME}}
295
+
296
+You are analyzing GitHub repositories for the **{{TOPIC_NAME}}** channel.
297
+Audience: {{AUDIENCE}}
298
+Tone: {{TONE}}
299
+
300
+## Emphasis
301
+{{EMPHASIS_LIST}}
302
+
303
+## De-emphasis
304
+{{DE_EMPHASIS_LIST}}
305
+
306
+## Learned Wisdom (from prior reskill cycles)
307
+{{WISDOM_CONTENT}}
308
+
309
+## Active Skills
310
+{{SKILLS_CONTENT}}
311
+
312
+## Prediction Track Record
313
+{{PREDICTION_SCORECARD}}
314
+
315
+## Instructions
316
+Analyze the scored repositories in `data/scored/{{TOPIC_ID}}/YYYY-WNN.json`.
317
+...
318
+```
319
+
320
+### 4. Content Namespacing
321
+
322
+| Asset | Current Path | Topic-Aware Path |
323
+|-------|-------------|-----------------|
324
+| Raw crawl data | `data/raw/YYYY-WNN.json` | `data/raw/{topic_id}/YYYY-WNN.json` |
325
+| Scored data | N/A (new) | `data/scored/{topic_id}/YYYY-WNN.json` |
326
+| Analysis output | `data/analyzed/YYYY-WNN-summary.md` | `data/analyzed/{topic_id}/YYYY-WNN-summary.md` |
327
+| Star snapshots | `data/snapshots/YYYY-WNN.json` | `data/snapshots/{topic_id}/YYYY-WNN.json` |
328
+| Hugo content | `content/weekly/YYYY-WNN.md` | `content/topics/{topic_id}/YYYY-WNN.md` |
329
+| RSS feed | `/index.xml` | `/topics/{topic_id}/index.xml` |
330
+| Wisdom | `.squad/identity/wisdom.md` | `topics/{topic_id}/wisdom.md` |
331
+| Skills | `.squad/skills/` | `topics/{topic_id}/skills/` |
332
+| Predictions | N/A (new) | `topics/{topic_id}/predictions.jsonl` |
333
+
334
+### 5. RSS Per Topic
335
+
336
+Hugo taxonomy configuration:
337
+
338
+```toml
339
+# hugo.toml additive changes
340
+[taxonomies]
341
+ tag = "tags"
342
+ category = "categories"
343
+ topic = "topics"
344
+
345
+[outputFormats.RSS]
346
+ mediaType = "application/rss+xml"
347
+ baseName = "index"
348
+
349
+[params]
350
+ topicId = "ai-ml" # From squadscope.topic.yml
351
+```
352
+
353
+Each topic gets its own RSS feed at `/topics/{topic_id}/index.xml`. The site root `/index.xml` remains as an aggregate feed (or is removed in single-topic mode).
354
+
355
+---
356
+
357
+## Learning System Integration
358
+
359
+### Per-Topic Learning State
360
+
361
+Each topic maintains isolated learning state:
362
+
363
+```
364
+topics/{topic_id}/
365
+├── wisdom.md # Accumulated heuristics for this domain
366
+├── skills/ # Extracted patterns and rules
367
+│ ├── SKILL-001.md
368
+│ └── SKILL-002.md
369
+├── predictions.jsonl # Prediction ledger (append-only)
370
+└── scorecards/ # Hindsight validation results
371
+ ├── 2026-W21.json
372
+ └── 2026-W25.json
373
+```
374
+
375
+### Why Isolation Matters
376
+
377
+From the learning audit: "shared wisdom across domains produces uncalibrated judgments." Examples:
378
+
379
+- AI/ML wisdom: "Repos with HuggingFace integrations tend to gain adoption quickly" → **meaningless for Rust**
380
+- Rust wisdom: "Crates with `no_std` support indicate systems-level seriousness" → **meaningless for AI/ML**
381
+- Security wisdom: "CVE-related repos spike and fade within 2 weeks" → **misleading if applied to general software**
382
+
383
+### Prediction Ledger (`predictions.jsonl`)
384
+
385
+Each analysis produces machine-readable predictions appended to the ledger:
386
+
387
+```jsonl
388
+{"week":"2026-W21","repo":"owner/name","claim":"signal","confidence":0.8,"category":"framework","predicted_stars_4w":500}
389
+{"week":"2026-W21","repo":"owner/name","claim":"noise","confidence":0.7,"category":"wrapper","reason":"thin wrapper around existing API"}
390
+{"week":"2026-W21","repo":"owner/name","claim":"gap","confidence":0.6,"category":"missing-tooling","description":"No good Rust WASM debugger exists yet"}
391
+```
392
+
393
+**Fields:**
394
+- `week`: ISO week of the prediction
395
+- `repo`: Full repository name (or null for gap predictions)
396
+- `claim`: One of `signal`, `noise`, `gap`
397
+- `confidence`: 0.0-1.0 how sure the system is
398
+- `category`: Domain-specific category
399
+- `predicted_stars_4w`: Expected star count in 4 weeks (for signal/noise)
400
+- `reason`/`description`: Human-readable explanation
401
+
402
+### Hindsight Validation (`scripts/validate_predictions.py`)
403
+
404
+Runs 4 weeks after predictions are made. Compares claims to outcomes:
405
+
406
+```python
407
+# Validation logic
408
+for prediction in load_predictions(topic_id, target_week):
409
+ if prediction["claim"] == "signal":
410
+ actual_stars = get_current_stars(prediction["repo"])
411
+ predicted = prediction["predicted_stars_4w"]
412
+ score = min(actual_stars / predicted, 2.0) # Cap at 2x
413
+ scorecard.append({"prediction": prediction, "actual": actual_stars, "score": score})
414
+ elif prediction["claim"] == "noise":
415
+ # Noise repos should have plateaued or declined
416
+ delta = get_star_delta(prediction["repo"], weeks=4)
417
+ score = 1.0 if delta < prediction.get("predicted_stars_4w", 50) else 0.0
418
+ scorecard.append({"prediction": prediction, "actual_delta": delta, "score": score})
419
+```
420
+
421
+**Scorecard output** feeds into reskill: "Last month we were 72% accurate on signal calls but only 45% on noise calls in ai-ml. We tend to overestimate wrapper libraries."
422
+
423
+### Reskill Integration
424
+
425
+The reskill prompt (run every 5th cycle) now receives:
426
+1. Topic-specific wisdom from `topics/{topic_id}/wisdom.md`
427
+2. Latest scorecard from `topics/{topic_id}/scorecards/`
428
+3. Prediction accuracy trend across last 5 scorecards
429
+4. Topic config context (what we're optimizing for)
430
+
431
+Reskill outputs are written back to topic-specific paths, ensuring one topic's learnings never contaminate another.
432
+
433
+---
434
+
435
+## Content Architecture
436
+
437
+### Topic Channels
438
+
439
+URL structure:
440
+```
441
+/ → Home (links to topic channel)
442
+/topics/{topic_id}/ → Topic landing page (latest + archive)
443
+/topics/{topic_id}/2026-W21 → Weekly issue page
444
+/topics/{topic_id}/index.xml → RSS feed for this topic
445
+```
446
+
447
+### Hugo Content Structure
448
+
449
+```
450
+content/
451
+└── topics/
452
+ └── ai-ml/
453
+ ├── _index.md # Topic landing page
454
+ ├── 2026-W21.md # Weekly issue
455
+ ├── 2026-W22.md
456
+ └── ...
457
+```
458
+
459
+### Navigation
460
+
461
+For v1 (single-topic instance), the site homepage redirects to the topic channel. The topic archive page lists all weekly issues with summaries.
462
+
463
+For v2 (multi-topic), a topic selector would appear in navigation.
464
+
465
+---
466
+
467
+## v1 Scope: Single Configurable Topic
468
+
469
+### What ships in v1
470
+
471
+1. **`squadscope.topic.yml` config format** — fully specified, validated at pipeline start
472
+2. **Topic-aware crawler** — reads queries from config, outputs to namespaced paths
473
+3. **Scoring pipeline** — `scripts/score_repos.py` with configurable weights
474
+4. **Topic-aware analysis prompt** — injects topic context, wisdom, and scorecard
475
+5. **Prediction ledger** — appended to after each analysis
476
+6. **Hindsight validation script** — runs on 4-week-old predictions
477
+7. **Per-topic learning state** — isolated wisdom, skills, scorecards
478
+8. **Topic RSS feed** — at `/topics/{topic_id}/index.xml`
479
+9. **Two example configs** — `examples/topics/ai-ml.yml` and `examples/topics/rust.yml`
480
+10. **Config validation script** — `scripts/validate_topic_config.py`
481
+
482
+### What does NOT ship in v1
483
+
484
+- Multi-topic in a single instance
485
+- Topic discovery or marketplace
486
+- Cross-topic learning transfer
487
+- Dynamic query generation
488
+- Topic health monitoring dashboard
489
+
490
+### Deployment Model (v1)
491
+
492
+One SquadScope fork per topic. Each fork:
493
+- Has its own `squadscope.topic.yml`
494
+- Runs its own GitHub Actions schedule
495
+- Produces its own GitHub Pages site
496
+- Accumulates its own learning state
497
+- Has its own RSS feed
498
+
499
+This is intentionally simple. Forks share the same codebase but diverge on configuration and learned state.
500
+
501
+---
502
+
503
+## v2 Vision: Multi-Topic Single Instance
504
+
505
+**Deferred.** Documented here for future planning only.
506
+
507
+### What v2 would add
508
+
509
+- Single instance running multiple topics on different schedules
510
+- Shared infrastructure, isolated topic state
511
+- Topic health monitoring (auto-disable topics below quality thresholds)
512
+- Cross-topic signals ("this repo is trending in BOTH ai-ml and rust channels")
513
+- Topic marketplace (community-contributed topic configs)
514
+- Unified navigation across topics
515
+
516
+### Why v2 is premature now
517
+
518
+- Adds orchestration complexity (per-topic cron, per-topic secrets)
519
+- Learning isolation is harder in shared instances (accidental cross-contamination)
520
+- No user demand signal yet — need v1 adoption data first
521
+- GitHub Actions concurrency constraints make multi-topic scheduling complex
522
+
523
+---
524
+
525
+## Implementation Plan
526
+
527
+### Issues to Create
528
+
529
+| # | Title | Phase | Depends On | Assignee Profile |
530
+|---|-------|-------|-----------|-----------------|
531
+| 1 | Define `squadscope.topic.yml` schema and validator | Foundation | — | Architect |
532
+| 2 | Namespace data directories by topic ID | Foundation | #1 | Crawler |
533
+| 3 | Implement scoring pipeline (`scripts/score_repos.py`) | Pipeline | #1, #2 | Crawler |
534
+| 4 | Make crawler read queries from topic config | Pipeline | #1, #2 | Crawler |
535
+| 5 | Create topic-aware analysis prompt template | Pipeline | #1 | Analyzer |
536
+| 6 | Add prediction ledger output to analysis | Pipeline | #5 | Analyzer |
537
+| 7 | Implement hindsight validation script | Learning | #6 | Analyzer |
538
+| 8 | Per-topic learning state directories and seeding | Learning | #1 | Architect |
539
+| 9 | Wire prediction scorecard into reskill prompt | Learning | #7 | Analyzer |
540
+| 10 | Hugo topic taxonomy and per-topic RSS | Content | #2 | Site |
541
+| 11 | Topic landing page template | Content | #10 | Site |
542
+| 12 | Example config: ai-ml | Validation | #1-#4 | Validator |
543
+| 13 | Example config: rust | Validation | #1-#4 | Validator |
544
+| 14 | Topic quality threshold enforcement | Quality | #3 | Crawler |
545
+| 15 | End-to-end integration test with example topic | Validation | All | Validator |
546
+
547
+### Dependencies
548
+
549
+```
550
+#1 (schema) ─┬─► #2 (namespacing) ─┬─► #3 (scorer) ──► #4 (crawler)
551
+ │ │ │
552
+ │ └─► #10 (Hugo) ▼
553
+ │ #12, #13 (examples)
554
+ └─► #5 (prompt) ──► #6 (predictions) ──► #7 (validation)
555
+ │
556
+ ▼
557
+ #9 (reskill wiring)
558
+```
559
+
560
+### Estimated Effort
561
+
562
+- **Foundation (Issues 1-2):** 1 session
563
+- **Pipeline (Issues 3-6):** 2-3 sessions
564
+- **Learning (Issues 7-9):** 2 sessions
565
+- **Content (Issues 10-11):** 1 session
566
+- **Validation (Issues 12-15):** 1-2 sessions
567
+
568
+**Total:** ~7-9 work sessions
569
+
570
+---
571
+
572
+## Open Questions
573
+
574
+| # | Question | Impact | Proposed Resolution |
575
+|---|----------|--------|-------------------|
576
+| OQ1 | Should topic configs live in repo root or `topics/` dir? | File organization | Repo root for v1 (single topic); move to `topics/` in v2 |
577
+| OQ2 | How to handle repos that span multiple topics? | Dedup in multi-topic v2 | v1: irrelevant (single topic). v2: each topic scores independently |
578
+| OQ3 | What's the minimum weeks of data before learning is meaningful? | Reskill timing | Propose 4 weeks minimum before first hindsight validation runs |
579
+| OQ4 | Should prediction confidence be system-generated or human-calibrated initially? | Learning accuracy | Start with fixed confidence (0.7 for signal, 0.5 for noise), calibrate after 8 weeks of scorecard data |
580
+| OQ5 | Enrichment signals beyond stars — which to add first? | Prediction quality | Forks and contributor count (cheapest API calls, highest signal per learning-audit G13) |
581
+| OQ6 | Should topic quality thresholds auto-disable a topic or just warn? | Reliability | Warn-only for v1 (log to workflow summary), auto-disable in v2 |
582
+
583
+---
584
+
585
+## Success Metrics
586
+
587
+### Quantitative (measurable after 8 weeks of operation)
588
+
589
+| Metric | Target | Measurement |
590
+|--------|--------|-------------|
591
+| False positive rate | < 25% per topic | Manual audit of 20 random "signal" calls per month |
592
+| Prediction accuracy (signal) | > 65% | Hindsight validation scorecard |
593
+| Prediction accuracy (noise) | > 55% | Hindsight validation scorecard |
594
+| Repos per weekly issue | ≥ quality.min_repos_per_week from config | Automated count |
595
+| RSS subscribers per topic | > 0 within 4 weeks | Analytics (if available) |
596
+| Learning improvement trend | Prediction accuracy increases by ≥ 5% over 8 weeks | Scorecard comparison |
597
+
598
+### Qualitative
599
+
600
+- Topic experts find the digest "saves them time" vs. manual GitHub browsing
601
+- Analysis tone matches configured audience expectations
602
+- Signal/Noise/Gaps sections feel calibrated to the specific domain
603
+- Learned wisdom in `wisdom.md` contains domain-specific (not generic) heuristics after 3 reskill cycles
604
+
605
+---
606
+
607
+## Relationship to Existing Work
608
+
609
+### Analysis Spec (`docs/analysis-spec.md`)
610
+This PRD extends the approved analyzer contract. Topic-aware raw and scored artifacts keep the existing top-level payload shape (`week`, `new_repos`, `trending_repos`, `signals`, `metadata`) while adding a topic namespace prefix and repo-level `relevance_score` data.
611
+
612
+### Pipeline Validation (`docs/pipeline-validation.md`)
613
+This PRD preserves the current Crawl → Analyze → Generate workflow expectations while adding one topic-aware scoring step between crawl and analyze. Existing quality gates and artifact validation remain in force.
614
+
615
+### Learning Audit (`docs/learning-audit.md`)
616
+This PRD directly addresses:
617
+- **G7 (prompt feedback loop):** Topic-aware prompt template with `{{WISDOM_CONTENT}}` injection
618
+- **G8 (hindsight validation):** `scripts/validate_predictions.py` with per-topic scorecards
619
+- **G9 (prediction registry):** `predictions.jsonl` format defined
620
+- **G13 (enrichment signals):** Fork/contributor data noted as OQ5, planned for scorer enrichment
621
+
622
+### Decisions (`.squad/decisions.md`)
623
+- Respects Decision 3 (pipeline stage contracts) — adds a scoring stage but preserves existing boundaries
624
+- Respects Decision 4 (reviewer gate) — quality gate applies per-topic
625
+- Extends Decision 6 (reskill) — reskill reads per-topic state instead of global state