Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

QEP Purpose and Process

QEP-1: QEP Purpose and Process

QEP1
TitleQEP Purpose and Process
Authormmcky
StatusAccepted
Typeprocess
Version1
Created2026-06-16
DiscussionQuantEcon/meta#325

Summary

A QuantEcon Enhancement Proposal (QEP) is a short, durable document that records a decision affecting more than one QuantEcon repository, or that changes how the team works. This QEP defines what a QEP is, when one is needed, where QEPs live, how a proposal moves from draft to decision, and how an accepted QEP is maintained over time. It is deliberately lightweight: the aim is a ten-minute read, a clear deadline, and a clean close — not governance for its own sake. As the first proposal, this document is also a worked example of the template and the in-place versioning it describes.

Motivation

Decisions that touch every repository, or change how the whole team works — label standardisation, style-guide rules, tooling standards, editorial conventions, licensing — currently happen in ad-hoc issues and email threads scattered across repos. Once closed, they are hard to find again, and there is no consistent bar for what counts as “decided”.

A lightweight enhancement-proposal process gives us:

Many of these decisions are not one-shot: a label set, a style guide, or this process itself is a standard that keeps evolving in small steps. The current state is the point, so a QEP must also be maintainable in place — adding a label should not mean retiring the whole document and chasing the live standard across a chain of superseding proposals.

This mirrors the role PEPs play for Python and MEPs for MyST, but is scoped more broadly: QEPs cover governance and editorial decisions as well as software, and the process is kept far lighter than PEPs (no sponsors, delegates, or editor role).

Proposal

When a QEP is needed

Open a QEP when a decision crosses repositories or changes how the whole team works. Typical examples: a standard label set, a shared style or editorial rule, a cross-repo tooling or CI convention, a licensing choice, or a change to a team-wide workflow.

When a QEP is not needed

Everyday work never needs a QEP: bugs, features, and lecture content in a single repo, or anything scoped to one repository’s own lifecycle. If you are unsure whether something needs a QEP, it does not. The default for normal work is unchanged.

Where QEPs live

QEPs live in the QuantEcon/qeps repository, one Markdown file per proposal under qeps/, named qep-XXXX-short-slug.md with a zero-padded four-digit number. The repository renders to a browsable index via GitHub Pages. Discussion of an individual proposal happens on its PR (and optionally a preceding discussion issue in this repo), keeping proposal traffic out of the general issue trackers.

Lifecycle and statuses

A QEP carries one status:

StatusMeaning
DraftUnder discussion on an open PR.
AcceptedAgreed; the decision is now in effect.
RejectedConsidered and declined (the record is kept, with one reason).
WithdrawnPulled by the author before a decision.
SupersededReplaced by a later QEP (link it).

Accepted, Rejected, and Withdrawn QEPs are all merged, so the record is durable; only abandoned or spam drafts are closed without merging. There is no separate Final state — Accepted is final. status records only whether a QEP was agreed and is independent of a QEP’s version; Superseded marks a wholesale replacement by a later QEP (below), not the routine in-place evolution of a living standard.

How a QEP is decided

  1. (Optional) Float the idea. Open a QEP discussion issue to socialise it and confirm it warrants a QEP.

  2. Draft. Open a PR adding qeps/qep-XXXX-slug.md from the template (with Status: Draft and a discussion link) and a matching row in the README index.

  3. Set a deadline. The author announces the PR and sets a comment window — normally one to two weeks — recording the decision deadline in the PR description.

  4. Decide. At the deadline, the Core Maintainers decide by lazy consensus: objections are raised as PR comments, and no sustained objection means the QEP is Accepted. If there is no consensus, the lead (jstac) decides or defers.

  5. Record. On acceptance, set Status: Accepted — in the frontmatter, the header table, and the README index row — confirm the number, and merge. A newly accepted QEP carries no version: it is implicitly v0 until first amended.

Amending an accepted QEP

An accepted QEP is not frozen. A QEP that sets an ongoing rule — a label schema, a style guide, editorial conventions — is a living standard: its current state is the point, and it evolves in small, frequent steps. Two paths keep that change orderly:

Squash-merge only. Each amendment lands as a single commit, so a QEP’s history reads as one line per change. This is a repository setting, not a convention to remember.

Commit subjects carry the substantive/editorial distinction (below): a substantive amendment uses QEP-N vM: <summary> (e.g. QEP-2 v1: add release-blocker label); an editorial fix uses QEP-N: <summary>. The vM: token in the log is itself the substantive-milestone marker.

Versioning: version and its git anchor

A QEP gains a version the first time it is substantively changed after acceptance:

versionMeaning
absentImplicitly v0 — as originally accepted, never substantively changed. Many QEPs (a one-off decision) stay here forever.
1, 2, …The current substantive revision. The first substantive amendment introduces version: 1; each later substantive change climbs to 2, 3, …

From v1 onward a sibling version-hash field carries the short commit hash that anchors the revision to git history:

version: 1
version-hash: a1b2c3d  # stamped by CI; do not edit

version is a plain number; the commit hash lives in the separate version-hash field — a real key, so any YAML parser keeps it. The hash is stamped automatically at merge — a commit cannot contain its own hash, so a post-merge step (see Automation) writes it; never hand-write it. Tooling that pins a standard (for example a labels-sync command) reads version and verifies against version-hash. A per-QEP version is the right anchor because a git tag tags the whole repository, not one QEP’s revision.

Substantive vs editorial decides whether the number moves:

One-line rule: editorial = no change to normative content; substantive = any change to normative content. This keeps version numbers meaningful — not inflated by typos — while every change stays precisely pinned by the hash and visible in git. The call sits with author and reviewer; CI does not classify it.

History and publication

The change record is git itself, surfaced rather than duplicated into a hand-maintained changelog (which would drift and clutter the document):

Type and version are surfaced two ways:

Automation

Two mechanical steps are enforced by CI rather than left to memory:

The author-side judgement — substantive vs editorial, bumping version, the commit subject — is documented in AGENTS.md; CI enforces the mechanical steps that a maintainer merging through the GitHub UI would otherwise have to remember.

Numbering

Numbers are assigned sequentially. An author may propose the next free number when opening the PR; it is confirmed (and adjusted if two proposals collide) at merge. Numbers are written unpadded in text (QEP-1, QEP-2, …); only the filename zero-pads them to four digits (qep-0001-…). This QEP is QEP-1.

Roles

No sponsor, delegate, or editor role is introduced; the process is intended to stay as light as the decisions it records.

Format

Each QEP is a Markdown file with YAML frontmatter (qep, title, author, status, type, created, discussion — plus version and its CI-stamped version-hash, which sit just after type once the QEP is first amended) followed by the sections in qeps/template.md: Summary, Motivation, Proposal, Alternatives considered, Rollout. The type field describes the kind of content the QEP carries:

A policy is a normative rule — 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 only records rationale.

Alternatives considered

Rollout

  1. (v0) Establish the process. Merge this QEP to set the process; re-record the label-set decision (QuantEcon/meta#324) as a standard-type QEP; add a short “How we make cross-repo decisions” entry to the team manual pointing at QuantEcon/qeps, and have Core Maintainers watch the repository so proposal deadlines are seen.

  2. (v1) Adopt in-place versioning. Add the version field and its version-hash anchor, the substantive/editorial rule, the amend-in-place + squash-merge + commit-subject conventions, the standard/process/informational type taxonomy, and git (surfaced on the site) as the change record. Supporting changes: update qeps/template.md; add Type/Version columns to the README index; add the post-merge stamp + README-sync action and the pull-request check; document the author-side steps in AGENTS.md; and set the repository to squash-merge only. Adopting this mechanism is QEP-1’s own first substantive amendment, so QEP-1 becomes v1.