How spasoye-json/winnow works
Purpose and entry points
Winnow is a Python-based YouTube curator: it ingests channel/feed content, obtains transcripts, scores videos, and presents a review/calibration interface. Primary executable entry points are the package launcher and CLI in src/winnow/__main__.py and src/winnow/cli.py; the web application is centered in src/winnow/web.py. A system-service deployment artifact exists at packaging/winnow.service.
Application runtime and persistence
The core runtime is a single-process Python service, consistent with docs/adr/0001-single-process-due-check-runtime.md. Database access and schema/lifecycle concerns are concentrated in src/winnow/db.py; application authentication is isolated in src/winnow/auth.py. The system’s durable boundary is therefore the database rather than an external job queue or distributed scheduler.
Feed ingestion and YouTube boundary
Content acquisition is split between feed discovery, ingestion orchestration, and YouTube-specific API behavior:
• src/winnow/feed.py
• src/winnow/ingest.py
• src/winnow/youtube.py
Ingestion converts upstream channel/video information into locally persisted candidates. youtube.py is the external-service adapter boundary, keeping provider interaction separate from downstream scoring and presentation.
Transcript acquisition pipeline
Transcript handling is a distinct enrichment stage after video ingestion. src/winnow/transcript.py manages transcript retrieval/processing, while src/winnow/local_transcript.py provides a local transcript source or fallback. Research documents identify transcript fetching and truncation as material constraints: docs/research/transcript-fetching.md and docs/research/transcript-truncation-strategy.md.
Scoring, verdicts, and calibration
src/winnow/scoring.py turns available video/transcript context into a relevance verdict or score. The scoring design is intentionally constrained to a single Flash Lite model according to docs/adr/0003-flash-lite-single-model-scoring.md, making the model provider a key external dependency and rate-limit boundary (docs/research/gemini-free-tier-rate-limits.md). Scores are final once written, rather than continuously recalibrated, per docs/adr/0002-organic-calibration-scores-final-once-written.md. Calibration UI and verdict rendering are represented by src/winnow/templates/calibration.html and src/winnow/templates/_verdict.html.
Scheduling, synchronization, and control flow
src/winnow/scheduler.py determines when due checks run; src/winnow/sync.py coordinates recurring refresh/synchronization work. The principal flow is: scheduler/CLI triggers sync → feeds and YouTube metadata are ingested → transcripts are acquired → scoring persists a verdict → web views expose queue, feed, details, and calibration. This separation permits manually initiated CLI work and service-driven scheduled work to use the same domain pipeline.
Web presentation and operational automation
The server-rendered web layer uses templates under src/winnow/templates/, notably feed.html, queue.html, detail.html, and settings.html, with static styling in src/winnow/static/custom.css. It is a presentation boundary over the persisted ingestion/scoring state, not the core processing engine.
Repository automation is separately implemented as TypeScript Sandcastle agent workflows, invoked by GitHub Actions. Workflow entry implementations include .sandcastle/agent-workflows/explore/explore.ts, .sandcastle/agent-workflows/implement/implement.ts, and .sandcastle/agent-workflows/review/review.ts; CI/agent triggers live in .github/workflows/ci.yml and related agent workflow files. These support repository maintenance rather than the Winnow runtime.
Main entry points: transcript.py Transcript enrichment · db.py Application database · web.py Web application · ingest.py Video ingestion
Components 18
Winnow runtime
Single-process Python service.
- Package launcher
src/winnow/__main__.py - CLI · Manual trigger for the shared pipeline.
src/winnow/cli.py - Due scheduler
src/winnow/scheduler.py - Sync coordinator · Coordinates recurring refresh work.
src/winnow/sync.py - Application database · Schema and lifecycle are managed locally.
src/winnow/db.py - Authentication
src/winnow/auth.py - System service
packaging/winnow.service
Content pipeline
- Feed discovery
src/winnow/feed.py - Video ingestion · Persists discovered channel and video candidates.
src/winnow/ingest.py - YouTube adapter
src/winnow/youtube.py - Transcript enrichment
src/winnow/transcript.py - Local transcripts
src/winnow/local_transcript.py - Verdict scoring · Writes final relevance scores and verdicts.
src/winnow/scoring.py - Flash Lite model · External scoring and rate-limit boundary.
Presentation
- Web application · Reads persisted state for review and calibration.
src/winnow/web.py - Review templates · Feed, queue, detail, settings, and calibration views.
Repository automation
Maintenance workflows outside the application runtime.
- Sandcastle workflows · Explore, implement, and review maintenance workflows.
.sandcastle/agent-workflows/explore/explore.ts - GitHub Actions
.github/workflows/ci.yml
Connections 18
- Package launcher → CLI: launches
- CLI → Sync coordinator: triggers
- Due scheduler → Sync coordinator: triggers due work
- System service → Due scheduler: runs
- Sync coordinator → Feed discovery: refreshes
- Feed discovery → YouTube adapter: discovers via
- YouTube adapter → Video ingestion: supplies metadata
- Video ingestion → Application database: persists candidates
- Video ingestion → Transcript enrichment: enriches videos
- Transcript enrichment → Local transcripts: uses fallback
- Transcript enrichment → Application database: persists transcripts
- Transcript enrichment → Verdict scoring: provides context
- Verdict scoring → Flash Lite model: requests score
- Verdict scoring → Application database: writes final verdict
- Web application → Application database: reads review state
- Web application → Authentication: authenticates
- Web application → Review templates: renders
- GitHub Actions → Sandcastle workflows: invokes