diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 49e694c..9af7093 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,8 +1,9 @@ { "name": "freethinking", - "version": "0.5.0", + "version": "0.5.1", "description": "Shared engineering skills: standard workflows and prompts for day-to-day development.", "author": { "name": "Developer Handbook contributors" - } + }, + "license": "MIT" } diff --git a/.claude/specs/contributing-guide/design.md b/.claude/specs/contributing-guide/design.md new file mode 100644 index 0000000..bcec6bd --- /dev/null +++ b/.claude/specs/contributing-guide/design.md @@ -0,0 +1,267 @@ +# Design Document + +## Overview + +The work is documents, templates, settings and one small skill change. Everything a contributor reads lives under `.github/`, beside the pull request template GitHub already picks up from there: the Guide, the code of conduct, the security policy, three Issue_Forms and the Template_Chooser. `LICENSE` stays at the repository root, the one place GitHub detects a license. The README keeps a pointer of at most three sentences. The pr skill changes in three places: its ticket question takes a Ticket_Reference in any form; its title example and Why bullet write the reference the way its tracker does, nothing more; and its template sentence, which already prefers a repository's own template, says that the template's comments decide what each section holds and points the fallback at `skills/pr/pull_request_template.md`, so that `.github/pull_request_template.md` becomes this repository's own PR_Template. That template is where the `Closes #` convention lives, and the skill follows it here the way it follows any repository's own template. Everything else that is this repository's own process, the branch names, the labels, the triage pass, lives in the Guide and in repository settings, never in the plugin, so the plugin fits any organization's tracker. Labels and Issue_Types are applied with the gh CLI as part of the work, and the repository is made public with private vulnerability reporting on. Nothing here runs on a schedule or in CI beyond the existing `make validate`. + +## Architecture + +One flow matters: how an issue becomes a merged pull request, and which part of it each piece owns. The plugin's part is the single node on the left of the pull request; the Guide describes the rest. + +```mermaid +flowchart LR + C["Contributor"] --> TC["Template_Chooser
.github/ISSUE_TEMPLATE/config.yml"] + TC -->|a form| I["Issue
label triage, Issue_Type set"] + TC -->|question or idea| D["Discussions
Q&A, Ideas"] + TC -->|vulnerability| S["Report a vulnerability
security policy page"] + I --> T["Weekly triage
type confirmed, priority and areas or question,
milestone or not planned, triage removed"] + T --> B["Branch n-short-slug"] + B --> PR["/freethinking:pr
ticket #n in the title and Why,
Closes #n when the merge finishes it"] + PT["PR_Template
.github/pull_request_template.md"] -.used by.-> PR + G -.points at.-> PT + PR -->|merge| I + D -->|becomes work| I + G["Guide
.github/CONTRIBUTING.md"] -.describes.-> TC + G -.describes.-> T + G -.describes.-> B + G -.describes.-> PR +``` + +**Decision 1: the contributor documents live under `.github/`** (Requirements 1, 6; the author's choice). GitHub reads `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` and `SECURITY.md` from `.github/` first, then the root, then `docs/`; it links the guide from the new-issue and new-pull-request pages and lists all three in the community profile. Keeping them with the pull request template leaves the root to what a plugin consumer sees: `.claude-plugin/`, `skills/`, `rules/`, `agents/`, `hooks/`. Trade-off: a reader browsing the tree does not see the guide at the top level; the README pointer of Requirement 1.5 covers that. `LICENSE` is the exception: GitHub's license detection reads only the root, so it stays there, and `plugin.json` gains `"license": "MIT"` so the marketplace listing shows it too. + +**Decision 2: issue forms, not Markdown templates** (Requirements 2.1 to 2.3). A YAML form types its fields: a dropdown or checkboxes hold one of a known set of values, so a filter or a triage step reads a value rather than prose, and a field marked required cannot be left empty, so GitHub refuses an incomplete report at submission. Its top-level `labels` and `type` keys apply the Triage_Label and the Issue_Type at submission with no automation. A Markdown template can apply labels and a type from its frontmatter too, but its body is free text with nothing required. Trade-off: a form cannot be previewed locally and GitHub reads templates only from the default branch, so the forms are checked by hand after the merge (Requirement 7.4). + +**Decision 3: kind lives in the Issue_Type, everything else in nine bare labels** (Requirement 3.1). The organization's `Bug`, `Feature` and `Task` types exist and are enabled (checked with `gh api orgs/freethinkingit/issue-types`), so `bug`, `enhancement` and `documentation` would duplicate them. `quality` and `compatibility` are bare rather than `area:` prefixed: with two values a prefix adds nothing, and the small and mid-sized repositories surveyed use bare labels. Rejected: effort labels, a guess nobody acts on; prefixed areas, keys without a payoff; both the author's calls. + +**Decision 4: triage is a habit the Guide defines, not an automation** (Requirement 2.5). One weekly pass over the `triage` label in the six steps of Requirement 2.5. Rejected: a stale bot or a labelling action; the author wants no automation around this repository's project management. + +**Decision 5: the pr skill stays tracker-agnostic** (Requirement 4). The skill asks for the ticket in whatever form the author has it and writes it the way its tracker writes it: `#12` in the title's brackets and in Why for a GitHub issue, `ENG-1234` for a key, a link when the author gave a URL. It does not look the ticket up, add a closing keyword, or know this repository's branch names or labels; if confirming a ticket is worth doing, the session decides from the reference it was given. Rejected: a `gh issue view` check and an automatic `Closes #n` line, because they bake one organization's GitHub process into a skill meant to fit any tracker. The `Closes` convention lives in this repository's PR_Template instead (Decision 8): the skill uses a repository's own template when there is one, so here it drafts Why to that template's comment, and in another repository it never writes the line. + +**Decision 6: no new eval; the existing pr case is the confirmation** (Requirement 7.2). The change is to the wording of one question, two examples and the template sentence, with nothing new the cases could grade, since neither case's workspace has a template of its own, so the two existing pr cases are run at three runs after the change and nothing is added to them; no grader reads the description, so the fallback's new path is confirmed from one kept transcript, as the Testing Strategy says. Rejected: graders for a ticket in the title and a `Closes` line, which would have tested behaviour Decision 5 removed. + +**Decision 7: settings are applied with gh commands recorded in the task list, not by a script or a check in the repository** (Requirements 3.1, 7.5). Three `gh label create`, ten `gh label delete`, four `gh issue edit`, run once by the executor. Rejected: a `make labels` target, a labels file, or a unit test over the forms, because the plugin and its checks maintain the plugin, not this repository's project management; the Guide names the settings as settings so a reader knows they do not travel with the tree, and stops there. The `component` dropdown of the three forms lists the plugin's skills, agents, rules and hooks, so it changes when they do; the Guide's Make a change section says to update it, and no check guards it, for the same reason. + +**Decision 8: two templates, one role each** (Requirements 1.3, 4.5, 4.6). Today `.github/pull_request_template.md` plays two roles: GitHub shows it for this repository's pull requests, and the pr skill ships it as the generic fallback for any repository without a template of its own, so anything this repository writes into it leaks into every other repository's fallback. The generic template moves to `skills/pr/pull_request_template.md`, inside the skill that reads it and named by its own path, so that it is grouped with its skill and a second skill with a template of its own would be as clear. `.github/pull_request_template.md` is then this repository's own template: the same sections, with the Why comment asking for the issue to be linked and for `Closes #` when merging finishes the issue. `Closes` is one of GitHub's closing keywords, a documented feature: a pull request whose description carries it is linked to the issue as soon as it is opened, the link shows under Development on both, and the issue closes when the pull request merges into the default branch. The line reaches the description only through the draft the author approves, and only when the author said the merge finishes the issue; where their answers do not say, the skill asks, as it already does for anything it would otherwise have to invent. So the skill carries no closing keyword and no default, and follows the template's comments here the way it follows any repository's. The Guide points at the template rather than restating it, which closes the loop. Rejected: one file in both roles with the `Closes` prompt in it, because the fallback would carry this repository's convention into other organizations' pull requests; a convention stated only in the Guide, which the skill would not see; and a `Closes` line the skill writes unless told otherwise, because a line the author did not ask for is wiring the author does not want. + +## Components and Interfaces + +### Guide + +`.github/CONTRIBUTING.md`, the one document. Sections in the order a new Contributor needs them. + +| Section | Says | Requirements | +|---|---|---| +| Opening | What the plugin is, links to the Community_Files | 1.4 | +| Set up | Clone; `claude --plugin-dir .` loads the working copy for one session; `make validate` | 1.1 | +| Test | Free checks: `make validate`, the unit tests under `checks/` then the two validators. Evals by component with `make eval--`, model calls run by hand, the table into the pull request. A change to a skill, agent, rule or hook comes with an eval case or a change to one; when none is touched no eval is needed and `make validate` is the whole check. The commands and these rules only: for how the checks and evals work, the section links the README's Testing section and `evals/README.md` rather than restating them, so that the detail has one place | 1.1, 1.2, 1.6 | +| Make a change | Small change: make it. Sizeable: `/freethinking:start-task`, then `/freethinking:implement-spec`. Bug: `/freethinking:bugfix`. Branch `-` when an issue exists, a short slug otherwise, never the default branch; GitHub's Create a branch link on the issue gives that shape, and when `/freethinking:implement-spec` proposes the task name as the branch, the answer puts the issue number in front. `/freethinking:pr`, giving the issue number as the ticket; the PR_Template, linked from here, is the one place that says how the issue is linked and when to write `Closes #`. A new skill, agent, rule or hook is also added to the `component` dropdown of all three forms, which name the plugin's components | 1.2, 1.3 | +| Issues | The three Issue_Forms and what submitting one does; the Template_Chooser links; triage as the six steps of Requirement 2.5, at least weekly; an issue opened without a form; a discussion that becomes work is converted to an issue, an issue that is a question is converted to a discussion at triage | 1.4, 2.5, 2.6, 5.3 | +| Labels and milestones | One Issue_Type per issue; each label group in one sentence; labels are lowercase and Issue_Types are written as GitHub names them; at most one Priority_Label and the Area_Labels that apply, none included; a new label is proposed in an issue; `good first issue` feeds the contribute page and GitHub's surfacing of approachable issues, `help wanted` is work a Maintainer would merge but will not write; a Milestone names a goal, the open ones are the roadmap | 3.2, 3.3, 3.4, 3.5, 3.6 | +| Compatibility | What portability across Claude Code, Cursor, Codex and other agent tools means for a skill or rule here; the checks known so far, seeded with the three in Data Models; the list grows from issues labelled `compatibility` | 1.7 | +| Settings, not files | One short paragraph: labels, Milestones, Discussions and Issue_Types are repository or organization settings and do not travel with the tree | 7.5 | + +### Issue_Forms and Template_Chooser + +Four files under `.github/ISSUE_TEMPLATE/`. Fields are in Data Models. + +| File | `name` | Issue_Type | Labels | Requirements | +|---|---|---|---|---| +| `misbehaving-skill-or-agent.yml` | Misbehaving skill or agent | Bug | `triage` | 2.1, 2.2, 2.3 | +| `proposal.yml` | Proposal: a new or changed skill, agent or rule | Feature | `triage` | 2.1, 2.2, 2.3 | +| `eval-case.yml` | New eval case | Task | `triage` | 2.1, 2.2, 2.3 | +| `config.yml` | blank issues off; links for a question, an idea and a vulnerability | | | 2.4, 5.2 | + +### Community_Files + +| File | Content | Requirements | +|---|---|---| +| `LICENSE` | The MIT License text with `Copyright (c) 2026 freethinkingit` | 6.1 | +| `.claude-plugin/plugin.json` | gains `"license": "MIT"`, a documented manifest field holding an SPDX identifier; the marketplace entry has a relative-path source, so the listing shows it before install too | 6.1 | +| `.github/CODE_OF_CONDUCT.md` | Contributor Covenant 2.1, the contact placeholder replaced by the method the author names | 6.2 | +| `.github/SECURITY.md` | Report through GitHub's private vulnerability reporting, the Report a vulnerability button on the repository's Security tab, never in a public issue; the latest release is supported | 6.3 | + +### README pointer + +The "Add or change a skill or rule" section of `README.md` shrinks to at most three sentences that link the Guide; its command block moves into the Guide's Set up section, and the one contributing rule in the README's Testing section, that a new skill needs an eval case, moves to the Guide's Test section, so the README keeps the how and the Guide the must (Requirement 1.5). + +### pr skill + +`skills/pr/SKILL.md`. Three places change; step numbers, `allowed-tools` and every other step stay as they are. + +| Operation | Where | Change | Requirements | +|---|---|---|---| +| Ask for the ticket | step 5, the Ticket bullet | asks for the ticket in whatever form the author has it, an issue number such as `#12`, an issue URL, or a key from another tracker such as `ENG-1234`, and says any form works | 4.1 | +| Write the ticket | step 6, Title and the Why bullet | the title example gains a GitHub form beside the key form, `[#12] Add retry to upload worker` and `[ENG-1234] Add retry to upload worker`; Why names the ticket as the tracker writes it and links it when the author gave a URL; nothing is added that the author's answers or the repository's own template did not call for | 4.2, 4.3, 4.4 | +| Template sentence | step 6, Description | keeps preferring a repository's own template and gains two things: the template's sections and the comments inside them say what that repository wants in each and take precedence over the bullets below, and a comment that asks for something the author's answers did not give is asked before drafting; the fallback becomes `${CLAUDE_PLUGIN_ROOT}/skills/pr/pull_request_template.md`. Proposed wording, for the author to amend: "If the repository has its own template (`.github/pull_request_template.md`), use it: its sections and the comments inside them say what that repository wants in each, and they take precedence over the bullets below; when a comment asks for something the author's answers did not give, ask before drafting. Otherwise use the skill's default template at `${CLAUDE_PLUGIN_ROOT}/skills/pr/pull_request_template.md`." | 4.5, 4.6 | + +### PR_Template and the generic template + +`.github/pull_request_template.md` keeps its six sections and becomes this repository's own: its Why comment asks for the problem or goal, for the issue to be linked, and for `Closes #` when merging finishes the issue. The skill drafts to that comment from the author's answers and asks when they do not say whether the merge finishes the issue, so the line appears only when the author said so, and the author reads it again in the draft at approval. The generic template at `skills/pr/pull_request_template.md` is the current file moved unchanged, its Why comment still "Link the ticket if there is one" (Requirements 4.5, 4.6). + +### pr eval case + +`evals/skills/pr/` is not changed. Both cases, `opens-pr-with-stub-gh` and `stops-at-starting-checks`, run at three runs after the skill change as the confirmation, and one kept transcript of the first confirms the generic template's new path (Requirement 7.2). + +### Repository settings + +| Setting | Action | Requirements | +|---|---|---| +| Labels | create `quality`, `compatibility`, `triage`; delete `bug`, `enhancement`, `documentation`, `duplicate`, `invalid`, `wontfix`, `effort: high`, `effort: medium`, `effort: low`, `area: quality`; keep the rest as they are; applied before the merge, since a form's `labels` key adds only labels that already exist | 3.1 | +| Issues #1 to #4 | drop their `effort:` and `area: quality` labels, add `quality`, keep their priorities; set the Issue_Type with `gh issue edit --type `, proposed as #1 Feature (a rule change), #2 Task (an eval case), #3 Task, #4 Task, confirmed by the author at hand-back | 7.5 | +| Discussions | already enabled on the repository (checked with `gh repo view`); confirm the Q&A and Ideas categories exist so the Template_Chooser links resolve | 5.1, 5.2 | +| Visibility | public, with GitHub's private vulnerability reporting enabled since `SECURITY.md` names it: `gh repo edit freethinkingit/dev_tools --visibility public --accept-visibility-change-consequences`, then `gh api -X PUT repos/freethinkingit/dev_tools/private-vulnerability-reporting`; the author's action, before the merge | 6.4, 7.5 | +| Milestone | "Listing on Claude marketplace" exists with the four issues; nothing to change | 3.3 | +| Version | `0.5.0` to `0.5.1` in `.claude-plugin/plugin.json`, a version string the docs do not check against semver, moved by a patch step for a wording change in the pr skill; the marketplace entry sets no version, so nothing else must agree | 7.3 | + +## Data Models + +### Labels + +Nine rows. Colours are GitHub's for its own labels and the current ones for the priorities, so only the three new labels get a colour chosen here. + +| Label | Group | Colour | Description | +|---|---|---|---| +| `priority: high` | Priority_Label | `d73a4a` | Do first | +| `priority: medium` | Priority_Label | `fbca04` | Do soon | +| `priority: low` | Priority_Label | `0e8a16` | When convenient | +| `quality` | Area_Label | `5319e7` | The behaviour of the skills, agents and rules, and the evals and checks that guard it | +| `compatibility` | Area_Label | `1d76db` | Working across Claude Code, Cursor, Codex and other agent tools | +| `triage` | Triage_Label | `e4e669` | Not yet classified by a maintainer | +| `question` | Info_Label | `d876e3` | Further information is requested | +| `good first issue` | contributor | `7057ff` | Good for newcomers | +| `help wanted` | contributor | `008672` | Extra attention is needed | + +Names are lowercase, the GitHub convention the kept defaults already follow, and the forms' `type` keys and the Guide write the Issue_Types as GitHub names them, `Bug`, `Feature` and `Task` (Requirement 3.6). An issue carries the Area_Labels that apply; most issues about a new capability, the docs or the listing carry none, and the Milestone says what they are for. + +An issue's lifecycle under these labels: + +```mermaid +stateDiagram-v2 + [*] --> Untriaged: opened through a form (triage, Issue_Type) + [*] --> Unmarked: opened without a form + Unmarked --> Untriaged: a Maintainer applies triage by hand + Untriaged --> Triaged: type confirmed, one priority, the areas that apply, milestone, triage removed + Untriaged --> NeedsInfo: question applied instead, triage removed + NeedsInfo --> Triaged: author answered, classified at the next pass + Untriaged --> Closed: closed as not planned + Untriaged --> Discussion: converted, it was a question + Triaged --> Closed: pull request merged +``` + +Constraints: a Triaged issue carries exactly one Priority_Label, the Area_Labels that apply, none, one or both, and never `triage`; a NeedsInfo issue carries `question` and no Priority_Label; `good first issue` and `help wanted` may sit on a Triaged issue; nothing else exists to apply (Requirements 3.1, 3.2). + +### Issue_Form fields + +Every form opens with a one-line Markdown pointer to the Guide's Issues section, then the preflight checkboxes, each required. Every field with a known answer set is a dropdown or checkboxes, and every field triage reads is required, so a submission is typed and complete (Requirement 2.3). Field ids are the YAML `id` values. + +Misbehaving skill or agent (`type: Bug`): + +| id | Kind | Required | Content | +|---|---|---|---| +| `preflight` | checkboxes | each | I searched the existing issues and found nothing that covers this; I am on the latest plugin version | +| `component` | dropdown | yes | start-task, implement-spec, pr, bugfix (skills); task-executor, wave-reviewer (agents); code-changes, comments, workflow (rules); the SessionStart hook; the SubagentStart hook; something else | +| `version` | input | yes | The plugin version, as `claude plugin list` shows it | +| `prompt` | textarea | yes | The prompt or command you gave | +| `happened` | textarea | yes | What happened | +| `expected` | textarea | yes | What you expected, with the sentence of the skill, agent, rule or hook it should have followed | +| `reproduces` | dropdown | yes | Every time; Sometimes; Once so far | + +Proposal (`type: Feature`): + +| id | Kind | Required | Content | +|---|---|---|---| +| `preflight` | checkboxes | each | I searched the existing issues; I looked in Discussions under Ideas | +| `kind` | dropdown | yes | New skill; Change to a skill; New agent; Change to an agent; New rule; Change to a rule | +| `component` | dropdown | no | the same list as the misbehaviour form, chosen for a change and left empty for something new | +| `behaviour` | textarea | yes | The behaviour or wording you propose | +| `case` | textarea | yes | The case that motivates it: what went wrong, or took too long, without it | + +New eval case (`type: Task`): + +| id | Kind | Required | Content | +|---|---|---|---| +| `preflight` | checkboxes | each | I searched the existing issues; I read `evals/README.md` | +| `component` | dropdown | yes | the same list as the misbehaviour form | +| `behaviour` | textarea | yes | The behaviour the case should guard | +| `grading` | textarea | yes | How a run would be graded: what in the transcript or in the files shows pass or fail | +| `prompt` | textarea | no | A prompt that would exercise it | + +Template_Chooser (`config.yml`): `blank_issues_enabled: false` and three `contact_links`, each with `name`, `url` and `about`: a question to `https://github.com/freethinkingit/dev_tools/discussions/categories/q-a`, an idea to `https://github.com/freethinkingit/dev_tools/discussions/categories/ideas`, a vulnerability to `https://github.com/freethinkingit/dev_tools/security/policy`, the page that shows `SECURITY.md` with GitHub's Report a vulnerability button. + +### Ticket_Reference in the pr skill + +What the skill writes for what it is given (Requirements 4.2 to 4.4). The rule is one line: write the reference the way its tracker writes it, and add nothing the author's answers or the repository's own template did not call for. + +| Given | Title prefix | Why section | +|---|---|---| +| `#12`, or an issue URL of a GitHub repository | `[#12]` | names `#12`, linked when a URL was given | +| `ENG-1234`, or any other key | `[ENG-1234]` | names the key, linked when a URL was given | +| none | none | no ticket line | + +### Branch name + +`-`, for example `12-ticket-reference`; the slug alone without an issue. It is the shape of the branch GitHub makes from the Create a branch link under Development on an issue page, `-`, so a contributor who starts there complies without reading the rule, and the Guide's slug is only shorter. When `/freethinking:implement-spec` proposes the task name as the branch, the author answers with the issue number in front, `12-contributing-guide`; the skill proposes and the author decides, so the skill stays as it is (Requirement 1.3). The Guide states it; no skill checks it. + +### Compatibility checks, the seed list + +Three checks the Guide starts with, from what the plugin already shows (Requirement 1.7): + +1. A rule is plain Markdown with no tool-specific syntax, so another tool can load it as instructions as it is. +2. A skill's steps run on commands anyone has, `git`, `gh`, `make`, and name a Claude-only tool only where no command does the job. +3. The Claude-only pieces are listed so a `compatibility` issue can start from the list: `$ARGUMENTS` and `${CLAUDE_PLUGIN_ROOT}` in skills, the hooks that load the rules, and the agents' frontmatter. + +## Correctness Properties + +### Property 1: Label closure + +*For any* label on the repository after the work, it is one of the nine in the Labels table, and each of the nine exists exactly once. + +**Validates: Requirements 3.1** + +### Property 2: Every form arrives marked + +*For any* Issue_Form under `.github/ISSUE_TEMPLATE/`, submitting it applies exactly the label `triage`, sets exactly one Issue_Type from `Bug`, `Feature` and `Task`, cannot be submitted with a preflight box unticked or a field triage reads empty, and holds one of a known set of values in every field whose answer set is known. + +**Validates: Requirements 2.1, 2.2, 2.3** + +### Property 3: No unrouted entry + +*For any* visit to the Template_Chooser, the blank issue is disabled, and a question, an idea and a vulnerability each have a link to their destination. + +**Validates: Requirements 2.4, 5.2** + +### Property 4: The ticket passes through untouched + +*For any* Ticket_Reference the author gives, the drafted title begins with it in brackets and the Why section names it, and the description contains no closing keyword, lookup result or convention that neither the author's answers nor the repository's own PR_Template called for, a line the PR_Template asks for on a condition appearing only when the author said the condition holds; *for any* run with no ticket, the title and the Why section are what the skill produced before this change. + +**Validates: Requirements 4.2, 4.3, 4.4, 4.5** + +### Property 5: One source for the contributing conventions + +*For any* rule in the Guide's Set up, Test and Make a change sections about what a change must come with, no other document in the repository states it, the README's Testing section included; and the README's "Add or change a skill or rule" section is at most three sentences, one of them linking the Guide. + +**Validates: Requirements 1.5** + +## Error Handling + +| Condition | Category | Behaviour | Requirements | +|---|---|---|---| +| The skill cannot tell which tracker a Ticket_Reference belongs to | AMBIGUITY | it writes the reference as the author gave it, in brackets in the title and named in Why, and asks nothing more | 4.2 | +| The repository the skill runs in has no template of its own | DEFAULT | the generic template at `skills/pr/pull_request_template.md` is used, as the `.github/` one was before the move; in another repository that read falls outside the working directory and may ask for approval, as it does today | 4.6 | +| The Ticket_Reference is not a GitHub issue, and the PR_Template asks for `Closes` | NOT_APPLICABLE | the template's comment speaks of an issue; Why names the ticket and no `Closes` line is written | 4.5 | +| The PR_Template's comment asks whether merging finishes the issue and the author's answers do not say | GAP | the skill asks before drafting, as its rule never to invent an answer says; it does not write the line by default | 4.5 | +| A form is submitted with a required field empty | VALIDATION | GitHub refuses the submission, so an incomplete report never reaches triage | 2.3 | +| An issue is opened without a form, by API, by email or by a Maintainer's quick note | GAP | a Maintainer applies `triage` by hand at the next pass | 2.6 | +| A label outside the nine seems needed | POLICY | proposed in an issue, not created on the spot | 3.4 | +| A vulnerability arrives as a public issue | SECURITY | a Maintainer closes it, removes the details if needed, and asks for a private report as `SECURITY.md` says | 6.3 | +| The repository is forked to an account without organization Issue_Types | PORTABILITY | GitHub cannot apply the form's `type`; the Guide's "Settings, not files" paragraph says Issue_Types are an organization setting | 7.5 | +| `plugin.json` rejects the `license` field | VALIDATION | `claude plugin validate --strict` fails in `make validate`; the field is dropped and `LICENSE` alone carries the license | 6.1, 7.1 | + +## Testing Strategy + +No property-based library applies to documents and settings, and the plugin's checks are not extended to cover this repository's project management. Each property is checked where its fact lives: the existing free checks and eval where the plugin holds it, a recorded manual check where GitHub holds it. + +- Free checks: `make validate` stays green, with no new check (Requirement 7.1). +- Eval: `make eval-skill-pr` at three runs after the skill change, with both pr cases as they are (Requirement 7.2). `opens-pr-with-stub-gh` exercises the no-ticket half of Property 4, the prompt's "No ticket" answer being unchanged, and, since its workspace has no template of its own, takes the generic template's new path; its graders see only the push, the `gh pr create` call and the URL, so the path is confirmed from one run kept with `EVAL_ARGS="--keep-temp --case opens-pr-with-stub-gh"`, whose `out/trace.jsonl` shows the read of `skills/pr/pull_request_template.md` and a body carrying the template's six headings, recorded in the Testing section beside the eval table. `stops-at-starting-checks` stops at step 1 and never reaches the template. +- Manual checks, each recorded in the pull request's Testing section: the pr skill run from the working copy (`claude --plugin-dir .`) on this branch, once with `#<n>` as the ticket, to see the question's wording, the `[#n]` title, the skill asking whether the merge finishes the issue when the answer did not say, and a Why drafted to this repository's PR_Template, naming the issue and ending with `Closes #<n>` when it does (the ticket half of Property 4); `gh label list` showing exactly the nine (Property 1); `gh issue list --json number,labels,issueType` showing the four issues relabelled and typed; `gh repo view --json hasDiscussionsEnabled` and the two category pages; the README section counted at three sentences or fewer with its link (Property 5). +- After the merge, since GitHub reads templates and community files from the default branch only: the Template_Chooser shows the three forms and the three links, each form opens with its fields and required marks (Requirement 7.4, the GitHub half of Properties 2 and 3), and the community profile lists the license, code of conduct, contributing guide and security policy. A problem found there is fixed in a follow-up pull request; the eval and the free checks cannot see it. diff --git a/.claude/specs/contributing-guide/requirements.md b/.claude/specs/contributing-guide/requirements.md new file mode 100644 index 0000000..c3d067f --- /dev/null +++ b/.claude/specs/contributing-guide/requirements.md @@ -0,0 +1,111 @@ +# Requirements Document + +## Introduction + +The contributing guide is the one document that tells a contributor how to develop, test and submit a change to the plugin, and how the repository uses issues, labels, milestones and discussions to turn requests into work. It comes with the community files an open-source repository is expected to carry, a license, a code of conduct and a security policy, and with a small change to the pr skill so that a pull request names its ticket however the author gives it, and with this repository's own pull request template carrying how an issue is linked, which the skill follows because it uses a repository's own template when there is one. Maintainers get a triage routine they can run in minutes; contributors get the conventions in one place instead of reverse-engineering them from the tree. The documents are written for a public repository. + +## Glossary + +- **Guide**: `.github/CONTRIBUTING.md`, beside the pull request template GitHub already reads from that folder. +- **Contributor**: Anyone proposing a change to the repository, maintainer or outside developer. +- **Maintainer**: A person with write access who triages issues and reviews pull requests. +- **Issue_Form**: A GitHub issue template under `.github/ISSUE_TEMPLATE/` that renders as a form with named fields. +- **Template_Chooser**: The page GitHub shows when a new issue is opened, listing the Issue_Forms and the links from `.github/ISSUE_TEMPLATE/config.yml`. +- **Issue_Type**: The organization-level type GitHub attaches to an issue, one of `Bug`, `Feature` or `Task`, saying what sort of thing it is. +- **Priority_Label**: One of `priority: high`, `priority: medium` or `priority: low`. +- **Area_Label**: A bare topic label saying which part of the plugin's value an issue serves: `quality` for the behaviour of the skills, agents and rules and for the evals and checks that guard it, and `compatibility` for working across Claude Code, Cursor, Codex and other agent tools. An issue carries the Area_Labels that apply, which may be none. +- **Info_Label**: GitHub's default `question` label, kept with GitHub's own meaning: the issue needs more from its author before it can be classified or worked. +- **Triage_Label**: The `triage` label that marks an issue no Maintainer has classified yet. +- **Milestone**: A GitHub milestone named after a goal, such as "Listing on Claude marketplace". +- **Ticket_Reference**: Any identifier of the work a pull request serves: a GitHub issue number such as `#12`, an issue URL, or a key from another tracker such as `ENG-1234`. +- **PR_Template**: `.github/pull_request_template.md`, this repository's own pull request template, which GitHub shows for a pull request opened by hand and which the pr skill uses in place of its generic one, as it does for any repository that has one. +- **Discussions**: GitHub's forum feature on the repository, used for questions and ideas that are not yet work. +- **Community_Files**: `LICENSE` at the repository root, the only place GitHub detects a license, and `CODE_OF_CONDUCT.md` and `SECURITY.md` under `.github/`, where GitHub reads them as it does from the root. + +## Requirements + +### Requirement 1: The contributing guide + +**User Story:** As a Contributor, I want one document that says how to set up, test and submit a change, so that I do not have to reverse-engineer the repository's conventions. + +#### Acceptance Criteria + +1. THE Guide SHALL say how to load a working copy for a session (`claude --plugin-dir .`), how to run the free checks (`make validate`: the unit tests under `checks/`, then the validator) and how to run the evals for one component, including that evals are model calls run by hand and that their table goes in the pull request. +2. THE Guide SHALL say that a change to a skill, agent, rule or hook comes with an eval case or a change to one, and that a sizeable change starts with `/freethinking:start-task` and a spec, as the workflow rule says. +3. THE Guide SHALL say how a change is submitted: on a branch named `<issue number>-<short slug>` when an issue exists and a short slug otherwise, never on the default branch, through a pull request opened with `/freethinking:pr`, giving the issue number as the Ticket_Reference, and SHALL point at the PR_Template as the one place that says how the issue is linked, so that the Guide and the template cannot drift. +4. THE Guide SHALL describe the issue workflow of Requirement 2, the labels and Milestones of Requirement 3 and the Discussions of Requirement 5 in a few sentences each, and SHALL link the Community_Files. +5. THE README SHALL keep its "Add or change a skill or rule" heading as a pointer to the Guide of at most three sentences, so that the two documents cannot drift apart. +6. IF a change touches no skill, agent, rule or hook, THEN THE Guide SHALL say that no eval is required and that `make validate` is the whole check. +7. THE Guide SHALL have a compatibility section that says what portability across agent tools (Claude Code, Cursor, Codex and others) means for a skill or rule here, lists the checks known so far, and says the list grows from issues labelled `compatibility`. + +### Requirement 2: Issue forms and triage + +**User Story:** As a Maintainer, I want every new issue to arrive with the facts I need and a mark saying it is untriaged, so that triage is a short weekly pass rather than a conversation. + +#### Acceptance Criteria + +1. THE repository SHALL provide three Issue_Forms, each opening with a required preflight checklist, the existing issues searched on every form, plus the latest plugin version in use on the misbehaviour form, Discussions Ideas looked in on the proposal form and `evals/README.md` read on the eval-case form: a misbehaving skill or agent (a dropdown naming the skill, agent, rule or hook, the prompt, what happened, what was expected, the plugin version, and whether it reproduces), a proposal for a new or changed skill, agent or rule (a dropdown for which kind, the proposed behaviour or wording, and the case that motivates it), and a new eval case (the behaviour to guard, the component, how it would be graded). +2. WHEN an Issue_Form is submitted, THE repository SHALL apply the Triage_Label and set the Issue_Type through the form's own settings, `Bug` for a misbehaving skill or agent, `Feature` for a proposal and `Task` for a new eval case, and SHALL apply no Area_Label, since the area is a triage decision. +3. THE Issue_Form SHALL mark every field a Maintainer cannot triage without as required, and SHALL use a dropdown or checkboxes wherever the answer set is known, so that an incomplete or free-form report cannot be submitted. +4. THE Template_Chooser SHALL disable blank issues and SHALL offer three links: questions to Discussions Q&A, ideas to Discussions Ideas, and security reports to the repository's security policy page, which shows `SECURITY.md` with GitHub's Report a vulnerability button, so that a report started in the wrong place still lands in the right one. +5. THE Guide SHALL define triage as: at least weekly, a Maintainer reviews every issue carrying the Triage_Label, confirms or corrects its Issue_Type, sets one Priority_Label and the Area_Labels that apply, none, one or both, applies the Info_Label instead when the issue cannot be classified without more from its author, assigns a Milestone or closes the issue as not planned, and removes the Triage_Label. +6. IF an issue is opened without an Issue_Form, THEN THE Guide SHALL say that a Maintainer applies the Triage_Label by hand at the next triage, so that no issue escapes classification. + +### Requirement 3: Labels and milestones + +**User Story:** As a Maintainer, I want the organization's issue types for kind, a small fixed label vocabulary for everything else, and milestones that read as a roadmap, so that a filter means the same thing to everyone. + +#### Acceptance Criteria + +1. THE repository SHALL carry exactly nine labels: the three Priority_Labels, the Area_Labels `quality` and `compatibility`, the Triage_Label, the Info_Label, `good first issue` and `help wanted`; every other label SHALL be removed: GitHub's `bug`, `enhancement` and `documentation` because the Issue_Types carry kind, `duplicate`, `invalid` and `wontfix` because close reasons cover them, and the `effort:` and `area:` labels created earlier, whose values move into this set. +2. THE Guide SHALL say that every issue has one Issue_Type, SHALL define each label group in one sentence, and SHALL say that an issue carries at most one Priority_Label and the Area_Labels that apply, so that an issue with no Area_Label is normal. +3. THE Guide SHALL say that a Milestone names a goal and that the open Milestones are the repository's roadmap. +4. IF a label or an Area_Label outside these seems needed, THEN THE Guide SHALL say to propose it in an issue rather than create it, so that the vocabulary stays small. +5. THE Guide SHALL say that `good first issue` is the one label with behaviour on GitHub, feeding the repository's contribute page and GitHub's surfacing of approachable issues, and that `help wanted` marks work a Maintainer would merge but will not write. +6. THE label names SHALL be lowercase throughout, in the repository, the Guide and the Issue_Forms, and the Issue_Types SHALL be written as GitHub names them, `Bug`, `Feature` and `Task`, in the forms' `type` keys and in the Guide. + +### Requirement 4: Ticket references and the pull request template + +**User Story:** As an author opening a pull request, I want to give the ticket in whatever form I have, so that the pull request names its ticket without my learning a format. + +#### Acceptance Criteria + +1. WHEN the pr skill asks for a ticket, THE skill SHALL accept a Ticket_Reference of any kind and SHALL say so in its question. +2. WHEN a Ticket_Reference is given, THE skill SHALL carry it in the title in brackets and name it in the Why section, written the way its tracker writes it, `#12` for a GitHub issue and `ENG-1234` for a key, and SHALL leave anything further, such as a check that the ticket exists, to the judgement of the session. +3. WHEN the author says there is no ticket, THE skill SHALL proceed without one, as it does today. +4. THE skill SHALL NOT carry this repository's own conventions, such as branch names, labels or closing keywords, so that it fits any organization's tracker; those conventions live in the Guide and the PR_Template. +5. THE PR_Template SHALL ask, in the Why section's comment, for the issue to be linked and for `Closes #<number>` when merging finishes the issue, and THE pr skill SHALL follow it as this repository's own template, the way it follows any repository's own template, asking the author when the comment wants something their answers did not give, so that the convention is enforced here without living in the skill and nothing enters the description that the author did not say. +6. THE pr skill's generic template SHALL move from `.github/pull_request_template.md` to `skills/pr/pull_request_template.md`, with the skill's path to it updated, so that `.github/pull_request_template.md` is this repository's own PR_Template and the fallback other repositories get carries none of its conventions. + +### Requirement 5: Discussions + +**User Story:** As a Maintainer, I want questions and ideas to go somewhere other than the tracker, so that issues stay work. + +#### Acceptance Criteria + +1. THE repository SHALL have Discussions enabled with GitHub's default categories. +2. THE Template_Chooser SHALL link questions to the Q&A category and ideas to the Ideas category. +3. THE Guide SHALL say that a discussion which becomes work is converted to an issue, and that an issue which is really a question is converted to a discussion at triage. + +### Requirement 6: Community files + +**User Story:** As a future outside Contributor, I want the license, conduct and security expectations stated, so that contributing is safe for both sides. + +#### Acceptance Criteria + +1. THE repository SHALL carry `LICENSE` at the repository root with the MIT License text and the copyright line `Copyright (c) 2026 freethinkingit`. +2. THE repository SHALL carry `.github/CODE_OF_CONDUCT.md` with the Contributor Covenant version 2.1, unmodified except for the contact method, which names how a report reaches the Maintainers. +3. THE repository SHALL carry `.github/SECURITY.md` saying that a vulnerability is reported through GitHub's private vulnerability reporting, the Report a vulnerability button on the repository's Security tab, never in a public issue, and that the latest release is the supported version. +4. THE repository SHALL be public with GitHub's private vulnerability reporting enabled, so that the path `SECURITY.md` names exists. + +### Requirement 7: Ship + +**User Story:** As a Maintainer, I want the new documents and settings to land without breaking the plugin's checks, so that the change is safe to merge on its own. + +#### Acceptance Criteria + +1. THE free checks SHALL pass after the change: `make validate` with the unit tests and both validators. +2. THE pr skill's eval SHALL still pass after the changes of Requirement 4, at three runs, and the transcript of one of those runs, kept with `--keep-temp`, SHALL show the skill reading `skills/pr/pull_request_template.md`, since the case's graders see only the push, the `gh pr create` call and the URL and cannot tell which template was used. +3. THE plugin version SHALL be bumped, since the pr skill's behaviour changed. +4. WHEN the change is merged to the default branch, the only branch GitHub reads templates from, THE Template_Chooser SHALL show the three Issue_Forms and the three links, and each form SHALL open with its fields rendered; this is checked by hand on GitHub right after the merge. +5. THE label set of Requirement 3, the Discussions setting of Requirement 5 and the visibility and private vulnerability reporting of Requirement 6 SHALL be applied to the repository as part of the work, the four existing issues SHALL be relabelled to the new set and given their Issue_Type, and the Guide SHALL say that labels, Milestones, Discussions and Issue_Types are repository or organization settings rather than files, so that a reader knows they do not travel with the tree. diff --git a/.claude/specs/contributing-guide/tasks.md b/.claude/specs/contributing-guide/tasks.md new file mode 100644 index 0000000..fe609f2 --- /dev/null +++ b/.claude/specs/contributing-guide/tasks.md @@ -0,0 +1,108 @@ +# Implementation Plan + +## Overview + +The labels and the two pull request templates come first, because the issues, the forms, the Guide and the skill all refer to them; then the documents, the forms and the skill change; then the README pointer and the version; then the runs and the author's own actions. The repository has no code, so proof is `make validate` after every change, an ephemeral check that reads each file back against the design, one eval run with a kept transcript, and manual checks the author performs, two of them after the merge because GitHub reads templates and community files from the default branch only. + +## Tasks + +- [x] 1. Repository settings + - [x] 1.1 Create the three new labels + - `gh label create quality --color 5319e7 --description "The behaviour of the skills, agents and rules, and the evals and checks that guard it"`, `gh label create compatibility --color 1d76db --description "Working across Claude Code, Cursor, Codex and other agent tools"`, `gh label create triage --color e4e669 --description "Not yet classified by a maintainer"`. Prove it with `gh label list --json name,color,description`: the three present with those colours and descriptions + - _Requirements: 3.1, 7.5_ + - [x] 1.2 Relabel and type issues #1 to #4 + - For each: `gh issue edit <n> --add-label quality --remove-label "effort: <its value>" --remove-label "area: quality"`, keeping its priority label, then `gh issue edit <n> --type <Type>` with #1 Feature and #2, #3 and #4 Task, confirmed by the author. Prove it with `gh issue list --state all --json number,labels,issueType`: each issue carries one `priority:` label and `quality`, no `effort:` or `area:` label, and its type + - _Requirements: 7.5_ + - [x] 1.3 Delete the ten labels that are no longer in the set + - `gh label delete <name> --yes` for `bug`, `enhancement`, `documentation`, `duplicate`, `invalid`, `wontfix`, `effort: high`, `effort: medium`, `effort: low` and `area: quality`; after 1.2, so that the issues already carry `quality`. Prove it with `gh label list`: exactly the nine of the design's Labels table, each once, nothing else + - **Property 1: Label closure** + - **Validates: Requirements 3.1, 7.5** + - [x] 1.4 The author makes the repository public and enables private vulnerability reporting + - An action for the author, by hand: `gh repo edit freethinkingit/dev_tools --visibility public --accept-visibility-change-consequences`, then `gh api -X PUT repos/freethinkingit/dev_tools/private-vulnerability-reporting`. Ticked on their report, checked with `gh repo view --json visibility` showing `PUBLIC` and `gh api repos/freethinkingit/dev_tools/private-vulnerability-reporting` showing `"enabled": true` + - _Requirements: 6.4, 7.5_ + +- [ ] 2. The pr skill and its two templates + - [x] 2.1 Two templates, one role each + - `git mv .github/pull_request_template.md skills/pr/pull_request_template.md`, content unchanged. Then write a new `.github/pull_request_template.md` with the same six sections and comments, the Why comment now reading "The problem or goal behind the change. Link the issue, and write `Closes #<number>` when merging finishes it." Nothing else differs. Prove it: `diff <(git show HEAD:.github/pull_request_template.md) skills/pr/pull_request_template.md` is empty; the two files' `## ` headings are identical; `grep -c "Closes #"` is 1 in the `.github/` file and 0 in the `skills/pr/` file + - _Requirements: 4.5, 4.6_ + - [x] 2.2 Three places in `skills/pr/SKILL.md` + - Step 5, the Ticket bullet: ask for the ticket in whatever form the author has it, an issue number such as `#12`, an issue URL, or a key from another tracker such as `ENG-1234`, and say any form works. Step 6, Title: the GitHub example `[#12] Add retry to upload worker` beside the key example; the Why bullet: name the ticket as its tracker writes it, link it when the author gave a URL, and add nothing the author's answers or the repository's own template did not call for. Step 6, Description: the sentence as the design's pr skill table quotes it, preferring the repository's own template, whose sections and comments say what that repository wants and take precedence over the bullets below, asking when a comment wants something the author's answers did not give, and falling back to `${CLAUDE_PLUGIN_ROOT}/skills/pr/pull_request_template.md`. Step numbers, `allowed-tools` and every other step stay as they are. Prove it: `grep -rn 'CLAUDE_PLUGIN_ROOT}/.github' .` finds nothing, `grep -c 'pull_request_template.md' skills/pr/SKILL.md` is 2, and `make validate` + - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6_ + - [x] 2.3 Run the pr eval and keep one transcript + - `make eval-skill-pr`, both cases at three runs, then `make eval-skill-pr EVAL_ARGS="--keep-temp --runs 1 --case opens-pr-with-stub-gh"`. Read the kept run's `out/trace.jsonl` by its full path from outside the directory, as `evals/README.md` says, and confirm a Read of a path ending `skills/pr/pull_request_template.md` and a `gh pr create` body carrying the template's six headings; the graders cannot see either. Record the eval table and the two facts for the PR's Testing section, then `rm -rf` the kept directory. This run is the no-ticket half of Property 4, the case's "No ticket" answer being unchanged + - _Requirements: 7.2_ + - [x] 2.4 Manual check: the pr skill with an issue number as the ticket + - An action for the author: in a session started with `claude --plugin-dir .` on this branch, run `/freethinking:pr`, answer the ticket question with `#<n>` for an open issue of this repository, and stop at the draft without approving it. Confirm that the question says any form works, that the title begins `[#n]`, that the skill asked whether merging finishes the issue unless the answer came with the ticket, that Why names `#n` and ends with `Closes #n` when it does, and that the description carries this repository's six headings. Record it for the PR; the real pull request is opened afterwards with the skill as usual + - **Property 4: The ticket passes through untouched** + - **Validates: Requirements 4.1, 4.2, 4.5** + +- [x] 3. The Guide and the README + - [x] 3.1 Write `.github/CONTRIBUTING.md` + - The eight sections of the design's Guide table, in its order, each saying what its row says and no more. Opening: what the plugin is, links to `LICENSE`, `CODE_OF_CONDUCT.md` and `SECURITY.md`. Set up: clone, `claude --plugin-dir .` for one session, `make validate`, taking over the README's command block. Test: `make validate` is the unit tests under `checks/` then the two validators; evals run by component with `make eval-<kind>-<name>`, are model calls run by hand, and their table goes in the PR's Testing section; a change to a skill, agent, rule or hook comes with an eval case or a change to one, a new skill with at least one case under `evals/skills/<name>/`; a change touching none of them needs no eval and `make validate` is the whole check; the README's Testing section and `evals/README.md` are linked for the how, not restated. Make a change: small, make it; sizeable, `/freethinking:start-task` then `/freethinking:implement-spec`; bug, `/freethinking:bugfix`; branch `<issue number>-<short slug>` when an issue exists and a short slug otherwise, never the default branch, the shape GitHub's Create a branch link gives, and the issue number in front when implement-spec proposes the task name; `/freethinking:pr` with the issue number as the ticket, and the pull request template, linked, as the one place that says how the issue is linked and when to write `Closes #<number>`; a new skill, agent, rule or hook is added to the `component` dropdown of all three forms. Issues: the three forms by name and what submitting one does, the chooser's three links, triage at least weekly in the six steps of Requirement 2.5, an issue opened without a form gets `triage` by hand, a discussion that becomes work is converted to an issue and an issue that is a question to a discussion. Labels and milestones: one Issue_Type per issue; each label group in one sentence; labels lowercase, Issue_Types as GitHub names them; at most one priority and the areas that apply, none being normal; a new label is proposed in an issue; `good first issue` feeds the contribute page and GitHub's surfacing, `help wanted` is work a Maintainer would merge but will not write; a Milestone names a goal, the open ones are the roadmap. Compatibility: what portability across Claude Code, Cursor, Codex and other agent tools means for a skill or rule here, the three seed checks of the design's Data Models, and that the list grows from issues labelled `compatibility`. Settings, not files: one paragraph. Plain Markdown, no company name. Prove it: the eight headings present in order; every relative link resolves to a path in the tree; the README's Testing section is linked, not copied + - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.6, 1.7, 2.5, 2.6, 3.2, 3.3, 3.4, 3.5, 3.6, 5.3, 7.5_ + - [x] 3.2 The README pointer + - Shrink "Add or change a skill or rule" in `README.md` to at most three sentences, one of them linking `.github/CONTRIBUTING.md`, and drop its command block, now in the Guide's Set up. In the Evals subsection remove the sentence "A new skill needs at least one case under `evals/skills/<name>/`.", now in the Guide's Test section; everything else in Testing stays, it is the how. Prove it: count the section's sentences; `grep -rn "at least one case" README.md .github/CONTRIBUTING.md` shows the rule once, in the Guide; `make validate` + - **Property 5: One source for the contributing conventions** + - **Validates: Requirements 1.5** + +- [x] 4. Issue forms and the chooser + - [x] 4.1 `.github/ISSUE_TEMPLATE/misbehaving-skill-or-agent.yml` + - `name: Misbehaving skill or agent`, a one-line `description`, `labels: ["triage"]`, `type: Bug`, and a `body` in the order of the design's table: a `markdown` pointer to the Guide's Issues section; `preflight` checkboxes, searched the existing issues and on the latest plugin version, each required; `component` dropdown with the twelve options the design lists; `version` input, the version as `claude plugin list` shows it; `prompt`, `happened` and `expected` textareas; `reproduces` dropdown. Every field required. Prove it by parsing with `ruby -ryaml -e 'p YAML.load_file(ARGV[0])'` (the machine has no PyYAML; ruby is used for the check only, nothing is added to the repository): the top-level keys, unique ids, `required: true` on every field, the dropdown options as listed; then `make validate` + - _Requirements: 2.1, 2.2, 2.3, 3.6_ + - [x] 4.2 `.github/ISSUE_TEMPLATE/proposal.yml` + - `name: Proposal: a new or changed skill, agent or rule`, `labels: ["triage"]`, `type: Feature`; body: the Guide pointer; `preflight` checkboxes, searched the existing issues and looked in Discussions under Ideas, each required; `kind` dropdown with the six options; `component` dropdown with the same list as 4.1, not required, for a change; `behaviour` and `case` textareas, required. Same proof as 4.1 + - _Requirements: 2.1, 2.2, 2.3, 3.6_ + - [x] 4.3 `.github/ISSUE_TEMPLATE/eval-case.yml` + - `name: New eval case`, `labels: ["triage"]`, `type: Task`; body: the Guide pointer; `preflight` checkboxes, searched the existing issues and read `evals/README.md`, each required; `component` dropdown as in 4.1, required; `behaviour` and `grading` textareas, required; `prompt` textarea, optional. Same proof as 4.1 + - _Requirements: 2.1, 2.2, 2.3, 3.6_ + - [x] 4.4 `.github/ISSUE_TEMPLATE/config.yml` + - `blank_issues_enabled: false` and three `contact_links`, each with `name`, `url` and `about`: a question to `https://github.com/freethinkingit/dev_tools/discussions/categories/q-a`, an idea to `https://github.com/freethinkingit/dev_tools/discussions/categories/ideas`, a vulnerability to `https://github.com/freethinkingit/dev_tools/security/policy`. Prove it: the ruby parse as in 4.1, and `gh api graphql -f query='{ repository(owner:"freethinkingit", name:"dev_tools") { discussionCategories(first:20) { nodes { slug } } } }'` listing `q-a` and `ideas`, the categories Discussions already has + - _Requirements: 2.4, 5.1, 5.2_ + +- [x] 5. Community files + - [x] 5.1 `LICENSE` and the manifest's license field + - `LICENSE` at the repository root: the text of `gh api licenses/mit --jq .body` with `[year]` replaced by `2026` and `[fullname]` by `freethinkingit`. `.claude-plugin/plugin.json` gains `"license": "MIT"`. Prove it: `diff` the file against the API text with the two replacements made, and `make validate`; if `claude plugin validate --strict` rejects the field, drop it, keep `LICENSE`, and say so in the summary + - _Requirements: 6.1, 7.1_ + - [x] 5.2 `.github/CODE_OF_CONDUCT.md` + - The Contributor Covenant 2.1 Markdown from `https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md`, unchanged except `[INSERT CONTACT METHOD]`, replaced by `hello@freethinking.it`, the address the organization's profile already lists. Prove it: `diff` against the downloaded source shows that one line and nothing else + - _Requirements: 6.2_ + - [x] 5.3 `.github/SECURITY.md` + - A short policy: a vulnerability is reported through GitHub's private vulnerability reporting, the Report a vulnerability button on the repository's Security tab, never in a public issue or discussion; the latest release is the supported version. Nothing the requirement does not say. Manual check: read it against Requirement 6.3 + - _Requirements: 6.3_ + +- [ ] 6. Ship + - [x] 6.1 Bump the plugin version + - `.claude-plugin/plugin.json` from `0.5.0` to `0.5.1`; `make validate` + - _Requirements: 7.1, 7.3_ + - [ ]* 6.2 After the merge: the forms on GitHub + - An action for the author, once the branch is on `main`: open each of the three forms from the Template_Chooser and confirm its fields, dropdown options and required marks render, that the title shows the form's `name`, and that submitting with a required field empty is refused; do not submit. Record the result in a follow-up or the merged PR's conversation + - **Property 2: Every form arrives marked** + - **Validates: Requirements 2.1, 2.2, 2.3, 7.4** + - [ ]* 6.3 After the merge: the chooser and the community profile + - An action for the author, once the branch is on `main`: the Template_Chooser lists the three forms and the three links with no blank issue offered, each link resolves, and the repository's community profile page lists the license, the code of conduct, the contributing guide and the security policy; `gh repo view --json licenseInfo` shows MIT. Record the result as in 6.2 + - **Property 3: No unrouted entry** + - **Validates: Requirements 2.4, 5.2, 6.1, 7.4** + +## Notes + +- Sub-tasks marked `*` are optional; implementation sub-tasks are never optional. 6.2 and 6.3 can only run after the merge, so a run of this plan skips them and the author does them from the merged branch. +- `make validate` is the unit test and the full suite for this repository. Run it after every change under `skills/`, `.claude-plugin/`, `checks/` or `README.md`, and at the end of every wave. Nothing in this plan adds a check, a script, a grader or a skill sentence about this repository's labels, branches or `Closes` lines: the plugin stays tracker-agnostic and the checks maintain the plugin, not this repository's project management. +- Evals cost real model calls. Run them only at task 2.3. +- The author settled the two inputs on 2026-10-12: the code of conduct contact is `hello@freethinking.it` (5.2), and the Issue_Types are #1 Feature and #2 to #4 Task, as the design proposes (1.2). +- Tasks 1.4, 2.4, 6.2 and 6.3 are the author's own actions; the executor asks for them and ticks them on the author's report. +- Ephemeral checks of the YAML forms use `ruby -ryaml`, which macOS ships; the machine has no PyYAML, and no parser is added to the repository. +- Task numbers identify tasks; the graph below decides the order. Review happens at the end of each task or each wave, by the author's cadence. +- Work on the `contributing-guide` branch, never on main. + +## Task Dependency Graph + +```json +{ + "waves": [ + { "id": 0, "tasks": ["1.1", "2.1", "5.1", "5.2", "5.3"] }, + { "id": 1, "tasks": ["1.2", "2.2", "3.1", "4.1", "4.2", "4.3", "4.4"] }, + { "id": 2, "tasks": ["1.3", "3.2", "6.1"] }, + { "id": 3, "tasks": ["1.4", "2.3", "2.4", "6.2", "6.3"] } + ] +} +``` diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..577f732 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,85 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at hello@freethinking.it. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..283cc49 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,132 @@ +# Contributing + +This repository is a Claude Code plugin: shared skills, agents and rules for a +software team; its hooks put the rules in every session's context. This guide +says how to set up, test and submit a change, and how the repository uses +issues, labels, milestones and discussions. + +The plugin is under the [MIT License](../LICENSE). Taking part means following +the [Code of Conduct](CODE_OF_CONDUCT.md). Report a vulnerability as the +[security policy](SECURITY.md) says, never in a public issue. + +## Set up + +Clone the repository and work from its root: + +```bash +claude --plugin-dir . # load your working copy for one session +make validate # the unit tests and the validators; CI runs this on every PR +``` + +## Test + +The free checks are `make validate`: the unit tests under `checks/`, then the +two validators. They make no model call. + +Evals run by component with `make eval-<kind>-<name>`, such as +`make eval-skill-pr`, and `make eval-hook` for the hooks. Every case is a model +call on your own Claude seat, so evals run by hand, never in CI. Paste the +table the command prints into the Testing section of your pull request. + +A change to a skill, agent, rule or hook comes with an eval case or a change to +one. A new skill comes with at least one case under `evals/skills/<name>/`. A +change that touches no skill, agent, rule or hook needs no eval, and +`make validate` is the whole check. + +For how the checks and evals work, see the README's +[Testing](../README.md#testing) section and +[`evals/README.md`](../evals/README.md). + +## Make a change + +A small change, such as a typo or a one-line fix: make it. A sizeable change: +run `/freethinking:start-task` to write the spec, then +`/freethinking:implement-spec` to build it. A bug: run `/freethinking:bugfix`. + +Work on a branch named `<issue number>-<short slug>` when an issue exists, such +as `12-ticket-reference`, and on a short slug otherwise. Never work on the +default branch. The Create a branch link under Development on an issue page +gives that shape. When `/freethinking:implement-spec` proposes the task name as +the branch, answer with the issue number in front. + +Open the pull request with `/freethinking:pr` and give the issue number as the +ticket. The [pull request template](pull_request_template.md) is the one place +that says how the issue is linked and when to write `Closes #<number>`. + +A new skill, agent, rule or hook is also added to the `component` dropdown of +the "Misbehaving skill or agent", "Proposal: a new or changed skill, agent or +rule" and "New eval case" forms. + +## Issues + +New issues start from one of three forms: + +- **Misbehaving skill or agent**, for something the plugin did wrong. +- **Proposal: a new or changed skill, agent or rule**, for something it should + do. +- **New eval case**, for a behaviour an eval should guard. + +Submitting a form labels the issue `triage` and sets its Issue_Type: `Bug`, +`Feature` and `Task` respectively. Blank issues are off. The new-issue page +also links questions to Discussions Q&A, ideas to Discussions Ideas, and +vulnerabilities to the security policy page. + +At least weekly, a maintainer triages: + +1. Reviews every issue labelled `triage`. +2. Confirms or corrects its Issue_Type. +3. Sets one priority label and the area labels that apply, none, one or both. +4. Applies `question` instead when the issue cannot be classified without more + from its author. +5. Assigns a milestone, or closes the issue as not planned. +6. Removes `triage`. + +An issue opened without a form gets `triage` by hand at the next triage. A +discussion that becomes work is converted to an issue, and an issue that is +really a question is converted to a discussion at triage. + +## Labels and milestones + +Every issue has one Issue_Type, `Bug`, `Feature` or `Task`, which says what +sort of thing it is. Labels are lowercase, and there are nine: + +- `priority: high`, `priority: medium` and `priority: low` say how soon the + issue should be done. +- `quality` and `compatibility` are areas: the behaviour of the skills, agents + and rules with the evals and checks that guard it, and working across agent + tools. +- `triage` marks an issue no maintainer has classified yet. +- `question` marks an issue that needs more from its author. +- `good first issue` and `help wanted` invite contributors. + +An issue carries at most one priority label and the area labels that apply; an +issue with no area label is normal. If another label seems needed, propose it +in an issue rather than create it. + +`good first issue` is the one label with behaviour on GitHub: it feeds the +repository's contribute page and GitHub's surfacing of approachable issues. +`help wanted` marks work a maintainer would merge but will not write. + +A milestone names a goal. The open milestones are the roadmap. + +## Compatibility + +Portability means a skill or rule here does its job when another agent tool, +such as Cursor or Codex, loads it, not only Claude Code. The checks known so +far: + +1. A rule is plain Markdown with no tool-specific syntax, so another tool can + load it as instructions as it is. +2. A skill's steps run on commands anyone has, `git`, `gh`, `make`, and name a + Claude-only tool only where no command does the job. +3. The Claude-only pieces are listed so a `compatibility` issue can start from + the list: `$ARGUMENTS` and `${CLAUDE_PLUGIN_ROOT}` in skills, the hooks that + load the rules, and the agents' frontmatter. + +The list grows from issues labelled `compatibility`. + +## Settings, not files + +Labels, milestones, Discussions and Issue_Types are repository or organization +settings, not files, so they do not travel with the tree. A fork or a copy of +this repository does not carry them. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..1a50abc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/freethinkingit/dev_tools/discussions/categories/q-a + about: Ask how to use the plugin or its skills in Discussions. + - name: Share an idea + url: https://github.com/freethinkingit/dev_tools/discussions/categories/ideas + about: Talk through an idea before it becomes a feature request. + - name: Report a vulnerability + url: https://github.com/freethinkingit/dev_tools/security/policy + about: Report security issues privately, never in a public issue. diff --git a/.github/ISSUE_TEMPLATE/eval-case.yml b/.github/ISSUE_TEMPLATE/eval-case.yml new file mode 100644 index 0000000..52a65d4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/eval-case.yml @@ -0,0 +1,58 @@ +name: New eval case +description: Propose an eval case that guards a behaviour of a skill, agent, rule or hook. +labels: ["triage"] +type: Task +body: + - type: markdown + attributes: + value: "Before you file, read the [Issues section of the contributing guide](https://github.com/freethinkingit/dev_tools/blob/main/.github/CONTRIBUTING.md#issues)." + - type: checkboxes + id: preflight + attributes: + label: Before you file + options: + - label: I searched the existing issues. + required: true + - label: "I read [`evals/README.md`](https://github.com/freethinkingit/dev_tools/blob/main/evals/README.md)." + required: true + - type: dropdown + id: component + attributes: + label: Component + description: The skill, agent, rule or hook the case would guard. + options: + - start-task (skill) + - implement-spec (skill) + - pr (skill) + - bugfix (skill) + - task-executor (agent) + - wave-reviewer (agent) + - code-changes (rule) + - comments (rule) + - workflow (rule) + - SessionStart hook + - SubagentStart hook + - Something else + validations: + required: true + - type: textarea + id: behaviour + attributes: + label: Behaviour + description: The behaviour the case should guard. + validations: + required: true + - type: textarea + id: grading + attributes: + label: Grading + description: "How a run would be graded: what in the transcript or in the files shows pass or fail." + validations: + required: true + - type: textarea + id: prompt + attributes: + label: Prompt + description: A prompt that would exercise it. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/misbehaving-skill-or-agent.yml b/.github/ISSUE_TEMPLATE/misbehaving-skill-or-agent.yml new file mode 100644 index 0000000..191da1f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/misbehaving-skill-or-agent.yml @@ -0,0 +1,75 @@ +name: Misbehaving skill or agent +description: A skill, agent, rule or hook of the plugin did not do what it says it does. +labels: ["triage"] +type: Bug +body: + - type: markdown + attributes: + value: Before you file, read the [Issues section of the contributing guide](https://github.com/freethinkingit/dev_tools/blob/main/.github/CONTRIBUTING.md#issues). + - type: checkboxes + id: preflight + attributes: + label: Before you file + options: + - label: I searched the existing issues and found nothing that covers this. + required: true + - label: I am on the latest plugin version. + required: true + - type: dropdown + id: component + attributes: + label: Component + description: The skill, agent, rule or hook that misbehaved. + options: + - start-task (skill) + - implement-spec (skill) + - pr (skill) + - bugfix (skill) + - task-executor (agent) + - wave-reviewer (agent) + - code-changes (rule) + - comments (rule) + - workflow (rule) + - SessionStart hook + - SubagentStart hook + - Something else + validations: + required: true + - type: input + id: version + attributes: + label: Plugin version + description: The plugin version, as `claude plugin list` shows it. + validations: + required: true + - type: textarea + id: prompt + attributes: + label: Prompt + description: The prompt or command you gave. + validations: + required: true + - type: textarea + id: happened + attributes: + label: What happened + description: What happened. + validations: + required: true + - type: textarea + id: expected + attributes: + label: What you expected + description: What you expected, with the sentence of the skill, agent, rule or hook it should have followed. + validations: + required: true + - type: dropdown + id: reproduces + attributes: + label: Does it reproduce? + options: + - Every time + - Sometimes + - Once so far + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/proposal.yml b/.github/ISSUE_TEMPLATE/proposal.yml new file mode 100644 index 0000000..1bde0b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/proposal.yml @@ -0,0 +1,65 @@ +name: "Proposal: a new or changed skill, agent or rule" +description: Propose a new skill, agent or rule, or a change to one that exists. +labels: ["triage"] +type: Feature +body: + - type: markdown + attributes: + value: Before you file, read the [Issues section of the contributing guide](https://github.com/freethinkingit/dev_tools/blob/main/.github/CONTRIBUTING.md#issues). + - type: checkboxes + id: preflight + attributes: + label: Before you file + options: + - label: I searched the existing issues. + required: true + - label: I looked in [Discussions under Ideas](https://github.com/freethinkingit/dev_tools/discussions/categories/ideas). + required: true + - type: dropdown + id: kind + attributes: + label: Kind + description: What the proposal adds or changes. + options: + - New skill + - Change to a skill + - New agent + - Change to an agent + - New rule + - Change to a rule + validations: + required: true + - type: dropdown + id: component + attributes: + label: Component + description: For a change, the component it changes. Leave it empty for something new. + options: + - start-task (skill) + - implement-spec (skill) + - pr (skill) + - bugfix (skill) + - task-executor (agent) + - wave-reviewer (agent) + - code-changes (rule) + - comments (rule) + - workflow (rule) + - SessionStart hook + - SubagentStart hook + - Something else + validations: + required: false + - type: textarea + id: behaviour + attributes: + label: Proposed behaviour + description: The behaviour or wording you propose. + validations: + required: true + - type: textarea + id: case + attributes: + label: Motivating case + description: "The case that motivates it: what went wrong, or took too long, without it." + validations: + required: true diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..f6aa4ce --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,11 @@ +# Security policy + +## Reporting a vulnerability + +Report a vulnerability privately through GitHub's private vulnerability +reporting: open the repository's **Security** tab and select +**Report a vulnerability**. Never report one in a public issue or discussion. + +## Supported versions + +The latest release is the supported version. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 290b18b..e55200e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ ## Why -<!-- The problem or goal behind the change. Link the ticket if there is one. --> +<!-- The problem or goal behind the change. Link the issue, and write `Closes #<number>` when merging finishes it. --> ## What @@ -8,11 +8,11 @@ ## Changes -<!-- How it was done. One bullet per logical change: what changed and why. Every source change is covered. Export-only index files and generated files are left out. --> +<!-- How it was done. One bullet per logical change: what changed, then why in your own words, e.g. "Move the default template under skills/pr/. The skill is repository-agnostic and this repository's own template carries the Closes convention." Every source change is covered. Export-only index files and generated files are left out. --> ## Testing -<!-- What was actually run or checked, and the result. "Not tested" is a valid answer. For UI changes, attach screenshots or a recording here. --> +<!-- What was actually run or checked, and the result. "Not tested" is a valid answer. A change to a skill, agent, rule or hook pastes the eval table here. For UI changes, attach screenshots or a recording here. --> ## Risk and rollout diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..79ab1d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2026 freethinkingit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index a92737f..9f527bd 100644 --- a/README.md +++ b/README.md @@ -106,16 +106,8 @@ changes come from a spec wave. ## Add or change a skill or rule Each skill is a folder under `skills/` with a `SKILL.md`; each rule is a Markdown -file under `rules/`. To work on one: - -```bash -claude --plugin-dir . # load your working copy for one session -make validate # the unit tests and the validator under Testing; CI runs this on every PR -``` - -Then open a PR. `/freethinking:pr` works on this repo too. Evals are tests like the -rest, so the table for any skill, rule, agent or hook you changed goes in the -PR's Testing section with everything else you ran. +file under `rules/`. The [contributing guide](.github/CONTRIBUTING.md) says how +to set up, test and submit a change. ## Testing @@ -204,9 +196,7 @@ invocation runs under a list-price ceiling, `EVAL_MAX_COST_USD`, default $2. Each case runs three times by default; iterate with `EVAL_ARGS="--runs 1"`, and run the table you paste for a rule or skill-description change with `EVAL_ARGS="--ablation with-without"` so the Δ column shows what the plugin -added. Results land in `evals/results/<timestamp>/` (gitignored). Paste the -table the command prints into the PR's Testing section. A new skill needs at -least one case under `evals/skills/<name>/`. +added. Results land in `evals/results/<timestamp>/` (gitignored). `evals/README.md` covers writing a case, reading results, keeping a run's transcript to debug a failure, and what the eval sandbox allows. diff --git a/checks/test_issue_forms.py b/checks/test_issue_forms.py new file mode 100644 index 0000000..1a9f466 --- /dev/null +++ b/checks/test_issue_forms.py @@ -0,0 +1,68 @@ +"""The component dropdown of every issue form lists exactly the skills, agents, rules and hooks on disk, in the same order.""" + +import json +import re +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +FORMS_DIR = ROOT / ".github" / "ISSUE_TEMPLATE" +CATCH_ALL = "Something else" + +COMPONENT_BLOCK = re.compile(r"^\s*id: component\n(?P<body>(?:^ {4,}.*\n?)+)", re.MULTILINE) +OPTION = re.compile(r"^ {8}- (?P<option>.+?)\s*$", re.MULTILINE) + + +def component_options(path): + """Return the ``options`` of the dropdown whose id is ``component``, or None if the form has none.""" + match = COMPONENT_BLOCK.search(path.read_text(encoding="utf-8")) + if match is None: + return None + return OPTION.findall(match.group("body")) + + +def components_on_disk(): + """Return the dropdown entries the repository's components call for, catch-all excluded.""" + skills = ["{} (skill)".format(p.parent.name) for p in sorted((ROOT / "skills").glob("*/SKILL.md"))] + agents = ["{} (agent)".format(p.stem) for p in sorted((ROOT / "agents").glob("*.md"))] + rules = ["{} (rule)".format(p.stem) for p in sorted((ROOT / "rules").glob("*.md"))] + events = json.loads((ROOT / "hooks" / "hooks.json").read_text(encoding="utf-8"))["hooks"] + hooks = ["{} hook".format(event) for event in sorted(events)] + return set(skills + agents + rules + hooks) + + +class IssueFormComponentTest(unittest.TestCase): + def forms(self): + forms = sorted(p for p in FORMS_DIR.glob("*.yml") if p.name != "config.yml") + self.assertTrue(forms, "no issue forms under .github/ISSUE_TEMPLATE/") + return forms + + def test_every_form_has_a_component_dropdown_ending_in_the_catch_all(self): + for path in self.forms(): + with self.subTest(form=path.name): + options = component_options(path) + self.assertIsNotNone(options, "no dropdown with id: component") + self.assertTrue(options, "component dropdown has no options") + self.assertEqual(options[-1], CATCH_ALL, "last option is not the catch-all") + self.assertEqual(len(options), len(set(options)), "duplicate options") + + def test_component_options_match_the_components_on_disk(self): + expected = components_on_disk() + for path in self.forms(): + with self.subTest(form=path.name): + listed = set(component_options(path) or []) - {CATCH_ALL} + self.assertEqual( + sorted(listed - expected), [], "options that name no skill, agent, rule or hook on disk" + ) + self.assertEqual(sorted(expected - listed), [], "components on disk missing from the dropdown") + + def test_forms_list_components_in_the_same_order(self): + forms = self.forms() + reference = component_options(forms[0]) + for path in forms[1:]: + with self.subTest(form=path.name, reference=forms[0].name): + self.assertEqual(component_options(path), reference) + + +if __name__ == "__main__": + unittest.main() diff --git a/skills/pr/SKILL.md b/skills/pr/SKILL.md index 7d219a0..a756aa4 100644 --- a/skills/pr/SKILL.md +++ b/skills/pr/SKILL.md @@ -1,6 +1,6 @@ --- name: pr -description: Open or update a GitHub pull request for the current branch using the team's PR standard. Use when asked to open, create, raise, or update a PR. Walks the diff between the branch and its target one change at a time, saying what each source change does and asking the author to say why in their own words, which become the description's Changes bullets; asks once for what the diff cannot answer, drafts the title and description from the team template, and creates the PR with the gh CLI after the author approves. +description: Open or update a GitHub pull request for the current branch using the team's PR standard. Use when asked to open, create, raise, or update a PR. Walks the diff between the branch and its target one change at a time, saying what each source change does and asking the author to say why in their own words, which become the description's Changes bullets; asks once for what the diff cannot answer, drafts the title and description from the repository's own pull request template or the skill's default one, and creates the PR with the gh CLI after the author approves. argument-hint: "[base-branch]" allowed-tools: Bash(git status *) Bash(git branch *) Bash(git remote *) Bash(git rev-parse *) Bash(git fetch *) Bash(git log *) Bash(git diff *) Bash(gh auth status *) Bash(gh repo view *) Bash(gh pr view *) Bash(gh pr list *) --- @@ -66,7 +66,7 @@ Before leaving this step, check the list from `git diff --name-status` against t Ask once, in plain prose in a single message, and only for what the walkthrough and the author's earlier answers have not already covered. Where an earlier answer covered part of a question, ask only for the missing part. - **Why**: the problem or goal behind the branch, when the group reasons do not already say it. -- **Ticket**: the ticket key if there is one (for example `ENG-1234`). +- **Ticket**: the ticket if there is one, in whatever form the author has it: an issue number such as `#12`, an issue URL, or a key from another tracker such as `ENG-1234`. Say that any form works. - **Testing**: what they ran or checked, and the result. - **Risk and rollout**: anything that needs care on deploy, such as migrations, flags, config, infra, or a required order of deploys. - **Review focus**: where reviewers should look hardest, whether this should open as a draft, and for a UI change the screenshots or recording to attach. @@ -77,13 +77,13 @@ Never invent an answer. If the author did not test the change, the Testing secti **Title.** PRs are squash-merged, so the title becomes the commit on the base branch. -- With a ticket: `[ENG-1234] Add retry to upload worker` +- With a ticket: `[#12] Add retry to upload worker` or `[ENG-1234] Add retry to upload worker`, the ticket written the way its tracker writes it - Without: `Add retry to upload worker` - Imperative mood, under 70 characters, no trailing period, specific enough to make sense in `git log` a year from now. -**Description.** If the repository has its own template (`.github/pull_request_template.md`), use it. Otherwise use the team template at `${CLAUDE_PLUGIN_ROOT}/.github/pull_request_template.md`. +**Description.** If the repository has its own template (`.github/pull_request_template.md`), use it: its sections and the comments inside them say what that repository wants in each, and they take precedence over the bullets below; when a comment asks for something the author's answers did not give, ask before drafting. Otherwise use the skill's default template at `${CLAUDE_PLUGIN_ROOT}/skills/pr/pull_request_template.md`. -- Write the Why section from the ticket and the group reasons, or from the author's step 5 answer when those do not give the goal. Write the Changes section from the settled groups: one bullet per logical change, saying what changed and, in the author's words, why. Every source change in the branch is covered by some bullet. Export glue and generated files are left out. +- Write the Why section from the ticket and the group reasons, or from the author's step 5 answer when those do not give the goal. Name the ticket as its tracker writes it, and link it when the author gave a URL; add nothing the author's answers or the repository's own template did not call for. Write the Changes section from the settled groups: one bullet per logical change, saying what changed and, in the author's words, why. Every source change in the branch is covered by some bullet. Export glue and generated files are left out. - A group the author called self-explanatory gets a bullet that says what changed and nothing more. A change the author did not explain keeps its bullet, with "Reason not provided by the author" in place of the why, and is repeated under Notes for reviewers. - Fill every section. Write "None" where a section does not apply, except Notes for reviewers, which is removed when empty. - Say only what the diff and the author's answers support. No filler, no claims about quality. diff --git a/skills/pr/pull_request_template.md b/skills/pr/pull_request_template.md new file mode 100644 index 0000000..290b18b --- /dev/null +++ b/skills/pr/pull_request_template.md @@ -0,0 +1,23 @@ +## Why + +<!-- The problem or goal behind the change. Link the ticket if there is one. --> + +## What + +<!-- 1-3 sentences: what this PR changes, described as behaviour, not as a list of files. --> + +## Changes + +<!-- How it was done. One bullet per logical change: what changed and why. Every source change is covered. Export-only index files and generated files are left out. --> + +## Testing + +<!-- What was actually run or checked, and the result. "Not tested" is a valid answer. For UI changes, attach screenshots or a recording here. --> + +## Risk and rollout + +<!-- Migrations, feature flags, config or infra changes, backwards compatibility, how to roll back. "None" if none. --> + +## Notes for reviewers + +<!-- Where to look first, known gaps, planned follow-ups. Remove this section if there is nothing to add. -->