Adopt modern-di's lean planning convention - #6
Merged
Conversation
Location is status now: presence in decisions/ means accepted, and superseded_by is the only state worth recording. Also repoints the dangling planning/deferred.md reference in 2026-08-21-mutation-requires-membership.md at the deferred item that now owns that content, orphaned by the deferred.md -> deferred/ split.
Restore the login-401 reasoning to the domain-error decision, replace the coverage-omit enumeration with its criterion, and link the sibling deferred item by name. Six smaller fixes: one-line `check-links` doc comment, the full host-recipe list, an author-supplied test count in the PR template, the `deferred/` row in the admission table, both halves of the `service_debug` hazard, and a pointer to upstream's APPLY.md in deviation 5.
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.
Adopts
modern-di's lean planning convention: the spec for a change becomes thePR body,
planning/changes/andarchitecture/are deleted,deferred.mdbecomes an indexed directory, and a Markdown link checker joins the lint gate.
This body is the first written under the convention it introduces — there is no
change file, by design.
Why
A capability page had gone silently wrong.
architecture/chats.mddescribedchat_typeassa.Enum(native_enum=False, create_constraint=True), "not anative Postgres enum type", and argued that a native enum "would need
alembic-postgresql-enum... a dependency not worth buying to store twovalues." #4 reversed exactly that. The convention's promotion rule says the
implementing PR hand-edits the affected page; it did not happen, nothing
enforced it, and the page stayed wrong. A second stale claim turned up during
review:
architecture/messages.mdcited a# pragma: no coverthat existsnowhere in
app/.The pages were largely a second telling of
planning/decisions/. Twelvedecision files already owned the load-bearing calls while the pages re-narrated
them —
auth.md's 401-vs-PermissionDeniedError, anonymous prefixes andcookie-secure flag against
domain-error-vocabulary,anonymous-doc-and-metrics-pathsandexplicit-cookie-secure-flag;chats.md'supsert and 403-vs-404 against
upsert-via-duplicate-key-recoveryandmutation-requires-membership. The dependence ran one way:git grep architecture/ -- planning/decisions/returned nothing. The decision files neverneeded the pages.
The change file and the PR body were already duplicates. Every one of #1–#5
wrote a substantial body and linked a change file that largely restated it.
Design
Every fact gets one owner.
app/for anything readable from the module; a namedtest for an enforceable claim;
planning/decisions/for a rejected alternative;planning/deferred/for real work not scheduled. Prose about mechanism has nohome — there is no file to add a paragraph to.
planning/README.mdcarries theadmission check that decides where a given fact belongs.
An enforceable claim is now a test whose name is the claim, with a docstring
opening
INVARIANT:and a second paragraph naming what breaks it. Adopted as aconvention and applied to the claims rehomed here; the existing 109 tests are
not retrofitted and there is no census test.
Deleting the pages was preceded by an audit, not a
git rm. Every fact inthe five pages was classified against the admission check, and the ones with no
other home got one: the cookie-vs-bearer rationale and the login-401 reasoning
became decision records, the
service_debugcredential-leak hazard(
echo/echo_poollogpassword_hashon every registration) became a one-linecomment on the setting, the race-simulation and malformed-subject claims became
INVARIANT:docstrings, andglossary.md's seven terms became a## Vocabularysection in
CLAUDE.md. Facts derivable fromapp/were dropped deliberately.Six deviations from upstream convention 2.2.0 are recorded in
planning/README.md; five matchmodern-di's practice. The sixth is local: thisrepo has no
lint-cirecipe, solinks.pyruns as a step in the workflow'slintjob.Non-goals
nothing to publish, the source-link problem (relative links escaping
docs_dirfailmkdocs --strict, so source pointers become uncheckedabsolute URLs) disappears with them.
planning/releases/. Not a published package, no tags.tests/test_invariant_census.py. Real machinery for a repo this size.INVARIANT:shape.Verification
just test— 109 passed, 100% coverage.just test-migrations— 4 passed.No application logic changed, so green confirms the coverage-omit edit and
nothing else.
just lint— clean.just check-planning—planning: OK, now validatingdeferred/+decisions/including the revisit-trigger gate on all 10 deferred items.just check-links—links: OK. The checker was verified to actually fail ona missing target and on a link escaping the repo, so the pass means something.
just index— 10 deferred, 13 decisions, no## Changessection.Known consequence, accepted
#1–#5 have merged bodies containing repo-relative links into
planning/changes/*.md, which GitHub resolves against the default branch. Thoselinks 404 once this merges. The rationale itself survives in the PR bodies.
readme.mdnow points a reader at #1 for the bootstrap design, so that is thefirst place a newcomer will encounter it.