Skip to content

docs(meta): add the context glossary and the plumbing-contract ADR - #249

Merged
Syndic merged 3 commits into
mainfrom
claude/meta-context-and-base-image-adr
Aug 22, 2026
Merged

docs(meta): add the context glossary and the plumbing-contract ADR#249
Syndic merged 3 commits into
mainfrom
claude/meta-context-and-base-image-adr

Conversation

@Syndic

@Syndic Syndic commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Output of a design review of the devcontainer base image work. Two artifacts the repo's documented
structure called for but had never gained, plus the map entry that registers one of them.

meta/CONTEXT.md — the first context glossary

CONTEXT-MAP.md has listed both contexts as (CONTEXT.md not yet written) since it was created.
This writes the first one. Nine terms across two clusters — the base image and repo health — kept
to a glossary, with no implementation detail.

The one worth calling out: "plumbing" collides with git's own vocabulary. git's
plumbing/porcelain split means low-level commands, and .git-plumbing/ pairs the two words in the
way most likely to mislead. The term stays — it is apt in the ordinary sense, and renaming would
reach the devcontainer-plumbing command, the PLUMBING_* env prefix, the plumbing_* function
prefix, the directory, both repos' docs, and a consumer that hasn't adopted yet. Disambiguating it
in the glossary is the cheap fix.

Also opinionated where the code isn't: meta/scripts/README.md calls these things both "gates" and
"guards". The glossary picks check, since that is what the filenames and the on-save labels
already say, and records that whether one blocks is a property of where it runs.

meta/docs/adr/0001-… — the plumbing contract

The first decision scoped to a single context rather than the repo, so it exercises the
<context>/docs/adr/ path docs/agents/domain.md documents.

It records something the code already did and the docs denied. plumbing_apply_git_common
reads $PLUMBING_DIR/host-git-common-path by name and links it to /host-git-common;
plumbing_apply_all reads host-timezone by name. Those are contract terms whether or not anyone
wrote them down — so the base image README's claim that the host stub "does not constrain where the
host keeps anything" was only half true. It constrains the names; it leaves the sources free,
which is what the symlink indirection is for.

Syndic/.dotfiles is the evidence for why the distinction matters. It presents known_hosts and
allowed_signers as snapshot files where this repo presents them as bind-mounted symlinks, and its
copy of the plumbing consequently diverged in kind rather than in detail. Under this ADR, adoption
is conformance: that shape is an artefact of age, not a variant to support.

Numbering note for review

This is meta/docs/adr/0001-… while docs/adr/0001-… (the Go-pure/Python-impure policy) already
exists. ADR-FORMAT.md numbers per directory, so two 0001s in different scopes is what the
documented structure produces. Flagging it because it is the kind of thing that is cheap to change
now and annoying later — say the word if you would rather ADR numbers be globally unique.

What this does not do

The extraction itself — moving the four post-start.sh steps and the timezone parsing into the
shared library, and renaming lib.sh to plumbing.sh — is a separate PR. Filed alongside this
review: #247 (the ssh -G call on every up) and #248 (how consumers share what the image cannot
carry).

Validation

bazel test //... 23/23, pre-commit clean.

🤖 Generated with Claude Code

Syndic added a commit that referenced this pull request Aug 21, 2026
Review call on #249: ADR numbers become unique across the whole repo
rather than per directory, so meta/docs/adr/0001-... is renumbered to
0002 behind docs/adr/0001-....

This diverges from the /domain-modeling skill's ADR-FORMAT.md, which
scans a single directory and would hand you a number that is already
taken. docs/agents/domain.md is the durable place to say so -- the
skill ships from a read-only plugin cache that npx skills update
overwrites, and it is the file the skills are already pointed at.

check_adr_numbers.py enforces uniqueness and the NNNN-kebab-slug.md
filename shape. It takes check_go_work.py's profile -- CI job plus an
on-save task, no pre-commit hook -- because it is the closest sibling:
a tree walk over checked-in files with nothing to fix. The filename
shape rides along because a malformed name has no number to compare,
so silently skipping it would leave a hole in the very property being
enforced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Syndic

Syndic commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

Update: ADR numbering is now global, and enforced

The numbering question I flagged for review is resolved in 23749a7. ADR numbers are unique
across the whole repo
, not per directory, so meta/docs/adr/0001-… is renumbered to 0002-…
behind docs/adr/0001-….

The convention is documented where it survives

This diverges from the /domain-modeling skill's ADR-FORMAT.md, which says to scan a directory
for the highest number — under global numbering that hands you a number already taken. The skill
ships from a read-only plugin cache that npx skills update overwrites, so the durable place to
record the divergence is our own docs. docs/agents/domain.md now states the rule, says explicitly
that it overrides the skill, and names the enforcing check. CONTEXT-MAP.md gets a one-line
pointer rather than a second copy.

The check

meta/scripts/check_adr_numbers.py enforces two things: no reused number anywhere, and the
NNNN-kebab-slug.md filename shape. The shape is not scope creep — a malformed name has no number
to compare, so skipping it silently would leave a hole in the exact property being enforced. There
is a test for precisely that (test_malformed_name_is_not_also_counted_as_a_number).

Wiring follows check_go_work.py's profile: CI job plus on-save task, no pre-commit hook. It is
the closest sibling — a tree walk over checked-in files with nothing to fix — and the repo's README
already draws that line. Adding a hook would also pull in the test_precommit_docs.py coupling
(hook table plus classifying paragraph) for a check CI already gates.

README.md's script table gains a row; the "four guards" count becomes five.

Verified rather than assumed

  • Discovery genuinely spans both directories — printed the file list to confirm it finds
    docs/adr/ and meta/docs/adr/, not just the one the check happens to start from.
  • A live duplicate fails: dropped a colliding 0001 into meta/docs/adr/, got the expected
    path:1:1-2: diagnostic naming the file that already owns the number, exit code 1, then removed
    it.
  • 10 unit tests, including that a third use of a number is reported too (reporting only the first
    collision would let a fix land and leave a duplicate behind).

bazel test //... 24/24.

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.86%. Comparing base (5aaaf68) to head (58dec80).

Files with missing lines Patch % Lines
meta/scripts/check_adr_numbers.py 94.11% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #249      +/-   ##
==========================================
+ Coverage   83.60%   83.86%   +0.25%     
==========================================
  Files          40       41       +1     
  Lines        2025     2076      +51     
  Branches       99      111      +12     
==========================================
+ Hits         1693     1741      +48     
- Misses        316      318       +2     
- Partials       16       17       +1     
Components Coverage Δ
Go 85.14% <ø> (ø)
Python 80.94% <94.11%> (+1.15%) ⬆️
Category: apps ∅ <ø> (∅)
Category: infra ∅ <ø> (∅)
Category: libs ∅ <ø> (∅)
Category: meta 80.94% <94.11%> (+1.15%) ⬆️
Category: services ∅ <ø> (∅)
Category: tools 85.14% <ø> (ø)
Project: meta/scripts 80.94% <94.11%> (+1.15%) ⬆️
Project: tools/network_infrastructure_maintenance 85.14% <ø> (ø)
Files with missing lines Coverage Δ
meta/scripts/check_adr_numbers.py 94.11% <94.11%> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5aaaf68...58dec80. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Syndic and others added 2 commits August 21, 2026 13:52
Two artifacts the repo's documented structure called for but had never
gained: meta/ is the first context with a CONTEXT.md, and the base image's
contract is the first decision scoped to a single context rather than the
repo.

The ADR records what the code already did and the docs denied. The library
reads host-git-common-path and host-timezone by name and links to
/host-git-common; those are contract terms, so the README's claim that the
stub 'does not constrain where the host keeps anything' was only half true.
It constrains the names, not the sources. Syndic/.dotfiles is the evidence:
presenting the same host state in a different shape let its copy diverge in
kind rather than in detail.

The glossary also disambiguates 'plumbing' from git's own plumbing/porcelain
sense, which .git-plumbing/ maximises the collision with. The term stays --
it is apt in the ordinary sense and renaming it would reach the command, the
env prefix, the function prefix, both repos' docs and an unadopted consumer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review call on #249: ADR numbers become unique across the whole repo
rather than per directory, so meta/docs/adr/0001-... is renumbered to
0002 behind docs/adr/0001-....

This diverges from the /domain-modeling skill's ADR-FORMAT.md, which
scans a single directory and would hand you a number that is already
taken. docs/agents/domain.md is the durable place to say so -- the
skill ships from a read-only plugin cache that npx skills update
overwrites, and it is the file the skills are already pointed at.

check_adr_numbers.py enforces uniqueness and the NNNN-kebab-slug.md
filename shape. It takes check_go_work.py's profile -- CI job plus an
on-save task, no pre-commit hook -- because it is the closest sibling:
a tree walk over checked-in files with nothing to fix. The filename
shape rides along because a malformed name has no number to compare,
so silently skipping it would leave a hole in the very property being
enforced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Syndic
Syndic force-pushed the claude/meta-context-and-base-image-adr branch from 23749a7 to f690d07 Compare August 21, 2026 20:53
Global numbering left no way to find the next number except scanning
every docs/adr/ directory, which the convention documented but never
told anyone how to do.

--next prints the number alone, so it substitutes into a filename, and
it answers while a collision exists -- that is precisely when it is
needed. It is max+1 rather than count+1: a gap left by a deleted ADR
stays unused, because handing the number out again would break every
reference to the original.

A duplicate now reports a free number to move to, so the failure
carries its own fix. Both modes share one number parser, so the check
and the suggestion cannot disagree about what counts as a number.

No counter file. The numbers already live in the filenames; a second
copy would be a derived file needing its own freshness check, and a
merge conflict on every concurrent ADR PR. The race that usually
justifies one is already closed here -- the ruleset requires
up-to-date branches, so a second PR must rebase onto the first and
then fails this check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Syndic

Syndic commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

Added: --next

Global numbering left no way to find the next number except scanning every docs/adr/ directory — the convention was documented, the way to comply with it wasn't.

python3 meta/scripts/check_adr_numbers.py --next

Prints 0003 and nothing else, so it substitutes straight into a filename. Two behaviours worth knowing:

  • It answers during a collision. --next is how you get out of a duplicate, so refusing to answer while one exists would be backwards.
  • It is max+1, not count+1. A gap left by a deleted or renamed ADR is never handed out again, because a reused number breaks every reference to the original.

A duplicate now also reports a free number to move to (renumber to 0003 or later — "or later" because several duplicates all see the same free number and only the first can take it), so the failure carries its own fix. Both modes share one number parser, so the check and the suggestion cannot disagree about what counts as a number.

No counter file, deliberately

The numbers already live in the filenames. A counter would be a second source of truth — by this repo's own convention a derived file, needing its own freshness check — and a merge conflict on every concurrent ADR PR.

The race that usually justifies one is already closed here: the ruleset requires up-to-date branches, so a second ADR PR has to rebase onto a main that already contains the first, and then check_adr_numbers.py fails on it. The collision surfaces at the same moment either way, as a clear check failure rather than a conflict on a counter line.

Verified

  • Live: dropped a real colliding 0001 in, got duplicate ADR number 0001: already used by docs/adr/0001-go-builds-are-pure-python-is-not.md; renumber to 0003 or later, exit 1 — and --next still printed 0003 during it.
  • 21 unit tests now, including both CLI modes driven through a testable run(root, next_only) entry point rather than by shelling out.

bazel test //... 24/24.

@Syndic
Syndic merged commit 8e216dd into main Aug 22, 2026
45 checks passed
@Syndic
Syndic deleted the claude/meta-context-and-base-image-adr branch August 22, 2026 20:23
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