Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/planning.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Planning

on:
pull_request:
push:
branches: [main]

jobs:
check-planning:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: "3.11"
- name: Validate planning bundles
run: uv run python planning/index.py --check
17 changes: 17 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,20 @@ GitHub occasionally type-checks a **stale `refs/pull/<n>/merge`** after a push,
PR can show an old lint/test failure that no longer matches the branch. Confirm by
running the failing check locally at the branch HEAD (with the pinned tool version);
if it's clean, push a fresh commit to force GitHub to recompute the merge ref.

## Workflow

Planning follows the convention in [`planning/README.md`](planning/README.md) —
its **Quick path** is authoritative. Pick a lane (Full = `design.md` + `plan.md`,
Lightweight = `change.md`, Tiny = conventional commit), create a bundle under
`planning/changes/YYYY-MM-DD.NN-<slug>/` from `planning/_templates/`, and run
`just check-planning` before pushing. The applied convention version is in
`planning/.convention-version`; update it via the canonical repo's `APPLY.md`.

## Architecture

`architecture/` (repo root) is the living truth about what this repo does now —
one file per capability plus `glossary.md`, no frontmatter, authored lazily.
**When a change alters a capability's behavior, update the matching
`architecture/<capability>.md` in the same PR.** The change bundle in
`planning/changes/` stays as the *why*.
12 changes: 12 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
default: check-planning test

index:
uv run python planning/index.py

check-planning:
uv run python planning/index.py --check

lint-ci: check-planning

test:
uv run pytest
13 changes: 13 additions & 0 deletions architecture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Architecture — the truth home

This directory holds the **living truth** about what `modern-python/.github` does
*now*: one file per capability, plus a single `glossary.md` (the ubiquitous
language). These files carry **no frontmatter** and are dated by git.

**Promotion rule:** when a change alters a capability's behavior, hand-edit the
matching `architecture/<capability>.md` in the **same PR** as the code — the edit
rides in the same diff and is reviewed with it. The change bundle in
`planning/changes/` stays as the *why*; this directory stays *true*.

Capability files and `glossary.md` are authored **lazily** — each appears when the
first capability or term is worth pinning down.
1 change: 0 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ repo_name: modern-python
# Repo-internal docs that must not be published to the public site.
exclude_docs: |
DEPLOYMENT.md
superpowers/

theme:
name: material
Expand Down
1 change: 1 addition & 0 deletions planning/.convention-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.1.1
145 changes: 145 additions & 0 deletions planning/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Planning — modern-python/.github

This repo's planning home, following the portable two-axis convention from
[`lesnik512/planning-convention`](https://github.com/lesnik512/planning-convention)
(applied version in [`.convention-version`](.convention-version)). `architecture/`
(repo root) holds the living truth about what the system does now; the bundles in
[`changes/`](changes/) record how it got there. To update the convention itself,
re-run that repo's `APPLY.md` flow.

## Quick path (start here)

> The fast lane for making a change. The full reference is in
> [Conventions](#conventions) below — read it only when this isn't enough.

**1. Choose a lane — first matching rule wins:**

1. Any of: needs design judgment · new file/module · public-API change ·
cross-cutting or multi-file · non-trivial test design → **Full**
(`design.md` + `plan.md`)
2. Purely mechanical: typo · dep bump · linter/formatter/CI tweak ·
mechanical rename · single-line config → **Tiny** (no bundle, conventional
commit)
3. Small-but-real, none of the above: ≲30 LOC net · ≤2 files · no new file ·
no public-API change · one straightforward test → **Lightweight**
(`change.md`)

Ambiguous between two? Take the heavier. A `change.md` that outgrows its lane
splits into `design.md` + `plan.md`.

**2. Create the bundle** (Full / Lightweight only):
`planning/changes/YYYY-MM-DD.NN-<slug>/`, where `.NN` is a zero-padded
intra-day counter. Copy the matching template from
[`_templates/`](_templates/).

**3. Ship in the implementing PR:** hand-edit the affected
`architecture/<capability>.md`, finalize the bundle's `summary:` to the
realized result, and run `just check-planning` before pushing.

## Conventions

> This is the portable convention, sourced from the canonical repo
> [`lesnik512/planning-convention`](https://github.com/lesnik512/planning-convention)
> (applied version in [`.convention-version`](.convention-version)). To update
> it, run that repo's `APPLY.md` flow. The generated change index (`just index`)
> and the `## Other` pointers below are repo-local.

### Two axes, never mixed

- **`architecture/` (repo root) — the present.** One file per capability, plus
a single `glossary.md` (the ubiquitous language); living prose, updated in the
same PR that ships the change. The truth home.
- **`planning/changes/` — the past-and-pending.** One folder per change,
kept in place after ship.

A change **promotes** its conclusions into the affected
`architecture/<capability>.md` by hand **in the implementing PR, alongside the
code** — the edit rides in the same diff and is reviewed with it, never applied
as a separate post-merge step. That hand-edit is what keeps `architecture/`
true; the bundle stays in `changes/` as the *why*.

### Glossary

`architecture/glossary.md` is the project's **ubiquitous language** — one page
defining the domain terms that code, specs, and capability pages all share. Like
the capability files beside it, it is living prose with **no frontmatter**, dated
by git, and authored lazily: it appears when the first term is worth pinning down.

Each entry is a term, a one-or-two-sentence definition of what it *is* (not what
it does), and an optional `_Avoid_:` line naming the synonyms to reject:

```md
**Timer**:
A scheduled future delivery, identified by a timer id.
_Avoid_: job, task, alarm
```

Keep it a glossary, not a spec — no implementation detail. A change that
introduces or sharpens a term updates `glossary.md` in the same PR, the same way
a behavior change promotes into a capability file.

### Change bundles

A change is a folder `changes/YYYY-MM-DD.NN-<slug>/`:

- `YYYY-MM-DD` — proposal date; `.NN` — zero-padded intra-day counter
(`.01`, `.02`, …) that breaks same-date ties so the timeline sorts stably.
- `<slug>` — kebab-case description, not a story ID.

`summary` is written when the change is created (the intent one-liner) and
**finalized at ship** to state the realized result — set in the implementing
PR, alongside the code and the `architecture/` promotion. No post-merge
bookkeeping, no folder move. `date` and `slug` are never written — they are
read from the bundle's directory name.

### Three lanes

| Lane | Artifacts | Use when |
|------|-----------|----------|
| **Full** | `design.md` + `plan.md` | design judgment; new file/module; public-API change; cross-cutting/multi-file; non-trivial test design |
| **Lightweight** | `change.md` | small-but-real: ≲30 LOC net, ≤2 files, no new file, no public-API change, single straightforward test |
| **Tiny** | none — conventional commit | typo, dep bump, linter/formatter/CI tweak, mechanical rename, single-line config |

Heavier lane wins on ambiguity. A `change.md` that outgrows its lane splits
into `design.md` + `plan.md`.

### Artifacts at a glance

- **`design.md`** — the spec: the *thinking* (why, design, trade-offs, scope).
- **`plan.md`** — the plan: the *sequencing* (the executor's task checklist).
- **`change.md`** — both, condensed, for the lightweight lane.
- **`releases/<semver>.md`** — per-release user-facing notes.
- **`audits/<date>-<slug>.md`** — findings from a code/docs/bug-hunt sweep;
spawns fix changes.
- **`retros/<date>-<slug>.md`** — what we learned after a body of work.
- **`deferred.md`** — real-but-unscheduled items, each with a revisit trigger.
- **`decisions/<YYYY-MM-DD>-<slug>.md`** — one file per design decision taken
(especially options *rejected*), each with a revisit trigger; listed by
`just index`.

Templates live in [`_templates/`](_templates/).

### Frontmatter

`date` and `slug` are **derived from the directory / file name** — never
repeated in frontmatter. So:

- `design.md` / `change.md`: `summary` (single line) only.
- `plan.md`: **no frontmatter** — its identity is the bundle directory.
- `decisions/*.md`: `status` (accepted|superseded), `summary`, and optional
`supersedes` / `superseded_by`.
- Files in `architecture/` carry **no** frontmatter — living prose, dated by git.

**`summary`** is one line: written at creation as the intent, then **finalized
at ship** to state the realized result — what shipped and its effect. It is the
only field the index renders.

## Index

Run `just index` to print the change/decision listing — a query over the files,
never a committed artifact.

## Other

- [`launch-playbook.md`](launch-playbook.md) — internal Phase 4 launch asset (not published).
- [`deferred.md`](deferred.md) — real-but-unscheduled items, each with a revisit trigger.
32 changes: 32 additions & 0 deletions planning/_templates/change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
summary: One line — shown in the generated index. Written at creation; finalize at ship to state the realized result.
---

# Change: One-line capitalized title

**Lane:** lightweight — ≲30 LOC net, ≤2 files, no new file, no public-API
change, a single straightforward test. If it outgrows this, split into
`design.md` + `plan.md`.

## Goal

One or two sentences: what changes and why.

## Approach

The shape of the change in brief — enough that a reviewer sees the design
without a full spec. Link the truth home (`architecture/<capability>.md`) if a
capability contract moves.

## Files

- `path/to/file.py` — what changes
- `tests/test_x.py` — test added / updated

## Verification

- [ ] Failing test first — command + expected error.
- [ ] Apply the change.
- [ ] Test passes — command.
- [ ] `just test` — full suite green.
- [ ] `just lint` — clean.
23 changes: 23 additions & 0 deletions planning/_templates/decision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
status: accepted # accepted | superseded
summary: One line — shown in `just index`.
supersedes: null
superseded_by: null
---

# One-line capitalized title

**Decision:** What was decided, in a sentence.

## Context

Why this came up; the options that were on the table.

## Decision & rationale

The call and why — including why the alternatives were rejected. Enough that a
future explorer doesn't re-litigate it.

## Revisit trigger

The concrete signal that should reopen this decision.
49 changes: 49 additions & 0 deletions planning/_templates/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
summary: One line — shown in the generated index. Written at creation; finalize at ship to state the realized result.
---

# Design: One-line capitalized title

## Summary

One paragraph. What changes, at the level a reader needs to decide if this
spec is worth reading in full.

## Motivation

Why now. What is broken or missing. Concrete observations / numbers, not
abstract complaints. Link to memory entries or earlier specs when relevant.

## Non-goals

What is deliberately out of scope and (when nontrivial) why. Each item is
a sentence; one line each.

## Design

### 1. <First piece>

What changes, in enough detail that a reader who has not seen the codebase
can follow. Code samples / diagrams welcome.

### 2. <Second piece>

...

## Operations

Out-of-repo steps (DNS, infra, external account changes). Omit if none.

## Out of scope

Already covered above under Non-goals if appropriate. Repeat-list of
explicitly-excluded follow-ups belongs here when the list is long.

## Testing

How we know it landed correctly. New pytest? Smoke check on live URL?
Lint pass? Be specific.

## Risk

What could go wrong, ranked by likelihood × impact. Mitigations.
15 changes: 15 additions & 0 deletions planning/_templates/glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Glossary

The project's ubiquitous language — the domain terms that code, specs, and
capability pages share. Living prose, no frontmatter, dated by git. Each entry is
a term, what it *is* (not what it does), and the synonyms to avoid. No
implementation detail; this is a glossary, not a spec.

**Term**:
A one-or-two-sentence definition of what it is.
_Avoid_: rejected-synonym, another-one

**Another term**:
Define what it is, tightly. Group related terms under `##` subheadings when
natural clusters emerge; a flat list is fine when they don't.
_Avoid_: …
46 changes: 46 additions & 0 deletions planning/_templates/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# <slug> — implementation plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use
> superpowers:subagent-driven-development (recommended) or
> superpowers:executing-plans to implement this plan task-by-task. Steps
> use checkbox (`- [ ]`) syntax for tracking.

**Goal:** One sentence — what shipping this plan achieves. No design
rationale; link to the spec for that.

**Spec:** [`design.md`](./design.md)

**Branch:** `feat/my-change` (or `fix/`, `chore/`, etc.)

**Commit strategy:** Per-task commits / single commit / squash on merge.
Whichever fits.

---

### Task 1: <imperative description>

**Files:**
- Modify: `path/to/file.py`
- Create: `path/to/new.py`

One sentence on what this task accomplishes. No deeper reasoning — that's
in the spec.

- [ ] **Step 1: <action>**

Run / edit / verify command. Expected output.

- [ ] **Step 2: <action>**

...

- [ ] **Step 3: Commit**

```bash
git add path/to/file.py
git commit -m "<type>: <subject>"
```

---

### Task 2: ...
Loading