Skip to content

plan: single structured root CHANGELOG (B + C)#374

Merged
brianmhunt merged 2 commits into
mainfrom
plans/changelog-dark-factory
May 4, 2026
Merged

plan: single structured root CHANGELOG (B + C)#374
brianmhunt merged 2 commits into
mainfrom
plans/changelog-dark-factory

Conversation

@brianmhunt
Copy link
Copy Markdown
Member

Summary

The default changeset version writes a CHANGELOG.md per package. With TKO's 27-package fixed-version group, a single cross-cutting concern lands in eight package files verbatim β€” modernize-utils-dead-polyfills did exactly this on the 4.1.0 cut. To answer "what landed in 4.1.0?" an agent has to find every CHANGELOG, dedupe, and reconstruct a story we already wrote once in .changeset/*.md.

This plan replaces that with a single root CHANGELOG.md carrying structured per-release entries (B + C combined).

Options surveyed

  • A. Top-level CHANGELOG, per-package stubs.
  • B. Single source, no per-package files.
  • C. B + structured release notes (curated summary + bullet highlights + package list + changeset references).

Recommendation: B + C together

The same custom changelog generator that makes B work is the hook C needs for structured output β€” doing them in one PR is one piece of work, not two. The maintainer (or agent acting as release manager) edits a placeholder summary before merging the version PR; for trivial patch releases, the placeholder ships as-is.

Implementation surface

  • tools/changesets/root-changelog.cjs β€” implements the Changesets getReleaseLine/getDependencyReleaseLine interface.
  • tools/changesets/version.cjs β€” wraps changeset version, deletes per-package CHANGELOGs, aggregates into one structured root entry.
  • .changeset/config.json β€” points at the custom generator.
  • release.yml β€” one-line change to version: node tools/changesets/version.cjs.
  • One-time cleanup: rm packages/*/CHANGELOG.md builds/*/CHANGELOG.md.

Tradeoffs

  • npm package pages no longer show per-package histories β€” point them at the repo CHANGELOG via package descriptions.
  • Authoring discipline: maintainer edits placeholder summary for non-trivial releases.
  • Renovate/Dependabot fall back to GitHub Release when CHANGELOG.md is absent β€” verify before landing.

Out of scope

Test plan

  • Plan reviewed for accuracy
  • Recommendation (B+C together vs B-then-C) approved
  • Implementation surface scoped (tools/, config, workflow change, cleanup)

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv


Generated by Claude Code

Copilot AI review requested due to automatic review settings April 27, 2026 16:48
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Warning

Rate limit exceeded

@brianmhunt has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 30 minutes and 36 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

βŒ› How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 464a61d4-7f79-4e1f-bff4-7104fc3bcc7b

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between c4c1ae1 and 766aa5b.

πŸ“’ Files selected for processing (1)
  • plans/changelog-dark-factory.md
✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch plans/changelog-dark-factory

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share
Review rate limit: 0/1 reviews remaining, refill in 30 minutes and 36 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Dark Factory plan proposing a shift from per-package Changesets-generated changelogs to a single, structured root CHANGELOG.md, optimized for cross-release narrative and agent-friendly retrieval in TKO’s fixed-version monorepo.

Changes:

  • Introduces a plan documenting current changelog pain points in a 27-package fixed-version group.
  • Evaluates options (A/B/C) and recommends B+C: one root changelog plus structured per-release entries.
  • Sketches the intended implementation surface (custom Changesets changelog generator + version wrapper script + workflow change + one-time cleanup).

Comment thread plans/changelog-dark-factory.md Outdated
Comment on lines +248 to +250
- **Single-action release flow.** Tracked in
`plans/single-action-release.md` β€” about pipeline triggers, not
artifact shape.
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This plan links to plans/single-action-release.md, but that file doesn’t exist in plans/ on this branch. Update the reference to point at an existing plan (or include the referenced plan file in this PR) so readers can follow the intended cross-plan dependency.

Suggested change
- **Single-action release flow.** Tracked in
`plans/single-action-release.md` β€” about pipeline triggers, not
artifact shape.
- **Single-action release flow.** Tracked separately as release
automation work β€” about pipeline triggers, not artifact shape.

Copilot uses AI. Check for mistakes.
@brianmhunt
Copy link
Copy Markdown
Member Author

Adversarial review

Verified against repo state on main.

Verified βœ…

  • 8 CHANGELOG.md files contain the modernize-utils-dead-polyfills body verbatim. Pain point is real.
  • getReleaseLine / getDependencyReleaseLine are the correct interface (per @changesets/types).
  • All 27 packages' package.json files arrays are ["dist/"] β€” CHANGELOG.md is not included in published tarballs.

Findings

  1. Package count is 27, not 26. .changeset/config.json fixed[0] has 27 entries (verified via jq '.fixed[0] | length'). One adversarial pass got this wrong; just confirming.

  2. [blocker] version.cjs second-run is racy. The plan says step 2 is "read what would have been written, then delete". changeset version appends to existing CHANGELOG.md. After the one-time cleanup, file does not exist for run πŸš€ Merges and goalsΒ #1 β€” clean. Run Merges from mainlineΒ #2: file gets created by changeset version again. Run Separate tko.utils into multiple modulesΒ #3+: the wrapper would be reading multi-release files and needs a rule to distinguish "just-written" from "history". Plan does not specify. Either the wrapper deletes after every run (seems intended but not stated) or it diff-detects the new section.

  3. [blocker] Conflicts with PR chore(release): collapse release flow to single human actionΒ #377 / plan plan: collapse release to a single human actionΒ #373. PR chore(release): collapse release flow to single human actionΒ #377 (already open against main) sets version: npx changeset version in release.yml. This plan changes it to version: node tools/changesets/version.cjs. The two PRs edit the same field; whichever lands second silently overrides the first. Order must be defined: this plan's impl PR rebases on chore(release): collapse release flow to single human actionΒ #377, or the field is left as npx changeset version until both plans converge in a single impl PR.

  4. [blocker] Verification command is broken. Plan: find . -name CHANGELOG.md -not -path './node_modules/*'. The repo also has .claude/worktrees/*/packages/*/CHANGELOG.md and .claude/worktrees/*/builds/*/CHANGELOG.md (Claude worktree clones). The check needs to exclude .claude/worktrees and .git/worktrees or it returns false negatives forever.

  5. [tradeoff] npm-tarball motivation is softer than stated. The CHANGELOG.md is already excluded from every published tarball (files: ["dist/"] in all 27). npm package pages don't render CHANGELOG content from the tarball β€” they render it from the repo URL (repository.url field). Removing per-package CHANGELOG.md does break the "open npm page β†’ click CHANGELOG link" path, but it doesn't change tarball content. The plan's "npm pages no longer show per-package histories" framing implies the tarball matters; it doesn't. Re-state the cost as "deep-link experience for downstream consumers", not "tarball content".

  6. [tradeoff] "nx, pnpm itself" comparison cuts the wrong way. Both nx and pnpm do ship per-package CHANGELOG.md files. Spot-check before relying on this in the rationale.

  7. [tradeoff] Renovate fallback is unverified. Plan says "Renovate falls back gracefully to the GitHub Release when CHANGELOG.md is absent" but lists this in Tradeoffs as "verify before landing". Recommendation has already committed to B+C while the listed risk is open. Renovate's release-notes extractor reads CHANGELOG.md from the repo and renders nothing if absent β€” it does not automatically swap to the GitHub Release. Verify before recommendation hardens.

  8. [tradeoff] Deleted-history is asymmetric with the rest of the plan. Mitigation = "git log". External consumers who deep-linked to packages/utils/CHANGELOG.md#400 will 404. Suggest at least a redirect note in the release announcement, or a CHANGELOG-archive.md at the package root with a one-line "see root CHANGELOG for current". Cheap, preserves discoverability.

  9. [nit] Structured template hard-codes "All N fixed-group packages β†’ X.Y.Z". True today (fixed group covers everything published). If a package ever leaves the group (e.g. a builds/* goes independent), the template silently lies. Generate the count from .changeset/config.json at version time instead of hard-coding.

  10. [nit] .changeset/config.json snippet uses null as the second array element (changelog config). Valid per changesets schema but unusual β€” most generators take an options object. Plan should call out why null here (no per-changeset config; all formatting decisions are in the generator).

  11. [nit] Footnote [1] at the bottom is the only inline-link reference in that style. Inconsistent with the rest of the doc.

Verdict

Direction is right and the dark-factory framing fits TKO's reality (fixed group, agent-readable artifact). Request changes for: blockers #2, #3, #4 before this becomes the implementation; #5–#8 reframe in the doc; #9–#11 nits. Coordinate landing order with #377.

claude and others added 2 commits May 4, 2026 11:47
The default changesets-per-package layout assumes Lerna-era independent
packages with separate audiences. TKO ships as a 27-package fixed group
on one version line β€” same body lands in 8 changelogs on 4.1.0.

Plan recommends B + C together for 4.2.0:
- B: single root CHANGELOG.md, no per-package files
- C: structured release entries (curated summary + bullet highlights +
  package list + changeset references)

Rationale for B+C together: the same custom changelog generator that
makes B work is the hook C needs for structured output, so doing them
in one PR is one piece of work, not two. A and "B then C later" both
left on the table for context.

Documents the implementation surface (tools/changesets/version.cjs +
root-changelog.cjs, .changeset/config.json swap, one-time cleanup of
packages/*/CHANGELOG.md), authoring discipline (placeholder summary
mergeable as-is for trivial patches), and verification.

Out of scope: pipeline reforms (covered in
plans/single-action-release.md).

https://claude.ai/code/session_01Jnv99bwWtFWFmsyL7A2cJv
Three rounds of adversarial review took the recommendation from
B+C β†’ A only β†’ A with a wrapper rather than a custom generator.

- Custom getReleaseLine cannot reach root CHANGELOG (no package /
  version context, no FS side-effect contract). Plan now spells out
  why a wrapper is required and what the interface limitations are.
- Wrapper snapshots per-package CHANGELOGs and .changeset/*.md
  frontmatter pre-run, so dedup keys on declared package list
  (intentional scope), not bumped fixed-group set (always 27).
- Pre/post diff captures just-written bytes precisely; "topmost
  heading" was ambiguous on re-run.
- Root heading is bare `## X.Y.Z` (date in body line); GFM slug
  stable; per-package stub anchors `#410` etc resolve correctly.
- Drop Prettier reference; Biome doesn't format Markdown.
- Workflow invocation: `node tools/changesets/version.cjs`
  (matches existing tools/release-version.cjs pattern), not
  `bunx node ...`.

Recommendation: A only (root narrative + per-package stubs).
B and C explicitly rejected with reasoning preserved.

Adversarial review: three subagent passes. First flagged 5 blockers
in the initial generator-only design (interface limitations);
second confirmed the wrapper architecture is sound and flagged 3
remaining blockers (bunx-node syntax, slug collision with date in
heading, dedup on declared vs bumped). All three addressed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@brianmhunt brianmhunt force-pushed the plans/changelog-dark-factory branch from a3ccb98 to 766aa5b Compare May 4, 2026 15:56
@brianmhunt brianmhunt merged commit 681f036 into main May 4, 2026
9 checks passed
@brianmhunt brianmhunt deleted the plans/changelog-dark-factory branch May 4, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants