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

One Catalog, Three Languages

~13 min · codegen, vocabulary, validation, implementation

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

The Table That Kept Getting Copied

The list of assistant brains — their identifiers, their display labels, which effort levels each accepts, which endpoint each uses, what payload defaults each needs — is needed by the engines in Python, by the web clients in TypeScript, and by a native app in Swift.

Written by hand three times, this table has one guaranteed future: somebody adds a brain to one language and not the others, and nothing anywhere notices. The failure is not a crash; it is a picker that offers four options where another offers five, which reads as a product inconsistency rather than as drift.

So it is one data file, and the deploy renders it into all three languages on the way out. Not a build step in each consumer — a transform inside the same script that delivers everything else, so the same drift check that guards hand-written copies also guards these.

Validate at the Narrowest Point

The transform parses its source and asserts the shape before rendering anything: every entry carries the required fields and no extras, identifiers are unique, every effort list is non-empty, every route path looks like a route, and every alias points at an entry that actually exists.

Put that validation anywhere else and it is either absent or triplicated. Put it in the transform and a malformed catalog produces a failed deploy with a readable message — instead of three generated files that compile perfectly and misbehave at runtime in three different languages, each requiring its own debugging session to trace back to one bad line in a data file.

Validation belongs at the narrowest point the data passes through. Every consumer of a shared vocabulary could check it, and if the check lives there it will be written differently in each place, drift, and eventually be skipped by the newest consumer. One transform sits between the source and every output; a single assertion there covers every language, every consumer, and every future consumer nobody has written yet.

The Copy That Appeared Inside the Kit Itself

Worth recording because of where it happened. The alias map — the small table that says one brand name means one tool name — was generated correctly into every language. Then a shared component needed to compare two brain values, and rather than call the generated helper, it hand-mirrored the alias map inline. A fourth copy of the vocabulary, inside the repository whose entire purpose is to prevent fourth copies.

The lesson is not that somebody was careless. It is that generating a data structure is only half the job: unless you also generate and export the operations on it — the comparison, the normalization, the lookup — every consumer that needs one will write it, because writing three lines is easier than discovering that the three lines already exist somewhere.

Code

One source, validated once, rendered three ways·python
def load_catalog(raw: str) -> dict:
    """Parse and ASSERT THE SHAPE. This runs before any rendering, so
    a malformed catalog fails the deploy rather than producing three
    files that compile and misbehave."""
    data = json.loads(raw)
    brains, aliases = data.get("brains"), data.get("aliases")
    if not isinstance(brains, list) or not brains:
        raise ValueError("catalog needs a non-empty brains list")
    if not isinstance(aliases, dict):
        raise ValueError("catalog aliases must be an object")

    required = {"value", "label", "efforts", "chat_path",
                "reasoning_field", "chat_payload"}
    seen: set[str] = set()
    for brain in brains:
        # `!=` not `>=`: an EXTRA field is a typo, and silently
        # ignoring it is how a misspelled key gets shipped as nothing.
        if not isinstance(brain, dict) or set(brain) != required:
            raise ValueError(f"bad brain entry: {brain}")
        if brain["value"] in seen:
            raise ValueError(f"duplicate brain: {brain['value']}")
        if not brain["efforts"]:
            raise ValueError(f"empty efforts: {brain['value']}")
        if not brain["chat_path"].startswith("/api/"):
            raise ValueError(f"bad chat_path: {brain['value']}")
        seen.add(brain["value"])

    for alias, value in aliases.items():
        if value not in seen:
            raise ValueError(f"alias points nowhere: {alias} -> {value}")
    return data


def render_typescript(raw: str) -> str:
    data = load_catalog(raw)
    union = " | ".join(json.dumps(b["value"]) for b in data["brains"])
    return (
        "// Generated from the brain catalog. Do not edit.\n\n"
        f"export type Brain = {union};\n\n"
        "export const BRAIN_ALIASES: Record<string, Brain> = "
        f"{json.dumps(data['aliases'], indent=2)};\n\n"
        # Generate the OPERATION too, not just the data. Omitting this
        # is how a fourth hand-mirrored copy of the alias map appeared
        # INSIDE the shared repository: a component needed to compare
        # two brain values, found only a table, and wrote the three
        # lines itself.
        "export function canonicalBrain(brain: string): string {\n"
        "  const key = (brain || '').trim().toLowerCase();\n"
        "  return BRAIN_ALIASES[key] ?? key;\n"
        "}\n"
    )


TRANSFORMS = {
    "catalog_python": render_python,
    "catalog_ts": render_typescript,
    "catalog_swift": render_swift,
}

External links

Exercise

Find a vocabulary in your system that exists in more than one language — status enums, error codes, role names, feature identifiers. Pick one source of truth and write the generator for the second language. Then grep for hand-written comparisons or lookups against that vocabulary, and make sure the generator emits those operations too.
Hint
Search for the vocabulary's literal values rather than its type name. Hand-mirrored copies rarely import the shared type — that is precisely why they drifted — so a grep for the string values finds copies that a type-aware search never will.

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 inPlease sign in to comment.

No comments yet — be the first.