Skip to content

ci(release): baseline five squash-stamped identity trailers blocking v0.10.0 (#17717) - #17718

Merged
mrveiss merged 2 commits into
mainfrom
hotfix-trailer-baseline-17717
Sep 28, 2026
Merged

mrveiss merged 2 commits into
mainfrom
hotfix-trailer-baseline-17717

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Thinking Path

#17717 promotes main → release for v0.10.0 and fails No commit trailers on commits already landed. Per-PR scans passed — including #17696's own — because the gate compares BASE..HEAD and the trailer does not exist until GitHub's squash-merge creates it. Only a promotion re-scans the landed range, so the check runs against an object no PR ever showed it. That is the same "correct for all N inputs, cannot say which" seam as the rest of today; here the input the check never saw is the merge commit.

I derived the SHAs from the run log rather than taking a list, and the count needed resolving. The log has ten Offending commit: lines and nine unique SHAs. The tenth is the workflow's own printf template echoed into the log:

Offending commit: %s\n' "${bad[@]}"

So nine real commits. A count taken from the line count would have been an artefact of the measurement, not the population — and both the coordinator and I had independently seen "9 against 10" without resolving it.

Then I checked each one's trailer, and they are two classes, not one:

five   Co-authored-by: t <t@example.invalid>
four   Co-authored-by: Claude <noreply@anthropic.com>

What Changed

Only the five are baselined.

They are the existing test <test@test> cause a second time: a local user.name/user.email override shadowed the correct global identity, branch commits carried it, and the squash stamped it onto the result. t <t@example.invalid> is not a person and claims no attribution.

The source is closed — no worktree config and no global config resolves user.name to t. Checked, not assumed, and the header says so for a specific reason: the previous instance's header stated the override was "since corrected", and it was, and a different one appeared anyway. The claim was true and insufficient. What bounds this class at five is the check, not the sentence.

The four are held, and the header says why. Co-authored-by: Claude is a real attribution claim — precisely what no-commit-trailers.yml (#13654) exists to reject — not a misconfigured identity. The reasoning that justifies the five does not reach it, so including it would exempt the thing the rule is for, under a ruling about something else. The header states explicitly that a reader must not conclude a Claude co-author is baselinable.

And its source is not closed, which is why it is a separate question rather than a longer list: the agent harness instructs sessions to append that trailer, while this project's rules forbid it and this gate enforces them. Until that conflict is resolved at the source, new instances keep landing and a baseline entry treats the symptom. Filed separately.

The top rule is narrowed, not suspended. It said entries may only be removed. It now names the one class an addition is permitted for — a trailer no branch commit carried, created by the squash, discovered only at promotion, on a history that cannot be rewritten — and says that a trailer which was on a branch commit does not qualify, because that one was fixable and the fix is to fix it.

Verification

The gate's own parse, run against the file as committed:

$ sed 's/#.*//' .github/commit-trailer-baseline.txt | tr -d '[:blank:]' | grep -v '^$'
27 entries   (22 existing + 5)

exempted?  1c00b680ef2e  no      337434690c4c  yes
           31a01bdb387f  no      479618d81460  yes
           b0f3ac188c81  no      6d61b8778431  yes
           c9b412b4481c  no      969c1956792e  yes
                                 a4768fd323f5  yes

Exactly the five intended are exempt; the four named in the header comment are not — checked because their SHAs appear in prose in that file, and a #-stripping parser that treated them as entries would silently grant the exemption this PR deliberately withholds.

Each of the five verified with git log -1 --format=%B <sha> to carry t@example.invalid before being added. A SHA in a baseline that does not carry the trailer is an exemption for nothing, and it looks authoritative.

The workflow is untouched. The broader fix — exempting promotion PRs from the co-author arm — was considered and not taken here.

Not claimed

This does not fix the gate's blind spot. No commit trailers will keep passing per-PR and failing at promotion for any future squash-stamped trailer, because it scans BASE..HEAD by design. That is worth its own issue against #13654 and is not something a baseline entry addresses.

Refs #17717
Refs #13654

Model Used

Claude Opus 5 (1M context)

…v0.10.0 (#17717)

#17717 promotes main -> release and fails "No commit trailers" on commits
already landed. Per-PR scans passed -- including #17696's own -- because the
gate compares BASE..HEAD and the trailer does not exist until GitHub's
squash-merge creates it. Only a promotion re-scans the landed range, so the
check runs on an object no PR ever showed it.

DERIVED AND VERIFIED, NOT TAKEN FROM A LIST. The run log reports ten
"Offending commit:" lines and nine unique SHAs. The tenth is the workflow's own
printf template -- `Offending commit: %s\n' "${bad[@]}"` -- echoed into the log.
Nine real commits, and a count taken from the line count would have been the
measurement's artefact rather than the population.

The nine are TWO classes, established by reading each commit's trailers rather
than assuming one cause:

  five   Co-authored-by: t <t@example.invalid>
  four   Co-authored-by: Claude <noreply@anthropic.com>

ONLY THE FIVE ARE BASELINED. They are the same cause as the existing
`test <test@test>` entries, a second time: a local user.name/user.email
override shadowed the correct global identity, branch commits carried it, and
the squash stamped it onto the result. `t <t@example.invalid>` is not a person
and claims no attribution.

The source is CLOSED -- no worktree config and no global config resolves
user.name to `t`, checked rather than assumed. That check is what makes the
class bounded at five, and the header says so, because the previous instance's
header claimed the override was "since corrected" and a different one appeared
anyway. The claim was true and insufficient; the check is the thing.

THE FOUR ARE HELD, AND THE HEADER SAYS WHY. `Co-authored-by: Claude` is a real
attribution claim -- exactly what no-commit-trailers.yml (#13654) exists to
reject -- not a misconfigured identity. The reasoning that justifies the five
does not reach it, so folding it in would exempt the thing the rule is for on a
ruling about something else. A reader of this file must not conclude that a
`Claude` co-author is baselinable.

Its source is also not closed, which is why it is a separate question rather
than a longer list: the agent harness instructs sessions to append that
trailer while this project's rules forbid it and this gate enforces them. Until
that is resolved at the source, new instances keep landing and a baseline is
treating the symptom. Filed separately.

THE TOP RULE IS NARROWED, NOT SUSPENDED. It said entries may only be removed.
It now names the one class an addition is permitted for -- a trailer no branch
commit carried, created by the squash, found only at promotion, on a history
that cannot be rewritten -- and says explicitly that a trailer which WAS on a
branch commit does not qualify, because that one was fixable and the fix is to
fix it.

Verified the gate's own parse: of the nine, exactly the five appear in
`sed 's/#.*//' | tr -d '[:blank:]'`, and the four named in the header comment
do not.

The workflow is untouched.

Refs #17717
Refs #13654
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 45 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c2b60325-4d8c-4991-bb32-df0ad926799e

📥 Commits

Reviewing files that changed from the base of the PR and between 0811143 and 679cec8.

📒 Files selected for processing (1)
  • .github/commit-trailer-baseline.txt

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

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

@mrveiss mrveiss left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review — the five are right, confirmed by a second route. One paragraph is wrong, and the check this PR ran by hand is the guard the file does not have.

The five entries are correct. I measured the population independently of the run log, from the commit objects rather than from the workflow's output:

git log origin/release..origin/main --format='%(trailers:key=Co-authored-by,valueonly)'
  99 commits, 33 with a Co-authored-by
  35  github-actions[bot] (two spellings)  -> allowed, bot pattern
   4  Claude <noreply@anthropic.com>
   5  t <t@example.invalid>

The five SHAs added here are exactly those five, and the four named in the header comment are exactly the other four. Two routes to the same nine — the log parse whose %s template you correctly discounted, and the trailer field itself. No commit trailers is SUCCESS on this PR, so the gate agrees.

Strike the THE SOURCE IS CLOSED paragraph

no worktree config and no global config resolves \user.name` to `t` -- checked, not assumed`

The measurement is real and it is about the wrong object. t@example.invalid is this repository's own test-fixture identity — nine files carry it, every one a test suite:

.claude/hooks/block-dangerous-commands_test.sh   scripts/lib/branch-guards_test.sh
autobot_shared/paths_test.py                     scripts/lib/session-handoffs_test.sh
pipeline-scripts/check_baseline_no_growth_test.py  scripts/lint_conventions_test.py
repo_tests/hooks_path_override_15961_test.py     scripts/research/safe_clone_test.py
repo_tests/stylelint_changed_files_skip_deletions_17571_test.py

Three of them write it into a repository's config rather than passing it per-invocation:

scripts/lib/branch-guards_test.sh:117       git -C "$TESTREPO" config user.email t@example.invalid
scripts/lib/session-handoffs_test.sh:88     git -C "$TMP_REPO" config user.email t@example.invalid
repo_tests/hooks_path_override_15961_test.py:479  _git("config", "user.email", "t@example.invalid", cwd=main)

Two things follow, and together they undo the paragraph:

  1. Worktrees share one config through the common dir, so a single such write with a mis-resolved repo path sets the identity for every worktree, which is the shape of five commits across five branches in one afternoon.
  2. A config inspection afterwards cannot see a write that was later corrected — which is precisely the failure the paragraph immediately below it describes, one level up. The previous instance's "since corrected" was true and insufficient; "no config resolves t today" is true and insufficient in the same way.

This is #15353 (two shell test suites wrote git commits/config onto the real repo under an ambient GIT_DIR — no guard catches the class), open, with #16124 as the detection gap. So the class is not bounded at five and new ones will arrive. The sentence you already wrote — "What makes this bounded is the check, not the claim" — is the correct one; delete the claim it is contradicting and point at #15353 instead. Posted with the SHAs on #16268.

Holding the four Claude commits out, and saying in the header that a reader must not conclude they are baselinable, is right and is the part of this PR I would not change.

The verification in the body is the guard this file lacks

You verified by hand that each of the five carries the trailer, with the reason stated: "a SHA in a baseline that does not carry the trailer is an exemption for nothing, and it looks authoritative." Nothing in the tree checks that. stranded_recorder_entries_test.py:164-168 deliberately excludes this file — "neither entry is a path, so path-existence is the wrong staleness test" — which is correct about path-existence and leaves the file with no guard at all: its own header declares it "a RATCHET, not an allowlist" whose entries "may only be REMOVED", and that rule is a comment.

A SHA-specific check sidesteps the stated reason for the exclusion entirely: for each entry, (a) the SHA is reachable from release/main, and (b) its message carries a trailer the gate bans. (b) catches the exemption-for-nothing; (a) catches an entry stranded by a rewrite. Worth its own issue alongside the blind-spot one you already scoped under #13654 — the gate's BASE..HEAD scan cannot see the object the squash creates.

State

Green where it counts (No commit trailers, code-quality, smoke-test); 12 contexts still reporting, none red. Not approving — every session here authors as the repository owner, so GitHub refuses the approval and this is a comment review by necessity, not by reservation. On the header paragraph corrected in this PR rather than a follow-up, it is good to land.

…e class-1 cause (#17717)

Two corrections to the first commit on this branch, both because the original
was wrong about a fact it asserted.

ALL NINE ARE NOW BASELINED, by explicit owner ruling. The first commit carried
five and held four; the ruling reached both this session and the coordinator's
and now agrees. The class-2 paragraph is unchanged in substance -- it still says
a `Co-authored-by: Claude` trailer is the thing the gate exists to reject, is
exempted only because `main` cannot be rewritten, and is NOT a precedent -- and
it now also says its source is open, which makes it worse than class 1 rather
than merely different.

CLASS 1 WAS NOT A MISCONFIGURED USER IDENTITY. The first commit said the source
was closed because no worktree config and no global config resolves `user.name`
to `t`. That measurement was correct and about the wrong thing.

`t <t@example.invalid>` is this repository's TEST-FIXTURE identity -- ten files
carry it, eight run `git commit`, two of those under the pre-push hook. The
reason it reaches the real repository is that an inherited `GIT_DIR` OUTRANKS
`-C` and `cwd=`; `hooks_path_override_15961_test.py:450` records learning that
the hard way. So no config override was ever there to find, and checking config
is how you fail to find it.

Three of the fixture files write the identity with `git config` rather than a
per-invocation `-c`. Worktrees share one config through the common dir, so a
single unscrubbed write sets it for every worktree at once -- which is the shape
of five commits across five branches in one afternoon.

WHAT I VERIFIED RATHER THAN RELAYED: every one of those sites currently scrubs.
All eight committing tests pass `scrubbed_git_env()`, and all three config
writers unset the ambient variables -- checked per file, not inferred from the
helper existing. So no current test leaks, and the header says that instead of
claiming the source is closed.

The open window is a NEW fixture that commits or writes config, run before its
scrub is added. That is what happened here: 969c195 is the commit that
introduced one such test. Source is #15353, open; detection gap #16124;
release-facing home #16268, which already carries 18 from an earlier promotion.

So this baseline is the third containment of one root cause -- 22, then 18, now
9 -- and the header says so, because whoever picks up #15353 should be able to
find the cost of not doing it in one place.

Gate parse verified against the file as committed: 31 entries, all nine present.

Refs #17717
Refs #13654
Refs #15353
@mrveiss
mrveiss merged commit af1c060 into main Sep 28, 2026
51 checks passed
@mrveiss
mrveiss deleted the hotfix-trailer-baseline-17717 branch September 28, 2026 14:43
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.

1 participant