Skip to content
C.W.K.
Lesson 03 of 05 · published

Nobody Knows the Child

~14 min · ast, architecture-tests, dependency-inversion, plugins

Level 0Loose Note
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The Other Way a Root Gets Bent

Freezing the root stops one kind of erosion: editing the base class to suit a child. There is a second kind that never touches the root at all. A caller somewhere writes if vault.backend == "delta": ..., or isinstance(vault, DeltaVault), to reach a feature only that child has. From that line on, the caller knows which child it holds, and the root is no longer the contract; it is a suggestion. Add a second such caller and the next implementation has to imitate the first one's quirks to keep them working.

Soul Vault's architecture document states the rule plainly as invariant 2: outside backends/ and select.py, nothing imports a child, calls isinstance on one, or branches on .backend. And, as with the frozen hash, it is a test rather than a hope.

Three Tests Beside the Hash

  • T-NO-OVERRIDE. For every child the selector knows, the test walks the class and checks that each template method is still the root's own function object. A child that redefines write_batch fails, even if its version does exactly the same thing today. The same test checks the other direction: a child that leaves any primitive unimplemented fails too, and a child's backend name must equal the pointer value that selects it.
  • T-NO-ISINSTANCE. The test parses every Python file of the package outside backends/ and select.py into an abstract syntax tree and looks for two things: an isinstance or issubclass call that mentions a child's class name, and an import from the backends package. Either is an offence, reported with file and line. Parsing the code instead of grepping it means a comment or a string that mentions a child does not trip it, and a real call cannot hide behind formatting.
  • T-NO-LITERALS. No vault path literal anywhere in the package. Where the store lives is data in the pointer record (track 3), never a string in code.

The One Place That Knows

Something has to turn the pointer's word into a class. That place is soulvault/select.py, and it is tiny. It holds one dictionary, BACKENDS, mapping a pointer value to a "module:Class" string; today it has a single entry, "delta". The module is imported only when the pointer selects it. A pointer naming anything else raises backend_not_available with a cause that says which implementations this Mac has, and the comment above it is a quote from Dad: no default entry, no fallback chain.

Everything else receives a SoulVault from open_vault() and uses only the root's methods. That includes cwkPippa building a prompt, Vestige reading memory, the canary, every harness bootstrap. None of them could tell you which child they hold, and none of them needs to.

When a Caller Really Needs Something Special

The honest objection: what if one store really can do something the others cannot, like returning old revisions? The answer is the capability flags from lesson 1. The caller asks vault.capabilities().durable_history, or simply calls history() and handles unsupported_capability. Either way it is asking the contract a question, not identifying the child. If a third store gains history tomorrow, every such caller benefits with no edit. If a caller had written isinstance(vault, DeltaVault), it would ignore the new store forever.

Code

T-NO-ISINSTANCE, the idea (trimmed from tests/test_root_discipline.py)·python
import ast

def test_no_isinstance_on_a_child_outside_backends_and_select():
    child_names = {cls.__name__ for _, cls in _children()}
    offenders = []
    for path in _python_files():
        rel = path.relative_to(PACKAGE).as_posix()
        if rel.startswith("backends/") or rel == "select.py":
            continue
        tree = ast.parse(path.read_text(encoding="utf-8"))
        for node in ast.walk(tree):
            if isinstance(node, ast.Call) and getattr(node.func, "id", "") in ("isinstance", "issubclass"):
                names = {n.id for n in ast.walk(node) if isinstance(n, ast.Name)}
                if names & child_names:
                    offenders.append(f"{rel}:{node.lineno}")
            if isinstance(node, ast.ImportFrom) and (node.module or "").startswith("soulvault.backends"):
                offenders.append(f"{rel}:{node.lineno}")
    assert not offenders, offenders
select.py: the only map from a pointer value to a class·python
BACKENDS = {
    "delta": "soulvault.backends.delta:DeltaVault",
}

def _child(backend: str, pointer: Pointer) -> type:
    ref = BACKENDS.get(backend)
    if ref is None:
        raise VaultError("backend_not_available",
                         f"the pointer ({pointer.path}, generation {pointer.generation}) selects "
                         f"{backend!r}, and this Mac's cwkSoulVault implements only "
                         f"{sorted(BACKENDS)}; no other backend is tried")
    module_name, _, class_name = ref.partition(":")
    module = __import__(module_name, fromlist=[class_name])   # imported only when selected
    return getattr(module, class_name)

External links

Exercise

Write an architecture test for a project of yours: parse every module outside one allowed package with the ast module and fail on any isinstance or issubclass call that names a concrete implementation class, and on any import from the implementations package. Run it, fix or justify every hit, then add the dictionary-based selector so exactly one module maps configuration values to classes.
Hint
Collect the concrete class names from the selector itself rather than writing them into the test, so a new implementation is covered the day it is registered.

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.