Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/codetrial-contribute/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ the user with the draft rather than being acted on alone:
work may already be done or declined; read the closing comment and say which
in the draft.
- `closes` empty while `refs` names the issue the branch finishes: the last
line should be `Closes #N`, not `Refs #N`, or the merge will not close it.
line should be `Close #N`, not `Refs #N`, or the merge will not close it.
`refs` leaves out what `closes` already has, and both read `#N`,
`sysprog21/codetrial#N` and the issue URL.
- `same_files`: overlap by file is weak alone, since `web/interview.js` and
Expand Down Expand Up @@ -336,10 +336,10 @@ the git steps behind them:
commit.
- "Append `Closes #N`" or "avoid ambiguous `Refs`": the PR finishes the issue,
so end the PR body, and by this tree's habit the commit message, with
`Closes #N`, each on its own line. Avoid `Refs #N`: it belongs only beside a
`Closes #N`, or in a body that states the motivation and the implementation
in full, including what is left; a bare `Refs: #60` told the reviewer
nothing.
`Close #N`, each on its own line (`Closes #N` is accepted too). No `Refs #N` in a PR body: a bare
`Refs: #60` told the reviewer nothing, and an issue with parts that stay open
is split into sub-issues so the PR can close the part it finishes. In a
commit message a `Refs #N` goes in the description, never on the last line.
- A code change requested in review: check it against the
current head, then make it. When any item in the review is unclear, ask
about all of them before changing any, since items in one review are often
Expand Down
28 changes: 15 additions & 13 deletions .claude/skills/codetrial-contribute/collaboration.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,19 @@ for its own issue, not folded in.
- An alternative to an open PR has to justify itself against it: "Determine if
this pull request is worth proceeding." (#245, closed while #244 fixed
#131). Review the open one rather than writing a competitor.
- Close what the PR finishes. CONTRIBUTING.md has the rule: `Closes #N` when
the PR finishes the issue, and `Refs #N` avoided, since a reference that
closes nothing is easy to add to every PR. Maintainers call `Refs` on a PR
that does finish its issue ambiguous ("Avoid ambiguous `Refs #210`. Do close
an issue when you submit a pull request." on #256, "Avoid using ambiguous
`Refs #261`" on #265), so decide by the issue's acceptance criteria, not by
caution. `Refs` belongs only beside a `Closes #N`, or in a body that states
the motivation and the implementation in full, including what remains:
[#161](https://github.com/sysprog21/codetrial/issues/161#issuecomment-6131824178)
asked for `Refs #161` because the cap and modality toggle stay open. One PR
may close two issues when that was agreed, each `Closes #N` on its own line
- Close what the PR finishes. CONTRIBUTING.md has the rule: `Close #N` when
the PR finishes the issue, and no `Refs #N` in a PR body, since a reference
that closes nothing is easy to add to every PR. Maintainers call `Refs` on a
PR that does finish its issue ambiguous ("Avoid ambiguous `Refs #210`. Do
close an issue when you submit a pull request." on #256, "Avoid using
ambiguous `Refs #261`" on #265), so decide by the issue's acceptance
criteria, not by caution. An issue too large for the PR is split into
sub-issues, and the PR closes the one it finishes; [#161](https://github.com/sysprog21/codetrial/issues/161#issuecomment-6131824178)
was asked for `Refs #161` because the cap and modality toggle stay open, and
that remainder is what a sub-issue holds. In a commit message a `Refs #N`
may sit in the description and never on the last line, which the
commit-msg hook refuses. One PR
may close two issues when that was agreed, each `Close #N` on its own line
([#73](https://github.com/sysprog21/codetrial/issues/73#issuecomment-5991034506)
with #85).

Expand Down Expand Up @@ -192,9 +194,9 @@ so the newest visible one is current. Each maps to one action;
| "Fold similar commits" | One commit per functional change. |
| "Check `CONTRIBUTING.md` carefully and enforce the rules." / "Check https://cbea.ms/git-commit/" | Usually the commit messages: run `./scripts/git-commit-msg.sh --rules`, then reread every commit and the PR title and body against CONTRIBUTING. |
| "Remove AI agent markers" / "Remove unnecessary `Co-authored-by` markers" | Drop the AI-tool attribution trailers conventions names; keep a human co-author's credit. |
| "Avoid ambiguous `Refs #N`" | The PR finishes the issue, so it ends with `Closes #N`. |
| "Avoid ambiguous `Refs #N`" | The PR finishes the issue, so it ends with `Close #N`. |
| "Explain the rationale and context" instead of "Related to #N" | The body says why on its own; an issue reference is not a reason. |
| "Never write meaningless Git commit messages such as 'Update web/app.js'" | Reword that commit with a subject and body that say what changed and why. |
| "Never write meaningless Git commit messages such as 'Update web/app.js'" | Reword that commit with a subject and body that say what changed and why. The commit-msg hook refuses a subject that only names a file, and says why. |
| "Resolve regressions raised by CI." | The section above. |
| "Create another pull request." / "split it into a separate pull request" | Move that change to its own branch and PR; drop it from this one. |
| "Do more analysis and refinements." | Find the cause; a rerun or a local pass is not an answer. |
Expand Down
13 changes: 7 additions & 6 deletions .claude/skills/codetrial-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ calibration, so run this when a subject is hard to fit:
git log --no-merges --format=%s | awk '{print length}' | sort -n
```

Three things reviewers reject that the hook cannot see. The body stands on its
own: "Related to #263" in place of the reason got "explain the rationale and
context" (#268). No `Co-authored-by` or "Generated with" trailer attributing
an AI tool (#251, #256, #285); a human co-author's credit stays. And a subject
that names only the file, such as GitHub's web-editor default "Update
web/app.js", is what a maintainer calls meaningless (#276).
One thing reviewers reject that the hook cannot see: the body stands on its
own. "Related to #263" in place of the reason got "explain the rationale and
context" (#268). The hook now refuses the two other things maintainers used to
ask for by hand: a `Co-authored-by` or "Generated with" trailer attributing an
AI tool (#251, #256, #285; a human co-author's credit stays), and a subject that
names only the file, such as GitHub's web-editor default "Update web/app.js"
(#276), which says where and not what and which the refusal explains.

## Prose register

Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/codetrial-triage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ titles or the same broad symptom are insufficient to establish duplication.
Whether the merge closes it is a fact to check: read the PR's
`closingIssuesReferences` and its commit messages as well as its body, since a
Development-sidebar link or a closing keyword in a commit closes an issue even
when the body has none, and read what you find against the `Closes` and `Refs`
when the body has none, and read what you find against the `Close` and `Refs`
rule in CONTRIBUTING.md. Say it needs a manual close when nothing carries the
reference, and never promise the merge will close it otherwise. When the PR
delivers the issue and only says `Refs #N`, the fix is a comment on the PR
asking its author to make the last line `Closes #N`, so the merge records it
asking its author to make the last line `Close #N`, so the merge records it
(#143 for #141). The PR sweep's `refs_only` lists these pairs and whether a
maintainer already asked ([pulls.md](pulls.md#closing-references)).
- **Already shipped**: the tree already does what the request asks. `git grep -n`
Expand Down
5 changes: 0 additions & 5 deletions .claude/skills/codetrial-triage/agent-marker.jq

This file was deleted.

42 changes: 27 additions & 15 deletions .claude/skills/codetrial-triage/hygiene.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,32 @@ gh api "repos/$repo/pulls/$pr/commits" --paginate > "$work/commits.json"
gh api "repos/$repo/pulls/$pr/comments" --paginate > "$work/inline.json"

# Each commit through the commit-msg hook's own rules, the ones CI applies too.
# Merges are git's wording and get their own item; Dependabot's are skipped by
# author, as check-commit-log.sh does. One JSON string per line, so a message's
# own newlines survive the loop.
# Its exit status adds up what is wrong, so one call answers both questions and
# the AI-credit item and a refused commit cannot disagree: bit 1 is a style rule
# broken, bit 2 a credit to a tool, and anything above 3 (the hook itself
# failing) counts as a broken rule. A credit is looked for in every commit. The
# style rules skip a merge, which is git's wording, and Dependabot's commits,
# which are skipped by author as check-commit-log.sh does. Per commit, one line
# saying whether the style rules apply and one JSON string for the message, so
# its own newlines survive the loop.
bad_messages=0
jq -s -c 'add | .[]
| select((.parents | length) < 2
and .commit.author.email != "49699333+dependabot[bot]@users.noreply.github.com")
| .commit.message' "$work/commits.json" > "$work/messages"
while IFS= read -r message; do
printf '%s\n' "$message" | jq -r . \
| sh "$root/scripts/git-commit-msg.sh" - > /dev/null 2>&1 \
|| bad_messages=$((bad_messages + 1))
done < "$work/messages"
ai_messages=0
jq -c '.[] | ((.parents | length) < 2
and .commit.author.email != "49699333+dependabot[bot]@users.noreply.github.com"
| if . then 1 else 0 end), .commit.message' "$work/commits.json" > "$work/commits.lines"
while IFS= read -r styled && IFS= read -r message; do
printf '%s\n' "$message" | jq -r . > "$work/message"
status=0
sh "$root/scripts/git-commit-msg.sh" --stored "$work/message" > /dev/null 2>&1 || status=$?
if [ "$styled" -eq 1 ] && {
[ "$status" -gt 3 ] || [ $((status & 1)) -ne 0 ]
}; then
bad_messages=$((bad_messages + 1))
fi
if [ $((status & 2)) -ne 0 ]; then
ai_messages=$((ai_messages + 1))
fi
done < "$work/commits.lines"

# Failing checks at this head, and whether main shares them. A diagnosis that
# fails, or that read another head, yields no CI item rather than a guess.
Expand All @@ -99,10 +112,9 @@ again=$(gh pr view "$pr" --repo "$repo" --json headRefOid --jq .headRefOid)

jq -n -L "$(dirname "$0")" --slurpfile p "$work/pr.json" --slurpfile c "$work/commits.json" \
--slurpfile ci "$work/ci.json" --slurpfile inline "$work/inline.json" \
--argjson bad "$bad_messages" \
--argjson bad "$bad_messages" --argjson ai "$ai_messages" \
--argjson pushers "$pushers" --arg pushed "$pushed" \
--arg again "$again" '
include "agent-marker";
$p[0] as $pr | ($c | add) as $commits
| (if $ci[0].head == $pr.headRefOid then $ci[0] else {} end) as $ci
| def staff: .author.login | IN($pushers[]);
Expand Down Expand Up @@ -132,7 +144,7 @@ jq -n -L "$(dirname "$0")" --slurpfile p "$work/pr.json" --slurpfile c "$work/co
"Do not merge `main` into this branch with `git merge`. Drop the merge commits, put your own commits on top of the latest `main`, and force-push."),
item("commit_rules"; $bad > 0; "cbea\\.ms|chris\\.beams|make hooks";
"Improve git commit messages per https://cbea.ms/git-commit/ and `CONTRIBUTING.md`; CI applies the same rules. Hint: run `make hooks` once per clone, so the hooks check every message before you push."),
item("co_authored"; any($commits[]; .commit.message | ai_marker);
item("co_authored"; $ai > 0;
"co-authored|attribution|agent marker";
"Remove AI agent markers (`Co-authored-by` an agent, \"Generated with\") from Git commit messages, as this attribution does not improve collaboration."),
item("indent"; any($failing[]; .on_main == false
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/codetrial-triage/pr-sweep.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
# first review, a re-review after a push, or a merge
# refs_only PRs that close no issue and name one with Refs, which
# CONTRIBUTING.md asks PRs to avoid; asked says a maintainer
# already asked for Closes #N on that PR
# already asked for Close #N on that PR
# viewer the account that ran the sweep
# prerequisites PRs that must land first: stacked on the other's branch,
# or stated in the body as depending on it
Expand Down
20 changes: 10 additions & 10 deletions .claude/skills/codetrial-triage/pulls.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,16 +440,16 @@ compared), then say who each PR waits on from its
## Closing references

`refs_only` lists each PR that closes no issue and names one with `Refs #N`, so
its merge closes nothing. CONTRIBUTING.md asks PRs to avoid `Refs`: it stands
only beside a `Closes #N`, which is why a PR that closes something is left out
here, or in a body that states the motivation and the implementation in full,
as #110 does for #257 and #362 for its parent #306. Otherwise, when the PR
delivers the issue, ask its author to end the body with `Closes #N` (SKILL.md,
"Superseded by an open pull request"); when it does not, ask for the motivation
and the implementation in the body, or for the `Refs` line to go. `asked` true
means a maintainer already asked for `Closes` on that PR, so the next move is
the author's and nothing is posted again. Either comment needs a judgement of
the PR, so it goes on the confirmed list.
its merge closes nothing. CONTRIBUTING.md keeps `Refs` out of a PR body, and
the sweep lists the pairs where that costs most: it leaves out a PR that closes
something, as #110 does for #257 and #362 for its parent #306. Otherwise, when
the PR delivers the issue, ask its author to end the body with `Close #N`
(SKILL.md, "Superseded by an open pull request"); when it does not, ask for the
motivation and the implementation in the body, or for the `Refs` line to go
and, for an issue too large for the PR, a sub-issue holding the part it
finishes. `asked` true means a maintainer already asked for a closing line on
that PR, so the next move is the author's and nothing is posted again. Either
comment needs a judgement of the PR, so it goes on the confirmed list.

## Review the queue

Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/codetrial-triage/review-prs.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,11 @@ each of this PR's entries in outdated, or none>.
with one step: for an open PR, read the other diff and reply with how the
two relate (fold into one, rebase one on the other, or keep both with a
sentence on why); for an issue it delivers, end the body with
`Closes #M`; for work already shipped, check whether this PR is still
`Close #M`; for work already shipped, check whether this PR is still
needed and say so. It never picks a winner between two PRs. For an open
PR, `other_notice` is the mirror text for that PR's thread, unless that
thread already links this one; for an issue it is null, since the PR's
own `Closes` line is the link. Both follow comments.md.
own closing line is the link. Both follow comments.md.

## Verify and post each PR as it lands

Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/codetrial-triage/waiting.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ neither a reminder nor a first answer on the issue (#370 and #385 sat in
its author already knows the rule.
- `linked`, with `merged` set: a merged pull request referenced the issue after
it was filed, and GitHub did not close it, so that PR only touched it, or its
`Closes` line named another issue. Read the PR and the issue. When the PR does
closing line named another issue. Read the PR and the issue. When the PR does
what the issue asks, ask for a retest in the shape [the notice
shapes](SKILL.md#draft-the-notice-the-other-thread-needs) give, with the
recipe from [codetrial-contribute](../codetrial-contribute/SKILL.md); #82 got
Expand All @@ -72,7 +72,7 @@ neither a reminder nor a first answer on the issue (#370 and #385 sat in
that PR. Say how to check it out, since many reporters have never fetched a
pull request. A maintainer's own fix PR gets the short back-link "See if PR #N
resolves this issue." (#140, #149, #151). When a reporter closed an issue that
an open PR still `Closes`, check that the defect still holds on `main`; if it
an open PR still closes, check that the defect still holds on `main`; if it
does, propose reopening it on the confirmed list, so the merge records the fix
(#210).
- `claimed`: a non-maintainer assignee, which is how the maintainers record who
Expand Down
Loading