From fd87568f85f7b78d283e05ba99b74cdc7f3cf226 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 2 Sep 2026 12:19:52 +1000 Subject: [PATCH 1/9] workplan-project: replace the plan table with a phase table, and write the tracker once (#69) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mandated `Phase | Issue | Work item` table was a roster of the sub-issue list — membership and order restated in the body, which QEP-6 §7 forbids and its Alternatives section rejects by name. It is replaced with Appendix A's `Phase | Intent | Exit criterion`, which carries what the list cannot. With no Issue column there is nothing to backfill, so step 5's backfill edit and the placeholder-then-fill creation order go, and the tracking issue is written once. The read-back no longer guards a second body write. The construct was C2-conformant: C2 only rules that checkbox progress is never read. The skill now says where the two documents disagree and cites QEP-6 Adoption clause 3, which rules QEP-6 authoritative for structure until the handover, rather than resolving the gap itself. See QuantEcon/skills#69. Co-Authored-By: Claude Fable 5.1 --- qe/skills/workplan-project/SKILL.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index ea8f273..8f12fa6 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -29,7 +29,7 @@ Steps 1–4 write only local draft files, next to the report. Step 5 acts on a t |---|---|---| | `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 edit --type Project` | 5 | 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. @@ -79,10 +79,11 @@ Drop what no longer holds, and record every drop with its reason in the draft's 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): -- **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. +- **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified )`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → 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. 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. - **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. - **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 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. @@ -90,20 +91,19 @@ Present the drafts — including the method note listing what was extracted, wha ## 5. 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; each already cites `Part of #`. +3. Link each as a **native sub-issue** — this is what makes GitHub render the progress bar and the sub-issue list: ```bash id=$(gh api repos///issues/ --jq .id) gh api repos///issues//sub_issues -F sub_issue_id="$id" ``` -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. +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 the tracking issue back and confirm every sub-issue is listed and the stamp heading is intact. **Re-runs are safe if you look first**: before each create, `gh issue list --repo / --search " in:title"` — file only what is missing, and edit rather than duplicate. From 3931e2de81dbb84157b625e185df387bd62496f2 Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:20:27 +1000 Subject: [PATCH 2/9] workplan-project: state plan order, link with the native verb, and place re-run items in position (#70) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEP-6 §3 makes the sub-issue list the plan — position is sequence, topmost open item is next — and the skill never said so. A first run produced plan order by accident (create in draft order, link in draft order, linking appends); the documented re-run path broke it, because a recovered item is appended to the bottom regardless of its phase. The invariant is now stated in step 4 (draft file order is plan order; the NN- prefix never reaches a title). Step 5 links with `gh issue edit --add-sub-issue`, which takes plain numbers, in draft order, and the read-back asserts list order. A re-run places what it recovers with the reprioritise API and `after_id`, which is the one sub-issue operation with no gh verb and the one place a database id is needed — the id gotcha moves there, off the common path. The read-back says to check on GitHub rather than the dashboard, whose collector re-sorts children by issue number until status-projects#19 ships. See QuantEcon/skills#70. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index 8f12fa6..c98b0ae 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -28,7 +28,8 @@ Steps 1–4 write only local draft files, next to the report. Step 5 acts on a t | Call | Step | What it does | |---|---|---| | `gh issue create` | 5 | the tracking issue, then one issue per work item | -| `gh api …/issues/<parent>/sub_issues` (POST) | 5 | links each work item as a native sub-issue | +| `gh issue edit <parent> --add-sub-issue` | 5 | links each work item as a native sub-issue, in plan order | +| `gh api …/issues/<parent>/sub_issues/priority` (PATCH) | 5 | re-runs only: moves a recovered item into its plan position | | `gh issue edit --type Project` | 5 | 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. @@ -80,6 +81,7 @@ Drop what no longer holds, and record every drop with its reason in the draft's Write drafts into `<bundle>/workplan/`: `00-tracking.md`, then `NN-<slug>.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): - **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified <date>)`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → 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. 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-<slug>.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 5 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. - **Sub-issues**: open with `Part of #<tracking> (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. - **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. @@ -94,24 +96,34 @@ Present the drafts — including the method note listing what was extracted, wha 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 (`gh issue create --repo <o>/<r> --title … --body-file … --label …`). -2. Create each sub-issue the same way; each already cites `Part of #<tracking>`. -3. Link each as a **native sub-issue** — this is what makes GitHub render the progress bar and the sub-issue list: +2. Create each sub-issue the same way, **in draft order**; each already cites `Part of #<tracking>`. +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/<o>/<r>/issues/<child-number> --jq .id) - gh api repos/<o>/<r>/issues/<parent-number>/sub_issues -F sub_issue_id="$id" + gh issue edit <parent-number> --repo <o>/<r> --add-sub-issue <child-number> ``` + The verb takes plain issue numbers (`gh` ≥ 2.94); no database-id lookup on this path. 4. Apply the tracker's native type: `gh issue edit <parent> --repo <o>/<r> --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 the tracking issue back and confirm every sub-issue is listed and the stamp heading is intact. +5. Read back and confirm every sub-issue is listed, **in draft order**, and the stamp heading is intact. `gh api repos/<o>/<r>/issues/<parent>/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**: before each create, `gh issue list --repo <o>/<r> --search "<title> in:title"` — file only what is missing, and edit rather than duplicate. +**Re-runs are safe if you look first, and place what they file**: before each create, `gh issue list --repo <o>/<r> --search "<title> in:title"` — file only what is missing, and edit rather than duplicate. 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> +``` + +Then run the step-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. - **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.) - **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. From 35653bcad183b1e546f5c399b54d34566d8d6a12 Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:21:04 +1000 Subject: [PATCH 3/9] workplan-project: read an issue's parent before linking it, since linking is claiming (#72) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sub-issue membership is single-parent, and gh's `--add-sub-issue` and `--parent` both send replace_parent=true with no opt-out (cli/cli v2.98.0, api/queries_issue.go), so linking an already-parented issue silently detaches it from its tracker. The field test emptied the org's QEP-2 rollout tracker this way and left it publishing 100%. The skill now reads `.parent.number` for every work item that is an existing issue — at draft time, so the method note names the parent the item would leave and the approval covers it knowingly — and a re-run that discovers an issue the drafts did not know stops before linking. Detachments that go ahead are recorded. Step 5 documents the verb's replace behaviour and states why one link method with one guard replaces the two methods that behaved differently on the one case that matters. Cites QEP-6 Adoption clause 2, which makes the read an obligation on conform tooling. See QuantEcon/skills#72. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index c98b0ae..3619d31 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -83,6 +83,7 @@ Write drafts into `<bundle>/workplan/`: `00-tracking.md`, then `NN-<slug>.md` pe - **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified <date>)`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → 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. 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-<slug>.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 5 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. - **Sub-issues**: open with `Part of #<tracking> (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 <n> --repo <o>/<r> --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 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. @@ -103,11 +104,11 @@ The tracking issue is written **once**: its body names no sub-issue numbers, so gh issue edit <parent-number> --repo <o>/<r> --add-sub-issue <child-number> ``` - The verb takes plain issue numbers (`gh` ≥ 2.94); no database-id lookup on this path. + 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 step 2 are parentless and safe. For anything else, the parent read in step 4 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 <parent> --repo <o>/<r> --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/<o>/<r>/issues/<parent>/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 <o>/<r> --search "<title> in:title"` — file only what is missing, and edit rather than duplicate. 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: +**Re-runs are safe if you look first, and place what they file**: before each create, `gh issue list --repo <o>/<r> --search "<title> 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 @@ -124,6 +125,7 @@ When everything is filed, say plainly that the tracker is **not on the projects ## Gotchas - **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.) - **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. From dc4207608b6edffe49afe286ea35245dea2f77ff Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:21:50 +1000 Subject: [PATCH 4/9] workplan-project: add a membership gate between validation and drafting (#71) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The extraction bar (actionable, evidenced, material) and the validation step are a quality bar and a truth bar; nothing asked whether a survivor belongs to this project. Drafted from a report bundle, the goal described whatever survived instead of constraining it — the repository-sweep failure the QEP-6 field test produced on cli#20, reproduced by this skill from a different direction. New step 4: write the definition of done before membership is decided, test each survivor against QEP-6 §1's criterion (cited, with only its goal-widening diagnostic restated), split by definition of done rather than by count or phase, file non-members unparented, and record membership exclusions in the method note separately from verification drops. The ~15 gotcha now points at that question instead of at "raise the bar or split by phase". Draft and file become steps 5 and 6; cross-references renumbered. See QuantEcon/skills#71. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 42 ++++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index 3619d31..9c17140 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -11,7 +11,7 @@ The `workplan-*` family is three skills: this one builds a **project** (tracker > **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). -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 the [`audit`](https://github.com/QuantEcon/skills/tree/main/audit) plugin, but any evidence-cited report works. ## Invocation @@ -23,16 +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 issue edit <parent> --add-sub-issue` | 5 | links each work item as a native sub-issue, in plan order | -| `gh api …/issues/<parent>/sub_issues/priority` (PATCH) | 5 | re-runs only: moves a recovered item into its plan position | -| `gh issue edit --type Project` | 5 | applies the native issue type to the tracking issue | +| `gh issue create` | 6 | the tracking issue, then one issue per work item | +| `gh issue edit <parent> --add-sub-issue` | 6 | links each work item as a native sub-issue, in plan order | +| `gh api …/issues/<parent>/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 @@ -58,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 | @@ -76,23 +76,33 @@ 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 + +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). + +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 `<bundle>/workplan/`: `00-tracking.md`, then `NN-<slug>.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): - **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified <date>)`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → 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. 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-<slug>.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 5 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. +- **Plan order**: the draft file order *is* the plan order — `NN-<slug>.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. - **Sub-issues**: open with `Part of #<tracking> (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 <n> --repo <o>/<r> --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) 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. @@ -104,7 +114,7 @@ The tracking issue is written **once**: its body names no sub-issue numbers, so gh issue edit <parent-number> --repo <o>/<r> --add-sub-issue <child-number> ``` - 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 step 2 are parentless and safe. For anything else, the parent read in step 4 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.) + 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 <parent> --repo <o>/<r> --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/<o>/<r>/issues/<parent>/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. @@ -118,7 +128,7 @@ gh api --method PATCH repos/<o>/<r>/issues/<parent-number>/sub_issues/priority \ -F sub_issue_id=<child-id> -F after_id=<predecessor-id> ``` -Then run the step-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. +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". @@ -127,5 +137,5 @@ When everything is filed, say plainly that the tracker is **not on the projects - **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. From 3fb217fb07736a19767551daae4800d4b3ad2b84 Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:22:58 +1000 Subject: [PATCH 5/9] workplan-project: cite the exemplar for content and the evidence bar, and Appendix A for shape (#73) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QuantEcon.py#925 was cited as the shape to follow in the same step that calls the stamp form exact. Its heading has been fixed since the finding was filed (it now reads `verified 2026-08-31 14:53 AEST`, parseable under C2 §3.4), but its plan table still carries an Issue and a Status column, so it remains non-conformant for shape under QEP-6 §7 — and once the skill's own table goes (#69) the exemplar would teach what the skill just stopped doing. The shape now comes from QEP-6 Appendix A; #925 is cited for body content and #926 for a sub-issue's evidence bar, with the exemplar's table named as the thing not to copy. See QuantEcon/skills#73. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index 9c17140..9c66fa1 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -88,7 +88,7 @@ So, before drafting, write the project's **definition of done** in one sentence ## 5. Draft the work package (local files only) -Write drafts into `<bundle>/workplan/`: `00-tracking.md`, then `NN-<slug>.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): +Write drafts into `<bundle>/workplan/`: `00-tracking.md`, then `NN-<slug>.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 <date>)`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → 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. 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-<slug>.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. From 7564abda4ae9839b19ad0404f6a4db9571c3d782 Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:22:58 +1000 Subject: [PATCH 6/9] workplan-project: give split packages Related work and Gates (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill's own remedy for an oversized package produces the multi-project case and then had nowhere to record how the packages relate — the relationship lived in the author's head at split time. GitHub records only that a mention happened, so a deliberate relationship between projects is an assertion with no native carrier, which is what the body is for. A multi-package run now drafts a Related work section in each package naming its siblings (projects, never work items), and a gate between packages is written once, on the waiting side, at phase granularity by default — the field result on cli#20/#24 was that a tracker-to-tracker edge would have asserted a constraint that did not exist. Cites QEP-6 §7 and Appendix A rather than restating them. See QuantEcon/skills#74. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index 9c66fa1..578a2b7 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -90,8 +90,9 @@ So, before drafting, write the project's **definition of done** in one sentence Write drafts into `<bundle>/workplan/`: `00-tracking.md`, then `NN-<slug>.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 <date>)`** → a findings/gaps table with severity → a **phase table** (`Phase | Intent | Exit criterion`) → 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. 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. +- **Tracking issue**: Background (why now, with sources) → **`## Where we stand (verified <date>)`** → 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-<slug>.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 #<tracking> (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 <n> --repo <o>/<r> --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. From 16fdb0fea3edb3b27fe750e866d59142e9353216 Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:22:58 +1000 Subject: [PATCH 7/9] workplan-project: point the status banner at #65 and the report source at /qe:audit-issues The banner said first-run validation is tracked in #3; it is #65, under the family tracker #63. The intro linked the retired `audit` plugin directory, which folded into `qe` at 0.7.0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index 578a2b7..7977a01 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 6 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 From 7ddc851810bdf5e674eb42fe636cb092d69c234a Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:22:58 +1000 Subject: [PATCH 8/9] qe 0.10.0: version bump and changelog for the workplan-project QEP-6 conformance release Minor, because the procedure changes materially: the plan table goes, plan order and single-parent guarding come in, and a membership step is added. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- .claude-plugin/marketplace.json | 2 +- qe/.claude-plugin/plugin.json | 2 +- qe/CHANGELOG.md | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) 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). From f67787230a8eb65b5e42b81633aaecfb04a6dbc0 Mon Sep 17 00:00:00 2001 From: Matt McKay <mmcky@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:50:18 +1000 Subject: [PATCH 9/9] workplan-project: list non-member filings in the mutating-calls table, and use <parent-number> throughout step 6 Copilot's two review comments on #76: the "What this skill writes" table did not mention the unparented issues step 4 now files, and its type row had no target; and step 6 alternated between <parent> and <parent-number> in a section whose point is that the parent is a number and the children in the reprioritise body are database ids. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --- qe/skills/workplan-project/SKILL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qe/skills/workplan-project/SKILL.md b/qe/skills/workplan-project/SKILL.md index 7977a01..8d97238 100644 --- a/qe/skills/workplan-project/SKILL.md +++ b/qe/skills/workplan-project/SKILL.md @@ -27,10 +27,10 @@ Steps 1–5 write only local draft files, next to the report. Step 6 acts on a t | Call | Step | What it does | |---|---|---| -| `gh issue create` | 6 | the tracking issue, then one issue per work item | -| `gh issue edit <parent> --add-sub-issue` | 6 | links each work item as a native sub-issue, in plan order | -| `gh api …/issues/<parent>/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 | +| `gh issue create` | 6 | the tracking issue, one issue per work item, and any step-4 non-member findings, filed unparented | +| `gh issue edit <parent-number> --add-sub-issue` | 6 | links each work item as a native sub-issue, in plan order | +| `gh api …/issues/<parent-number>/sub_issues/priority` (PATCH) | 6 | re-runs only: moves a recovered item into its plan position | +| `gh issue edit <parent-number> --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 6 headlessly** — the approval gate after step 5 is the safety model. @@ -116,8 +116,8 @@ The tracking issue is written **once**: its body names no sub-issue numbers, so ``` 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 <parent> --repo <o>/<r> --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/<o>/<r>/issues/<parent>/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. +4. Apply the tracker's native type: `gh issue edit <parent-number> --repo <o>/<r> --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/<o>/<r>/issues/<parent-number>/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 <o>/<r> --search "<title> 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: