utils/md_naming/_tables.py
Krzysztof kuhy Rudnicki a10d190b65
Some checks failed
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
design_system tests / test (push) Has been cancelled
gatelock pre-commit / pre-commit (push) Has been cancelled
gatelock tests / test (3.10) (push) Has been cancelled
gatelock tests / test (3.11) (push) Has been cancelled
gatelock tests / test (3.12) (push) Has been cancelled
web_ui tests / test (push) Has been cancelled
Add the shared markdown-naming gate and migrate utils' own docs
Markdown across every repo had accumulated a dozen ad-hoc names
(refactor_claude_todo.md x23, DESIGN_AUDIT_TODO.md, NEXT_SESSION,
HANDOFF, PLAN, SPEC) with nothing distinguishing a live task from a
finished record. An audit found docs asserting work was pending that
shipped months earlier, and one asserting completion while the work
was open.

Four namespaces now: README.md, CLAUDE*.md, DOCS*.md, TODO*.md. Task
files carry the literal "REMOVE ME AFTER FINISH" and are deleted when
the work lands.

Enforced bidirectionally -- a TODO without the marker fails, AND any
file carrying the marker that is not named TODO*.md fails. Without the
reverse rule a task can be renamed into the permanent namespace and
quietly live forever, which is the failure being fixed.

md_naming/ mirrors the file_length topology and imports its tables
(EXCLUDED_DIRS, THIRD_PARTY_REPOS, VENDORED_ANYWHERE, GENERATED_PATTERN)
rather than restating them, so the two gates cannot disagree about the
same file. 71 tests, 100% branch coverage.

stale.py warns on TODOs past 90 days but never fails: no machine can
prove a task is finished, so the honest mechanism is a nudge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
2026-08-24 15:53:12 +02:00

106 lines
3.6 KiB
Python

"""Exemption tables and namespace rules for the markdown-naming convention.
The data half: which basenames are reserved by external tooling, which
directory trees are exempt, and the literal marker every TODO file carries.
:mod:`md_naming.rules` holds the predicates that read these, and
:mod:`md_naming.config` re-exports both as the public surface.
Deliberately built on top of :mod:`file_length._tables` for the shared notions
of "excluded dir", "third-party repo" and "vendored tree". Restating those
here is exactly what lets two gates disagree about the same file.
"""
from __future__ import annotations
import re
#: The four namespaces. A markdown file in a kuhy-owned repo must match one.
#:
#: README -- what this directory is and how to run it.
#: CLAUDE* -- instructions Claude must read.
#: DOCS* -- reference, findings, design records. Permanent.
#: TODO* -- outstanding work. Carries MARKER; deleted when the work lands.
ALLOWED_PATTERN = re.compile(r"^(README|CLAUDE.*|DOCS.*|TODO.*)\.(md|markdown)$")
#: The literal every TODO file must contain, and that no other file may.
#: CI greps this exact string, so it cannot be paraphrased.
MARKER = "REMOVE ME AFTER FINISH"
#: Prefix identifying a task file, for the reverse check (marker => TODO*).
TODO_PREFIX = "TODO"
#: A TODO older than this many days gets a CI ::warning::. Never a failure:
#: no machine can prove a task is finished, so the honest mechanism is a nudge.
STALE_DAYS = 90
#: Basenames owned by external tooling. Renaming these breaks the tool, so
#: they are exempt from the namespace rule rather than migrated.
#:
#: SKILL.md -- the skill loader requires the literal filename.
#: CLAUDE/AGENTS -- auto-loaded by the harness.
#: copilot-* -- read by GitHub Copilot.
HARNESS_NAMES = frozenset(
{
"SKILL.md",
"CLAUDE.md",
"AGENTS.md",
"copilot-instructions.md",
}
)
#: Community-health and license-compliance names. GitHub keys off these exact
#: filenames to surface contributing/security UI; CREDITS and ATTRIBUTION also
#: back the standing "always attribute assets" rule, so renaming them would
#: quietly weaken an unrelated guarantee.
COMMUNITY_NAMES = frozenset(
{
"CONTRIBUTING.md",
"SECURITY.md",
"CODE_OF_CONDUCT.md",
"CHANGELOG.md",
"NOTICE.md",
"CREDITS.md",
"ATTRIBUTION.md",
"THIRD_PARTY_NOTICES.md",
"LICENSE.md",
"ISSUE_TEMPLATE.md",
"PULL_REQUEST_TEMPLATE.md",
"BRANCH_PROTECTION.md",
}
)
#: Directory fragments whose markdown is machine-managed or vendored, wherever
#: they appear. `.github/` holds templates GitHub names itself; the rest are
#: agent-memory and skill-bundle trees dropped in from elsewhere.
EXEMPT_SUBPATHS = (
"/.github/",
"/.projectmem/",
"/.hippo/",
"/.agents/",
"/.claude/",
"/ios/Runner/",
"/android/app/src/",
)
#: Repos under ~ that are clones of other people's work, in addition to the
#: shared THIRD_PARTY_REPOS list. A dated backup clone is not a live repo.
EXTRA_THIRD_PARTY = frozenset(
{
# Dotfile clones of other people's projects. These live outside ~/*/
# so a survey that walks only project dirs misses them -- the 2026-08-24
# migration renamed 5 files in nvm and ohmyzsh before this was added.
".nvm",
".oh-my-zsh",
".cargo",
".rustup",
".pyenv",
".fzf",
".tmux",
"screen-locker-backup-20260705-200741",
"flax-editor",
"flax-mcp-test",
"diet-guard-peer-backup",
"claude_refactor_staging",
}
)