Skip to content

Automate issue status through a project board #60

Description

@milanmalhotra

Issues here carry no state between open and closed. An issue whose PR is under
review, an issue merged to dev and waiting for a release, and an issue nobody
has touched all look the same on the issue list. Nothing closes on its own
either, so closing is a manual sweep that gets forgotten.

Why nothing closes today

GitHub honors Closes #NN only when the PR merges into the repository's default
branch. The default here is master, and every feature PR targets dev under
git flow, so the keyword never fires. Promoting dev to master does not fire
it either, because the promotion's merge commit carries no keyword of its own.

Verified on 2026-08-17: closedByPullRequestsReferences is empty for #36, #48,
#49 and #53, every one of which has a merged PR whose body says Closes #NN.
GitHub records no closing link at all for a PR based on a non-default branch, so
the automation has to own that link and parse PR bodies itself.

What this adds

An organization level project with a Status field: Todo, In progress,
In review, Ready for release, Done.

Trigger Transition
First push of an issue-<number>-<slug> branch In progress, only from Todo or unset
PR opened, reopened, ready for review, or edited In review
PR merged into dev Ready for release
A release ships from master closed, and the board's own workflow moves it to Done

Closing by containment

An issue closes when the commit that fixed it is contained in the commit being
released, rather than by clearing the Ready for release column. An immediate
fix branched off master ships a release while dev still holds merged work,
and clearing the column would close issues that are not in the shipped jar.

Case Contained? Result
Merged to dev, dev promoted to master yes closes
Merged to dev, an immediate fix released instead no stays in Ready for release
Immediate fix merged straight to master yes closes, though it never entered Ready for release

The third row is why containment is the rule rather than a patch on top of
column scanning: an immediate fix never passes through dev, so the
Ready for release automation never touches it, and containment still closes it.

Scope

  • scripts/issue-status.sh, pure parsing, unit tested.
  • scripts/set-issue-status.sh, the Projects v2 writes.
  • scripts/close-released-issues.sh, called from a new final step in
    release.yml.
  • .github/workflows/issue-status.yml for the push and pull request triggers.
  • CLAUDE.md records that branch names and closing keywords are now load
    bearing rather than habit.

Nothing here ships to server owners, so no changeset is needed.

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