Updated after the discussion below. The original framing proposed a new Living/Active status; the converged design is simpler — it adds no new status. A QEP starts unversioned (implicitly v0) and gains a version the first time it is substantively changed after merge. type is repurposed to describe the kind of content a QEP carries.
The problem
QEP-1's lifecycle (Draft → Accepted / Rejected / Withdrawn / Superseded) treats every QEP as a one-shot decision: Accepted, frozen, and any change means Superseded by a brand-new QEP. That's right for a decision ("we adopt X"). It's wrong for a living standard that changes in small, frequent steps — tweak a colour, add a label, clarify a rule. Superseding the whole document for a one-label change is disproportionate, and it scatters "the current standard" across QEP-0002 → 0007 → 0012 that a reader has to chase.
The label policy (QEP-0002), a future style guide, and QEP-1 itself are standards of this kind: the current state is the point, and they evolve continuously.
The idea: QEPs gain a version when they change; type describes content
We do not add a Living status, and living-ness is not restricted by type — any QEP can evolve. The mechanism is a version that appears the first time a QEP is substantively amended after merge:
| Field |
Answers |
Behaviour |
status |
Was this agreed? |
Universal and unchanged: Draft → Accepted / Rejected / Withdrawn / Superseded. |
type |
What kind of content is it? |
Descriptive content taxonomy — see below. Does not gate behaviour. |
version |
Which revision is current? |
Absent = implicitly v0 (as originally accepted, never substantively changed). The first substantive amendment introduces v1; further substantive changes climb v2, v3…. |
In practice many QEPs stay at v0 forever (a one-off decision, never revised); a living standard climbs through versions. This keeps PEP's spirit (PEP 1, 8, 13 are amended in place) without PEP's Active status — which also sidesteps the MEP collision, where Active means frozen for voting.
The type set
With immutability no longer carried by type, it describes the kind of content a QEP holds. We adopt PEP's minimal, proven taxonomy:
| Type |
Contains |
Examples |
standard |
a normative spec or rule you conform to |
label schema, style guide, editorial rules, licensing, metadata conventions |
process |
how the team or the QEP system operates |
the QEP process (QEP-1), review/release procedure, governance |
informational |
non-binding guidance, rationale, reference |
design notes, recommendations, recorded rationale |
A "policy" is a normative rule — i.e. a standard you conform to — so it is not a separate type. A one-off "decision" is a standard if it sets an ongoing rule, or informational if it is a recorded rationale.
The version field and its git anchor
The per-QEP version is the canonical pin. A git tag tags the whole repo, so it can't identify a single QEP's revision; the version field can. From v1 onward it carries a short commit hash as a YAML comment, anchoring it to git history:
The hash is stamped automatically at merge — a commit cannot contain its own hash, so a post-merge step writes it. Tooling that pins a standard (e.g. qe gh labels sync) reads version and verifies against the hash.
Two granularities: version (substantive) and hash (every change)
- Substantive change (a rule, a value, a label row, the machine-readable appendix) → introduce/bump
version; the hash moves too.
- Editorial change (typo, wording, formatting, link) → version unchanged; only the hash moves (at v0, the change is simply a git commit).
One-line rule: editorial = no change to normative content; substantive = any change to normative content. For QEP-0002 this is unambiguous — any change to the label table or the appendix block is substantive.
Version numbers therefore stay meaningful (not inflated by typos), while editorial fixes are still precisely pinned by the hash and visible in git.
How amendments are made
- Amend in place via PR under QEP-1's normal lazy-consensus. A substantive evolution of the same standard bumps the version; a different decision that replaces it is a new QEP that supersedes (so
Superseded keeps its meaning — wholesale rethink only).
- Squash-merge only. One amendment = one commit, so the document's history stays clean. A repo setting, not a convention to remember.
- Commit subjects: substantive →
QEP-N vM: <summary> (e.g. QEP-2 v1: add release-blocker label); editorial → QEP-N: <summary>. A vM: token in the log is itself the substantive-milestone marker.
Automation: stamping the hash and README (enforced by CI)
The hash stamp and README sync are mechanical and must always happen, so they live in CI, not in agent guidance:
- A post-merge GitHub Action fires on merge to
main, reads the merged short SHA, writes it into the version: N # <sha> comment, and updates the README Version column — committing with [skip ci] (or an auto-merge PR if main is protected). This runs regardless of who merged.
- AGENTS.md / CLAUDE.md document the author-side judgement (substantive vs editorial, bump
version, commit subject). This is documentation, not the enforcement — a maintainer merging via the GitHub UI never runs it, so it can't be relied on for the mechanical steps.
History: git, surfaced on the site — not a hand-maintained changelog
The change record is git itself, surfaced two ways rather than duplicated into a hand-maintained changelog (which would drift and clutter the document):
- On the rendered site — the QuantEcon theme's git-history feature (QuantEcon/quantecon-theme.mystmd#83) renders a "Last changed: ⟨date⟩" control that expands a dropdown of recent commits per page, with GitHub-linked hashes and a full-history link. The squash-commit subjects are the changelog entries.
- On GitHub — the file's full commit history / blame for anything beyond the recent window.
Publication: making type and version visible
- A coloured
type pill (always) and a version pill (once v1+) on the rendered QEP header — e.g. standard · v2. A v0 QEP shows only the type pill.
- The "Last changed" + commit dropdown from the theme feature above.
- The README index gains
Type and Version columns — Version shows – at v0, then v{N} — mirroring the pills at index level.
Worked example — adding one label to QEP-0002
Illustration only; QEP-0002 is currently v0. Suppose the team later wants a release-blocker label. Under today's frozen model that one-label change would require superseding QEP-0002 wholesale. Under this proposal it is one small, in-place change:
- Open a PR against QEP-0002 adding the row to the Type table and the machine-readable appendix, and introducing
version: 1.
- Review under QEP-1 lazy-consensus; squash-merge with subject
QEP-2 v1: add core label release-blocker. CI stamps the hash and updates the README.
- On publish, the pill reads
v1, the README Version column reads v1, and the theme's history dropdown shows the change with a GitHub link.
qe gh labels sync pins v1 and creates release-blocker additively on its next run.
One reviewed, fully-traceable diff — not a new QEP.
CI / enforcement
- Post-merge: stamp the
version: N # hash comment and sync the README Version column.
- On PR:
version, if present, only ever increments by one; README Type/Version ⇄ front-matter parity, alongside the existing CLAUDE.md/CI label-parity check.
- The substantive-vs-editorial call sits with author and reviewer; CI does not classify it.
Open questions from the original framing — now resolved
| Original question |
Resolution |
| What counts as substantive vs editorial? |
Editorial = no change to normative content → hash only. Substantive → version. |
v1/v2 snapshots + version dropdown — build now or defer? |
Rendering exists: theme git-history feature (#83) + the version pill. Pins are the simple v{N} + hash. |
Living vs Active naming |
Moot — there is no new status word. |
| Should QEP-1 itself be versioned? |
It is versionable like any QEP; it gains v1 on its first substantive amendment (adopting this mechanism may be that amendment). |
Proposed path
Amend QEP-1 to: add the version field (implicit v0, + git-hash anchor) and the version/hash granularity rule; repurpose type as the standard/process/informational content taxonomy; adopt amend-in-place + squash-merge + the commit-subject convention; and point history at git (theme feature + GitHub) rather than a hand-maintained changelog. Supporting changes: update qeps/template.md; add Type/Version columns to the README index; add the post-merge hash-stamp + README-sync Action; document the author-side steps in AGENTS.md; and set the repo to squash-merge only.
Links
Updated after the discussion below. The original framing proposed a new
Living/Activestatus; the converged design is simpler — it adds no new status. A QEP starts unversioned (implicitly v0) and gains aversionthe first time it is substantively changed after merge.typeis repurposed to describe the kind of content a QEP carries.The problem
QEP-1's lifecycle (
Draft → Accepted / Rejected / Withdrawn / Superseded) treats every QEP as a one-shot decision:Accepted, frozen, and any change meansSupersededby a brand-new QEP. That's right for a decision ("we adopt X"). It's wrong for a living standard that changes in small, frequent steps — tweak a colour, add a label, clarify a rule. Superseding the whole document for a one-label change is disproportionate, and it scatters "the current standard" across QEP-0002 → 0007 → 0012 that a reader has to chase.The label policy (QEP-0002), a future style guide, and QEP-1 itself are standards of this kind: the current state is the point, and they evolve continuously.
The idea: QEPs gain a version when they change;
typedescribes contentWe do not add a
Livingstatus, and living-ness is not restricted by type — any QEP can evolve. The mechanism is aversionthat appears the first time a QEP is substantively amended after merge:statusDraft → Accepted / Rejected / Withdrawn / Superseded.typeversionv1; further substantive changes climbv2, v3….In practice many QEPs stay at v0 forever (a one-off decision, never revised); a living standard climbs through versions. This keeps PEP's spirit (PEP 1, 8, 13 are amended in place) without PEP's
Activestatus — which also sidesteps the MEP collision, whereActivemeans frozen for voting.The
typesetWith immutability no longer carried by
type, it describes the kind of content a QEP holds. We adopt PEP's minimal, proven taxonomy:standardprocessinformationalA "policy" is a normative rule — i.e. a
standardyou conform to — so it is not a separate type. A one-off "decision" is astandardif it sets an ongoing rule, orinformationalif it is a recorded rationale.The
versionfield and its git anchorThe per-QEP
versionis the canonical pin. A git tag tags the whole repo, so it can't identify a single QEP's revision; theversionfield can. Fromv1onward it carries a short commit hash as a YAML comment, anchoring it to git history:The hash is stamped automatically at merge — a commit cannot contain its own hash, so a post-merge step writes it. Tooling that pins a standard (e.g.
qe gh labels sync) readsversionand verifies against the hash.Two granularities: version (substantive) and hash (every change)
version; the hash moves too.One-line rule: editorial = no change to normative content; substantive = any change to normative content. For QEP-0002 this is unambiguous — any change to the label table or the appendix block is substantive.
Version numbers therefore stay meaningful (not inflated by typos), while editorial fixes are still precisely pinned by the hash and visible in git.
How amendments are made
Supersededkeeps its meaning — wholesale rethink only).QEP-N vM: <summary>(e.g.QEP-2 v1: add release-blocker label); editorial →QEP-N: <summary>. AvM:token in the log is itself the substantive-milestone marker.Automation: stamping the hash and README (enforced by CI)
The hash stamp and README sync are mechanical and must always happen, so they live in CI, not in agent guidance:
main, reads the merged short SHA, writes it into theversion: N # <sha>comment, and updates the READMEVersioncolumn — committing with[skip ci](or an auto-merge PR ifmainis protected). This runs regardless of who merged.version, commit subject). This is documentation, not the enforcement — a maintainer merging via the GitHub UI never runs it, so it can't be relied on for the mechanical steps.History: git, surfaced on the site — not a hand-maintained changelog
The change record is git itself, surfaced two ways rather than duplicated into a hand-maintained changelog (which would drift and clutter the document):
Publication: making type and version visible
typepill (always) and aversionpill (oncev1+) on the rendered QEP header — e.g.standard·v2. A v0 QEP shows only the type pill.TypeandVersioncolumns —Versionshows–at v0, thenv{N}— mirroring the pills at index level.Worked example — adding one label to QEP-0002
Illustration only; QEP-0002 is currently v0. Suppose the team later wants a
release-blockerlabel. Under today's frozen model that one-label change would require superseding QEP-0002 wholesale. Under this proposal it is one small, in-place change:version: 1.QEP-2 v1: add core label release-blocker. CI stamps the hash and updates the README.v1, the READMEVersioncolumn readsv1, and the theme's history dropdown shows the change with a GitHub link.qe gh labels syncpinsv1and createsrelease-blockeradditively on its next run.One reviewed, fully-traceable diff — not a new QEP.
CI / enforcement
version: N # hashcomment and sync the READMEVersioncolumn.version, if present, only ever increments by one; READMEType/Version⇄ front-matter parity, alongside the existing CLAUDE.md/CI label-parity check.Open questions from the original framing — now resolved
v1/v2snapshots + version dropdown — build now or defer?v{N}+ hash.LivingvsActivenamingv1on its first substantive amendment (adopting this mechanism may be that amendment).Proposed path
Amend QEP-1 to: add the
versionfield (implicit v0, + git-hash anchor) and the version/hash granularity rule; repurposetypeas thestandard/process/informationalcontent taxonomy; adopt amend-in-place + squash-merge + the commit-subject convention; and point history at git (theme feature + GitHub) rather than a hand-maintained changelog. Supporting changes: updateqeps/template.md; addType/Versioncolumns to the README index; add the post-merge hash-stamp + README-sync Action; document the author-side steps in AGENTS.md; and set the repo to squash-merge only.Links
type.