Skip to content

todo: propose a standard for commit messages merged into main - #1557

Merged
sergey-shandar merged 8 commits into
mainfrom
claude/functionalscript-version-pr-xkwo6u
Aug 14, 2026
Merged

todo: propose a standard for commit messages merged into main#1557
sergey-shandar merged 8 commits into
mainfrom
claude/functionalscript-version-pr-xkwo6u

Conversation

@sergey-shandar

@sergey-shandar sergey-shandar commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Adds two todo files and links them from todo/changelog-from-git-history.md.

todo/commit-message-standard.md (P2, proposed) — the format for commits merged into main. The changelog-from-git-history investigation (P4) can only read history that exists — every PR merged before the message format is standardized adds a commit a future generator cannot parse, so the format must be fixed now:

  • Squash-and-merge only — disable rebase merges (GitHub replays branch commits with rewritten SHAs but their own messages: no (#NNN), no reviewed body, so the PR becomes invisible to a generator) and merge commits (non-linear graph). Squash never lands branch commits — it always creates one new commit titled <PR title> (#NNN); no true fast-forward is possible through the GitHub UI, only through a direct push, which branch protection closes.
  • Title = PR title in the changelog-entry style: <topic>: <short description>, ≤ 72 chars including GitHub's appended (#NNN); a release PR's title is the bare version.
  • Body = PR description (repo setting: default squash message "Pull request title and description"), ending with a mandatory Changelog: section holding exactly the entry list items — or Changelog: none — with **BREAKING CHANGES:** marked as in the files.
  • Tag releases vX.Y.Z (the repo currently has zero tags) so a generator has factual release boundaries.

todo/commit-message-enforcement.md (P3, open) — machine-checking the format before merge: a PR-lint workflow (pull_request with the edited trigger, self-hosted fjs/ci module) marked as a required status check, plus a post-merge audit on push to main for the one hole GitHub leaves open (editing the squash message in the merge dialog). Deliberately a separate issue: it starts only after the format is adopted into AGENTS.md §8 — the linter enforces the documented rule, not the proposal.

Note: the repository merge-method settings and branch protection are admin settings, not repository files — they need to be flipped in the GitHub UI.

Changelog: none

🤖 Generated with Claude Code

https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ

Squash-only merges (no rebase/fast-forward, no merge commits), PR title
in the changelog-entry topic style, squash body from the PR description
ending in a mandatory Changelog: section, and vX.Y.Z release tags —
fixed now so the history the changelog-from-git-history investigation
would read accumulates in a parseable format.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
functionalscript fb68943 Commit Preview URL

Branch Preview URL
Aug 14 2026, 07:08 PM

claude added 4 commits August 14, 2026 18:30
GitHub's rebase merge is not a literal fast-forward: it rewrites
committer and SHAs but keeps each branch commit's own message, which is
the actual reason it defeats a changelog generator (no PR number, no
reviewed body). Also state explicitly that squash never lands branch
commits and that only a direct push can truly fast-forward main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ
Pre-merge: a required status check on pull_request (including the
edited trigger, so fixing the title or body re-runs it) lints the PR
title and the Changelog: section; the linter is a self-hosted fjs/ci
module reusing the planned changelog Markdown parser. Post-merge: an
audit job on push to main backstops the one hole GitHub leaves open —
editing the squash message in the merge dialog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ
Enforcement (PR-lint required check, post-merge audit) starts only
after the format is adopted into AGENTS.md §8 — the linter enforces the
documented rule, not the proposal. The standard keeps the format,
settings, and tagging tasks and points to the new file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ
The gap between AGENTS.md adoption and machine enforcement is
deliberate: format mistakes found during hand use are documentation
edits, not linter changes plus a rule migration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed at 5db378e960820ec639f89e5add36b97649d885f5, merge-base cb1fcdc457ecb5ba3a20d994e4ba4e0c31d9577e — which is also current origin/main, so the branch is not behind.

Docs-only, confirmed from the merge-base diff rather than the title: three files, all todo/*.md, +179/-0, no code touched. Per AGENTS.md §8.3 no changelog entry is owed, and the description's Changelog: none matches. The build and test gates would be identical to main by construction, so I did not run them.

Since a proposal document's value is its factual accuracy, I re-derived every claim it makes about this repo's history against origin/main. Most reproduce. Two do not.

1. "every commit on main today already has this shape" does not reproduce

todo/commit-message-standard.md line 35, closing the squash-merge bullet:

A squash merge never lands branch commits: it always creates one new commit, parented on the main tip, titled <PR title> (#NNN)every commit on main today already has this shape.

The mechanism claim before the dash is right. The evidence clause after it is not, under either ruler:

$ git rev-list --count origin/main                                        1881
$ git log --format='%s' origin/main | grep -vcE '\(#[0-9]+\)$'             560
$ git rev-list --count --first-parent origin/main                         1803
$ git log --first-parent --format='%s' origin/main | grep -vcE '\(#[0-9]+\)$'
                                                                          486
$ git rev-list --count --merges origin/main                                15

So 486 of 1803 first-parent commits (560 of 1881 overall) lack the (#NNN) suffix, and main carries 15 merge commits, so it is not linear today either. The newest counterexamples are recent, not ancient:

7b979e74 2026-08-03 0.41.0
830a0c5e 2026-08-02 Fix spelling in comment about optimization

7b979e74 is 11 days old and is a release commit that landed with no (#NNN) — i.e. by direct push, not a squash-merged PR. That is a mild tension with the same document's release-title rule, which is otherwise well supported: 0.45.0 (#1555), 0.44.0 (#1513), 0.43.1 (#1448), 0.43.0 (#1443), 0.42.0 (#1441) all do go through PRs with a bare-version title.

What is true is the narrower statement:

$ git log --first-parent -100 --format='%s' origin/main | grep -vcE '\(#[0-9]+\)$'
0

The last 100 first-parent commits are uniform. So "every commit from the last hundred merges already has this shape" holds; "every commit on main today" does not. Worth narrowing, because the claim is also in slight tension with the Problem section's own premise — the argument for acting now is precisely that pre-standardization history is not uniformly parseable.

2. The "body ends with the Changelog: section" rule is violated by this PR's own description

commit-message-standard.md requires the Changelog: section to be "the last section of the body", and commit-message-enforcement.md specifies the linter as failing "unless the title matches the format and the body ends with a Changelog: section".

The repo's established PR-description shape puts an automated trailer after the prose. Of the last 25 merged PRs, 14 have a Generated with [Claude Code] / Claude-Session: / Co-Authored-By: trailer at the end of the body — including #1556, #1555, #1554, #1553, #1547, #1546, #1545. And this PR's own description ends:

Changelog: none

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ

So the linter as specified would fail #1557 itself, and roughly half of recent PRs. The fix is small — require the body to contain a Changelog: section and allow a trailer block after it (or define the section as the last section before any trailer, the way git interpret-trailers treats a trailer block) — but "ends with" is the wording the enforcement issue hands to whoever writes the workflow, so it is worth fixing in the proposal rather than in the linter.

Observation, not a defect: the ≤ 72-character title rule

The rule is proposed for the future, so it is not a factual error, but the adoption cost is larger than it may look:

$ git log --first-parent --format='%s' origin/main | awk 'length($0)>72' | wc -l    219
$ git log --first-parent -200 --format='%s' origin/main | awk 'length($0)>72' | wc -l  38

219 of 1803 first-parent titles exceed 72 characters including the (#NNN), and 38 of the last 200 — about 19% of current practice, e.g. Migration close-out: drop the .js ignore, browser-native browser-testing plan, @import sweep (#1545) at 100 characters and Exact emitted declarations: keep every @module header, remove the last elided any (#1526) at 89. Fine as a deliberate tightening; just noting that a required status check would start rejecting titles at that rate on day one.

What checked out

  • Tags. "Releases are not tagged (git tag is empty)" — confirmed, 0 local tags and git ls-remote --tags origin returns 0.
  • The body premise. Recent squash bodies really are GitHub's default concatenation: cb1fcdc4, 5efbe375, 8804e783, 4fbf0b71 all begin * <branch commit title>.
  • The quoted noise strings are real, not illustrative inventions: Address review matches 82 lines and Drop stray blank line 1 line across git log --format=%B origin/main.
  • Every referenced path exists and every link resolves: todo/changelog-from-git-history.md, todo/changelog-website.md, todo/commit-message-enforcement.md, changelog/README.md, changelog/unreleased/, fjs/ci/, and AGENTS.md, which does have a §8 with §8.3 and §8.4. No path:line citations to range-check.
  • The cross-reference is precise. changelog-from-git-history.md's "design 1" is in fact 1. **Commit-message extraction.** (line 27), so "its Changelog: section is design 1's input" is right.
  • "no PR link" in entries matches changelog/README.md as it stands after #1552/#1555: "New entries … contain no links: the file name is the pull-request number".
  • Conventions. **Priority:** / **Status:** headers match the sibling todo/changelog-*.md files; P2/P3 versus the investigation's P4 is consistent. todo/README.md is a placement guide, not an index, so no listing was owed.
  • Link check. bin/linkcheck.mjs gives 129 broken links in both trees and the sets are byte-identical (diff empty) against origin/main at cb1fcdc4. The three new files strand nothing and add nothing.

Not verified

The claim that commit-metadata rulesets "require an Enterprise plan" is a GitHub plan-tier detail I cannot check from the tree; I did not confirm or contradict it. The pull_request edited trigger and Require linear history branch protection both exist as described.

Both findings are wording-level and neither blocks the direction, which I think is sound — fixing the format before the history accumulates is the right ordering. Commenting rather than approving because item 2 would be inherited by the linter as a spec bug, and item 1 is an existential claim about the repo that a reader would reasonably take as verified.

The uniform-shape claim now states what git history supports: the last
hundred first-parent commits, not all of main (486 of 1803 lack the
suffix, 15 are merges, 0.41.0 was direct-pushed on 2026-08-03 — the
Problem section's own point). The Changelog: section is the last
section before an optional trailer block, not the end of the body —
about half of recent PR bodies end with a trailer, so 'ends with' would
have made the linter reject them. Also note the 72-char title limit is
a tightening: 38 of the last 200 titles exceed it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M296KXwQHHuUGhryRReKpJ

Copy link
Copy Markdown
Contributor Author

Both findings fixed in fb68943 — all your numbers reproduced after unshallowing the clone (1803 first-parent, 486 without the suffix, 15 merges, 0.41.0 direct-pushed 2026-08-03).

  1. The uniform-shape claim is narrowed to the last hundred first-parent commits, with the older-history counts stated and tied back to the Problem section's premise — the shape holds only while every landing goes through a squash-merged PR, and nothing enforces that today.
  2. The Changelog: section is now specified as the last section before an optional trailer block in both files, so the linter spec no longer rejects half of recent PR bodies (this one's included).

Also folded in your observation: the 72-char title rule now says it is a deliberate tightening, with the 38-of-200 figure.


Generated by Claude Code

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-reviewed at fb689437f426cd6aa11d3e671640bcc4792ec06d (previously commented at 5db378e9), against origin/main = cb1fcdc457ecb5ba3a20d994e4ba4e0c31d9577e. Both findings from the last round are addressed. I recomputed every number from git log rather than reading the new wording.

Finding 1 — "every commit on main today already has this shape" — closed

The claim is now scoped, and each of its parts reproduces exactly on origin/main:

claim in the PR measured
"the last hundred first-parent commits already have this shape" git log --first-parent -100 | grep -vcE '\(#[0-9]+\)$'0
"486 of 1803 first-parent commits lack the (#NNN) suffix" 486 of 1803
"15 are merge commits" --first-parent --merges15
"release 0.41.0 landed by direct push as recently as 2026-08-03" 7b979e74, dated 2026-08-03, single parent b835248d, and commits/7b979e74/pulls returns 0 associated PRs

For completeness: across the last 200 first-parent commits there are exactly 2 counterexamples — that 0.41.0 push and Fix spelling in comment about optimization (830a0c5e, 2026-08-02). So "the last hundred" is the tightest true bound, and it is the one stated. Whole-history figures are unchanged since my last round (main has not advanced past #1554), so nothing drifted.

Finding 2 — the rule rejecting its own proposal PR — closed

The spec now reads "the last section of the body before an optional trailer block" in commit-message-standard.md, with the matching phrasing in commit-message-enforcement.md. Checked against this PR's actual description: it ends Changelog: none, then a blank line, then 🤖 Generated with [Claude Code](…) and a session URL — which is precisely the trailer block the reworded rule exempts. The proposal now passes its own linter.

One correction to my own last round, and to the PR's figure. I previously said "14 of the last 25 merged PRs end with such a trailer". That undercounted — it was matching only the Co-Authored-By: / 🤖 Generated with shape and missing the bare https://claude.ai/code/session_… and _Generated by [Claude Code](…)_ forms. Recounting the last 25 merged PRs (#1532#1556) by their final non-empty body line: 25 of 25 end with a trailer, all of them one of those forms. So the PR's "about half of recent PR bodies end with one" is itself an understatement — it is currently universal. That does not change the design (the exemption covers 100% as well as it covers 50%), but if the number is going to appear in a todo that a future linter is written against, "essentially every recent PR body" would be the accurate phrasing.

Title cap — now stated, and exact

"38 of the last 200 titles exceed it today" reproduces exactly: awk 'length($0)>72' over the last 200 first-parent subjects → 38. Over all 1803 first-parent commits it is 219. Calling the ≤72 cap "a deliberate tightening, not current practice" is the right framing, and my earlier observation is satisfied.

Everything else

Docs-only — three files under todo/, no code touched, so per §8.3 no CHANGELOG entry is needed and correctly none is added. linkcheck broken-link sets are byte-identical to origin/main, so the two new files and the new references from todo/changelog-from-git-history.md strand nothing. No Rust, no generator, no public surface.

Still explicitly unverified, as last round: the "commit-metadata rulesets require Enterprise" plan-tier claim in the enforcement doc. That is a GitHub billing-tier fact I cannot check from here, and the doc's fallback plan does not depend on it.

One cosmetic nit: todo/commit-message-enforcement.md:29 came out 94 characters wide —

when the title or description is fixed — no push needed to re-green. Branch protection marks

— which is the only line over 80 in either changed file; the reflow just missed it.

Approving.

@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit ecff2e8 Aug 14, 2026
19 checks passed
@sergey-shandar
sergey-shandar deleted the claude/functionalscript-version-pr-xkwo6u branch August 14, 2026 20:46
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