schedule-drip
The selection suite's drip stage. The pipeline today is produce-only: it renders
clips and stops. The human picks ~19 by hand, dies 37% of the time on topic, and
the channel takes 7-day dark gaps while ~80 quality clips rot on disk. With no
evergreen tail (86-100% of a short's views land in days 1-3), a dark day is
permanent lost reach — the #1 unconfounded leak in the 28-day analytics.
schedule-drip is the deterministic greedy scheduler that fixes it. It consumes
grade-clip's grade.json verdicts plus a checked-in topic scorelist and stages
a daily drip into output/_toupload/<date>/. Staging-handoff only — it never
uploads (there is no API key in the stack); a human posts from the dated
folders.
Usage
schedule-drip.sh [output_dir=output]
Inputs
- Every
output/**/*.grade.json (skips _preview / source / _toupload /
_triage). The locked schema lives in SELECTION-SUITE-CONTRACT.md.
topics.scorelist (checked in here) — one rule per line, <GO|HOLD> <regex>,
matched case-insensitively against source-slug + title. HOLD is a denylist
and WINS over an incidental GO substring (e.g. "focus" inside a
productivity-death source's "stay-focused"). Default when nothing matches:
HOLD (conservative).
- OPTIONAL
output/upload-log.json — a list, or {uploaded|posted: [...]}, of
already-posted clip paths / basenames / titles. Absence means "nothing posted
yet" — never a crash.
- "Today" comes from the system
date.
Algorithm (deterministic greedy)
- Gate (GOLD-first): keep a clip only if
tier==GOLD with no hard_caps, OR
grade>=GRADE_MIN_UPLOAD with no hard_caps. A clip with any unfixed hard cap
stays out of the drip until fix-cold-open repairs it.
- Drop posted: anything matched in
upload-log.json.
- Rank by grade desc (then source, then path for stable ties).
- De-dupe near-identical: same source + ≥0.7 title-token overlap → keep the
higher grade, drop the rest (collapses the
clip / clip-1.25x speed-up twins).
- Topic verdict: GO vs HOLD from
topics.scorelist.
- Place GO first into the horizon, one day at a time,
POSTS_PER_DAY per day,
MAX_PER_SOURCE_PER_DAY round-robin across sources (the 53-from-one-source
feed-fatigue fix — no day ever gets two clips from the same source).
- Backfill dark days with HOLD clips only — a HOLD clip is never dripped ahead
of a GO clip, only into a day that would otherwise be dark.
gap_warnings — any day still empty after backfill (the dark-gap alarm).
Output
output/_toupload/<YYYY-MM-DD>/ per filled day: the staged clip (copy) +
metadata.txt (lowercased title + #-hashtags from the matched GO rule and the
source slug, plus #shorts #fyp).
output/_toupload/schedule.json — {generated, horizon_days, posts_per_day, max_per_source_per_day, days:{date→[clips]}, gap_warnings, backfilled, drops}.
drops records every not_schedulable / already_posted /
dedupe_near_identical / horizon_full decision.
Idempotent: _toupload is rmtree'd and rebuilt every run — a re-run yields a
byte-identical plan and never duplicates staging. Non-fatal: any error exits 0
without staging.
Knobs
| Env |
Default |
Meaning |
POSTS_PER_DAY |
1 |
clips staged per day |
MAX_PER_SOURCE_PER_DAY |
1 |
per-source round-robin cap (feed-fatigue fix) |
DRIP_HORIZON_DAYS |
14 |
days to plan ahead |
GRADE_MIN_UPLOAD |
60 |
shared with grade-clip; the schedulable grade floor |
Why new
There is no selection / scheduling step at all. This is the only skill that
directly attacks the dark gap and the backlog rot — it turns "101 produced / 19
uploaded" into a multi-week daily drip with zero new source ingestion.
1---2name: schedule-drip3description: Deterministic greedy scheduler over the graded clips — the only skill that attacks the4---56# schedule-drip78The selection suite's drip stage. The pipeline today is produce-only: it renders9clips and stops. The human picks ~19 by hand, dies 37% of the time on topic, and10the channel takes 7-day dark gaps while ~80 quality clips rot on disk. With no11evergreen tail (86-100% of a short's views land in days 1-3), **a dark day is12permanent lost reach** — the #1 unconfounded leak in the 28-day analytics.1314`schedule-drip` is the deterministic greedy scheduler that fixes it. It consumes15`grade-clip`'s `grade.json` verdicts plus a checked-in topic scorelist and stages16a daily drip into `output/_toupload/<date>/`. **Staging-handoff only — it never17uploads** (there is no API key in the stack); a human posts from the dated18folders.1920## Usage2122```bash23schedule-drip.sh [output_dir=output]24```2526## Inputs2728- Every `output/**/*.grade.json` (skips `_preview` / `source` / `_toupload` /29 `_triage`). The locked schema lives in `SELECTION-SUITE-CONTRACT.md`.30- `topics.scorelist` (checked in here) — one rule per line, `<GO|HOLD> <regex>`,31 matched case-insensitively against `source-slug + title`. **HOLD is a denylist32 and WINS** over an incidental GO substring (e.g. "focus" inside a33 productivity-death source's "stay-focused"). Default when nothing matches:34 `HOLD` (conservative).35- OPTIONAL `output/upload-log.json` — a list, or `{uploaded|posted: [...]}`, of36 already-posted clip paths / basenames / titles. **Absence means "nothing posted37 yet" — never a crash.**38- "Today" comes from the system `date`.3940## Algorithm (deterministic greedy)41421. **Gate (GOLD-first):** keep a clip only if `tier==GOLD` with no `hard_caps`, OR43 `grade>=GRADE_MIN_UPLOAD` with no `hard_caps`. A clip with any unfixed hard cap44 stays out of the drip until `fix-cold-open` repairs it.452. **Drop posted:** anything matched in `upload-log.json`.463. **Rank** by grade desc (then source, then path for stable ties).474. **De-dupe near-identical:** same source + ≥0.7 title-token overlap → keep the48 higher grade, drop the rest (collapses the `clip` / `clip-1.25x` speed-up twins).495. **Topic verdict:** GO vs HOLD from `topics.scorelist`.506. **Place GO first** into the horizon, one day at a time, `POSTS_PER_DAY` per day,51 `MAX_PER_SOURCE_PER_DAY` round-robin across sources (the 53-from-one-source52 feed-fatigue fix — no day ever gets two clips from the same source).537. **Backfill dark days with HOLD** clips only — a HOLD clip is never dripped ahead54 of a GO clip, only into a day that would otherwise be dark.558. **`gap_warnings`** — any day still empty after backfill (the dark-gap alarm).5657## Output5859- `output/_toupload/<YYYY-MM-DD>/` per filled day: the staged clip (copy) +60 `metadata.txt` (lowercased title + `#`-hashtags from the matched GO rule and the61 source slug, plus `#shorts #fyp`).62- `output/_toupload/schedule.json` — `{generated, horizon_days, posts_per_day,63 max_per_source_per_day, days:{date→[clips]}, gap_warnings, backfilled, drops}`.64 `drops` records every `not_schedulable` / `already_posted` /65 `dedupe_near_identical` / `horizon_full` decision.6667**Idempotent:** `_toupload` is rmtree'd and rebuilt every run — a re-run yields a68byte-identical plan and never duplicates staging. **Non-fatal:** any error exits 069without staging.7071## Knobs7273| Env | Default | Meaning |74|---|---|---|75| `POSTS_PER_DAY` | 1 | clips staged per day |76| `MAX_PER_SOURCE_PER_DAY` | 1 | per-source round-robin cap (feed-fatigue fix) |77| `DRIP_HORIZON_DAYS` | 14 | days to plan ahead |78| `GRADE_MIN_UPLOAD` | 60 | shared with grade-clip; the schedulable grade floor |7980## Why new8182There is no selection / scheduling step at all. This is the only skill that83directly attacks the dark gap and the backlog rot — it turns "101 produced / 1984uploaded" into a multi-week daily drip with zero new source ingestion.