From 0f3a11ab59a6e87d19f0a3f3bf04061db6281305 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 04:08:48 +0000 Subject: [PATCH 1/9] QEP-6: Project Tracker Structure and Order (Draft) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds QEP-6 as a Draft: the project-tracking unit (one tracker issue, Project issue type, native sub-issues one level down), positional order with plan semantics, dependencies for real constraints, descriptive phase milestones, the fixed status stamp, and the body's no-mirror discipline with a single Next: resume line. Adds the README index row (standard / Draft / –). Discussion: https://github.com/QuantEcon/qeps/issues/15 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013WNYd4EW71564zVcW81yx7 --- README.md | 1 + qeps/qep-0006-project-trackers.md | 269 ++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 qeps/qep-0006-project-trackers.md diff --git a/README.md b/README.md index 17c03fb..b69e746 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ need a QEP. |-----|-------|------|--------|---------| | [QEP-1](qeps/qep-0001-purpose-and-process.md) | QEP Purpose and Process | process | Accepted | v2 | | [QEP-2](qeps/qep-0002-standard-github-labels.md) | Standard GitHub Label Set and Labelling Policy | standard | Accepted | – | +| [QEP-6](qeps/qep-0006-project-trackers.md) | Project Tracker Structure and Order | standard | Draft | – | QEPs that set an ongoing rule are **maintained in place**: a substantive amendment bumps the QEP's `version` (shown above) under the same review process, rather than superseding diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md new file mode 100644 index 0000000..32eba42 --- /dev/null +++ b/qeps/qep-0006-project-trackers.md @@ -0,0 +1,269 @@ +--- +qep: 6 +title: Project Tracker Structure and Order +author: "@mmcky" +status: Draft +type: standard +created: 2026-08-26 +discussion: https://github.com/QuantEcon/qeps/issues/15 +--- + +# QEP-6: Project Tracker Structure and Order + +| | | +| ------------ | -------------------------------------------------------------------- | +| **QEP** | 6 | +| **Title** | Project Tracker Structure and Order | +| **Author** | @mmcky | +| **Status** | Draft | +| **Type** | standard | +| **Created** | 2026-08-26 | +| **Discussion** | [QuantEcon/qeps#15](https://github.com/QuantEcon/qeps/issues/15) | + +## Summary + +This QEP defines the org's unit of project tracking: a **Project** is one tracker +issue, typed with the native `Project` issue type, whose work items are its +**native sub-issues** one level down. Every structural fact lives in exactly one +native carrier — membership in sub-issue edges, **order in list position**, +constraints in dependencies, grouping in milestones, freshness in one fixed +status stamp — and **sequence is never encoded in names**. The tracker body +carries what structure cannot: goal, current state, phase intent, gates, and +rationale, and it never restates what the platform already holds. + +## Motivation + +The practice exists; the convention doesn't. A 2026-08-23 audit of 99 +tracker-shaped open issues org-wide found 13 title conventions (the commonest +covering 23%), 92% with no issue type, 60% tracking work in body checkboxes or +prose rather than sub-issues, status stamps in three forms, and zero use of +native dependencies. Coordination issues also have no honest label under QEP-2 +([#11](https://github.com/QuantEcon/qeps/issues/11)), so "untyped = untriaged" +is currently unreliable. + +Two things force the decision now. First, trackers have a **machine consumer**: +the projects dashboard's collector reads every registered tracker nightly, and +three producer classes write them (people, the `qe` skills, and future +maintenance agents). Second, **ordering has arrived from the field**: the first +work plan reached for milestones to express sequence +(QuantEcon/textstrata#8), and a `wp{issue}-stage{n}` milestone naming +convention was proposed to generalise it (QuantEcon/skills#55). Milestones +group but do not order; the review of that first instance found the plan's own +phases and the milestone axis cutting across each other, with the real +dependencies left in prose. + +The root problem is structural: a sequence token in an issue title or a +milestone name **welds order to identity**. Every re-plan must then either +rename the work (breaking every prior reference to it) or let the names lie +against the visible list. Names are also the one carrier the machine consumer +cannot use: the dashboard's tracker contract deliberately **never reads title +conventions or labels**, and its redaction rules strip titles and milestone +names from private repositories (12 of the 28 registered trackers). Position, +edges, types, dates, and counts survive everywhere names do not — and each is a +single-writer fact: re-ordering a list is one gesture, renaming a token family +is one edit per issue. + +## Proposal + +### 1. The unit + +A **Project is one tracker issue**. It carries the org-level **`Project` issue +type** — a native issue type, not a label, so QEP-2's label set is untouched +and "untyped = untriaged" stays reliable. Issue types are org metadata rather +than repository content, so the type survives even where a private repository's +content is redacted. This resolves the gap reported in +[#11](https://github.com/QuantEcon/qeps/issues/11). + +Its **work items are its direct native sub-issues** — which may live in any +repository — and **progress is measured on direct children only, never +deeper**. Membership lives in the sub-issue edges: body checkboxes, plan +tables, and legacy tasklists are never work items. A tracker with no +sub-issues has **unmeasured** progress (never 0% — "no sub-issues" and +"nothing done yet" are different facts). + +### 2. Order is positional + +The sub-issue list is kept in **plan order**: **position is the order of the +plan**, and the **topmost open item is next**. The list is a plan, not a +queue — completed items keep their place, so the list stays readable as the +plan with progress visible in it. A new item is placed on arrival (it appends +by default; move it into its phase), and phase groups stay contiguous. + +**Re-ordering moves items and never renames them** — by drag in the UI or by +the sub-issue reprioritize API (`PATCH …/issues/{parent}/sub_issues/priority`; +GraphQL `reprioritizeSubIssue`); the two write the same fact. **Sequence is +never encoded in issue titles or milestone names** (no `W3 —`, no `Phase 2:` +prefixes, no `stage{n}` tokens). Issue numbers are the stable handles; titles +are pure descriptions. + +Order is an **operational property, not a published field** of the unit: a +consumer that publishes a child list should preserve list order (order is an +attribute of the membership edge and is observable exactly where membership +is), but no consumer may require, parse, or infer sequence from names. + +### 3. Constraints are dependencies + +A genuine sequencing constraint between work items is expressed as a **native +dependency** (blocked-by), never as prose. A dependency states a +**constraint** (a partial order), not a presentation order; position states +presentation. Dependencies are **optional** — most order is soft preference, +and edges are reserved for constraints that are real. Phase-level gates +("nothing in Phase 2 starts before Phase 1's exit criterion") are stated in +the body, not encoded as pairwise edges. + +### 4. Phases are milestones (optional) + +Where a tracker is large enough to want grouping, **milestones group work +items into phases**. Milestone names are **descriptive** ("Phase 1 — +Foundations"), never sequence tokens: a milestone is a single object that +issues *reference*, so renaming a phase is one edit that propagates +everywhere — the single-update-point property no title convention has. Phase +order shows as contiguity in the list; phase *meaning* (intent, exit +criterion, gate) lives in the body's phase table. + +Two platform limits are accepted rather than worked around: an issue takes +one milestone, so phases must partition the work; and milestones are +per-repository, so **cross-repo work items fall back to the body's phase +table** — a same-named milestone in another repository is a copy, and copies +diverge. + +### 5. The status stamp + +The tracker body carries **exactly one machine-read element**: the status +stamp, in one fixed form. + +- **Canonical**: a `## Where we stand (verified YYYY-MM-DD)` heading. +- **Accepted fallback**: a `> **Updated YYYY-MM-DD.**` banner line. + +The string `verified YYYY-MM-DD` must not appear anywhere else in the body — +free-text near-misses are the known failure mode of stamp parsing. Everything +else in the body is for people; no other body text is machine-read. + +### 6. The body + +The body opens with a **one-sentence goal**, then the **stamp section at the +top**. The stamp section may open with a single **`**Next:**` line** — a link +to one work item plus one line of pickup context (branch, failing thing, +where to resume). This is the body's **only** statement of what is next: it +is a dated claim inside the one section whose contract is +dated-claims-re-verified-on-update, and if it ever disagrees with the list, +**the list is the authority** and the line is stale prose awaiting its next +stamp. Trackers that do not run working sessions omit the line. + +The body carries what structure cannot: the goal and definition of done, the +where-we-stand narrative, phase intents and exit criteria, gates, sequencing +rationale, and scope boundaries ("what does not change"). The body **never +mirrors structure**: + +| Never in the body | It already lives in | +|---|---| +| Work-item rosters or checkbox work lists | the sub-issue list (membership, order, state) | +| Milestone→issue tables | the milestone (one click, always live) | +| Hand-written progress counts or percentages | native sub-issue progress | +| Pairwise dependency prose for edges that exist | the dependency edges | +| Any "next"/"currently on" claim outside the stamp's `Next:` line | list position | + +Work items are referenced from prose by number/URL (rendered live by GitHub), +never by copied titles. An informative body skeleton is given in Appendix A. + +### 7. Scope + +This QEP governs **project tracker issues** — the unit the projects registry +registers. The **programme layer** (programme → project → items) is +deliberately outside it. The surrounding *practice* — tracker vs period-plan +genres, session ledgers, succession, revision-log comment discipline — is +maintained in the org's `qe` skills, which cite this QEP as the authority on +the unit's structure. + +## Alternatives considered + +- **Sequence tokens in titles or milestone names** (`W0–W6`, `wp{n}-stage{m}`, + `Phase 2:` prefixes). Rejected: welds order to identity, so every re-order + renames k issues (breaking every prior "see W3" in immutable comment + history) or leaves the tokens lying against the visible list. Unreadable by + the machine consumer by contract, and stripped by redaction for private + repositories. Field evidence: token families harden into names on first + use, which is the tell that the real need is *handles* (issue numbers) and + *grouping* (milestones), not order. +- **The body's plan table as the order authority.** Rejected: the native list + renders in position order on the same page, so a table that enumerates a + second order is a mirror in visible conflict with structure, and the + discipline's own rule — claims re-verified, never carried forward — argues + against maintaining mirrors. The table's job is meaning (phases, gates, + rationale), not enumeration. +- **A `tracking` type label** (as floated in + [#11](https://github.com/QuantEcon/qeps/issues/11)). Rejected in favour of + the native `Project` issue type: QEP-2's one-type-per-issue label rule stays + untouched, and the type survives redaction where labels are never read. +- **GitHub Projects (v2) as the ordering surface.** Rejected as authority: + item order there is per-view, on a separate permission surface, in a second + system the collector does not read. Fine as a *lens* over the same issues; + never the source of truth. +- **Queue semantics for the list** (done sinks, next floats to top). + Rejected: destroys the list's readability as a plan, fragments phase + contiguity, and makes closing an item cost a move. Under plan semantics the + routine gesture (closing) costs nothing and moves always mean re-planning. +- **A standalone resume box above the body.** Rejected: a second dated prose + register beside the stamp section — two update points that diverge, and a + stylistic near-miss with the banner stamp form. Folded into the stamp + section as the single `Next:` line instead. + +## Adoption + +Obligations on the three producer classes and the one consumer: + +1. **New trackers** conform at creation: `Project` type, sub-issue work items, + stamp, plan-ordered list, order-free names. +2. **Registered existing trackers** are brought into this layout by the + conform tooling (QuantEcon/skills#49); the collector reports compliance + nightly. Adoption of the ordering rules is greenfield: at the 2026-08-23 + baseline only 3 of 28 registered trackers carry any milestone (all + descriptive) and none use dependencies, so nothing is renamed and no + history is rewritten. +3. **The `qe` skills** (`workplan-*`) operationalise the convention: create + sub-issues in plan position, reprioritize on re-plan, and refresh the + stamp section, `Next:` line, and list position as one atomic update. Their + lint/conform pass may verify mechanically: stamp form present and unique, + no sequence tokens in child titles or milestone names, no checkbox work + lists, at most one `Next:` line and only in the stamp section, phases + contiguous. +4. **The projects dashboard** treats the tracker's list order as the + published child order (its tracker contract currently re-sorts children by + issue number; that rule is amended to preserve list order — a one-row + contract change plus a sort removal, verified once against a deliberate + re-order on a public tracker). Order remains unpublished as a field; + dependencies continue to publish as counts. + +## Appendix A (informative): tracker body skeleton + +```markdown +*Goal: one sentence on what done looks like.* + +## Where we stand (verified YYYY-MM-DD) + +**Next:** Owner/repo#N — one line of pickup context. + +Narrative of state: recent movement, in flight, blockers. + +## Plan + +Work items and their order: the sub-issue list (top = next; completed items +keep their place). Constraints between items: native dependencies. Phases: +the milestones below. + +| Phase | Intent | Exit criterion | +|---|---|---| +| [Phase 1 — Name](milestone-url) | … | … | + +**Gates:** … + +**Sequencing rationale:** why this order — only what the list cannot say. + +## Out of scope / what does not change + +… +``` + +Free sections (Findings, Premises, Notes) may follow — the skeleton is a +floor, not a ceiling. Revision history lives in comments, per the practice +conventions maintained in the `qe` skills. From 1a0f063b195185075bd48a3286e91116186fcd49 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 27 Aug 2026 13:01:49 +1000 Subject: [PATCH 2/9] Apply the field-test amendment set from the QEP-6 field report Folds in the twelve amendments consolidated on the field report thread (see QuantEcon/qeps#19) plus two edits from its producer-tooling finding: - Scope the standard to GitHub.com (s7) - Ban purely numeric milestone names, verified live (s4) - Membership criterion: closing a work item advances the definition of done; unparented issues and unparented trackers are normal (s1) - Direct children homogeneous in kind; progress is a snapshot, not a time series (s1) - Dropped items leave the sub-issue list - percent_completed counts not_planned closures as completed (s2) - Gates: mostly phase-level in practice; a whole-project gate also carries the tracker-to-tracker dependency edge (verified live on a throwaway pair); stated once, in the project that waits (s3) - Parked-ness is derived from open blockers, never carried (s3) - Stamp refresh is a read-modify-write with a lossless read; sanitising surfaces get append-only treatment (s5) - Named replacements for the body bans; Related work section (s6) - Markdown template plus post-creation type read-back (Adoption 1) - Re-parenting guard on the conform tooling (Adoption 2) - QEP-6 authoritative over the dashboard contract during handover (Adoption 3) - Drop "untyped = untriaged" as a load-bearing justification (s1) Co-Authored-By: Claude Fable 5 --- qeps/qep-0006-project-trackers.md | 146 ++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 20 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index 32eba42..aeaa320 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -68,11 +68,10 @@ is one edit per issue. ### 1. The unit A **Project is one tracker issue**. It carries the org-level **`Project` issue -type** — a native issue type, not a label, so QEP-2's label set is untouched -and "untyped = untriaged" stays reliable. Issue types are org metadata rather -than repository content, so the type survives even where a private repository's -content is redacted. This resolves the gap reported in -[#11](https://github.com/QuantEcon/qeps/issues/11). +type** — a native issue type, not a label, so QEP-2's label set is untouched. +Issue types are org metadata rather than repository content, so the type +survives even where a private repository's content is redacted. This resolves +the gap reported in [#11](https://github.com/QuantEcon/qeps/issues/11). Its **work items are its direct native sub-issues** — which may live in any repository — and **progress is measured on direct children only, never @@ -81,6 +80,22 @@ tables, and legacy tasklists are never work items. A tracker with no sub-issues has **unmeasured** progress (never 0% — "no sub-issues" and "nothing done yet" are different facts). +An issue is a **work item** of a project if closing it advances the project's +definition of done. If the definition of done can be met without it, it is not +a member — however much it shares a repository, a theme, or an owner. **If the +goal must be widened to justify an item's membership, the item is not a +member.** Not every issue belongs to a project — an unparented issue is the +normal case, not a gap to be filled — and an unparented project tracker is +likewise normal: not every project answers to a programme. + +A tracker's direct children should be **homogeneous in kind** — all leaf work +items, or all project trackers. Progress is a count over direct children, so +mixing a multi-item project with leaf issues weights them equally and makes +the percentage meaningless. The percentage is also a **snapshot over current +direct children, not a time series**: membership changes — items dropped, +items moved to another project, a tracker split — move the number without +work occurring, so progress is never compared across a membership change. + ### 2. Order is positional The sub-issue list is kept in **plan order**: **position is the order of the @@ -89,6 +104,16 @@ queue — completed items keep their place, so the list stays readable as the plan with progress visible in it. A new item is placed on arrival (it appends by default; move it into its phase), and phase groups stay contiguous. +Dropping is different from completing. An item **dropped** from the plan — +closed `not_planned`, or superseded — is **removed from the sub-issue list**: +membership is the plan, and work no longer in the plan is not a member. The +issue itself persists with its own `state_reason` (`completed`, `not_planned`, +or `duplicate` with `duplicate_of`) and its cross-references intact — only +the membership edge goes. This is mechanical as well as semantic: +`sub_issues_summary.completed` counts every closed child regardless of +`state_reason`, so a dropped item left in place inflates the tracker's +reported progress. + **Re-ordering moves items and never renames them** — by drag in the UI or by the sub-issue reprioritize API (`PATCH …/issues/{parent}/sub_issues/priority`; GraphQL `reprioritizeSubIssue`); the two write the same fact. **Sequence is @@ -107,19 +132,40 @@ A genuine sequencing constraint between work items is expressed as a **native dependency** (blocked-by), never as prose. A dependency states a **constraint** (a partial order), not a presentation order; position states presentation. Dependencies are **optional** — most order is soft preference, -and edges are reserved for constraints that are real. Phase-level gates -("nothing in Phase 2 starts before Phase 1's exit criterion") are stated in -the body, not encoded as pairwise edges. +and edges are reserved for constraints that are real. Dependencies are +independent of the parent hierarchy, so an edge may cross project boundaries. + +**Gates** cover phase-level and project-level constraints. Most cross-project +gates are **phase-level** in practice — one phase of A waits on one phase of +B while the rest of both proceeds — and a phase-level gate ("nothing in +Phase 2 starts before Phase 1's exit criterion") is stated in the body, not +encoded as pairwise edges, because no object represents a phase. Where a +project genuinely waits on another project **in its entirety**, that gate +must also carry the native dependency edge between the two tracker issues, +with the body carrying only the rationale. State the constraint at the +granularity that is actually true: a tracker-to-tracker edge asserts that +*nothing* in the blocked project may start, and parks it wholesale in any +consumer deriving parked-ness from blockers. A gate is stated **once**, in +the body of the project that must wait; the other project points at it (see +§6, Related work) rather than restating it. + +Whether an item is **parked** is derived, not carried: an open item with an +open blocker is parked. Position states where an item sits in the plan; +dependencies state whether it can start. Where a gate is a decision rather +than an issue, create the issue for the decision rather than describing the +park in prose. ### 4. Phases are milestones (optional) Where a tracker is large enough to want grouping, **milestones group work items into phases**. Milestone names are **descriptive** ("Phase 1 — -Foundations"), never sequence tokens: a milestone is a single object that -issues *reference*, so renaming a phase is one edit that propagates -everywhere — the single-update-point property no title convention has. Phase -order shows as contiguity in the list; phase *meaning* (intent, exit -criterion, gate) lives in the body's phase table. +Foundations"), never sequence tokens, and never purely numeric (`gh issue +list --milestone` parses its argument as a number first, so a milestone +titled "2026" is looked up as milestone *number* 2026 and fails). A milestone +is a single object that issues *reference*, so renaming a phase is one edit +that propagates everywhere — the single-update-point property no title +convention has. Phase order shows as contiguity in the list; phase *meaning* +(intent, exit criterion, gate) lives in the body's phase table. Two platform limits are accepted rather than worked around: an issue takes one milestone, so phases must partition the work; and milestones are @@ -139,6 +185,19 @@ The string `verified YYYY-MM-DD` must not appear anywhere else in the body — free-text near-misses are the known failure mode of stamp parsing. Everything else in the body is for people; no other body text is machine-read. +Body writes are **whole-replace**: GitHub offers no partial update and no +compare-and-swap, so a stamp refresh is a **read-modify-write within a +single turn**, preserving all body content outside the stamp section — +concurrent writers otherwise silently discard each other's edits. The read +half must be **lossless**: read the body from a surface that returns its +source (the REST issues endpoint returns raw markdown). Some agent surfaces +return a sanitised rendering instead — HTML entities substituted, and +anything tag-shaped, such as a `` in a code snippet, silently +dropped — and writing such a read back destroys content while appearing to +preserve it. Where only a sanitising surface is available, treat the body as +append-only and record corrections as comments. This applies to every +programmatic body write, not only the stamp. + ### 6. The body The body opens with a **one-sentence goal**, then the **stamp section at the @@ -163,6 +222,21 @@ mirrors structure**: | Pairwise dependency prose for edges that exist | the dependency edges | | Any "next"/"currently on" claim outside the stamp's `Next:` line | list position | +The bans have named replacements: the body's **phase table carries phase +intent and exit criteria, never membership** — the question a roster answers +(which items are in which phase) is already answered by the sub-issue list +and the milestones. A ban with a named replacement survives contact with +producers; a ban alone gets worked around. + +A **Related work** section (optional) names sibling **projects** — one line +each on how they relate: informs, spawned by, shares an engine, gated by +(pointing at the body that states the gate). GitHub records only that a +mention happened; a deliberate relationship between projects is an assertion +with no native carrier, which is what the body is for. Entries name +projects, never work items — an entry may cite item numbers as the specifics +of a project-level relationship, but an entry whose subject is a work item +is the sub-issue list restated or dependency prose, both banned above. + Work items are referenced from prose by number/URL (rendered live by GitHub), never by copied titles. An informative body skeleton is given in Appendix A. @@ -175,6 +249,10 @@ genres, session ledgers, succession, revision-log comment discipline — is maintained in the org's `qe` skills, which cite this QEP as the authority on the unit's structure. +The QEP is scoped to **GitHub.com**: the sub-issue reordering endpoint is +absent from GitHub Enterprise Server API descriptions through 3.18, so the +ordering rule has no verified mechanism there. + ## Alternatives considered - **Sequence tokens in titles or milestone names** (`W0–W6`, `wp{n}-stage{m}`, @@ -213,20 +291,42 @@ the unit's structure. Obligations on the three producer classes and the one consumer: 1. **New trackers** conform at creation: `Project` type, sub-issue work items, - stamp, plan-ordered list, order-free names. + stamp, plan-ordered list, order-free names. A tracker template is a + **markdown template**, never an issue form: forms render field labels as + `###` headings and cannot emit the stamp heading or its dynamic date, + while a markdown template passes the body through verbatim and can set + `type:`. Templates cannot set milestone, parent, dependencies, or order — + those are post-creation for every producer. Creation is never trusted to + carry the type: after creating a tracker, assert the type by read-back + (`gh issue view N --json issueType`) and set it explicitly if absent — + non-interactive `gh issue create` does not apply templates at all, and + the REST `type` parameter is silently dropped for callers without push + access; both fail silently. Assert against the issue itself, not an org + type enumeration (`GET /orgs/{org}/issue-types` is 403 for app identities + where the repo-scoped read succeeds), and key automation on + `issue.type.name` — webhook payloads carry no template identifier. 2. **Registered existing trackers** are brought into this layout by the conform tooling (QuantEcon/skills#49); the collector reports compliance - nightly. Adoption of the ordering rules is greenfield: at the 2026-08-23 - baseline only 3 of 28 registered trackers carry any milestone (all - descriptive) and none use dependencies, so nothing is renamed and no - history is rewritten. + nightly. Claiming a work item that already has a parent **detaches it + from that parent**: sub-issue membership is single-parent, and both + `gh issue edit --add-sub-issue` and `--parent` set `replace_parent` + unconditionally, with no opt-out. The conform tooling must read an item's + existing parent before linking, and re-link that parent or record the + detachment — a programme tracker silently emptied this way reports its + remaining closed children as complete. Adoption of the ordering rules is + greenfield: at the 2026-08-23 baseline only 3 of 28 registered trackers + carry any milestone (all descriptive) and none use dependencies, so + nothing is renamed and no history is rewritten. 3. **The `qe` skills** (`workplan-*`) operationalise the convention: create sub-issues in plan position, reprioritize on re-plan, and refresh the stamp section, `Next:` line, and list position as one atomic update. Their lint/conform pass may verify mechanically: stamp form present and unique, no sequence tokens in child titles or milestone names, no checkbox work lists, at most one `Next:` line and only in the stamp section, phases - contiguous. + contiguous. The skills currently cite the dashboard's tracker contract, + which permits body constructs this QEP forbids; until that contract's + planned handover to this QEP, this QEP is authoritative for tracker + structure wherever the two disagree. 4. **The projects dashboard** treats the tracker's list order as the published child order (its tracker contract currently re-sorts children by issue number; that rule is amended to preserve list order — a one-row @@ -255,10 +355,16 @@ the milestones below. |---|---|---| | [Phase 1 — Name](milestone-url) | … | … | -**Gates:** … +**Gates:** … (phase-level gates live here; a whole-project gate also carries +a tracker-to-tracker dependency edge, with only the rationale stated here, +once, in the project that waits) **Sequencing rationale:** why this order — only what the list cannot say. +## Related work + +- [Project name](tracker-url) — one line on the relationship. + ## Out of scope / what does not change … From e61b1dac6840780edafef3b5b66c7bebe1ee23b1 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Fri, 28 Aug 2026 15:11:34 +1000 Subject: [PATCH 3/9] =?UTF-8?q?QEP-6:=20add=20the=20issue-type=20axis=20?= =?UTF-8?q?=E2=80=94=20three=20structural=20roles,=20content=20stays=20on?= =?UTF-8?q?=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Types carry structural role (Project / Task / Decision, each with its own meaning of "closed"); QEP-2 labels carry content, and the axes compose. Adoption gains the one-time reversible reshape of the org type set (Bug/Feature disabled, Task re-described, Decision created). Co-Authored-By: Claude Fable 5 --- qeps/qep-0006-project-trackers.md | 82 +++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index aeaa320..e4fe8ff 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -25,9 +25,10 @@ discussion: https://github.com/QuantEcon/qeps/issues/15 This QEP defines the org's unit of project tracking: a **Project** is one tracker issue, typed with the native `Project` issue type, whose work items are its **native sub-issues** one level down. Every structural fact lives in exactly one -native carrier — membership in sub-issue edges, **order in list position**, -constraints in dependencies, grouping in milestones, freshness in one fixed -status stamp — and **sequence is never encoded in names**. The tracker body +native carrier — kind in issue types, membership in sub-issue edges, **order +in list position**, constraints in dependencies, grouping in milestones, +freshness in one fixed status stamp — and **sequence is never encoded in +names**. The tracker body carries what structure cannot: goal, current state, phase intent, gates, and rationale, and it never restates what the platform already holds. @@ -96,7 +97,45 @@ direct children, not a time series**: membership changes — items dropped, items moved to another project, a tracker split — move the number without work occurring, so progress is never compared across a membership change. -### 2. Order is positional +### 2. Issue types + +Native issue types carry **structural role**: what kind of object an issue is +in the tracking system. The axis is orthogonal to QEP-2's labels, which +classify **content** at triage regardless of project membership — the two +compose rather than compete (a work item whose content is a bug is `Task` +plus the `bug` label, never a `Bug` type). Types are org metadata: the +machine consumer branches on them, and they survive redaction where labels +are never read. + +The org type set is three structural roles, each with its own meaning of +"closed": + +| Type | Role | Closing means | +|---|---|---| +| `Project` | a tracker (§1) | the definition of done is met | +| `Task` | a leaf work item | the work shipped | +| `Decision` | a decision point in a plan | the choice is recorded | + +A **`Decision`** is open while the choice is pending, closed `completed` when +decided — with the choice recorded in the issue — and closed `not_planned` +when mooted. Work that cannot start before a choice is made is blocked-by +the decision (§4), so "parked on a decision" is derived like any other +parked-ness, and a consumer can render a project's open decisions as the +distinct objects they are. A `Decision` may still carry QEP-2's `discuss` +label: the label states what the conversation is; the type states the +object's role in the plan. + +`Project` is required on trackers and `Decision` on decision points; `Task` +on leaf work items is recommended, not required — an untyped issue remains +the normal pre-triage state. Hierarchy level is never encoded in type: +sub-issue edges carry level, so a tracker whose children are trackers is +still a `Project`. + +A type is admitted to the set only when it names a structural role with its +own meaning of "closed" that a machine consumer renders or branches on. +Content classification is never a type — that axis belongs to QEP-2. + +### 3. Order is positional The sub-issue list is kept in **plan order**: **position is the order of the plan**, and the **topmost open item is next**. The list is a plan, not a @@ -126,7 +165,7 @@ consumer that publishes a child list should preserve list order (order is an attribute of the membership edge and is observable exactly where membership is), but no consumer may require, parse, or infer sequence from names. -### 3. Constraints are dependencies +### 4. Constraints are dependencies A genuine sequencing constraint between work items is expressed as a **native dependency** (blocked-by), never as prose. A dependency states a @@ -147,15 +186,15 @@ granularity that is actually true: a tracker-to-tracker edge asserts that *nothing* in the blocked project may start, and parks it wholesale in any consumer deriving parked-ness from blockers. A gate is stated **once**, in the body of the project that must wait; the other project points at it (see -§6, Related work) rather than restating it. +§7, Related work) rather than restating it. Whether an item is **parked** is derived, not carried: an open item with an open blocker is parked. Position states where an item sits in the plan; dependencies state whether it can start. Where a gate is a decision rather -than an issue, create the issue for the decision rather than describing the -park in prose. +than an issue, create a `Decision`-typed issue for it (§2) rather than +describing the park in prose. -### 4. Phases are milestones (optional) +### 5. Phases are milestones (optional) Where a tracker is large enough to want grouping, **milestones group work items into phases**. Milestone names are **descriptive** ("Phase 1 — @@ -173,7 +212,7 @@ per-repository, so **cross-repo work items fall back to the body's phase table** — a same-named milestone in another repository is a copy, and copies diverge. -### 5. The status stamp +### 6. The status stamp The tracker body carries **exactly one machine-read element**: the status stamp, in one fixed form. @@ -198,7 +237,7 @@ preserve it. Where only a sanitising surface is available, treat the body as append-only and record corrections as comments. This applies to every programmatic body write, not only the stamp. -### 6. The body +### 7. The body The body opens with a **one-sentence goal**, then the **stamp section at the top**. The stamp section may open with a single **`**Next:**` line** — a link @@ -240,7 +279,7 @@ is the sub-issue list restated or dependency prose, both banned above. Work items are referenced from prose by number/URL (rendered live by GitHub), never by copied titles. An informative body skeleton is given in Appendix A. -### 7. Scope +### 8. Scope This QEP governs **project tracker issues** — the unit the projects registry registers. The **programme layer** (programme → project → items) is @@ -273,6 +312,11 @@ ordering rule has no verified mechanism there. [#11](https://github.com/QuantEcon/qeps/issues/11)). Rejected in favour of the native `Project` issue type: QEP-2's one-type-per-issue label rule stays untouched, and the type survives redaction where labels are never read. +- **Content-classifying issue types** (`Bug` and `Feature`, GitHub's default + set). Rejected: content classification is QEP-2's label axis, and a second + carrier for the same fact lets the two disagree — an issue typed `Bug` and + labelled `enhancement`, or typed and unlabelled and so invisible to every + QEP-2 query. The type axis is reserved for structural roles (§2). - **GitHub Projects (v2) as the ordering surface.** Rejected as authority: item order there is per-view, on a separate permission surface, in a second system the collector does not read. Fine as a *lens* over the same issues; @@ -288,9 +332,17 @@ ordering rule has no verified mechanism there. ## Adoption -Obligations on the three producer classes and the one consumer: - -1. **New trackers** conform at creation: `Project` type, sub-issue work items, +One org-level precondition, then obligations on the three producer classes +and the one consumer: + +1. **The org issue-type set** is reshaped once to the three roles of §2, by + four reversible calls to the issue-types API: `Bug` and `Feature` are + **disabled, not deleted** — disabling hides a type from every picker but + retains existing assignments latent, and is undone by one call — `Task`'s + description is edited to name the work-item role, and `Decision` is + created. Because the change is reversible it ships at the start of the + field test, not at acceptance. +2. **New trackers** conform at creation: `Project` type, sub-issue work items, stamp, plan-ordered list, order-free names. A tracker template is a **markdown template**, never an issue form: forms render field labels as `###` headings and cannot emit the stamp heading or its dynamic date, From 361be8c71bd2e866705a4f7ad98e43e6b332b160 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Fri, 28 Aug 2026 15:28:00 +1000 Subject: [PATCH 4/9] =?UTF-8?q?QEP-6:=20record=20the=20two-tier=20ruling?= =?UTF-8?q?=20=E2=80=94=20a=20programme=20is=20registry=20grouping,=20not?= =?UTF-8?q?=20a=20tier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the outcome of the #24 discussion in one scope sentence: the grammar has two normative tiers and no third; a programme is a named collection of projects maintained by the projects registry. Co-Authored-By: Claude Fable 5 --- qeps/qep-0006-project-trackers.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index e4fe8ff..446276c 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -282,8 +282,12 @@ never by copied titles. An informative body skeleton is given in Appendix A. ### 8. Scope This QEP governs **project tracker issues** — the unit the projects registry -registers. The **programme layer** (programme → project → items) is -deliberately outside it. The surrounding *practice* — tracker vs period-plan +registers. The grammar has **two normative tiers — project and work item — +and no third**: a *programme* is a named collection of projects, a grouping +the projects registry maintains, with no structural duties of its own. No +producer or consumer reads programme membership as structure, an unparented +tracker remains the normal case (§1), and hierarchy level never enters the +type set (§2). The surrounding *practice* — tracker vs period-plan genres, session ledgers, succession, revision-log comment discipline — is maintained in the org's `qe` skills, which cite this QEP as the authority on the unit's structure. From bc856da2ad7ef66bd4d67b1ad21b2cce484f5c68 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 10:36:23 +0000 Subject: [PATCH 5/9] QEP-6: the phase gate is a conjunction; strike the back-pointer; Adoption 1-5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Draft repairs ruled on 2026-09-08, from the two field reports on this PR and a review of all four dashboard projects. The comment window has not opened and there are no reviews on this PR, so every edit here is a free Draft edit: no version, no window reset. SECTION 4, THE PHASE GATE. The clause that sent phase-level gates to the body "because no object represents a phase" is what produced six uncarried orderings across two conformant trackers. The dependency graph is many-to-many, which is enough: the first item of the waiting phase is blocked by every item of the phase it waits on. Linear in the upstream phase rather than a cross-product, it publishes how many upstream items are still open, and it clears when the PHASE closes rather than when the upstream tracker closes. The alternatives were checked against the live graph rather than reasoned about. A tracker-to-tracker edge over-parks: section 4 says it asserts nothing in the blocked project may start, and status-projects#89's Decide phase is being worked now. A single tail edge under-carries: of #69's five Build items, #76 and #77 both report blocking 0 and the five have no chain between them, so closing the tail implies nothing about the rest. SECTION 4, THE ITEM EDGE. Made explicit that an item-level constraint is a native edge whether or not the two items share a project (FR-2's first half) — a producer read the gate paragraph's "cross-project" as covering item edges and wrote three of them as prose. SECTION 4, THE DECISION GATE. Where a phase's exit criterion is a single Decision closing, the gate is a decision gate: the object exists, so each item that waits carries the edge to it (FR-1). Both producers read section 2 and section 4 as pointing different ways here and both chose the body. SECTION 4, THE BACK-POINTER IS STRUCK. Blocked-by and blocking are two ends of one edge — verified live, #73 reports blocking 1 while #94 reports blocked_by 3 — so the waited-on project needs no back-pointer to stay in step. No qe skill writes a dependency edge at all, so the obligation had zero producers and a free platform inverse: it will be missed every time, and was. A Related work line stays available prose in section 7, not an obligation. SECTION 1. Decision children are compatible with either homogeneous kind, a closed-completed Decision counts toward progress like any child, and a consumer may render decisions apart (FR-3). Two of eleven and two of nine children on the reviewed trackers are decisions, and the Themes programme tracker carries seven beside four project trackers. SECTION 8. "No producer or consumer reads programme membership as structure" stops being true the day the dashboard's programme read ships, so it now separates the producer duty from a consumer's observation: observing a front door creates no duty on any tracker and no third tier. APPENDIX A. Its Gates line restated both struck rules verbatim, and it is the text producers copy — so it changes in the same commit, as the field report asked. ADOPTION IS RENUMBERED 1-5. The source read 1., 2., 2., 3., 4.; Markdown renumbers on render, so the published document showed 1-5 while every external "clause N" citation after the first was off by one. Clause 4 also gains FR-4's obligation: re-stamp the section in the same turn as any change to the list, because the phase table is an undated claim outside the one section whose contract is that its claims are re-verified on update, and it went stale within a day of a re-plan. FRONTMATTER gains related: [2] with the matching header-table row, which check.mjs has always enforced as a pair and which this QEP — the one that reasons most explicitly about another — did not carry. THE README INDEX ROW IS REMOVED rather than merged. This branch predated QEP-3's merge and both inserted a row after QEP-2's, which is what made the PR unmergeable. Under the generated-index rule the row is derived from frontmatter, so stripping it returns README to its base content and the merge with main is clean. Note the consequence: until #30 lands, this branch's own check.mjs still requires a row per QEP file, so CI here is red until then. That ordering is deliberate. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Hwm5shrGXmaq4r3Lp9kS33 --- README.md | 1 - qeps/qep-0006-project-trackers.md | 96 +++++++++++++++++++++---------- 2 files changed, 67 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index b69e746..17c03fb 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ need a QEP. |-----|-------|------|--------|---------| | [QEP-1](qeps/qep-0001-purpose-and-process.md) | QEP Purpose and Process | process | Accepted | v2 | | [QEP-2](qeps/qep-0002-standard-github-labels.md) | Standard GitHub Label Set and Labelling Policy | standard | Accepted | – | -| [QEP-6](qeps/qep-0006-project-trackers.md) | Project Tracker Structure and Order | standard | Draft | – | QEPs that set an ongoing rule are **maintained in place**: a substantive amendment bumps the QEP's `version` (shown above) under the same review process, rather than superseding diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index 446276c..ee5cb19 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -4,6 +4,7 @@ title: Project Tracker Structure and Order author: "@mmcky" status: Draft type: standard +related: [2] created: 2026-08-26 discussion: https://github.com/QuantEcon/qeps/issues/15 --- @@ -18,6 +19,7 @@ discussion: https://github.com/QuantEcon/qeps/issues/15 | **Status** | Draft | | **Type** | standard | | **Created** | 2026-08-26 | +| **Related** | [QEP-2](qep-0002-standard-github-labels.md) — the content axis §2 composes with | | **Discussion** | [QuantEcon/qeps#15](https://github.com/QuantEcon/qeps/issues/15) | ## Summary @@ -92,7 +94,12 @@ likewise normal: not every project answers to a programme. A tracker's direct children should be **homogeneous in kind** — all leaf work items, or all project trackers. Progress is a count over direct children, so mixing a multi-item project with leaf issues weights them equally and makes -the percentage meaningless. The percentage is also a **snapshot over current +the percentage meaningless. **`Decision` children (§2) are compatible with +either kind**: a decision is a point in the plan rather than a unit of work, +so it does not distort the comparison the rule protects. A `Decision` closed +`completed` counts toward progress like any other child — a recorded choice +advances the definition of done — and a consumer may render a project's +decisions apart from its work. The percentage is also a **snapshot over current direct children, not a time series**: membership changes — items dropped, items moved to another project, a tracker split — move the number without work occurring, so progress is never compared across a membership change. @@ -172,27 +179,48 @@ dependency** (blocked-by), never as prose. A dependency states a **constraint** (a partial order), not a presentation order; position states presentation. Dependencies are **optional** — most order is soft preference, and edges are reserved for constraints that are real. Dependencies are -independent of the parent hierarchy, so an edge may cross project boundaries. - -**Gates** cover phase-level and project-level constraints. Most cross-project -gates are **phase-level** in practice — one phase of A waits on one phase of -B while the rest of both proceeds — and a phase-level gate ("nothing in -Phase 2 starts before Phase 1's exit criterion") is stated in the body, not -encoded as pairwise edges, because no object represents a phase. Where a -project genuinely waits on another project **in its entirety**, that gate -must also carry the native dependency edge between the two tracker issues, -with the body carrying only the rationale. State the constraint at the -granularity that is actually true: a tracker-to-tracker edge asserts that +independent of the parent hierarchy, so an edge may cross project boundaries: +an item-level constraint is a native edge on the two items **whether or not +they share a project**, and the body carries only the rationale. + +**A phase-level gate is carried as a conjunction.** Most cross-project gates +are phase-level in practice — one phase of A waits on one phase of B while +the rest of both proceeds — and no object represents a phase. The dependency +graph is many-to-many, though, which is enough: the **first item of the +waiting phase is blocked by every item of the phase it waits on**. That is +linear in the size of the upstream phase rather than a cross-product; it +publishes a real quantity, how many upstream items are still open; and it +**clears when the phase closes** rather than when the upstream tracker +closes. Where the items of the waiting phase are themselves ordered, the +conjunction reaches them through that order; where they are genuinely +parallel, only the first is gated and the body says so. A phase gate with no +edges behind it is prose, and prose is the one carrier no consumer reads. + +**Where a phase's exit criterion is a single `Decision` closing, the gate is +a decision gate rather than a phase gate**: the object exists, so each item +that waits carries the native edge to it and the body states only why. Where +a gate is a decision that has no issue, create a `Decision`-typed one for it +(§2) rather than describing the park in prose. + +Where a project genuinely waits on another project **in its entirety**, that +gate must also carry the native dependency edge between the two tracker +issues, with the body carrying only the rationale. State the constraint at +the granularity that is actually true: a tracker-to-tracker edge asserts that *nothing* in the blocked project may start, and parks it wholesale in any -consumer deriving parked-ness from blockers. A gate is stated **once**, in -the body of the project that must wait; the other project points at it (see -§7, Related work) rather than restating it. +consumer deriving parked-ness from blockers — which makes it the wrong +instrument for a gate that holds one phase. + +A gate's rationale is stated **once**, in the body of the project that must +wait. **The reverse direction is read, never written:** blocked-by and +blocking are two ends of one edge, so the waited-on project needs no +back-pointer to stay in step. A `Related work` line pointing at the gate (§7) +is available prose for a human reader; it is not an obligation this standard +relies on, because an obligation with a free platform inverse is one that +will simply be missed. Whether an item is **parked** is derived, not carried: an open item with an open blocker is parked. Position states where an item sits in the plan; -dependencies state whether it can start. Where a gate is a decision rather -than an issue, create a `Decision`-typed issue for it (§2) rather than -describing the park in prose. +dependencies state whether it can start. ### 5. Phases are milestones (optional) @@ -269,7 +297,9 @@ producers; a ban alone gets worked around. A **Related work** section (optional) names sibling **projects** — one line each on how they relate: informs, spawned by, shares an engine, gated by -(pointing at the body that states the gate). GitHub records only that a +(pointing at the body that states the gate — available prose, never an +obligation, since §4's reverse direction is read rather than written). +GitHub records only that a mention happened; a deliberate relationship between projects is an assertion with no native carrier, which is what the body is for. Entries name projects, never work items — an entry may cite item numbers as the specifics @@ -285,9 +315,12 @@ This QEP governs **project tracker issues** — the unit the projects registry registers. The grammar has **two normative tiers — project and work item — and no third**: a *programme* is a named collection of projects, a grouping the projects registry maintains, with no structural duties of its own. No -producer or consumer reads programme membership as structure, an unparented -tracker remains the normal case (§1), and hierarchy level never enters the -type set (§2). The surrounding *practice* — tracker vs period-plan +producer treats programme membership as structure, an unparented tracker +remains the normal case (§1), and hierarchy level never enters the type set +(§2). A consumer may *observe* a programme's front door — read its list, count +its decisions — and doing so creates no duty on any tracker and no third tier; +what this scope excludes is a programme tier in the grammar, not a +consumer's reading of one issue. The surrounding *practice* — tracker vs period-plan genres, session ledgers, succession, revision-log comment discipline — is maintained in the org's `qe` skills, which cite this QEP as the authority on the unit's structure. @@ -361,7 +394,7 @@ and the one consumer: type enumeration (`GET /orgs/{org}/issue-types` is 403 for app identities where the repo-scoped read succeeds), and key automation on `issue.type.name` — webhook payloads carry no template identifier. -2. **Registered existing trackers** are brought into this layout by the +3. **Registered existing trackers** are brought into this layout by the conform tooling (QuantEcon/skills#49); the collector reports compliance nightly. Claiming a work item that already has a parent **detaches it from that parent**: sub-issue membership is single-parent, and both @@ -373,9 +406,13 @@ and the one consumer: greenfield: at the 2026-08-23 baseline only 3 of 28 registered trackers carry any milestone (all descriptive) and none use dependencies, so nothing is renamed and no history is rewritten. -3. **The `qe` skills** (`workplan-*`) operationalise the convention: create +4. **The `qe` skills** (`workplan-*`) operationalise the convention: create sub-issues in plan position, reprioritize on re-plan, and refresh the - stamp section, `Next:` line, and list position as one atomic update. Their + stamp section, `Next:` line, and list position as one atomic update — and + **re-stamp the section in the same turn as any change to the list**, because + the body's phase intents and exit criteria are undated claims sitting outside + the one section whose contract is that its claims are re-verified on update, + and they go stale on a re-plan otherwise. Their lint/conform pass may verify mechanically: stamp form present and unique, no sequence tokens in child titles or milestone names, no checkbox work lists, at most one `Next:` line and only in the stamp section, phases @@ -383,7 +420,7 @@ and the one consumer: which permits body constructs this QEP forbids; until that contract's planned handover to this QEP, this QEP is authoritative for tracker structure wherever the two disagree. -4. **The projects dashboard** treats the tracker's list order as the +5. **The projects dashboard** treats the tracker's list order as the published child order (its tracker contract currently re-sorts children by issue number; that rule is amended to preserve list order — a one-row contract change plus a sort removal, verified once against a deliberate @@ -411,9 +448,10 @@ the milestones below. |---|---|---| | [Phase 1 — Name](milestone-url) | … | … | -**Gates:** … (phase-level gates live here; a whole-project gate also carries -a tracker-to-tracker dependency edge, with only the rationale stated here, -once, in the project that waits) +**Gates:** … (only the rationale lives here, once, in the project that waits: +a phase-level gate is carried as a conjunction of native edges onto the first +item of the waiting phase, a decision gate as an edge to the `Decision`, and a +whole-project gate as a tracker-to-tracker edge — see §4) **Sequencing rationale:** why this order — only what the list cannot say. From 3673475171b11c33abe1d814216f8be0ae4b96b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 11:39:58 +0000 Subject: [PATCH 6/9] =?UTF-8?q?QEP-6:=20the=20output=20test=20for=20an=20e?= =?UTF-8?q?dge;=20=C2=A78=20defers=20the=20front-door=20question;=20Adopti?= =?UTF-8?q?on=204=20names=20its=20discharging=20item?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three repairs from the adversarial review of the 2026-09-08 rulings. §4 states the test that decides whether an ordering is an edge at all: output, not order. An edge is written where the downstream item cannot start without something the upstream produces; two items that touch the same file or will be worked in sequence are plan order, and stay with the body's sequencing rationale. The same test decides what a gate is — a phase-level gate is a real constraint by definition, a phase-level preference is not a gate. The case study that prompted it: #89's "Build waits on #69's Build" was coordination written as a gate, and the conjunction was about to carry it. Appendix A's Gates line follows. §8 no longer says a consumer "may observe" a programme's front door — that is QuantEcon/status-projects#10's open question and this QEP has no business answering it. The scope keeps what is its own: no programme tier in the grammar, no duty on a tracker from being listed. Adoption clause 4 names the item that discharges the tracker-contract handover, QuantEcon/status-projects#114, in place of an open-ended "until that contract's planned handover" — the form QEP-1 v3 requires for a precedence clause over an external standard. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Hwm5shrGXmaq4r3Lp9kS33 --- qeps/qep-0006-project-trackers.md | 40 ++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index ee5cb19..28a42ba 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -183,6 +183,20 @@ independent of the parent hierarchy, so an edge may cross project boundaries: an item-level constraint is a native edge on the two items **whether or not they share a project**, and the body carries only the rationale. +**The test for a real constraint is output, not order.** An edge is written +where the downstream item cannot start without something the upstream item +produces — a ruling, a field, a document, a page it draws from. That two items +touch the same file, or that one agent will work them in sequence, is +coordination: it stays in the body as plan order and sequencing rationale, +because written as an edge it shows the downstream as *blocked* while it could +proceed, and it teaches producers that plan position is edge material. The +same test decides what a *gate* is. A phase-level gate is a real constraint by +definition — the waiting phase needs what the upstream phase's exit criterion +delivers — so it carries edges (below); a phase-level *preference* is not a +gate, is not written among the body's gates, and stays with the sequencing +rationale. Where the waiting item needs only some of an upstream phase's +outputs, the constraint is item-level and those edges alone are written. + **A phase-level gate is carried as a conjunction.** Most cross-project gates are phase-level in practice — one phase of A waits on one phase of B while the rest of both proceeds — and no object represents a phase. The dependency @@ -317,10 +331,12 @@ and no third**: a *programme* is a named collection of projects, a grouping the projects registry maintains, with no structural duties of its own. No producer treats programme membership as structure, an unparented tracker remains the normal case (§1), and hierarchy level never enters the type set -(§2). A consumer may *observe* a programme's front door — read its list, count -its decisions — and doing so creates no duty on any tracker and no third tier; -what this scope excludes is a programme tier in the grammar, not a -consumer's reading of one issue. The surrounding *practice* — tracker vs period-plan +(§2). Whether a consumer reads a programme's front door at all is that consumer's +question — for the projects dashboard, an open decision of its own +(QuantEcon/status-projects#10) — and nothing in this scope answers it either +way. What this scope excludes is a programme tier in the *grammar*: a front +door a consumer does read is one issue read under that consumer's own +contract, and no tracker acquires a duty by being listed on it. The surrounding *practice* — tracker vs period-plan genres, session ledgers, succession, revision-log comment discipline — is maintained in the org's `qe` skills, which cite this QEP as the authority on the unit's structure. @@ -416,10 +432,15 @@ and the one consumer: lint/conform pass may verify mechanically: stamp form present and unique, no sequence tokens in child titles or milestone names, no checkbox work lists, at most one `Next:` line and only in the stamp section, phases - contiguous. The skills currently cite the dashboard's tracker contract, - which permits body constructs this QEP forbids; until that contract's - planned handover to this QEP, this QEP is authoritative for tracker - structure wherever the two disagree. + contiguous. The skills currently cite the dashboard's tracker contract + (`docs/contracts/tracker.md` in QuantEcon/status-projects), which permits + body constructs this QEP forbids. This QEP is authoritative for tracker + structure wherever the two disagree. The contract's handover — it stops + claiming it will become this QEP, keeps only what the collector parses, and + this sentence is then struck — is QuantEcon/status-projects#114, blocked on + this QEP's acceptance: the item that discharges the obligation, as QEP-1's + rule that a QEP touching another standard carries the amendment itself + requires. 5. **The projects dashboard** treats the tracker's list order as the published child order (its tracker contract currently re-sorts children by issue number; that rule is amended to preserve list order — a one-row @@ -451,7 +472,8 @@ the milestones below. **Gates:** … (only the rationale lives here, once, in the project that waits: a phase-level gate is carried as a conjunction of native edges onto the first item of the waiting phase, a decision gate as an edge to the `Decision`, and a -whole-project gate as a tracker-to-tracker edge — see §4) +whole-project gate as a tracker-to-tracker edge — see §4; an ordering with no +edges behind it is not a gate and belongs under *Sequencing rationale*) **Sequencing rationale:** why this order — only what the list cannot say. From b9f31a1e2f3d704e916cf58fa347eb3eb3072712 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 9 Sep 2026 09:02:02 +1000 Subject: [PATCH 7/9] QEP-6: drop the precedence clause over the dashboard's tracker contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEPs are the source of truth and are independent of every repository: a downstream consumer adapts to a QEP, and the QEP owes it nothing back. The dashboard's tracker contract is a consumer that must conform, not a peer standard this QEP supersedes — so Adoption item 4 no longer asserts precedence over it, no longer prescribes its handover, and no longer names a tracking issue in another repository. How each consumer reaches conformance, and when, is that consumer's own business. Also drops the citation of QEP-1's cross-standard amendment rule, which was withdrawn from the v3 amendment in QuantEcon/qeps#23; the reasoning is recorded on QuantEcon/qeps#9. Co-Authored-By: Claude Opus 5 (1M context) --- qeps/qep-0006-project-trackers.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index 28a42ba..db22f2d 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -434,13 +434,9 @@ and the one consumer: lists, at most one `Next:` line and only in the stamp section, phases contiguous. The skills currently cite the dashboard's tracker contract (`docs/contracts/tracker.md` in QuantEcon/status-projects), which permits - body constructs this QEP forbids. This QEP is authoritative for tracker - structure wherever the two disagree. The contract's handover — it stops - claiming it will become this QEP, keeps only what the collector parses, and - this sentence is then struck — is QuantEcon/status-projects#114, blocked on - this QEP's acceptance: the item that discharges the obligation, as QEP-1's - rule that a QEP touching another standard carries the amendment itself - requires. + body constructs this QEP forbids; the skills conform to this QEP. How each + consumer reaches conformance, and on what schedule, is that consumer's own + business. 5. **The projects dashboard** treats the tracker's list order as the published child order (its tracker contract currently re-sorts children by issue number; that rule is amended to preserve list order — a one-row From 16bd8101d2c1a97b5aab9a11c73bf9f3e6e2d6d0 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 9 Sep 2026 09:46:35 +1000 Subject: [PATCH 8/9] QEP-6: drop Adoption item 5; the dashboard's conformance is not this QEP's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Item 5 prescribed an edit to another repository's contract and code — "a one-row contract change plus a sort removal, verified once against a deliberate re-order on a public tracker" — which is the same overreach removed from item 4 on 2026-09-09, in a quieter register. Nothing normative is lost. Section 3 already states the rule for every consumer: order is an operational property, not a published field; a consumer that publishes a child list preserves list order, and none may require, parse, or infer sequence from names. Item 4's closing sentence generalises the rest — how each consumer reaches conformance, and when, is that consumer's own business. The item was also stale. QuantEcon/status-projects ruled the same change itself on 2026-09-07 in their issue 19: the contract row now reads "list order, exactly as subIssues returns it", and the collector's sort is gone rather than rewritten. The consuming repo got there on its own reasoning, without the QEP obliging it. Co-Authored-By: Claude Opus 5 (1M context) --- qeps/qep-0006-project-trackers.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index db22f2d..3565e8e 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -437,12 +437,6 @@ and the one consumer: body constructs this QEP forbids; the skills conform to this QEP. How each consumer reaches conformance, and on what schedule, is that consumer's own business. -5. **The projects dashboard** treats the tracker's list order as the - published child order (its tracker contract currently re-sorts children by - issue number; that rule is amended to preserve list order — a one-row - contract change plus a sort removal, verified once against a deliberate - re-order on a public tracker). Order remains unpublished as a field; - dependencies continue to publish as counts. ## Appendix A (informative): tracker body skeleton From 59fa37403124acedac1375aa8e4d09a0f31a423b Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 9 Sep 2026 09:48:00 +1000 Subject: [PATCH 9/9] QEP-6: state the consumer rule as a principle, not a child-list special case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Section 3's consumer clause covered one behaviour — preserve list order when publishing a child list. It now states the principle it was a case of: a consumer that republishes a tracker presents the structure the tracker expresses, membership as recorded and in the position the tracker places it, rather than an arrangement of its own. The reason is stated with it, and it is the general one: a consumer that re-sorts or re-groups makes a conformant tracker and a non-conformant one publish identically, so the standard cannot be evaluated from what is published. That covers re-grouping as well as re-sorting, and applies to any consumer rather than to the one this QEP happened to know about. Names no repository and describes no repository's current behaviour, which is what Adoption item 5 did before it was removed. Co-Authored-By: Claude Opus 5 (1M context) --- qeps/qep-0006-project-trackers.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/qeps/qep-0006-project-trackers.md b/qeps/qep-0006-project-trackers.md index 3565e8e..00477b1 100644 --- a/qeps/qep-0006-project-trackers.md +++ b/qeps/qep-0006-project-trackers.md @@ -167,10 +167,14 @@ never encoded in issue titles or milestone names** (no `W3 —`, no `Phase 2:` prefixes, no `stage{n}` tokens). Issue numbers are the stable handles; titles are pure descriptions. -Order is an **operational property, not a published field** of the unit: a -consumer that publishes a child list should preserve list order (order is an +Order is an **operational property, not a published field** of the unit. A +consumer that republishes a tracker should present the structure the tracker +expresses — membership as recorded, in the position the tracker places it — +rather than an arrangement of its own. A consumer that re-sorts or re-groups +makes a conformant tracker and a non-conformant one publish identically, and +the standard cannot then be evaluated from what is published. Order is an attribute of the membership edge and is observable exactly where membership -is), but no consumer may require, parse, or infer sequence from names. +is; no consumer may require, parse, or infer sequence from names. ### 4. Constraints are dependencies