Weekly update — Friday GitHub business summary [Flow]
Source path: knowledge-base/diagrams/flows/weekly-update.md
# Weekly update — Friday GitHub business summary `[Flow]`
CI automation, not app runtime: turns a week of git + PR activity into a plain-English business update committed to `main`. **Manual dispatch only** since 2026-08-13 (the Friday cron is off — unpaid Actions minutes); because every run backfills the missing Fridays, running it on demand loses no week. Source: [`.github/workflows/weekly-update.yml`](../../../.github/workflows/weekly-update.yml), [`scripts/insights/weekly-update/`](../../../scripts/insights/weekly-update/).
```mermaid
flowchart TB
Cron["schedule — Fri 06:00 UTC<br/>(DISABLED — restore to re-arm)"] -.-> Guard
Manual["manual dispatch<br/>(Actions tab, any branch)"] --> Guard
Guard["serialize — queue, never kill mid-push"] --> Setup["checkout main · full history ·<br/>refresh knowledge-base/generated/TREE.md"]
Setup --> Backfill["**backfill** — every missing Friday<br/>(idempotent; quiet weeks cost nothing)"]
subgraph Week["per missing Friday · 7-day window"]
direction TB
Collect["collect commits + merged PRs (gh)"]
Collect -->|"none → skip"| Quiet(["no report, no tokens"])
Collect --> Classify["dedup · classify by product area ·<br/>enrich surfaces from TREE.md"]
Classify --> Polish["**--polish** — LLM exec summary<br/>(voice.md; degrades gracefully)"]
Polish --> Render["render report .md + context .json"]
end
Backfill --> Week
Collect -.-> GH(["GitHub API (gh)"])
Polish -.-> OR(["OpenRouter"])
Week --> Commit["stage updates/ + TREE.md<br/>(only if changed)"]
Commit --> Push["push HEAD:main<br/>retry×3: rebase on origin/main"]
Push -.->|rejected| Push
```
**What it produces** (into `updates/`, committed straight to `main`):
- `reports/YYYY-MM-DD.md` — the readable update, grouped by **product area** (not commit type / file path), for non-engineering readers.
- `context/YYYY-MM-DD.json` — the versioned, deterministic record the report derives from (`schema_version`). Downstream tools consume this shape, never scrape the markdown.
- `updates/README.md` — the index, regenerated each run.
**Invariants**
- **Idempotent backfill.** Every run generates only the absent Fridays; a normal week appends the latest, the first run seeds the whole archive. Quiet weeks return early before any LLM call.
- **The pipeline owns every fact; the model only writes prose.** All hard numbers are rendered deterministically; `--polish` adds narrative via the editable [`voice.md`](../../../scripts/insights/weekly-update/voice.md). If the key is absent or the call fails, the deterministic report still ships — a voice edit can't corrupt the metrics.
- **Always lands on `main`, whatever triggered it.** Checkout pins `ref: main` and push targets `HEAD:main`; a dispatch from a feature branch still updates `main`.
- **Serialized, never raced.** Overlapping runs queue rather than cancel; the push retries ×3, rebasing the single update commit onto `origin/main`.
- **TREE.md is refreshed before generation** so the week's surface enrichment keys on files-as-of-Friday; the tree diff is computed for the latest Friday only (older weeks would show spurious churn).
Open on GitHubRaw Markdown source