Skip to content
C.W.K.
Lesson 07 of 07 · published

Retire a Door With a Gate

~15 min · retirement, enforcement, history, migration

Level 0Loose Parts
0 XP0/52 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

Two Doors to One Room

Every duplicate in this track so far was code: the same function in three repositories, eight copies of one release program. In November the family retired a duplicate of a different kind — two ways to reach the same thing.

The assistant's memory vault had always been a folder tree on disk, and over months the family had learned to open it directly: scripts read notes by path, prompts named folders, launch files pointed into it, tests built fixtures shaped like it. Then the vault moved behind its own service, with one pointer saying which implementation serves it and a thin client in the kit that every app uses to reach it. The client answers four questions — load the identity, read a note, open the vault, show the pointer — and when it cannot, it raises one named error with a code and the real cause. There is no fallback path, no default record and no cached identity. The kit's old path helper for the vault now refuses unconditionally.

That left two doors to one room. The new one had a lock and an owner. The old one was still written into thousands of lines, and every one of those lines would keep working right up to the day the folder stopped being where the data lives — and then fail somewhere nobody was looking.

Count First, Then Close

The retirement started with a census, not a sweep. Every repository in the kit's consumer manifest — fifty-six of them — was scanned for the retired product's name and its folder tree: 4,201 hit locations in 686 files. Each one got a disposition: repair it, or show that it is history. The census itself was frozen as a dated record of what was found, with no source snippets in it, and with a note that the guard's live exit status outranks the table.

Then the gate. The guard runs from the kit's existing drift check — the same command that every consumer's Python, shell or Swift suite already calls — so it reached every repository the day it shipped, with no new test file to add anywhere. It judges the checkout under test, worktrees included, and a missing policy or an unreadable source fails rather than passing. Two small decisions keep it honest. The retired name is spelled inside the guard as a sequence of character codes, so the guard's source and its planted fixtures never contain the word they hunt. And its output names a file and a line without echoing what the line says, because some of what it scans holds values that should not be printed.

History Is Frozen by Hash, Not by Folder

Not every mention is a door. A dated work order, a captured review, an archived plan: these record what was true when they were written, and rewriting them would falsify the record. So history is exempt — but never because of a comment, a filename or a folder called archive. Each reviewed historical file is pinned by repository, path and the SHA-256 of its whole content, with a category and a reason. Edit the file and the pin no longer matches, so it is judged again like anything else. Executable source and harness entry points can never be pinned at all, and new code placed beside a frozen record still fails.

Published courses got their own approval, separate from history: the owner pinned ninety-four exact course files, because a lesson that teaches a past design is teaching, not a live door. The same rule holds there — changed or neighbouring course content does not inherit the approval. This quest's own lessons never named the retired tree, so none of them needed a pin; a lesson written today that did would fail the family check.

A retired interface is dead only when naming it fails a build. Retire it with a gate that runs where the consumers already run, count what it finds before you repair anything, freeze genuine history by hash rather than by location, and prove the gate can fail before you trust its green.

The Gate Met a Story

Two days later the gate failed a video episode. The episode's owner had talked about the retired product on camera, so a dubbing note, a translation log and a reviewer's list of names repeated the word — three lines that were simply the episode's words. The owner's rule for the sweep was that past records are exempt and the guard matches machine references, never prose.

So the guard now asks two questions in order. First: is this file a record — writing people read, or dated data kept beside it? A file a program or a harness loads is never a record, and neither is a document that says it is active; those still fail on any mention. Second, inside a record: is this line a machine reference? The name as part of a path, a URL, an identifier, a key or a code read fails. The bare name in running text is reported as prose, counted in the summary, and passes. Anything attached to the name counts as attached, so a line the guard cannot place fails. The family scan after the change: fifty-six repositories, zero failures, three prose mentions, and every pinned historical hit unchanged.

The detector is proved the way track 4 proves every new check: a test plants the retired name in a startup file, watches the guard fail with the exact location, replaces the name with a vault identifier, and watches it pass. That proves the detector. The kit's notes are careful to say what it does not prove — that every service starts cleanly without the old door is separate evidence, gathered separately.

Code

A retirement gate: records versus machine references, history pinned by hash·python
from __future__ import annotations

import hashlib
from pathlib import Path

# The retired name, built from codepoints so the guard's own source and
# its planted test fixtures never contain it. (These codepoints spell a
# made-up stand-in, not the family's real retired name.)
MARKER = "".join(map(chr, (111, 108, 100, 118, 97, 117, 108, 116)))

READ_BY_PEOPLE = {".md", ".mdx", ".rst", ".txt"}
DATED_DATA = {".json", ".jsonl", ".csv"}
DATED_DIRS = {"history", "archive", "archives", "audits", "briefs"}
LOADED_BY_PROGRAMS = {"config", "prompts", "templates", "skills", "hooks", "tests"}
HARNESS_FILES = {"AGENTS.md", "CLAUDE.md", "GEMINI.md"}   # loaded every session
EXECUTABLE = {".py", ".sh", ".swift", ".ts", ".js", ".plist"}
PROSE_BEFORE = set(" \t\"'(*“‘")
PROSE_AFTER = set(" \t\"',.;:!?)*”’")


def is_record(rel: Path, text: str) -> bool:
    """Writing people read, or dated data kept beside it - never a file a
    program or a harness loads, and never a doc that says it is active."""
    dirs = {part.lower() for part in rel.parts[:-1]}
    if rel.name in HARNESS_FILES or dirs & LOADED_BY_PROGRAMS:
        return False
    if rel.suffix in READ_BY_PEOPLE:
        front = text.split("---", 2)[1] if text.startswith("---") else ""
        return "status: active" not in front
    return rel.suffix in DATED_DATA and bool(dirs & DATED_DIRS)


def particle(ch: str) -> bool:
    """Korean and Japanese particles attach to a name in running text."""
    return "\uac00" <= ch <= "\ud7a3" or "\u3040" <= ch <= "\u309f"


def machine_reference(line: str) -> bool:
    """Anything attached to the name - a slash, a dot, an underscore, an
    equals sign - makes it part of a path, a key or code. When unsure, fail."""
    low, start = line.lower(), 0
    while (at := low.find(MARKER, start)) != -1:
        before = line[at - 1] if at else " "
        after = line[at + len(MARKER): at + len(MARKER) + 2] or " "
        if before not in PROSE_BEFORE or not (after[0] in PROSE_AFTER
                                              or particle(after[0])):
            return True
        if after[0] in ".:" and after[1:] not in ("", " "):
            return True                      # "x.app", "x://", "x:key"
        start = at + len(MARKER)
    return False


def verdict(repo: str, rel: Path, data: bytes,
            history: dict[tuple[str, str], str]) -> list[str]:
    text = data.decode("utf-8", errors="replace")
    hits = [(n, line) for n, line in enumerate(text.splitlines(), 1)
            if MARKER in line.lower()]
    if not hits:
        return []
    # Frozen history: the exact bytes that were reviewed, by hash. Edit
    # the file and it is judged again; executable source and harness
    # entry points are never frozen.
    pinned = history.get((repo, str(rel))) == hashlib.sha256(data).hexdigest()
    if pinned and rel.suffix not in EXECUTABLE and rel.name not in HARNESS_FILES:
        return [f"exempt-history {rel}"]
    if not is_record(rel, text):
        return [f"FAIL {rel}:{n}" for n, _ in hits]   # any wording at all
    return [f"FAIL {rel}:{n}" if machine_reference(line) else
            f"exempt-prose {rel}:{n}" for n, line in hits]

External links

Exercise

Pick one interface you have replaced but not removed — an old endpoint, a deprecated function, a config key, a path. Count every place that still names it, across every repository that could, and sort each hit into repair or history. Then write the gate: where would it run so that every consumer already executes it, how would it tell a record from a live reference, and how would you pin the history so an edit cancels the exemption?
Hint
The count is the step people skip, and it is the one that tells you whether a gate is even possible yet. A few dozen hits can be repaired in a day. Thousands mean the census itself is the first deliverable, and the gate lands only after it.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.