Skip to content

The squash message lint runs from agentrepo merge #865

Description

@tbhb-agent

Problem

The squash message must pass the repository's lint against dictionaries from the pull request head, not from whichever branch the operator has checked out. Today lint-squash-msg.sh (37 lines) finds the pull request, reads its head, and calls ma_lint in merge-agent.sh (about 45 lines), which fetches the pinned commit, copies .cspell.jsonc and .cspell-words.txt out of it into a temporary directory, and runs either mise run lint-squash-msg or the configured lint_squash_cmd. Review and merge both call it. It is under the shell freeze in #588.

Part of #324, within #588. Child 06 in #324's Children list. Replaces packages/agents/source/skills/merge-pr/shared/scripts/lint-squash-msg.sh and the lint function of packages/agents/source/skills/merge-pr/shared/scripts/merge-agent.sh.

Acceptance criteria

  1. AC1: Given a pull request number, or a SQUASH_AGENTMSG subject ending in (#<number>), when agentrepo merge lint runs, then it lints against the dictionaries at the pull request's head and exits with the lint command's status.
  2. AC2: Given no number and a subject without (#<number>), or a head that is not a full 40-character commit, when the command runs, then it exits 1 with the same message as the shell script.
  3. AC3: Given the head carries .cspell.jsonc or .cspell-words.txt, when the lint runs, then those head copies are used and removed afterwards; given the head lacks them, then the lint runs without them and any inherited DEVTOOLS_CSPELL_CONFIG is dropped.
  4. AC4: Given the pinned head commit is not in the object store and cannot be fetched, when the command runs, then it exits 1 with Could not fetch the pinned pull request head.; given the commit is present but a dictionary file cannot be extracted from it, then it exits 1 with Could not read <file> from the pull request head. (the file exists in the head but its extraction fails, as in tests/skills/merge-pr/scripts/merge-workflow.bats:277; a head with no dictionary is the successful optional case of AC3); the fetch moves no branch.
  5. AC5: Given unattended mode, when the command runs, then it runs the repository's lint_squash_cmd (default mise run lint-squash-msg) and refuses an empty value; given attended mode, then it runs mise run lint-squash-msg.
  6. AC6: Given a successful lint, a failing lint, a configuration refusal, and a dictionary-read failure, when the command ends, then the temporary directory is gone in every case.
  7. AC7: Non-functional: the lint behavior is reusable by later commands, so that a second caller gets the same result as agentrepo merge lint, and the command runs on macOS, Linux and Windows in CI.
  8. AC8: Given a divergent checkout, when the command runs, then lint_squash_cmd comes from the pinned base through Read gate settings from the pinned base in Go #980's reader, a checkout value cannot replace or suppress it, and an empty base value refuses.

Scope and non-goals

Scope: agentrepo merge lint and the shared lint behavior the review and merge commands use, with tests ported from the lint cases in merge-workflow.bats and policy.bats.

Non-goal: the commit-msg rules themselves, which stay in commit-lint and mise. Non-goal: editing the shell scripts before child 14.

Approach

Meet AC1 to AC8 with failing cases first, using a fake mise and a scratch repository whose head carries dictionary files, and compare the shell script's output on the same fixtures. Mechanism is the implementer's choice.

Dependencies

Blocked by: #862, #333, #980.

Prerequisites: #321 (repository configuration reader), #333 and #980 (pinned-base settings reader).

Size

S. One pull request.

Verify plan

  • AC1, AC2: fake-gh table cases comparing messages and statuses with the shell script.
  • AC3, AC4: scratch repositories with and without dictionaries, one with an absent unfetchable commit, one with a present commit lacking a dictionary (success) and one with an existing dictionary whose extraction fails, asserting what the lint saw, each message and the cleanup.
  • AC5: stub mise and a configured lint_squash_cmd, including the empty value.
  • AC6: a test per exit path asserting the temporary directory is gone.
  • AC7: a test that invokes the shared lint behavior a second way (not through the command) and gets the same result as the command; the CI matrix run. The review and merge integration checks belong to children 09 and 10.
  • AC8: a scratch repository whose checkout .agents/agent-skills.conf sets or blanks lint_squash_cmd differently from the base, asserting the base command runs, the checkout value neither replaces nor suppresses it, and an empty base value refuses.
  • Definition of done: D1 each AC is verified at the merged head, with the commands and results cited in the pull request's Verification. D2 an independent review approves the exact merged head, and every finding has a recorded disposition. D3 required checks pass with no gate loosened. D4 the leak scans the repository specifies are clean, and every post made for this work passes the per-post leak review before it is posted. D5 every commit is signed. D6 the documentation matches the change or the Verification says why there is no impact. D7 every commit has an Assisted-by trailer and the pull request has the provenance:agent-assisted label. D8 the pull request stays within the size limits. D9 follow-ups are filed as issues and listed under Related. D10 the merge closes the issue through a closing reference, the board shows Done, and the final Status: comment names the merge commit.

Risks

Linting against the wrong dictionaries would pass or fail a message for the wrong reasons. AC3 pins the head copies. Undo: revert; the shell script is untouched.

Operator authority

None.

Activity

  1. tbhb-agent commented on Oct 27, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — The squash message lint runs from agentrepo merge
    Model: gpt-6-astra

    Child 06 The squash message lint runs from agentrepo merge

    Verdict: READY

    Checked: The extraction-failure fixture now uses an existing dictionary whose extraction fails, matching tests/skills/merge-pr/scripts/merge-workflow.bats:277. AC7 can be verified before downstream commands exist, with integration assigned to children 09 and 10. Fetch errors, optional dictionaries and cleanup match packages/agents/source/skills/merge-pr/shared/scripts/merge-agent.sh:32.

    Gaps: None.

    Split: None needed.

    Definition of done

    The definition of done for this issue (Feature). Tick each item and give its evidence.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
    • D5 The author signed each commit, the squash included.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
    • devtools-1 mise run check is green, and so are the coverage floors mise run cover-go-check and mise run cover-py-check, which check doesn't run.
    • devtools-2 A change to skill sources under packages/agents/source/ commits what mise run generate-skills, then mise run skill-tokens, then apm install rewrite, in that order, and check-generated-skills (part of lint) passes.
    • devtools-3 A consumer-visible change (the vendored payload, a skill, a reusable workflow, a task or a pin) has its consumer steps in docs/agent-skills-migration.md or the adoption docs, or the PR's Verification says "No consumer impact" and why.

    Round: 3 of 3

  2. added
    refinement:activeA refinement round is open; not ready for implementation
    and removed
    refinement:readyPassed the Definition of Ready; may be picked up
    on Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refinement:activeA refinement round is open; not ready for implementation

    Fields

    Priority

    None yet

    Work type

    None yet

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions