Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0fab915
feat(ticket-draft): draft and file a GitHub issue under an approved w…
mmaclay Nov 6, 2026
7a56ffa
fix(ticket-draft): name the approval label and the sent texts' folder…
mmaclay Nov 6, 2026
113a059
test(sandbox): pass the refine scenario's issue as a bare number, whi…
mmaclay Nov 6, 2026
b8535bd
fix(using-github): keep the issue body's section headings; let the re…
mmaclay Nov 6, 2026
8828c51
fix(work): an answer given before a gate is asked is no yes to it
mmaclay Nov 6, 2026
fcaf64c
fix(ticket-draft): name opening a GitHub issue first in the descripti…
mmaclay Nov 6, 2026
0dcac51
Merge origin/main into feat/59-github-issues-draft-file
mmaclay Nov 6, 2026
1c77493
fix(issue-gate): allow only a plan's arguments, and match a create by…
mmaclay Nov 7, 2026
fa09164
fix(work): a standing answer that does not name a gate never answers …
mmaclay Nov 7, 2026
3a3737f
test(sandbox): the blanket scenario holds whether or not the gate was…
mmaclay Nov 7, 2026
ce785d5
fix(using-github): open an issue's baseline comment with its plain fi…
mmaclay Nov 7, 2026
9398d2d
fix(ticket-draft): reach any request to put a bug or change in the tr…
mmaclay Nov 7, 2026
d6cc6ba
fix(issue-gate): compare the target inside jq, build the record from …
mmaclay Nov 7, 2026
4cd94c7
Merge origin/main into feat/59-github-issues-draft-file
mmaclay Nov 7, 2026
5991ce8
fix(ticket-draft): a standing answer never takes Write all as the rec…
mmaclay Nov 7, 2026
b5d55b9
fix(ticket-draft): name an issue on another project, such as a librar…
mmaclay Nov 7, 2026
e13e70f
test(sandbox): check a standing-answer run reached the gate by its Wr…
mmaclay Nov 7, 2026
2c261bc
fix(issue-gate): say a create item allows every create of that title,…
mmaclay Nov 7, 2026
2d8ce32
test(sandbox): check the plan file only in the filing scenarios
mmaclay Nov 7, 2026
c979e7a
Merge origin/main, with #115, into feat/59-github-issues-draft-file
mmaclay Nov 7, 2026
8fc9e81
Merge remote-tracking branch 'origin/main' into feat/59-github-issues…
mmaclay Nov 7, 2026
24f8e25
fix(test): quote the stub writes annotation so test_sandbox imports o…
mmaclay Nov 7, 2026
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
2 changes: 1 addition & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Every skill, agent and hook is held to these; a change that breaks one says why.
header row, so rename or renumber any of them only with those citations.
- The Python environment is uv's (`pyproject.toml`, `.python-version`; the devcontainer and CI run
`uv sync`). The dev tools' pins live in `pyproject.toml` only; `uv.lock` is not committed.
- Before a pull request: `uv run python -m unittest discover -s tests`, the four
- Before a pull request: `uv run python -m unittest discover -s tests`, the five
`tests/*.sh`, and `uv run python scripts/check_static.py`, so the tests use the project's
environment outside the devcontainer too; CI also runs codespell, `claude plugin validate .`, and the unit tests and `check_static.py`
on Python 3.9, the README's floor.
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ jobs:
- name: Atlassian write gate
run: bash tests/test_atlassian_gate.sh

- name: GitHub issue write gate
run: bash tests/test_issue_gate.sh

- name: What the skills and agents name exists
run: uv run python scripts/check_static.py

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ measures it. `.claude/CLAUDE.md` holds the conventions every skill and agent fol
`/lasp-dev:skill-feedback` in the session where it went wrong, or open one with the
"Skill feedback" template.
- **Before a pull request**, run the checks CI runs:
`uv run python -m unittest discover -s tests`, the four `tests/*.sh`, and
`uv run python -m unittest discover -s tests`, the five `tests/*.sh`, and
`uv run python scripts/check_static.py`.
- **Security issues** go through `SECURITY.md`, not a public issue.

Expand Down
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ can be started in its place when a skill names it, so give the personal one anot

### Permissions

Each skill asks before it posts, pushes, opens a pull request or writes to Jira or Confluence. The plugin's
Each skill asks before it posts, pushes, opens a pull request, files or changes a GitHub issue, or writes to Jira or Confluence. The plugin's
`hooks/atlassian-gate.sh`, whatever the server is named and whether or not it runs read-only,
allows a Jira write only when your session makes it and `.review/jira-approval.json`, which the
skill writes at your yes to a write plan, lists that tool on that ticket, so a plan's writes do
Expand All @@ -283,7 +283,13 @@ record from this session in the last 30 minutes lists, a write a subagent makes,
removed link, a move to another project, an edited comment, an update that uploads local files,
and any write to Confluence. It asks rather than staying silent because a hook's ask still
prompts in auto mode, where a silent hook leaves the call to the classifier. An `ask` rule you add for a Jira write tool prompts again at each call of a plan,
whatever the hook decides. Claude Code's own permission rules are a second
whatever the hook decides. Where the tracker is GitHub Issues, `hooks/issue-gate.sh` allows a
plan's `issue_write` and `add_issue_comment` calls the same way, on any server name, from
`.review/github-approval.json`, a create by its title, so an item allows any create with that exact title on that
repository in the window, and an update or a comment by its issue, so an item allows any such call on that issue in the window, as the Jira gate's do, but gives every other call no decision:
one no record lists, `skill-feedback`'s own create among them, or one carrying an argument no plan
sends, prompts or passes as your settings say. Your own `ask` rule on `issue_write`
or `add_issue_comment`, as in the block below, still prompts at each call of a plan. Claude Code's own permission rules are a second
layer that does not depend on the skill text, and that still asks in auto mode. A recommended block for your user settings,
`~/.claude/settings.json`, with `github-libera` replaced by your GitHub server's name:

Expand Down Expand Up @@ -487,7 +493,7 @@ Most days need one command, `work`. Started, in the skills table: `typed only` r
| Entry point | `work` | typed or asked | Take a ticket, a Jira ticket or, where the standard's tracker is GitHub's, an issue, through the workflow: reads where it stands and runs the next phase skill; each phase keeps its own approval gates |
| | `quickstart` | typed or asked | Say where this machine and repository stand, what to fix first and what to type next, with `onboard` first in a repository with no standard, and the skills by group; with a question, answer it from the plugin's own files. Reads only |
| | `onboard` | typed or asked | Survey a repository for the facts the plugin works from, its committed templates, contributing guide, test commands (instruction files, docs, CI) and pre-commit, and its history, and the tracker's statuses and fields when the servers are connected; say where each fact would be read from, the repository's own file first; first asking where the repository's rules live, here or in another repository's standard; then ask where facts should live, which value holds where the repository's files disagree, and which proposals to keep. Writes only `.review/onboard/` unless the person says yes to a named write: the repository's standard, or its `standard:` pointer and the standard's `profiles/<repo>.yaml` |
| Phases, also run alone | `ticket-draft` | typed or asked | Turn a prompt, an existing ticket or a pull request with none into one in the team's format: duplicates merged, dependencies, follow-up tickets for out-of-scope work, an in-session estimate, and an Open Questions section for what cannot be settled yet. `investigate` answers your questions about the code first; `settle` folds later answers in and records a departure the work keeps as a comment; `reformat` moves a drafted ticket to the current format, asking only what the format needs. Run directly for an epic or a group ticket session |
| Phases, also run alone | `ticket-draft` | typed or asked | Turn a prompt, an existing ticket or a pull request with none into one in the team's format, a Jira ticket or, on a GitHub tracker, an issue: duplicates merged, dependencies, follow-up tickets for out-of-scope work, an in-session estimate, and an Open Questions section for what cannot be settled yet. `investigate` answers your questions about the code first; `settle` folds later answers in and records a departure the work keeps as a comment; `reformat` moves a drafted ticket to the current format, asking only what the format needs. Run directly for an epic or a group ticket session |
| | `implement-change` | typed or asked | Take a change request to an agreed plan, an implementation and a diff verified by a wave of reviewers |
| | `pr-create` | typed or asked | Write a reviewer-focused description and open the pull request |
| | `pr-review` | typed or asked | Review a pull request and present the findings at their lines, each citing a rule, decision, term, ticket criterion or test check when one applies, and no key otherwise; draft comments when asked, and post one review with the verdict you choose only on your yes. A posted review's threads are what the revision reads; a review kept local leaves it nothing |
Expand Down Expand Up @@ -532,7 +538,7 @@ since been rewritten; the rest were written here.

| Other | Role |
| --- | --- |
| `hooks/` | Measure a build (lint passes and test runs, never a `--collect-only`, `--version` or `--help` call, reviewer rounds and wave reviewers, wall clock less the waits before each resume, tokens) into `.review/build/`; hold the branch for one session at a time through `.review/lock`; at session start, print where the ticket on this branch stands and load the review rules that apply to the repository, fetching a standard elsewhere the first time a clone needs it; gate the Atlassian calls that are not reads, stop a commit, a `gh` write or a GitHub or Atlassian write that carries what looks like a credential, and ask before a read past the first line of a `.review/` file marked `Controlled:`, as "Permissions" above says; save each `pull_request_read` result to `.review/pages/` as the server returned it, for `pr-reader` to copy into its dump |
| `hooks/` | Measure a build (lint passes and test runs, never a `--collect-only`, `--version` or `--help` call, reviewer rounds and wave reviewers, wall clock less the waits before each resume, tokens) into `.review/build/`; hold the branch for one session at a time through `.review/lock`; at session start, print where the ticket on this branch stands and load the review rules that apply to the repository, fetching a standard elsewhere the first time a clone needs it; gate the Atlassian calls that are not reads, allow the GitHub issue writes of a plan you approved, stop a commit, a `gh` write or a GitHub or Atlassian write that carries what looks like a credential, and ask before a read past the first line of a `.review/` file marked `Controlled:`, as "Permissions" above says; save each `pull_request_read` result to `.review/pages/` as the server returned it, for `pr-reader` to copy into its dump |
| `standards/` | The plugin's shared standards files, listed in `standards/README.md`. A project's own terminology, decisions, context, archive and review records live in its standard |

Text the skills compose for people on a pull request, or a page `design-doc`, `architecture-doc` or `process-doc` publishes,
Expand Down Expand Up @@ -693,7 +699,7 @@ comment an agent drafted stays the agent's.
```text
.claude-plugin/ # marketplace.json, plugin.json ("lasp-dev", rooted here)
skills/ agents/ # the skills and agents
hooks/ # build measurement, the branch lock, the session-start line, the Atlassian write gate, the secret scan, the controlled-file gate, the saved pages
hooks/ # build measurement, the branch lock, the session-start line, the Atlassian write gate, the issue write gate, the secret scan, the controlled-file gate, the saved pages
scripts/ # stdlib scripts the skills and hooks run: record.py, profile.py and the rest;
# jira_markup.py is mcp-atlassian 0.23.1's converter, copied (MIT)
standards/ # the plugin's shared standards files (standards/README.md)
Expand Down Expand Up @@ -729,10 +735,13 @@ session-start line or `work` says something unexpected, these files are what it
| `context/<ticket>.md` | `ticket-draft`, with the `Branch:` line for a pull request's ticket; `implement-change` adds its `Branch:` line | `work`, to see whether the ticket was drafted, and, with a `Branch:` line and no plan, which branch's pull request to read | Not cleared by the tools |
| `context/<ticket>.pasted.md` | `work`, `ticket-draft` and `implement-change`: a ticket's text the person pasted when the Atlassian server could not be reached | `source-reader`, given it as a file | Not cleared by the tools |
| `context.md` | `ticket-draft`, a copy of the latest page, never of a pull request's; `implement-change` adds `Branch:` | `work` and the session-start line, for the ticket the branch is on | Replaced by the next `ticket-draft` |
| `context/<ticket>.jira.txt`, `context/<ticket>.original.txt` | `ticket-draft`: the paste file, and the text before the session | The person, pasting into Jira what the write plan did not write; `ticket-draft`, for the `Original request` comment it writes | Not cleared by the tools |
| `context/<ticket>.jira.txt`, `context/<ticket>.issue.md`, `context/<ticket>.original.txt` | `ticket-draft`: the paste file, for Jira or, on a GitHub tracker, for an issue, and the text before the session | The person, pasting into the tracker what the write plan did not write; `ticket-draft`, for the `Original request` comment it writes | Not cleared by the tools |
| `context/<ticket>.send/` | `ticket-draft`'s write plan: each description and comment as shown (`.txt`) and as sent (`.md`), and the description as read back (`stored.txt`) | `scripts/jira_markdown.py`; the person, to see what was sent | Rewritten at each plan |
| `jira-plan.md` | The write plan, run by `ticket-draft` and `ticket-audit`: each item, numbered, with its text in full as it will be sent, written before the plan is shown | You, from the link at the plan's gate | Replaced at the next showing of a plan |
| `jira-approval.json` | The write plan, run by `ticket-draft` and `ticket-audit`: this session's id, the time and each approved item's tool and target, written at your yes before the first call | `hooks/atlassian-gate.sh`, which allows only a call it lists | Removed by the skill when the plan finishes or stops; allows nothing after 30 minutes |
| `github-plan.md` | The GitHub write plan, run by `ticket-draft` on a GitHub tracker: each item, numbered, with its text in full as it will be sent, written before the plan is shown | You, from the link at the plan's gate | Replaced at the next showing of a plan |
| `github-approval-items.json` | The GitHub write plan, run by `ticket-draft` on a GitHub tracker: the approved items, written with the Write tool so a title's quotes never pass through a command | The plan's `jq` command, which builds `github-approval.json` from it | Removed with the record |
| `github-approval.json` | The GitHub write plan, run by `ticket-draft` on a GitHub tracker: this session's id, the time and each approved item's tool and target, written at your yes before the first call | `hooks/issue-gate.sh`, which allows only a call it lists and leaves every other to your own permissions | Removed by the skill when the plan finishes or stops; allows nothing after 30 minutes |
| `controlled-cleared.json` | `hooks/controlled-gate.sh`, after a read of a `.review/` file marked `Controlled:` that you allowed, or the session's own write of one: this session's id and the items cleared | The same hook, which lets this session's later reads of those items through | Replaced when another session clears an item |
| `context/<ticket>.writes.md` | `ticket-draft`'s write plan: each item and whether it was written, with its key, failed, dropped or left to paste | The person | Rewritten at each plan |
| `implementation-plan-<ticket>.md` | `implement-change`: a draft from the planner, before the plan review, `Approved by:` at it, `Base:` at the branch, `## Corrections` at the plan and diff gates, `## Provisional decisions` for each decision made for now, and each `## Revision` with its own `Approved by:`; named for the branch with no ticket | `implement-change` on resume and at step 8 (`Depth:`, and `## Corrections` for a lowered depth), `work` and the session-start line (`Approved by:`), `pr-create` (`Base:`, `Approved by:`, `## Provisional decisions`), `commit` (the reason) | Not cleared by the tools |
Expand Down Expand Up @@ -891,6 +900,7 @@ CI (`.github/workflows/checks.yml`) runs, on every pull request:
- `bash tests/test_where.sh`: `hooks/where.sh`, the start-of-session line.
- `bash tests/test_standard_file.sh`: `scripts/standard_file.sh`, `scripts/standard.sh`, which it fetches through, and `hooks/rules.sh`, the rules loaded at session start.
- `bash tests/test_atlassian_gate.sh`: `hooks/atlassian-gate.sh`, against every tool of mcp-atlassian 0.23.1.
- `bash tests/test_issue_gate.sh`: `hooks/issue-gate.sh`, against github-mcp-server's `issue_write` and `add_issue_comment`.
- `uv run python scripts/check_static.py`: what the skills and agents name exists, each has its
Copilot link and each skill its row in "What is included", and no
shipped file gains a mention of Libera (`scripts/coupling.json`; after removing mentions,
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ merged pull request is its record. Owners are roles. Status is `planned`, `defer
| --- | --- | --- | --- | --- |
| Neutral examples in generic skills (#21) | `quickstart` still checks for the plugin this one replaced, and `using-curryer` still ships in the plugin, loaded only in curryer and the repositories that depend on it (#67). `scripts/coupling.json` allows the rest and does not scan the docs (#15). `using-curryer` leaves for curryer's own standard, or a domain plugin installed beside this one | plugin maintainer | before the public flip | planned |
| Forge, tracker and wiki as roles (#24) | Every skill speaks GitHub, Jira or Confluence directly, so a repository on GitLab or tracking work in GitHub Issues gets little of the workflow. One documented shape per role, the neutral thread JSON as the forge's, with the current skills as the first adapters; GitLab next, then GitHub Issues as a tracker | plugin maintainer | a team on GitLab or GitHub Issues adopts the plugin | idea |
| The skills on the ticket shape (#58) | The shape, the format and the profile's `issue_tracker:` are tracker-neutral (`docs/contracts.md`, "The ticket shape"; `standards/ticket-format.md`), but `ticket-draft`, `work` and `ticket-audit` still read and write only Jira, and `onboard` still writes `jira:`. No ticket is kept as a file in the repository: a ticket is drafted once, in the format, for whichever tracker files it | plugin maintainer | with #59 | planned |
| GitHub Issues as a tracker (#59) | Most repositories outside LASP track work in GitHub Issues, through a server already connected for pull requests; it is the first tracker adapter after Jira. Reading an issue and `work` routing by its state and labels have landed. Left: drafting and filing one under an approved write plan with an allow-only gate, and `ticket-draft` taking an issue `work` routes to it; onboard detecting GitHub Issues as the tracker, the project's branch convention and mapping its labels to the roles; then the audit sweeps; no Projects fields or estimates | plugin maintainer | the ticket shape's profile and format land | planned |
| The skills on the ticket shape (#58) | The shape, the format and the profile's `issue_tracker:` are tracker-neutral (`docs/contracts.md`, "The ticket shape"; `standards/ticket-format.md`), but `ticket-audit` still reads and writes only Jira, and `onboard` still writes `jira:`. No ticket is kept as a file in the repository: a ticket is drafted once, in the format, for whichever tracker files it | plugin maintainer | with #59 | planned |
| GitHub Issues as a tracker (#59) | Most repositories outside LASP track work in GitHub Issues, through a server already connected for pull requests; it is the first tracker adapter after Jira. Reading an issue, `work` routing by its state and labels, and `ticket-draft` drafting and filing one under an approved write plan with an allow-only gate have landed. Left: onboard detecting GitHub Issues as the tracker, the project's branch convention and mapping its labels to the roles; then the audit sweeps; no Projects fields or estimates | plugin maintainer | the ticket shape's profile and format land | planned |
| A GitLab forge adapter (#61) | A repository on GitLab gets local-only reviews; the adapter writes the thread shape (#60) through a GitLab server or `glab` | plugin maintainer | step 5 of #24's order, after #60 | planned |
| Bitbucket, other trackers, Markdown files as a wiki (#62) | No team has asked yet | plugin maintainer | a team asks | idea |

Expand Down
Loading
Loading