Skip to content

chore: migrate off the planning/ convention - #89

Merged
lesnik512 merged 1 commit into
mainfrom
chore/migrate-off-planning
Sep 6, 2026
Merged

chore: migrate off the planning/ convention#89
lesnik512 merged 1 commit into
mainfrom
chore/migrate-off-planning

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Migrates compose2pod off the planning/ convention and onto PR-body-as-spec, with CONTEXT.md owning the vocabulary and docs/adr/ holding rejected alternatives. Part of modern-python/.github#67.

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/).

Verification

  • just lint-ci — clean (eof-fixer, ruff format, ruff check, ty).
  • Test suite green at the repo's 100% coverage gate.
  • Offline link gate (lychee --offline --no-progress '**/*.md') — 0 errors.
  • No planning/, architecture/, check-planning, check-links or convention-version reference remains.

Note

release.yml previously read planning/releases/<tag>.md, both as a hard gate for stable tags and as the Release body source; deleting planning/ without changing it would have broken the next stable release. It now uses GitHub's generated notes, matching modern-di post-modern-python/modern-di#449. This retires the mandatory-curated-notes policy.

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/`).
@lesnik512
lesnik512 merged commit ca0e7ce into main Sep 6, 2026
9 checks passed
@lesnik512
lesnik512 deleted the chore/migrate-off-planning branch September 6, 2026 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant