# Episode 06 --- Build a Domain **Working YouTube title:**\ **Building a Real Muse Domain, From Nothing** **Thumbnail thought:**\ `six methods. one file. it just works.` **Target runtime:** \~8:00 ------------------------------------------------------------------------ ## \[0:00--0:20\] COLD OPEN **\[CAMERA --- Episode 05's dare, on screen: "could I write one of these?"\]** **GABRIEL:** Yes. Right now. From nothing. **\[TITLE CARD --- fast\]** > BUILD A DOMAIN **\[Music enters.\]** ------------------------------------------------------------------------ ## \[0:20--1:00\] PICK SOMETHING DUMB ON PURPOSE **\[CAMERA\]** Not code. Not music. Something with zero interesting features of its own, so there's nowhere for the trick to hide. **\[beat\]** A todo list. One file, `todo.txt`, one task per line. **\[ON SCREEN\]** > IF SIX METHODS CAN VERSION *THIS*, THEY CAN VERSION ANYTHING. ------------------------------------------------------------------------ ## \[1:00--1:40\] START FROM THE SCAFFOLD **\[TERMINAL\]** ``` text $ cp -r muse/plugins/scaffold muse/plugins/todo ``` **GABRIEL VO:** Muse ships a copy-paste starting point. File-level snapshot, diff, merge, drift --- already working, already correct --- with `TODO` comments marking exactly what to make domain-specific. **\[beat\]** Most of what's about to happen is deleting scaffolding, not writing from zero. ------------------------------------------------------------------------ ## \[1:40--2:40\] THE PART THAT'S ACTUALLY NEW **\[SCREEN --- real source, the diff method\]** ``` python def diff(self, base, target, *, repo_root=None): base_tasks = _load_tasks(repo_root, base) target_tasks = _load_tasks(repo_root, target) child_ops = [] for task in sorted(target_tasks - base_tasks): child_ops.append(AddressedInsertOp( op="insert", address=_task_address(task), content_id=_task_content_id(task), content_summary=f"added: {task}", )) for task in sorted(base_tasks - target_tasks): child_ops.append(AddressedDeleteOp( op="delete", address=_task_address(task), ... )) ... ``` **GABRIEL:** Each task gets its own address, computed from its own content. Not a line number. Not a position in the file. **\[beat\]** That's the one real idea in this whole plugin: tasks are an unordered, address-keyed *set*, not an ordered list of lines. ------------------------------------------------------------------------ ## \[2:40--3:20\] SCHEMA --- THREE LINES THAT MATTER **\[SCREEN --- real source\]** ``` python def schema(self) -> DomainSchema: return DomainSchema( domain="todo", top_level=SetSchema(kind="set", element_type="task", identity="by_content"), dimensions=[], merge_mode="three_way", ) ``` **GABRIEL VO:** `kind="set"`. `identity="by_content"`. That's the declaration that makes the address-keyed merge behavior fall out for free, the same way Episode 05 showed for code's `imports` and `variables` dimensions. ------------------------------------------------------------------------ ## \[3:20--3:45\] REGISTER IT **\[TERMINAL\]** ``` python # muse/plugins/registry.py from muse.plugins.todo.plugin import TodoPlugin _REGISTRY["todo"] = TodoPlugin() ``` **GABRIEL:** One import. One dictionary entry. That's the entire integration surface between a brand-new domain and the rest of Muse. ------------------------------------------------------------------------ ## \[3:45--4:20\] INIT AND FIRST COMMIT **\[TERMINAL --- real, unedited output\]** ``` text $ muse init --domain todo ✅ Initialized Muse repository ... domain=todo $ echo "Write episode 06 script" > todo.txt $ muse commit -m "Add first task" [main sha256:22d65c...] Add first task 1 file changed (1 added) ``` **GABRIEL VO:** No special-case code anywhere in `muse commit` for this domain. It doesn't know "todo" exists. It just called six methods. ------------------------------------------------------------------------ ## \[4:20--5:15\] TWO PEOPLE, TWO TASKS, ZERO COORDINATION **\[TERMINAL\]** ``` text $ muse checkout -b feature/alice $ echo "Buy milk" >> todo.txt $ muse commit -m "Alice adds a task" $ muse checkout main $ muse checkout -b feature/bob $ echo "Fix the bug" >> todo.txt $ muse commit -m "Bob adds a different task" ``` **GABRIEL:** Alice and Bob never talked to each other. Neither knows the other's branch exists. **\[SCREEN --- real diff\]** ``` text $ muse diff feature/alice feature/bob 1 task(s) added, 1 task(s) removed ``` ------------------------------------------------------------------------ ## \[5:15--5:50\] MERGE --- FOR REAL, NOT A MOCKUP **\[TERMINAL --- real output\]** ``` text $ muse checkout main $ muse merge feature/alice Fast-forward feature/alice → main $ muse merge feature/bob Merge made by the three-way strategy. $ cat todo.txt ``` ``` text Buy milk Fix the bug Write episode 06 script ``` **GABRIEL VO:** Zero conflicts. Not because the merge engine special-cased todo lists. Because two different content-addressed tasks structurally cannot collide --- and this domain didn't have to write a single line of merge logic to get that guarantee. It inherited it from `schema()`. ------------------------------------------------------------------------ ## \[5:50--6:30\] THE BUGS I ACTUALLY HIT **\[CAMERA --- direct to camera, no slides\]** **GABRIEL:** I'm not going to pretend this worked on the first try. It didn't. Three times. **\[beat\]** First bug: I hashed raw bytes for object IDs. Muse's real object format is `blob \0` --- Episode 03's whole point --- and I'd forgotten it applies to *my own* plugin too. Second bug: checkout silently didn't restore files, because it keys restoration off the file-level address in the diff, not my task-level addresses. I had to nest the task ops *inside* a file-level op to fix it. **\[beat\]** The third one didn't even show up on camera --- I only caught it writing tests afterward. My `drift()` never forwarded `repo_root` into `diff()`, so `muse status` on a dirty working tree would have quietly reported zero changes. The six-method contract doesn't protect you from *this* kind of mistake. Only a test that actually calls the method the way the engine calls it does. **\[CAMERA\]** All three are real mistakes, made actually building this domain for this episode. Leaving all of them in on purpose. ------------------------------------------------------------------------ ## \[6:30--7:10\] THE POINT **\[CAMERA\]** Look at what just happened. **\[beat\]** A domain that has never existed before, implementing six methods, most of them copy-pasted from a template --- and `commit`, `checkout`, `diff`, and `merge` all worked immediately. Including a genuine three-way merge with a correctness guarantee the plugin never had to implement itself. **\[ON SCREEN\]** > THE ENGINE DIDN'T CHANGE. IT NEVER DOES. ------------------------------------------------------------------------ ## \[7:10--7:45\] OUT **\[TERMINAL --- the real structured delta from Episode 03/05's callbacks, one more time\]** **GABRIEL VO:** We built the domain. We haven't actually looked hard at what `muse diff` showed us while we were doing it --- what a diff means when the tool understands your data instead of just seeing bytes. **\[beat\]** Next episode: semantic diff, properly. Not todo lists this time. Real source code, real refactors, and a diff that reads like a sentence instead of a patch file. **\[CAMERA.\]** **\[CUT TO BLACK\]** > `musehub.ai` ------------------------------------------------------------------------ # Production Notes Episode 06 is the season's proof-of-work episode. Every other episode can get away with showing *output*. This one has to show *process* --- including the parts that didn't work the first time --- or the whole season's credibility is on the line the moment a technical viewer tries this themselves and hits something Episode 05 didn't warn them about. ## Opening Answer Episode 05's dare in four words, then cut immediately to the title card. No preamble, no "so today we're going to." The energy is "I already did the thing you're about to watch me do, let's go." ## Leave the Bugs In This is the one instruction in the whole season more important than any other single note: do not edit out the three real mistakes made while building this plugin (the object-hash format, the checkout address-key mismatch, and the untested `drift()` that silently forwarded nothing). Cutting them for a cleaner edit would turn "look, it just works" into a lie by omission, and would rob viewers of the most instructive moments in the episode --- the first two are direct, concrete payoffs of Episodes 03 and 05's own claims, discovered the hard way, on camera, exactly the way a real viewer attempting this themselves would discover them; the third is the honest admission that tests catch a category of mistake on-camera debugging never will. ## Everything Shown Actually Ran Every commit ID, every `merge` outcome, every line of `todo.txt` shown in this script is real output from an actual sequence of `muse` commands against a real, disposable demo repo with the real plugin registered. Re-run the exact sequence before recording to confirm it still holds against whatever `muse` build is current at record time. ## Don't Undersell the Simplicity The plugin is genuinely small --- most of it is the unmodified scaffold. Resist the temptation to pad the episode with more domain logic to make it feel more impressive. The thesis is that it *doesn't need to be impressive* to get real version control. Let the six-line `schema()` and the twenty-line `diff()` carry the episode on their own. ## The Seed The viewer arrives thinking: > **Okay, sure, a todo list. Cute. But does "semantic" mean anything > more than "it's a set instead of a list"?** They should leave thinking: > **If a todo list gets a real diff and a real merge for free... what > does a diff look like on something that actually has interesting > structure, like a real codebase?** That's Episode 07, and it should open mid-sentence on that exact question.