feat(cli): add GitHub issue URLs to problem pages and rename guides to appendix

This commit is contained in:
Prad Nukala committed 2026-09-30 17:58:06 -04:00
1 parent 76c2a64fc6
commit c05e7f0ef2
2 files changed
+97 -33

No files matched your search

+12 -9
View File
@@ -14,17 +14,21 @@
* the same day.
*
* Each release is a changelog snapshot at that instant: the issue's problems
* (a topic's sub-issues plus those since re-parented into Review issues, or a
* review's sub-issues in solve order — see topics.ts) grouped as closed on the
* finish day (ET), closed earlier, and open at release, each with its set,
* difficulty, and the solution files as they stood in the tagged commit — the
* last main commit before the finish.
* that had closed by then (a topic's sub-issues plus those since re-parented
* into Review issues, or a review's sub-issues in solve order — see
* topics.ts), grouped as closed on the finish day (ET) and closed earlier,
* each with its set, difficulty, and the solution files as they stood in the
* tagged commit — the last main commit before the finish. Problems still open
* at the finish are left out: the release records what finished, and an open
* optional problem is tracked by its issue (or its Review day), not here.
* apps/docs/lib/releases.ts reads these `### Closed …` tables back for the
* docs `/changelog` timeline, so their row shape is its contract.
*
* The tag is annotated and backdated (tagger date = finish), so
* `git tag --sort=taggerdate` reads as the campaign's history even for tags
* minted by a backfill. The release's own published date cannot be backdated
* (the API has no field for it), so the docs changelog dates each entry from
* this timever tag instead (`releasesDatedByTag` in apps/docs/blume.config.ts)
* this timever tag instead (apps/docs/lib/releases.ts)
* — the tag format is that contract.
*
* Reconciles instead of reacting: every finished topic or review without a
@@ -201,11 +205,11 @@ function bodySection(body: string, heading: string): string | undefined {
function render(f: Finished, tag: string, sha: string, files: Map<number, string[]>): string {
const { issue, problems } = f;
const finishDay = etDate(new Date(f.finishedAt));
// Snapshot at the finish instant: a problem closed later was open then.
// Snapshot at the finish instant: a problem closed later was open then, and
// open problems are not part of the release.
const closedBy = (p: Problem) => p.closedAt !== null && p.closedAt <= f.finishedAt;
const onDay = problems.filter((p) => closedBy(p) && etDate(new Date(p.closedAt!)) === finishDay);
const earlier = problems.filter((p) => closedBy(p) && etDate(new Date(p.closedAt!)) !== finishDay);
const open = problems.filter((p) => !closedBy(p));
const row = (p: Problem): string[] => {
const lc = Number(p.title.match(/^LC (\d+)/)?.[1]);
@@ -248,7 +252,6 @@ function render(f: Finished, tag: string, sha: string, files: Map<number, string
const sections = [`<!-- release-topics: #${issue.number} -->\n${summary}`, fields.join(" · ")];
sections.push(`### Closed ${ET_DAY.format(new Date(f.finishedAt))} — the day the ${f.kind} finished\n\n${table(onDay)}`);
if (earlier.length) sections.push(`### Closed earlier\n\n${table(earlier)}`);
if (open.length) sections.push(`### Open at release\n\n${table(open)}`);
const notes = NOTES.flatMap((h) => {
const text = bodySection(issue.body, h);
return text ? [`**${h}.** ${text}`] : [];