From 7e9ea8d7c213c96f8903c58782e7908b2a3310f9 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Wed, 30 Sep 2026 16:31:22 -0400 Subject: [PATCH] =?UTF-8?q?docs(agents):=20clarify=20release=20job=20inclu?= =?UTF-8?q?des=20Review=E2=80=AFN=20in=20architecture=20diagram?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3a77d8c..ef2dc85 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,7 @@ flowchart LR work -->|close-solved.yml| PI[problem issues] work -->|close-solved.yml POST /admin/solved| D1[(D1 = SRS state)] PI -->|close-topics.yml| TI[topic + Review N issues] - PI -->|close-solved.yml release job| Rel[timever release per finished topic] + PI -->|close-solved.yml release job| Rel[timever release per finished topic / Review N] GH[GitHub issues = catalog] -->|nightly reconcile| D1 D1 -->|8 AM ET cron| Mail[digest email + one-tap links] Mail -->|GET /log| D1 @@ -39,7 +39,7 @@ flowchart LR - **DST-proof crons:** each event has two UTC crons; code fires only on the computed ET hour (`etHour`), unit-checked in `apps/api/src/srs.test.ts`. - **Chained workflows:** `GITHUB_TOKEN`-driven issue closes fire no `issues` events, so `close-topics.yml` chains off `workflow_run` of Close Solved instead. The Worker's webhook uses its own `GH_PAT`, so its events flow normally. - **One "finished topic" rule, two consumers.** `apps/cli/topics.ts` (`coreSet()`: every `set:core` sub-issue closed, zero core = never) is read by `close-topics.ts` (closes the topic issue) and `release-topics.ts` (the `release` job of `close-solved.yml`). The release reads the sub-issues, not the topic issue, so the push that finishes a topic releases it in the same run instead of waiting for Close Topics to chain after it. Version = [timever](https://gist.github.com/twolfson/de1b004dd22536b8e668) (`YYYYMMDD.HHMMSS.NNNNNNNNN`, UTC) of the instant the last core problem closed; the tag is annotated and backdated to that instant, on the last first-parent main commit before it; the body is a changelog snapshot at that instant (a one-sentence summary first — Blume's changelog makes it the page's meta description — then closed on the finish day / earlier / open at release, with solution links at the tag). A release is found by its `` body marker, never by tag name, so re-closing a core problem never mints a second one; an existing release keeps its tag and has its name (the topic title) and body rewritten whenever the render changes. The docs `/changelog` is these releases (`githubReleases` in `apps/docs/blume.config.ts`, fetched at build with `GITHUB_TOKEN`), dated by the timever tag, not `published_at` (which the API cannot backdate): `releasesDatedByTag` rewrites each timever release's `published_at` before Blume maps it, via `custom(githubReleasesSource(…))` because the config-level `githubReleases()` takes no `fetchImpl`. Changing the tag format breaks the changelog dates. -- **Review issues re-parent problems; topics still own them.** The transfer phase's 20 `Review — ` issues (label `review`, milestone `Review — Transfer & Mocks`; not the Worker's `Review — Week N` gate issues) hold open optional/deferred problems as sub-issues in solve order, moved there with `replace_parent`. `readReviews()`/`movedFromTopics()` in `apps/cli/topics.ts` recover each moved problem's topic from its last `parent_issue_removed` event naming a topic, and `readProblems()` returns it with the topic's direct sub-issues — so the finished rule and release bodies do not change when a problem moves. `close-topics.ts` also closes a Review issue once every sub-issue is closed (any set). The Worker's catalog reconcile still walks topic sub-issues only, so a moved problem's D1 row keeps its last-synced labels and topic. +- **Review issues re-parent problems; topics still own them.** The transfer phase's 20 `Review — ` issues (label `review`, milestone `Review — Transfer & Mocks`; not the Worker's `Review — Week N` gate issues) hold open optional/deferred problems as sub-issues in solve order, moved there with `replace_parent`. `apps/api/src/reviews.ts` is the ONE recovery of ownership — `readReviews()` (one GraphQL request per 50 `review` issues, cheap enough for the Worker's cron subrequest budget) reads each moved problem's topic off its last `parent_issue_removed` event naming a topic, `movedFromTopics()` groups them — shared by the Worker's catalog reconcile (a moved problem's D1 row keeps syncing its labels and `topic_issue`) and `apps/cli/topics.ts` (`readProblems()` returns a topic's sub-issues plus its moved problems, so the finished rule and existing release bodies do not change when a problem moves). A Review issue finishes by `completion()` in `topics.ts` — every sub-issue closed, any set — which `close-topics.ts` closes it on and `release-topics.ts` releases it on, exactly like a topic: timever of the last close, same `` marker (issue numbers never collide), body fields from the review's `**Scheduled:**`/`**Phase:**`/`**Load:**` lines. The docs changelog tags each row `Topic` or `Review` from the release name (`releasesByKind` in `apps/docs/blume.config.ts`, rewriting Blume's fixed `Release` category in both `data` and the staged frontmatter). ## Key Directories