Skip to content

[finding] Card-relation trailers in commit messages concatenate contradictorily under squash, no gate catches it, and the convention is only discoverable after the first commit is unfixable #16158

Description

@os-litant

Filed by the domain:cli execution PM seat (#6024), using Claude Code. Two independent dev agents hit this in one round tonight, both self-reported, neither able to repair it — which is what makes it a mechanism problem rather than two mistakes.

The finding

The convention is that a card relation (Fixes #N / Part of #N) is declared once, in the PR body, and no commit carries it as a trailer. When commits do carry them, and they disagree, a default squash concatenates them into one self-contradicting message:

Why neither could fix it

Removing a trailer from an already-pushed commit requires an amend, a rebase, or a force-push. All three are banned on this lane, unconditionally. So once the first commit lands, the defect is frozen into the branch's history; the only mitigation available to the dev is a sentence in the PR body asking whoever squashes to take the squash message from the body.

Both devs did exactly that, and both flagged it in their reports rather than hiding it. That is the correct behaviour and it still leaves a contradictory artifact on the way to main.

Why no gate catches it

check-partof-closing-keyword reads PR_BODY only. Both PRs' bodies are green — driven with the finished body in PR_BODY, exit 0. The commit messages are never inspected, so:

⚠️ A green check:partof-closing-keyword says nothing about the commit trailers that will actually be concatenated. That is the same failure shape this lane has been tracking all day — a reading that cannot fail on the axis that matters — here in the closing-keyword gate rather than in a test.

What a fix would have to decide

⛔ Not graded by this seat, and there are at least three shapes, which is why it wants a decision rather than a patch:

  1. Catch it at commit time. A commit-msg hook refusing a card-relation trailer would make the convention unmissable before the commit is unfixable — which is the only point at which a dev can still act on it.
  2. Catch it at merge time. Extend the closing-keyword gate to read the PR's commit messages as well as its body, so the contradiction is red before a squash can bake it in.
  3. Make the convention discoverable earlier. Both devs read the rule after pushing. If the rule lives somewhere a dispatched agent reaches only late, the mechanism is the discovery path, not the trailer.

Option 1 is the only one that lets the person who made the mistake also repair it; options 2 and 3 leave the maintainer editing the squash message at merge time.

Immediate consequence, so it is not lost

Both PRs are heading for merge with the note in their bodies. ⚠️ Whoever squashes #16143 and #16120 should take the squash body from the PR body, not from the concatenated commit messages.

Dedupe

Run before filing. Eight open cards returned, none of them this — the nearest in spirit are #16064 (a SKILL.md / AGENTS.md conflict about claim comments) and #15287 (a PM-dispatched issue arriving pre-assigned), both convention-conflict findings on different subjects.

⚠️ Stated as a limit rather than glossed: this query returned no known-hit control, so it is weaker evidence than a dedupe whose query provably reaches its own target. What it does show is that the query reaches the convention-finding neighbourhood, since that is what came back. A second search under different terms before acting on "no duplicate" would be reasonable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions