chore: migrate off the planning/ convention - #89
Merged
Conversation
Replaces `planning/` + `architecture/` with `CONTEXT.md` + `docs/adr/`, matching `modern-python/.github`, `modern-di` and the `modern-di-*` integrations (modern-python/.github#67). What `planning/` held, and where it went: - `decisions/` (13 files) -> `docs/adr/0001..0013`, renumbered sequentially by date, frontmatter stripped (no chain was lost: every record carried `supersedes: null` / `superseded_by: null`, and none was superseded). Cross-references between them, and from `planning/changes/` and `architecture/`, are rewritten to the new `NNNN-slug.md` names or inlined as prose; each ADR now closes with a bolded `**Revisit trigger:**`. 0005 keeps its resolution as a `**Resolved.**` amendment above the trigger. 0001 healthcheck-start-period-retries-passthrough, 0002 zero-dependency-core, 0003 reject-namespace-network-keys, 0004 stop-lifecycle-keys-inert, 0005 sysctls-pod-level, 0006 reject-parse-dont-validate, 0007 keep-graph-query-as-validator, 0008 reject-structural-key-registry, 0009 docker-rejection-parity, 0010 reject-strict-schema-validator-unification, 0011 list-of-str-refusals, 0012 negative-numeric-values-deferred-to-runtime, 0013 volumes-stays-hand-rolled. - `releases/` (15 files) -> deleted. Each is byte-identical to the published GitHub Release body (spot-checked 0.1.0, 0.3.0, 0.4.1, 0.4.2 with `gh release view`; 0.1.0 differs only by the `# title` line GitHub renders separately). The Releases are the record. - `changes/` (57 files), `audits/` (3), `_templates/`, `index.py`, `.convention-version` -> deleted. Git history is the record for how the code got here; nothing in them was a claim the code does not already make. - `deferred.md` -> three self-contained issue bodies, each with its revisit trigger, drafted outside the repo. Not opened. Every claim re-measured against the current gate: the Windows drive-letter bind and both non-target `depends_on` false greens still reproduce. Non-goals listed in change files were left as scope fences, not promoted to issues. `architecture/` (3 files) is dropped. `supported-subset.md` is prose about mechanism that the module docstrings, the key registries and the conformance harness already carry; `README.md` was the promotion rule for a convention that no longer exists. One claim in it was enforceable and unenforced — that every value grammar in `values.py` is anchored with `\Z`, never `$`, since Python's `$` also matches before a trailing newline and would accept a YAML block-scalar value `docker compose config` refuses. It is now `test_every_value_grammar_in_values_py_ends_at_the_true_end_of_string`, which introspects the module rather than listing grammars, so one added later inherits the guarantee. Verified: re-anchoring `_STRICT_INT_STRING` with `$` turns it red; adding a new `$`-anchored grammar turns it red and turns *nothing else* red (the pre-existing hand-listed newline test only covers the three grammars it names); restoring turns it green; and two benign narrowings (dropping the `µs` unit from `_DURATION`, dropping the leading `+` from `_STRICT_INT_STRING`) leave it green. `CONTEXT.md` is seeded from `architecture/glossary.md`, audited down from its seven terms to seven different ones: `service-key spec` folded into `service-key registry`, `store kind`/`store registry` folded into `store`, and `closure` and `rule one / rule two` added — both are load-bearing project nouns used bare in code comments and tests. Every `_Avoid_` line was dropped after `grep -rin` over `*.py`/`*.md` (excluding the deleted trees and `.venv`). Of the glossary's seventeen rejected synonyms, thirteen were distinctive enough to grep for: `plugin`, `dispatch table`, `special key`, `secret type`, `store type`, `store list`, `kinds table` and `placeholder` appear nowhere at all, and `handler`, `lookup`, `bespoke`, `backend` and `flag value` appear only in unrelated senses (a test fixture network literally named `backend`, "lookup table" used pejoratively inside an ADR). The remaining four — `rule`, `map`, `arg`, `variable` — are ordinary English this codebase uses constantly and were never competing spellings for the terms that rejected them. Nothing is in competing use, so no term earned an `_Avoid_` line and the audit forced no source edits. `AGENTS.md` gains `## Workflow` and `### Where a fact goes`, loses the planning lanes and the `architecture/` promotion rule, and then had the admission check applied to itself: the zero-dependency, module-level-imports, argument-annotation and 100%-coverage lines restated `pyproject.toml`, ruff's `select = ["ALL"]` and justfile comments, and are gone. The `never run bare ruff check` warning stays — `[tool.ruff]` sets `fix = true` and `unsafe-fixes = true`, which the justfile does not say. `justfile` loses `index` and `check-planning` and the `planning/index.py --check` line in `lint-ci`. `.github/workflows/_checks.yml` gains a `links` job running lychee `--offline` over `**/*.md`, so a relative link a diff breaks now fails CI. `.github/workflows/release.yml` **retires the mandatory-curated-release-notes policy.** It read `planning/releases/${GITHUB_REF_NAME}.md` both as a hard gate for stable tags and as the Release body source; deleting `planning/` without this would break every future stable release. The gate step is removed, `Resolve release metadata` is reduced to the prerelease flag, and the publish step takes `generate_release_notes: true` — same shape as `modern-di` (#449). A release wanting prose is edited after the fact with `gh release edit`. `docs/agents/domain.md` is deliberately not authored: `origin/main` has no `docs/` tree and `AGENTS.md` had no "Agent skills" section to link it from, so writing it alone would be a half-rollout of a separate convention. Verification: `just lint-ci` clean, `just test-ci` 1404 passed at 100% coverage, `just test-conformance` 862 passed with the same three catalogued over-rejections as before, and lychee `--offline` reports 0 errors (it reported 4 before — all four dangling links lived inside `planning/`).
21 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates
compose2podoff theplanning/convention and onto PR-body-as-spec, withCONTEXT.mdowning the vocabulary anddocs/adr/holding rejected alternatives. Part of modern-python/.github#67.Replaces
planning/+architecture/withCONTEXT.md+docs/adr/, matchingmodern-python/.github,modern-diand themodern-di-*integrations(modern-python/.github#67).
What
planning/held, and where it went:decisions/(13 files) ->docs/adr/0001..0013, renumbered sequentially bydate, frontmatter stripped (no chain was lost: every record carried
supersedes: null/superseded_by: null, and none was superseded).Cross-references between them, and from
planning/changes/andarchitecture/, are rewritten to the newNNNN-slug.mdnames or inlined asprose; each ADR now closes with a bolded
**Revisit trigger:**. 0005 keepsits resolution as a
**Resolved.**amendment above the trigger.0001 healthcheck-start-period-retries-passthrough, 0002 zero-dependency-core,
0003 reject-namespace-network-keys, 0004 stop-lifecycle-keys-inert,
0005 sysctls-pod-level, 0006 reject-parse-dont-validate,
0007 keep-graph-query-as-validator, 0008 reject-structural-key-registry,
0009 docker-rejection-parity, 0010 reject-strict-schema-validator-unification,
0011 list-of-str-refusals, 0012 negative-numeric-values-deferred-to-runtime,
0013 volumes-stays-hand-rolled.
releases/(15 files) -> deleted. Each is byte-identical to the publishedGitHub Release body (spot-checked 0.1.0, 0.3.0, 0.4.1, 0.4.2 with
gh release view; 0.1.0 differs only by the# titleline GitHub rendersseparately). The Releases are the record.
changes/(57 files),audits/(3),_templates/,index.py,.convention-version-> deleted. Git history is the record for how the codegot here; nothing in them was a claim the code does not already make.
deferred.md-> three self-contained issue bodies, each with its revisittrigger, drafted outside the repo. Not opened. Every claim re-measured against
the current gate: the Windows drive-letter bind and both non-target
depends_onfalse greens still reproduce. Non-goals listed in change fileswere left as scope fences, not promoted to issues.
architecture/(3 files) is dropped.supported-subset.mdis prose aboutmechanism that the module docstrings, the key registries and the conformance
harness already carry;
README.mdwas the promotion rule for a convention thatno longer exists. One claim in it was enforceable and unenforced — that every
value grammar in
values.pyis anchored with\Z, never$, since Python's$also matches before a trailing newline and would accept a YAML block-scalarvalue
docker compose configrefuses. It is nowtest_every_value_grammar_in_values_py_ends_at_the_true_end_of_string, whichintrospects the module rather than listing grammars, so one added later inherits
the guarantee. Verified: re-anchoring
_STRICT_INT_STRINGwith$turns it red;adding a new
$-anchored grammar turns it red and turns nothing else red(the pre-existing hand-listed newline test only covers the three grammars it
names); restoring turns it green; and two benign narrowings (dropping the
µsunit from
_DURATION, dropping the leading+from_STRICT_INT_STRING) leaveit green.
CONTEXT.mdis seeded fromarchitecture/glossary.md, audited down from itsseven terms to seven different ones:
service-key specfolded intoservice-key registry,store kind/store registryfolded intostore, andclosureandrule one / rule twoadded — both are load-bearing project nounsused bare in code comments and tests. Every
_Avoid_line was dropped aftergrep -rinover*.py/*.md(excluding the deleted trees and.venv). Of theglossary's seventeen rejected synonyms, thirteen were distinctive enough to grep
for:
plugin,dispatch table,special key,secret type,store type,store list,kinds tableandplaceholderappear nowhere at all, andhandler,lookup,bespoke,backendandflag valueappear only inunrelated senses (a test fixture network literally named
backend, "lookuptable" used pejoratively inside an ADR). The remaining four —
rule,map,arg,variable— are ordinary English this codebase uses constantly and werenever competing spellings for the terms that rejected them. Nothing is in
competing use, so no term earned an
_Avoid_line and the audit forced nosource edits.
AGENTS.mdgains## Workflowand### Where a fact goes, loses the planninglanes and the
architecture/promotion rule, and then had the admission checkapplied to itself: the zero-dependency, module-level-imports, argument-annotation
and 100%-coverage lines restated
pyproject.toml, ruff'sselect = ["ALL"]andjustfile comments, and are gone. The
never run bare ruff checkwarning stays —[tool.ruff]setsfix = trueandunsafe-fixes = true, which the justfiledoes not say.
justfilelosesindexandcheck-planningand theplanning/index.py --checkline in
lint-ci..github/workflows/_checks.ymlgains alinksjob runninglychee
--offlineover**/*.md, so a relative link a diff breaks now fails CI..github/workflows/release.ymlretires the mandatory-curated-release-notespolicy. It read
planning/releases/${GITHUB_REF_NAME}.mdboth as a hard gatefor stable tags and as the Release body source; deleting
planning/withoutthis would break every future stable release. The gate step is removed,
Resolve release metadatais reduced to the prerelease flag, and the publishstep takes
generate_release_notes: true— same shape asmodern-di(#449). Arelease wanting prose is edited after the fact with
gh release edit.docs/agents/domain.mdis deliberately not authored:origin/mainhas nodocs/tree andAGENTS.mdhad no "Agent skills" section to link it from, sowriting it alone would be a half-rollout of a separate convention.
Verification:
just lint-ciclean,just test-ci1404 passed at 100% coverage,just test-conformance862 passed with the same three cataloguedover-rejections as before, and lychee
--offlinereports 0 errors (it reported4 before — all four dangling links lived inside
planning/).Verification
just lint-ci— clean (eof-fixer, ruff format, ruff check, ty).lychee --offline --no-progress '**/*.md') — 0 errors.planning/,architecture/,check-planning,check-linksorconvention-versionreference remains.Note
release.ymlpreviously readplanning/releases/<tag>.md, both as a hard gate for stable tags and as the Release body source; deletingplanning/without changing it would have broken the next stable release. It now uses GitHub's generated notes, matchingmodern-dipost-modern-python/modern-di#449. This retires the mandatory-curated-notes policy.