Skip to content

Idea: Convention for use of milestones when building work plans #55

Open

Description

@mmcky

Adopt wp{issue#}-stage{n} milestone naming for work-plan stages

Proposal: when a work plan divides its work into stages, give each stage a GitHub milestone named after the work-plan issue that owns it — wp{issue#}-stage{n}, e.g. wp8-stage1 for stage 1 of the plan tracked in issue #8. Adopted ad hoc in QuantEcon/textstrata on 2026-08-25; filing it here to decide whether it becomes the documented convention for all work plans.

Why name the milestone after the plan

Milestones are a flat, repo-global namespace with no parent field, so a bare Stage 1 is ambiguous the second a repo has two work plans, and there is nothing in the milestone itself that says which plan it belongs to. Embedding the issue number makes the milestone self-describing and gives the convention four practical properties:

  • Ownership is readable from the name. Anyone landing on the milestone list can tell wp8-stage1 belongs to the plan in TRACKING: work plan 2026-07-22 — rubric v2 quick-wins, merge + email, skill wiring toward the #654 re-test #8, without opening anything.
  • No label sprawl. QEP-2 keeps the label set deliberately minimal, and stage-1/stage-2/stage-3 labels would sit outside it. Milestones carry percent-complete and an issue list natively, which is exactly what a phased plan wants, so the structure lands without touching the label vocabulary.
  • Retirement is unambiguous. When the plan closes, its wp8-* milestones close with it. A generic Stage 2 would linger and invite a later, unrelated plan to reuse it with different meaning.
  • It is the only native grouping axis left when sub-issues are taken. GitHub allows one parent per issue. In the textstrata case the roadmap issues were already native sub-issues of a cross-repo programme tracker (QuantEcon/project-translation#49), so claiming them as sub-issues of the local plan was not possible without breaking that hierarchy. Milestones grouped them inside the repo while the cross-repo parentage stayed intact — a situation any repo whose work belongs to a programme tracker will hit.

First instance

QuantEcon/textstrata#8 is a phased work plan produced from a repository audit. Its stage milestones were created and assigned on 2026-08-25:

Milestone Issues
wp8-stage1 #1, #2, #5, #6
wp8-stage2 #3
wp8-stage3 #7

The tracker issue itself was left unmilestoned, since it spans all three stages — worth confirming as part of the convention rather than leaving to taste.

Where it would be documented

File Section Change
qe/skills/workplan-project/SKILL.md step 4 (draft the work package) The drafting rules cover labels per QEP-2 but say nothing about milestones. Add the naming rule, and that the plan table's phases are what the milestones mirror.
qe/skills/workplan-project/SKILL.md step 5 (file it) The creation order does not create or assign milestones. Add both steps, after the sub-issues exist.
qe/skills/workplan/SKILL.md "The convention" Records observed practice pending a QEP. If this is adopted, it belongs in that list alongside the untyped-plan-issues rule.
the planned work-plan QEP — qe/skills/workplan/SKILL.md notes that formalising the convention as a QEP is planned; if that is where naming rules should live, this issue is an input to it rather than a skill edit.

Tooling note

Milestones must exist before anything can be assigned to one, and creating them is not covered by every agent surface: in the textstrata run the available tooling could assign a milestone by number but could neither create nor list milestones, so a human created the three by hand and the numbers had to be looked up before assignment. gh can do it directly — gh api repos/{owner}/{repo}/milestones -f title=wp8-stage1 — so if this convention is adopted, workplan-project step 5 should create the milestones itself rather than leaving a manual gap in an otherwise scripted flow.

Open questions

  • stage or phase? Tracking: sequenced work plan for the Stage 1–3 roadmap textstrata#8's own plan table is organised by "Phase 0–5" while its milestones track the programme's Stage 1–3 — the two divisions are real and distinct there, but a convention should say which one the milestone mirrors, or allow either token.
  • Cross-repo plans. Milestones are per-repo. When a plan in repo A drives work in repo B, wp8-stage1 in repo B names an issue number that does not exist there. Options: accept the ambiguity, or qualify the token.
  • Does the tracker carry a milestone? Left unmilestoned in the first instance; needs a stated rule.
  • Separator and padding. wp8-stage1 as used, versus wp-8-stage-1 or zero-padded forms. Worth fixing so milestone lists sort predictably once a repo has several plans.

Activity

  1. mmcky commented on Aug 25, 2026

    @mmcky
    ContributorAuthor

    Review of the first instance

    I reviewed QuantEcon/textstrata#8 and its three milestones against this proposal. The first instance is a good argument for the membership half of the convention, and it quietly demonstrates that the ordering half is still missing.

    The milestones carry the programme's axis, not the plan's

    QuantEcon/textstrata#8's body is explicit that the milestones were "taken from each issue's own text — the shared footer marks all of them as part of #49's programme Stage 1". But the plan's own sequencing is Phase 0–5, and the two axes cut across each other:

    Milestone Issues Their phases in the plan table
    wp8-stage1 QuantEcon/textstrata#1, QuantEcon/textstrata#2, QuantEcon/textstrata#5, QuantEcon/textstrata#6 2, 3, 4, 3
    wp8-stage2 QuantEcon/textstrata#3 parked
    wp8-stage3 QuantEcon/textstrata#7 5

    So wp8-stage1 is four issues spanning three phases in no stated order. The milestone answers "whose programme stage is this?" and says nothing about "what comes first?".

    Which means finding F4 is only half-done

    F4 reads: "the Stage 1/2/3 structure and the inter-issue dependencies (#2 → #1, #6 → #1) lived only in prose", marked ✅ Done. The milestones encoded the structure. The dependencies are still prose — they live in the plan's Sequencing paragraph and nowhere machine-readable. Milestones group; they do not order. That is the gap I think this issue should be widened to cover.

    The name has two owners

    wp8 names the repo-local sequencing companion; stage1 names QuantEcon/project-translation#49's division. This issue files "cross-repo plans" as a hypothetical open question — it is live in the very first instance. Ruling the other way does not rescue it: if the milestone must mirror the plan's phases, textstrata's become wp8-phase0…wp8-phase5, at which point QuantEcon/textstrata#3 has no home and phase 2 is a milestone of one. The phase axis is too fine for milestones; the stage axis is the right grain and it is not the plan's.

    The description field is empty on all three milestones

    That field is where "Stage 1 of QuantEcon/project-translation#49, sequenced by QuantEcon/textstrata#8" belongs, and it undercuts the "ownership must be readable from the name" argument — a description holds a working link, a name holds eight characters. Using both beats overloading the name, and it makes the wp{n}- prefix optional rather than load-bearing.

    Three separable things

    Conflating these is what makes the open questions hard to settle:

    Question Mechanism Status
    Membership which plan owns this issue? native sub-issue; milestone when the parent slot is taken works — this is textstrata's real contribution
    Grouping which phase or stage? milestone (QEP-2: grouping → Milestones, not labels) works, but the convention must state whose axis
    Order what comes first? prose, everywhere unsolved

    On "stage or phase"

    From the projects-dashboard side there is a hard collision. stage is already the declared lifecycle column of a project — idea, proposed, active, steady, parked, done, dropped — and the public pipeline view is built on it. A milestone token stage1 meaning "phase 1 of a plan" would render next to stage: active meaning a pipeline column, on the same pages. I would prefer phase, which also matches QuantEcon/textstrata#8's own plan table.

    What a milestone convention would actually reach

    Worth being concrete about the machine consumer, because the answer is "nothing", twice over. Links below are into QuantEcon/status-projects, which is private — they will 404 for non-members; the behaviour they describe is visible on the public dashboard.

    A child's milestone is unread by construction. The collector's tracker query reads milestone { title dueOn progressPercentage } on the tracker issue, while the child page carries only number title state repository (collector/collect.py#L249-L254). Stage milestones live on children. So under the convention exactly as drafted — which deliberately leaves the tracker unmilestoned because it spans all stages — the one field the dashboard reads is guaranteed empty, and the fields that carry the convention are never fetched.

    And a milestone name does not survive redaction anyway. For a tracker in a private repository the published milestone is stripped to its due date and progress percent; the title is dropped outright. That is not an oversight, it is the contract: a milestone title is repository content. So even if the convention put wp8-stage1 on the tracker itself, the name would be unreadable for every private project. Of the 28 projects on the public dashboard, 12 are in that class.

    Adoption would be greenfield, which is the good news. Only 3 of the 28 registered trackers carry a milestone at all today — actions#110 ("Maintenance audit 2026-07"), QuantEcon.py#925 ("JupyterLite / WASM browser support"), and one on a private tracker whose title is redacted to a bare 38%. None is stage-shaped, so a convention would not be fighting existing practice. But note what those three show: the useful thing about a milestone in the wild has been its prose description of a body of work, not a structural token.

    Ordering: numbered titles, and the primitive nobody uses

    The proposal @mmcky raised is that the milestone groups and a number in the issue title orders within it, degrading to plain numbered titles in the many repos that use no milestones. Two things in its favour, and two caveats.

    In favour: an issue title is the one signal that survives every consumer today. Child titles are fetched and are rendered in the project page's sub-issue list, so numbered titles would show up on the dashboard with no collector change at all — against the three separate changes reading child milestones would need. It also needs no push access: in the textstrata run the agent tooling could assign a milestone but could neither create nor list one, so a human created all three by hand.

    Caveat one: it does not solve visibility. A private tracker publishes no child list at all — not even its public children, because the parent-to-child edge is itself a fact held in the private repository. So numbered titles are invisible for the same 12 of 28 projects, for a different reason than milestone names are. Numbering addresses the tooling problem, not the privacy one. Better stated now than discovered later.

    Caveat two: GitHub already has a native ordering primitive the org does not use. Issue dependencies express #2 → #1 and #6 → #1 exactly, need no naming convention, and are already read nightly by the collector — blockedBy plus issueDependenciesSummary, published as a count that survives redaction. The 2026-08-23 org audit quoted in QuantEcon/qeps#15 measured 0 of 99 tracker-shaped issues using them.

    These are not competing. Dependencies express constraint (a partial order); numbering expresses presentation (a total order). Tiles need the second — a DAG does not tell you what to render third.

    One more thing the ordering rule has to survive

    Child order on the dashboard today is "ascending by issue number within a repository", and the stated reason is not meaning but determinism: a truncated list says "showing the first N", so N must be of a stated order. Issue number is creation order, which is an accidental proxy for planning order and stops being one the moment a step is inserted late. That is exactly the case sparse numbering (10, 20, 30) is for, and it is also why a numbering convention needs a stated tie-break for unnumbered issues rather than leaving them to fall wherever.

    Suggested shape

    • Milestones for grouping, with a description naming the owning plan by link, and the axis stated as the plan's own division rather than left to taste.
    • Sparse numbering (10, 20, 30) in titles for display order, so inserting a step does not renumber the set. Optional, since it is a readability tax on every notification, search result and PR reference.
    • Dependencies where an edge is a genuine constraint.
    • The tracker body's plan table stays the authority; a title number is a cached copy of it, and the convention should say which wins when the two drift.

    One hygiene note that this comment had to observe and is worth folding into the convention: bare #N references in a cross-repo discussion resolve against the reading repo. Every reference above is fully qualified for that reason — which is exactly F4's sibling finding F3 in the textstrata audit, and a good candidate for a stated rule rather than a per-audit catch.

    The QEP-scope half of this is posted at QuantEcon/qeps#15.

  2. added
    discussOpen-ended team deliberation or a decision to be made
    on Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussOpen-ended team deliberation or a decision to be made

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions