diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d2d43f6..c4da65e 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "qe", "source": "./qe", - "version": "0.9.0", + "version": "0.10.0", "description": "QuantEcon's shared agent skills under one namespace — working through a PR's review feedback, advising on and scoring lecture acceleration (benchmark), bulk read-only repository audits, turning audit reports into tracked work projects, creating, reading and carrying work-plan state across agent sessions, and drawing a project's roadmap (phases, decision gates, pathways and work items) from its tracker" } ] diff --git a/qe/.claude-plugin/plugin.json b/qe/.claude-plugin/plugin.json index 4b016c5..ae9c9f4 100644 --- a/qe/.claude-plugin/plugin.json +++ b/qe/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "qe", "description": "QuantEcon's shared agent skills under one namespace — working through a PR's review feedback, advising on and scoring lecture acceleration (benchmark), bulk read-only repository audits, turning audit reports into tracked work projects, creating, reading and carrying work-plan state across agent sessions, and drawing a project's roadmap (phases, decision gates, pathways and work items) from its tracker", - "version": "0.9.0", + "version": "0.10.0", "author": { "name": "QuantEcon" } diff --git a/qe/CHANGELOG.md b/qe/CHANGELOG.md index 59825f8..8dbab0a 100644 --- a/qe/CHANGELOG.md +++ b/qe/CHANGELOG.md @@ -6,6 +6,20 @@ Versions are [semver](https://semver.org) as a user of this plugin experiences i Repository: [QuantEcon/skills](https://github.com/QuantEcon/skills) ([every commit that touched this plugin](https://github.com/QuantEcon/skills/commits/main/qe)). How a release is made: [developing-skills § Versioning and releases](https://github.com/QuantEcon/skills/blob/main/docs/developing-skills.md#versioning-and-releases). +## 0.10.0 — 2026-09-02 + +`workplan-project` is brought into line with [QEP-6](https://github.com/QuantEcon/qeps/pull/18) (draft), the standard for project-tracker structure and order, following the six findings its field test ([qeps#19](https://github.com/QuantEcon/qeps/issues/19)) filed against the skill — [#69](https://github.com/QuantEcon/skills/issues/69) to [#74](https://github.com/QuantEcon/skills/issues/74), adopted into [#63](https://github.com/QuantEcon/skills/issues/63) as phase 1. The skill has still not run against a real bundle ([#65](https://github.com/QuantEcon/skills/issues/65)); this release changes what that run will produce. Where C2 and QEP-6 disagree the skill now says so and cites QEP-6's Adoption clause 3, which rules QEP-6 authoritative for structure until the contract's handover. + +**Changed** + +- **The tracker body carries a phase table, not a plan table.** `Phase | Intent | Exit criterion` replaces `Phase | Issue | Work item`: membership, order and state belong to the sub-issue list, and the old table was the body mirror QEP-6 §7 forbids — C2-conformant, since C2 only rules that checkbox progress is never read, and QEP-6-non-conformant. The tracking issue is now written once: no placeholder numbers, no backfill edit, and no second body write to lose the stamp in (#69). +- **Plan order is stated and kept.** Draft file order is plan order; sub-issues are linked with `gh issue edit --add-sub-issue` in that order, the read-back asserts it, and a re-run places a recovered item in its position with the reprioritise API instead of appending it to the bottom of the list. The database-id gotcha moves off the link path to the one call that needs it. Until [status-projects#19](https://github.com/QuantEcon/status-projects/issues/19) ships the dashboard cannot show the difference, so the check is on GitHub (#70). +- **Linking reads the parent first.** Sub-issue membership is single-parent and `gh`'s link verbs replace an existing parent silently, so every pre-existing issue's parent is read before it is linked; an already-parented item is named in the approval draft with the tracker it would leave, and a re-run that finds one stops before linking (#72). +- **A membership gate, new step 4**, sits between validation and drafting: the definition of done is written first, each survivor is tested against QEP-6 §1's criterion, packages split by definition of done rather than by count or phase, and findings that clear the bar but not the gate are filed unparented (#71). +- **The exemplar is cited for content, not shape.** QuantEcon.py#925's stamp now parses, but its plan table still carries an `Issue` and a `Status` column; the shape comes from QEP-6 Appendix A, with #925 cited for body content and #926 for the evidence bar (#73). +- **Split packages get `Related work` and `Gates`**: each package names its siblings (projects, never work items), and a gate between packages is written once, in the body of the package that waits, at phase granularity by default (#74). +- Housekeeping: the status banner points at #65 rather than #3, and the retired `audit` plugin link points at `/qe:audit-issues`. + ## 0.9.0 — 2026-09-02 The work-plan family gains a third skill, extracted from a hand run: the roadmap drawn for the Lectures monorepo project from its tracker ([project-monorepo#30](https://github.com/QuantEcon/project-monorepo/pull/30), members-only). Tracked in [#63](https://github.com/QuantEcon/skills/issues/63). diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index ea8f273..8d97238 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -9,9 +9,9 @@ Automates the loop **read a report bundle → extract the high-value findings The `workplan-*` family is three skills: this one builds a **project** (tracker + sub-issues, the phased-package shape) from a report; [`workplan`](https://github.com/QuantEcon/skills/blob/main/qe/skills/workplan/SKILL.md) carries the single **work-plan issue** (the cross-session state carrier) through its whole lifecycle — create, read, resume, update, close-and-succeed; [`workplan-roadmap`](https://github.com/QuantEcon/skills/blob/main/qe/skills/workplan-roadmap/SKILL.md) draws the project this skill files as mermaid flowcharts. -> **Status: merged, no validated run yet.** First-run validation — a real bundle, from an installed plugin — is tracked in [#3](https://github.com/QuantEcon/skills/issues/3). +> **Status: merged, no validated run yet.** First-run validation — a real bundle, from an installed plugin — is tracked in [#65](https://github.com/QuantEcon/skills/issues/65). -Requires `gh`, authenticated: step 3 reads the target repo, step 5 writes to it. The reports themselves are local files — typically bundles under `~/work/quantecon/_audits/` and `~/work/quantecon/_reviews/`, many produced by the [`audit`](https://github.com/QuantEcon/skills/tree/main/audit) plugin, but any evidence-cited report works. +Requires `gh`, authenticated: step 3 reads the target repo, step 6 writes to it. The reports themselves are local files — typically bundles under `~/work/quantecon/_audits/` and `~/work/quantecon/_reviews/`, many produced by [`/qe:audit-issues`](https://github.com/QuantEcon/skills/blob/main/qe/skills/audit-issues/SKILL.md), but any evidence-cited report works. ## Invocation @@ -23,15 +23,16 @@ The first argument is a report file or a bundle directory. The target repo defau ## What this skill writes -Steps 1–4 write only local draft files, next to the report. Step 5 acts on a third party, so every mutating call is listed here, and each happens only after the user has approved the drafts: +Steps 1–5 write only local draft files, next to the report. Step 6 acts on a third party, so every mutating call is listed here, and each happens only after the user has approved the drafts: | Call | Step | What it does | |---|---|---| -| `gh issue create` | 5 | the tracking issue, then one issue per work item | -| `gh api …/issues//sub_issues` (POST) | 5 | links each work item as a native sub-issue | -| `gh issue edit` | 5 | fills the created sub-issue numbers back into the tracking issue's plan table | +| `gh issue create` | 6 | the tracking issue, one issue per work item, and any step-4 non-member findings, filed unparented | +| `gh issue edit --add-sub-issue` | 6 | links each work item as a native sub-issue, in plan order | +| `gh api …/issues//sub_issues/priority` (PATCH) | 6 | re-runs only: moves a recovered item into its plan position | +| `gh issue edit --type Project` | 6 | applies the native issue type to the tracking issue | -A filed issue can be closed but not unfiled, so **do not run step 5 headlessly** — the approval gate after step 4 is the safety model. +A filed issue can be closed but not unfiled, so **do not run step 6 headlessly** — the approval gate after step 5 is the safety model. ## 1. Read the report @@ -57,7 +58,7 @@ Reports use several priority vocabularies, sometimes more than one at once. Read |---|---| | `P0`, 🔴, `GC`/`T0`, `high-priority`, `GAP-n (High)`, "Blocker" | high value — in | | `MERGE`, `MERGE AFTER MINOR CHANGES` (PR triage) | in: the work item is landing it | -| `NEEDS MAINTAINER DECISION` | in, as a *decision* item (see step 4) | +| `NEEDS MAINTAINER DECISION` | in, as a *decision* item (see step 5) | | `CLOSE`, `FIXED`, ledger lines under "Resolved:" | out — hygiene the report already dispatched | | Quality ★★ or below | usually out — the report itself says not to act on it as written; include only if the *underlying problem* independently clears the bar | | `P2`/`P3`, `low-priority`, `T2`/`T3` | out, unless several cluster into one coherent phase | @@ -75,43 +76,67 @@ The report is a snapshot and is stale by construction. For each candidate, check Drop what no longer holds, and record every drop with its reason in the draft's method note — the user should see what was filtered out, not just what survived. -## 4. Draft the work package (local files only) +## 4. Decide membership -Write drafts into `/workplan/`: `00-tracking.md`, then `NN-.md` per sub-issue. The shape follows the worked exemplar, [QuantEcon.py#925](https://github.com/QuantEcon/QuantEcon.py/issues/925) with sub-issue [#926](https://github.com/QuantEcon/QuantEcon.py/issues/926): +Step 2 is a quality bar and step 3 a truth bar; neither asks whether a finding belongs to *this* project. A report bundle covers a repository over a window, which is not a project, so a goal written from whatever survived describes the list instead of constraining it — and a package built that way passes every structural check while being a repository sweep with a goal fitted afterwards, which is exactly what the QEP-6 field test produced ([qeps#19](https://github.com/QuantEcon/qeps/issues/19), amendment 9). -- **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified )`** → a findings/gaps table with severity → a **work plan table** (`Phase | Issue | Work item`) → a sequencing paragraph (what gates what, what can land immediately) → what does *not* need to change → sources, including the report bundle this package came from and the snapshot SHA. That heading is the project tracker contract's status stamp and its form is exact — see **The tracker contract** below. When step 3 left genuine unknowns, phase 0 is the phase that converts them into knowns, and the dependent items say they are gated on it. +So, before drafting, write the project's **definition of done** in one sentence — what has to be true for the tracker to close — from the report's framing of the problem, not from the survivors. Then test each survivor against it using [QEP-6 §1](https://github.com/QuantEcon/qeps/pull/18)'s membership criterion, which is cited rather than restated here except for its diagnostic, because the diagnostic is the working tool: **if the goal must be widened to justify an item's membership, the item is not a member.** + +- **Two definitions of done are two packages**, whatever the count. Fifteen items that all serve one goal are a large project, not a bad one; the ~15 figure in the gotchas is a prompt to re-ask this question, never the criterion, and splitting by phase or by size leaves each half with the same fitted-afterwards goal. +- **Non-members that still clear step 2's bar are filed as ordinary issues and left unparented.** An unparented issue is the normal case (§1), and a skill that has just filtered a report will otherwise parent everything it kept. +- **The method note records each exclusion** as *kept, not a member of this project*, distinct from step 3's *dropped, no longer holds* — the user should be able to see both lists and disagree with either. + +## 5. Draft the work package (local files only) + +Write drafts into `/workplan/`: `00-tracking.md`, then `NN-.md` per sub-issue. The **shape** is [QEP-6 Appendix A](https://github.com/QuantEcon/qeps/pull/18)'s skeleton. Two worked exemplars cover what a skeleton cannot show, and each is cited for one thing: [QuantEcon.py#925](https://github.com/QuantEcon/QuantEcon.py/issues/925) for the *content* of a tracker body — background with sources, a gaps table with severities, sequencing rationale, what does not change — and its sub-issue [#926](https://github.com/QuantEcon/QuantEcon.py/issues/926) for the *evidence bar*. #925 is not cited for shape: its plan table carries an `Issue` and a `Status` column, which is the mirror QEP-6 §7 forbids, so copy its sections and not its table. A concrete artefact beats an abstract rule for any producer, which is why the exemplar's defect is named here rather than left for the reader to notice. + +- **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified )`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → **Gates** (which phase waits on what) and a sequencing paragraph (why this order, what can land immediately) → what does *not* need to change → sources, including the report bundle this package came from and the snapshot SHA. That heading is the project tracker contract's status stamp and its form is exact — see **The tracker contract** below. The phase table carries what the sub-issue list cannot — what each phase is for and when it is finished — and **never an `Issue` or `Status` column**: membership, order and state are the sub-issue list's, and a body that repeats them is the mirror [QEP-6 §7](https://github.com/QuantEcon/qeps/pull/18) forbids, one that can visibly disagree with the live list rendered on the same page. When step 3 left genuine unknowns, phase 0 is the phase that converts them into knowns, and the dependent items say they are gated on it. +- **Plan order**: the draft file order *is* the plan order — `NN-.md` files numbered in the order the work should happen, each phase's items contiguous — because the sub-issue list is the plan under [QEP-6 §3](https://github.com/QuantEcon/qeps/pull/18): position is sequence, and the topmost open item is next. Step 6 files and links in that order and checks the result against it. The `NN-` prefix is a draft filename and nothing else — it never reaches an issue title, since §3 bans sequence tokens in titles and milestone names outright. +- **More than one package**: when step 4 splits the survivors, each package's draft gets a `## Related work` section naming its siblings — one line each on how they relate, **projects only, never work items**, since an entry about a work item is the sub-issue list restated or dependency prose, both of which [QEP-6 §7](https://github.com/QuantEcon/qeps/pull/18) forbids. A gate between packages is stated **once**, in the body of the package that *waits*, at the granularity that is actually true — usually one phase waiting on one phase, not one project waiting on one project, which would park the whole project in any consumer that derives parked-ness from open blockers; the sibling's `Related work` entry points at where the gate is stated and does not restate it. Both sections are in Appendix A; they are named here because a producer fills the slots that exist and invents none. - **Sub-issues**: open with `Part of # (Phase k).`, then the problem with its evidence as SHA-pinned permalinks, the proposed fix, and an **acceptance criteria** checklist. A finding the report left as a judgement call becomes a *decision* sub-issue — the question, the options, and the report's lean — never a silently chosen fix. +- **A work item that is an existing issue** — landing PR #n, deciding issue #m, a finding the repo had already filed — is linked, not re-created, and linking is claiming: sub-issue membership is single-parent, so adding an issue here removes it from whatever tracker holds it now. Read its parent at draft time, `gh issue view --repo / --json parent --jq '.parent.number // empty'`, and where that is non-empty the method note says so in terms the user can act on: *item #n is currently a work item of #p; adding it here removes it from #p.* The approval that follows then covers the detachment knowingly, or the item stays where it is and the package cites it instead. - **Labels per [QEP-2](https://github.com/QuantEcon/qeps/blob/main/qeps/qep-0002-standard-github-labels.md)**: exactly one type label per issue (`bug`/`enhancement`/`infrastructure`/`maintenance`/`discuss`…), priority labels only for the genuine outliers — there is deliberately no `medium-priority`, unlabelled *is* the middle. Check the labels exist in the target repo (`gh label list`); if not, flag that the repo hasn't adopted the QEP-2 set and propose only labels it has. -- **The tracker contract**: what this skill produces *is* a project tracker — one issue, its direct sub-issues the work — so it is drafted to conform with [`docs/contracts/tracker.md`](https://github.com/QuantEcon/status-projects/blob/main/docs/contracts/tracker.md) (C2), which states the rules once and is not restated here. Three bear on the draft: the stamp heading above in its exact form; the work in **native sub-issues**, never body checkboxes, since checkbox progress publishes as `null`; and the native `Project` issue type, applied at step 5. +- **The tracker contract**: what this skill produces *is* a project tracker — one issue, its direct sub-issues the work — so it is drafted to conform with [`docs/contracts/tracker.md`](https://github.com/QuantEcon/status-projects/blob/main/docs/contracts/tracker.md) (C2), which states the rules once and is not restated here. Three bear on the draft: the stamp heading above in its exact form; the work in **native sub-issues**, never body checkboxes, since checkbox progress publishes as `null`; and the native `Project` issue type, applied at step 6. +- **The tracker structure**: [QEP-6](https://github.com/QuantEcon/qeps/pull/18) (draft) rules what the tracker body may and may not carry, and this skill follows it wherever it goes further than C2. The two disagree in one place worth knowing: C2 forbids nothing in the body beyond the stamp and never *reads* a plan table, so a roster of work items there is C2-conformant, while QEP-6 §7 forbids it. QEP-6's Adoption clause 3 rules that QEP-6 is authoritative for tracker structure until C2's planned handover to it; this skill applies that ruling rather than choosing between the two, and says so here so that a reader who checks the draft against C2 alone is not surprised. - **[QEP-1](https://github.com/QuantEcon/qeps/blob/main/qeps/qep-0001-purpose-and-process.md) check**: if the package crosses repositories or changes how the whole team works, it may warrant a QEP rather than (or before) a pile of issues — say so instead of filing. - Every body will be GitHub-rendered, so the [rules for writing to GitHub](https://github.com/QuantEcon/skills/blob/main/AGENTS.md#writing-to-github) apply: one unbroken line per paragraph, no prose in fenced blocks, and never a closing keyword before an `owner/repo#N` reference. -Present the drafts — including the method note listing what was extracted, what was dropped in step 3 and why — and **wait for approval** before step 5. +Present the drafts — including the method note listing what was extracted, what was dropped in step 3 and why, and what was kept but excluded in step 4 — and **wait for approval** before step 6. -## 5. File it (on approval) +## 6. File it (on approval) -Creation order resolves the numbering chicken-and-egg: +The tracking issue is written **once**: its body names no sub-issue numbers, so nothing has to be filled in after the children exist, and there is no second body write to lose the stamp in. -1. Create the tracking issue with `—` in the plan table's Issue column. -2. Create each sub-issue (`gh issue create --repo / --title … --body-file … --label …`); each already cites `Part of #`. -3. `gh issue edit` the tracking issue to fill the real numbers into the plan table. -4. Link each as a **native sub-issue** — this is what makes GitHub render the progress bar and the sub-issue list: +1. Create the tracking issue (`gh issue create --repo / --title … --body-file … --label …`). +2. Create each sub-issue the same way, **in draft order**; each already cites `Part of #`. +3. Link each as a **native sub-issue**, in the same order — this is what makes GitHub render the progress bar and the sub-issue list, and linking appends, so linking in draft order produces plan order: ```bash - id=$(gh api repos///issues/ --jq .id) - gh api repos///issues//sub_issues -F sub_issue_id="$id" + gh issue edit --repo / --add-sub-issue ``` -5. Apply the tracker's native type: `gh issue edit --repo / --type Project`. It is org-level and label-free, so QEP-2's set is untouched; if the type is missing the call fails harmlessly — report it and carry on, since an untyped tracker is a finding rather than a failure. -6. Read the tracking issue back and confirm every sub-issue is listed, and that the stamp heading survived the step-3 edit intact. + The verb takes plain issue numbers (`gh` ≥ 2.94); no database-id lookup on this path. It also **replaces an existing parent unconditionally** — `--add-sub-issue` and `--parent` both send `replace_parent=true` with no opt-out, so on an already-parented issue the same call succeeds and steals it. Issues created in item 2 above are parentless and safe. For anything else, the parent read in step 5 is the guard, and it has to have happened: never link an issue whose parent this run has not read. (The raw REST `POST …/sub_issues` behaves differently — it omits `replace_parent` and fails on a parented child — which is why the skill uses one method, the verb, with one guard, rather than two methods with two behaviours.) +4. Apply the tracker's native type: `gh issue edit --repo / --type Project`. It is org-level and label-free, so QEP-2's set is untouched; if the type is missing the call fails harmlessly — report it and carry on, since an untyped tracker is a finding rather than a failure. +5. Read back and confirm every sub-issue is listed, **in draft order**, and the stamp heading is intact. `gh api repos///issues//sub_issues --jq '.[].number'` returns the list in position order, which is what the rendered page shows. Check on GitHub, not on the projects dashboard: its collector re-sorts children by issue number until [status-projects#19](https://github.com/QuantEcon/status-projects/issues/19) ships, so a tracker in plan order and one in arbitrary order publish identically there. + +**Re-runs are safe if you look first, and place what they file**: before each create, `gh issue list --repo / --search " in:title"` — file only what is missing, and edit rather than duplicate. An issue the search finds is one the drafts did not know about, so its parent has not been read: read it now, and if it is set, **stop before linking** and bring it back to the user with the parent named — the approval covered filing the package, not detaching someone else's work item. A detachment that goes ahead is recorded in the method note and in the tracking issue's sources, so the change is visible to whoever owns the other tracker. A recovered item then has to be **moved into its plan position**, because linking appends it to the bottom whatever phase it belongs to — a phase-2 item recovered on a re-run lands after phase 4, silently, and the tracker no longer states the plan. Reordering is the one sub-issue operation with no `gh` verb; it is the reprioritise API, and it wants database ids, not numbers: + +```bash +# database ids of the parent's current children, in list order +gh api repos/<o>/<r>/issues/<parent-number>/sub_issues --jq '.[] | "\(.number) \(.id)"' +# place the recovered item directly after its intended predecessor +gh api --method PATCH repos/<o>/<r>/issues/<parent-number>/sub_issues/priority \ + -F sub_issue_id=<child-id> -F after_id=<predecessor-id> +``` -**Re-runs are safe if you look first**: before each create, `gh issue list --repo <o>/<r> --search "<title> in:title"` — file only what is missing, and edit rather than duplicate. +Then run the item-5 read-back again. The reprioritise write is reflected in the list read-back, and REST, GraphQL and the rendered page all return the same order. When everything is filed, say plainly that the tracker is **not on the projects dashboard until it is registered**: a row in [`projects.yml`](https://github.com/QuantEcon/status-projects/blob/main/projects.yml) carrying its slug, programme, stage, owner, one public sentence and the tracker in `Owner/repo#N` form, landed as a pull request against `QuantEcon/status-projects` and gated by that repo's validator. Offer to draft the row; leave opening the PR to the user. Then offer — don't do unasked — to move the bundle into its tree's `_processed/`, which is the local convention for "actioned". ## Gotchas -- **`sub_issue_id` is the issue's database `id`, not its number.** Posting the issue *number* either fails or links the wrong issue — always resolve via `--jq .id` first. +- **The reprioritise call mixes two kinds of integer.** The parent in the path is an issue *number*; `sub_issue_id` and `after_id` in the body are database *ids*, ten-digit and unrelated to the numbers. Both are bare integers and the API cannot tell a transposition from a request, so read the ids from the `sub_issues` listing in the same breath as the call. This is the only place the skill needs a database id; linking takes numbers. +- **Linking is claiming, and the theft is silent.** In the QEP-6 field test ([qeps#19](https://github.com/QuantEcon/qeps/issues/19), finding 1) ten items linked into a new tracker were detached from the org's QEP-2 rollout tracker by that act alone, leaving it with one closed child and a published 100%. No error, no warning, in a repository nobody was watching. One `--json parent` read per pre-existing item is the whole cost of not doing that; QEP-6 Adoption clause 2 makes it an obligation on conform tooling, and for the create path this skill is that tooling. - **The exemplar's quality bar is the target.** #926 carries benchmarks, a rewritten implementation, and pinned permalinks because the report behind it did; a sub-issue only ever restates *the report's* evidence and your step-3 verification — it does not decorate a thin finding into looking like a thick one. -- **A package that wants more than ~15 sub-issues is a signal**, not an achievement — raise the bar in step 2 or split by phase into separate packages. (GitHub's hard cap is 100 sub-issues per parent, but the readable limit is far lower.) +- **A package that wants more than ~15 sub-issues is a signal**, not an achievement — go back to step 4 and ask whether the survivors answer to one definition of done. Usually they answer to two, and the phase boundary turns out to be the membership boundary: an oversized package is most often a membership problem wearing a size costume. Split by definition of done, never by count. (GitHub's hard cap is 100 sub-issues per parent, but the readable limit is far lower.) - **Reports disagree with each other.** When two bundles cover the same item with different verdicts, the later snapshot wins, but say in the draft that an earlier report disagreed — the divergence is itself information.