Skip to content

feat(ticket-draft): draft and file a GitHub issue under an approved write plan - #116

Merged
mmaclay merged 22 commits into
mainfrom
feat/59-github-issues-draft-file
Nov 7, 2026
Merged

mmaclay merged 22 commits into
mainfrom
feat/59-github-issues-draft-file

Conversation

@mmaclay

@mmaclay mmaclay commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

Origin: #59
Sandbox: haiku throughout.

Round 4, on 2c261bc:

  • notrigger-ticket-draft-other-project, now "Open an issue on numpy's GitHub about this dtype bug: np.sum on an int8 array overflows silently", n=10: no run reached ticket-draft on main or on this branch (0/10 on each). The case stays in the smoke list.
  • tracker-gh-ticket-draft-new: 5/5, each filing through the gate and writing .review/github-plan.md.
  • tracker-gh-ticket-draft-refine: 2/2. The budget stopped it before run 3.
  • tracker-gh-ticket-draft-blanket: 5/5 filed nothing.
  • tracker-gh-ticket-draft-standing-answer: 5/5 filed nothing.

Round 3, against origin/main (A):

  • Pack cases, n=5, A against the branch: trigger-ticket-draft-github-issue 0 against 5/5. notrigger-ticket-draft-plugin-issue and trigger-skill-feedback were 5/5 to skill-feedback on both, and trigger-ticket-draft 5/5 on both.
  • Six held-out phrasings, n=5 each: 0/5 on A against 5/5.
  • -new with the title "Don't count a negative part in total()": 2/2.

Earlier rounds:

  • -declined 5/5.
  • tracker-ticket-draft-github-request 3/3.
  • onboard-blanket-answer 5/5 against 5/5.
  • ticket-draft-retitle, -control and -subtle: 2/2, 2/2 and 1/2 on A against 2/2, 2/2 and 2/2 on B.
  • tracker-ticket-draft-new 2/2 against 2/2.

No scenario commits to main after a waiver named in advance, and the sandbox has no commit scenario to extend.
Canary: none yet

What this does

Where a project's standard names GitHub Issues as its tracker, ticket-draft now drafts or refines a GitHub issue and files it through the GitHub server, under the same one-yes write plan Jira uses. A new allow-only hook removes the per-call prompt for the calls the person approved, and leaves every other issue write exactly as the person's own permissions have it.

The pieces

  • hooks/issue-gate.sh, registered for mcp__.*__(issue_write|add_issue_comment). It builds and compares a call's target inside one jq run against .review/github-approval.json (docs/contracts.md, "The GitHub approval record" v1).
    • No item is used up. A create's item, <owner>/<repo> <title>, matches every create with that exact title in the window. An update's or a comment's, <owner>/<repo>#<n>, matches every such call on that issue, as a Jira item does.
    • Comparison is byte for byte. Each tool's arguments are allowlisted.
    • Every other call gets no decision, and the person's permissions apply. atlassian-gate.sh is untouched.
  • skills/using-github/issues.md holds the issue's fields, the plan's three items and the label rules: existing labels only, each checked with get_label.
    • The plan is written to .review/github-plan.md before it is shown, and linked at the gate.
    • The record's items are written with the Write tool and read by jq --slurpfile.
    • A create that failed or timed out is searched for by its title before it is sent again, so a retry files no duplicate.
    • It also holds the read-back and the paste file <ticket>.issue.md.
  • skills/ticket-draft:
    • github.md holds the GitHub tracker's step differences.
    • Where a GitHub issue is asked for on a tracker that is not GitHub's, SKILL.md asks first.
    • Its gate cites decision-points.md: Write all is never taken on a standing answer.
    • The description reaches any request to put a bug or a change into this repository's tracker. It is not for an issue on another project, and every complaint about a skill stays with skill-feedback.
  • skills/using-jira/write-plan.md and decision-points.md: a standing answer that does not name a gate never answers it, whenever it was given.
  • source-reader, as in round 1. next_phase.py: on a GitHub tracker, unfiled names <slug>.issue.md ("The next phase" v7).
  • Sandbox:
    • The stub records writes and serves get_label.
    • run.py treats only the pack's own stub as a writing one.
    • The created issue's number is derived from the fixture.
    • Five tracker-github scenarios, one Jira-pack scenario and two trigger cases.

For the reviewer

Doubts

🤖 Generated with Claude Code

mmaclay and others added 18 commits November 6, 2026 15:28
…rite plan

On a GitHub tracker, ticket-draft drafts an issue in the ticket format, shows
its writes as one plan, and files them through the server on the person's yes.
hooks/issue-gate.sh allows issue_write and add_issue_comment only when
.review/github-approval.json lists the call for this session, and gives every
other call no decision. using-github/issues.md holds the issue's fields, the
plan's items, the read-back and the paste file. The description now covers a
GitHub issue and keeps a complaint about a skill with skill-feedback.

The tracker-github stub takes --writes to record write calls, so the sandbox
exercises the gate end to end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on a GitHub tracker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ch a slash command keeps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fine scenario take a follow-up create

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on, and keep a complaint asked as a plugin issue with skill-feedback

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… its title

The gate now allowlists each tool's arguments rather than denying a few, and a
create's target carries its title, so one approved create allows one call.
ticket-draft asks before drafting when a GitHub issue is asked for on a
tracker that is not GitHub's. The gate rule excludes only a standing answer
that does not name the gate. The stub serves get_label and reads back a label
a write created; run.py takes only the pack's own stub as a writing one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it, whenever it was given

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… shown

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rst line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…acker, and keep every skill complaint with skill-feedback

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…an items file, and derive the stub's next number

The hook now builds and compares a call's target in one jq run, so a title's
bytes reach the comparison unchanged. The plan writes its items with the Write
tool and jq reads them with --slurpfile, so a title's quotes never pass through
a command. The new-issue scenario and the stub test take the created number
from the fixture's highest. The description names this repository's tracker
and leaves another project's alone. New scenario: a plain request to file, then
a standing answer, files nothing; a new trigger case for another project.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ommendation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y, as not this skill's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ite all option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and search before resending one

No item is used up, so the README and the contract now say a create's item
matches every create with that exact title in the window, and issues.md has a
create that failed or timed out searched for by its title before it goes
again. The plan is written to .review/github-plan.md before it is shown and
linked at the gate, and the filing and standing-answer scenarios check for it.
The gate rule is cited from decision-points.md rather than restated. The
other-project trigger case asks for an issue on numpy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mmaclay and others added 2 commits November 6, 2026 22:35
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"The next phase" keeps #115's version 6 and this branch's unfiled line as
version 7. The pack keeps both sets of scenarios, every work scenario removes
the writes file, and the WORK set gains tracker-gh-work-pr and
tracker-gh-work-closed-labeled. The stub's docstring lists get_label and
get_parent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mmaclay
mmaclay marked this pull request as ready for review November 7, 2026 06:17
@mmaclay
mmaclay merged commit 34f5fec into main Nov 7, 2026
1 check passed
@mmaclay
mmaclay deleted the feat/59-github-issues-draft-file branch November 7, 2026 06:47
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