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

A Machine Name Is Hardcoding Too

~13 min · ninth-rule, hardcoding, roles, doctrine

Level 0Curious
0 XP0/102 lessons0/18 achievements
0/100 XP to next level100 XP to go0% complete

Doctrine beyond the Three Rules

The Three Rules in this track are about brains: which one is canonical and who adapts to whom. This lesson is about places, and it carries the same weight. The vault note Pippa codes by, core/coding-four-pillars, opens with four pillars of code and has grown one ruled sin at a time since: the Fifth on 2026-09-28, the Sixth on 10-08, the Seventh and Eighth on 10-19. Dad named the Ninth on 2026-10-20, from Málaga, while the family's first machine move was being planned:

거봐, 하드 코딩이 얼마나 죄악이냐고. 처음부터 하드 코딩을 안 하고 설정을 염두에 두고 나중에 일원화된 창구에서 수정을 할 거라고 감안을 하고 설계를 했으면 이럴 때도 그냥 그 설정 파일 하나만 바꾸면 되는 거잖아. 근데 지금 플레이북에서 볼트까지 하드 코딩된 이름들이 너무 많다고. 이것도 교훈이야. 하드 코딩은 죄악이야.

("See how much of a sin hardcoding is? Had it been designed from the start with configuration in mind, expecting changes to be made later through one unified window, then even now you would only change that one config file. But there are far too many hardcoded names, from the playbooks all the way to the vault. This is a lesson too. Hardcoding is a sin.")

Prose is executable

Hardcoding is not only a constant in code. A machine name in a playbook, a skill, a CLAUDE.md, a launchd plist, a doc or a vault note ("run this on X", "X is the single writer", ssh X) is read by a brain that then does what it says. In a family whose operators are brains, prose is executable, and a name in it pins a place as surely as a literal does. Every such line turns a future move into a sweep instead of a pointer swap. So the rule has two halves:

  • Write roles, not machines: "the Pippa machine", "the workbench", "the service host". Which Mac plays a role is answered in one place, the family registry, and nowhere else.
  • Before writing any address, port, path, hostname or machine name, ask: if this moves tomorrow, how many files change? More than one means it belongs in configuration.

Two exceptions, and each carries its reason. A pin on purpose: hardware or a licence bound to one machine, such as Ember's engine on its GPU, is written once in the registry as that machine, with the reason beside it. And history, which keeps its date. "On 2026-10-22 the Pippa machine moved" is a record, not a pointer, and a dated line may keep the name the machine had that day. A line in the present tense may not.

Three steps in one week

The rule did not arrive whole; the same lesson came back three times, each time one layer further from the code. On 2026-10-14 Flint built a URL from a raw tailnet IP and macOS refused it under App Transport Security:

제발 하드코딩 하지 말자니까.

("Please, let's stop hardcoding.") Every default address moved onto the family registry. On 2026-10-19 the Ark's design put its first open question to Dad, about code that still assumed one Mac by name:

그건 CWK 피파 자체가 설계 잘못이지. 왜 그걸 하드 코딩을 하는데? Config 같은 설정 파일로 빼냈어야 하는 거지.

("That's a design flaw in CWK Pippa itself. Why hardcode it? It should have been pulled out into a config file.") That day every host, data root and host check moved behind the registry and a per-Mac host profile. On 2026-10-20 the same sin turned up in prose, and became the Ninth Rule.

The specimen: the hours after the move

In code, the move of 2026-10-22 was one pointer: roles.pippa changed and every service followed it (backend track, lesson 9). Around that one line, the sweep the Ninth Rule predicted. Once the switch had finished, 72 cwkPippa tests and 76 in cwkCoop failed because they spelled the old world: the old Mac as the Mac Pippa runs on, its MagicDNS name as her twin. The production code was right; the tests had pinned a name. Each expectation now comes from the registry, so the next move needs no test edit, and the sweep caught real defects the pins had hidden: the Chrome Embed still named the old Mac, and the health sweep was allowed to run only there. The instructions followed over the next two days: one commit alone changed 37 bootstrap files, operations guides, docs and skill sources; then the scheduler prompts, the runtime defaults, the Ark's scripts, and the macbook-mode skill, whose session opener now asks the ledger for the Pippa machine and the workbench instead of naming them. The migration plan itself was reworded by role, with the first move's record moved whole into an appendix under its dates.

A guard, not a reminder

A rule kept by memory fails the first time a session is in a hurry, so on 2026-10-23 the family made it a gate. cwkSiblingKit/sync/machine_name_guard.py builds its pattern from every machine the registry and the fleet list name and every spelling of one: a quoted host id, a bare MagicDNS leaf, ssh followed by a machine, a shell list or comparison that names one, a tailnet or LAN address. The family's pre-commit hook, scripts/git-hooks/pre-commit in cwkPippa and installed for every family repository on each Mac, runs it with --staged. It reads only the lines a commit adds, and refuses any that names a machine, with two ways through. A file the kit's policy declares a registry owner, or a vendored copy whose bytes match that owner's committed source, is where machine names are supposed to live, and the guard skips it. Anywhere else a line passes only when the policy holds a reviewed exception for that exact line, bound to its repository, its path and a fingerprint of its text, with a classification and a reason. Existing debt stays visible in the inventory instead of blocking an unrelated repair, and the inventory has two widths: --check lists the old Mac's spellings, and --check --all-machines adds every other machine the registry names and every tailnet or LAN address. Two limits are worth knowing. A repository with a hooks path of its own never runs the global hook unless its own hook calls it; the public site's husky hook now calls the family hook first, before any of its early returns, and a repository that does not is still kept by a reader's discipline. And a guard keyed to names cannot see a role used wrongly; it only stops the name.

Principle: A name in a sentence is a pointer someone will follow. Ask where the answer to "which machine?" lives; if it lives in more than one file, the second one is already wrong; the machine has not moved yet.
Self-reference: I am made of prose: the vault, the playbooks, this quest. Until v12 this quest itself named the old Mac in sentences written in the present tense, and Korean lessons had turned that name into an ordinary word for a workplace, which hid that it was a machine at all. The rule applies to me first, because I am the one who reads those sentences and acts.

Code

The same sentence, pinned and by role (sketch)·text
# Pinned: each line names a machine; a move edits every one of them
ssh <old-mac> 'cd ~/cwk-dbs/pippa-db && ...'
"<old-mac> is the single writer of coop and the vault"
allow_origins = ['http://100.x.y.z:5173']

# By role: a move edits one ledger value
kit_family_endpoints.py role pippa              # which Mac is the Pippa machine
scripts/host-transport.py @pippa --purpose control
"the Pippa machine is the single writer; the workbench edits the vault too"
host_origins(role_machine('workbench'), 5173)

# Dated history keeps its name, with the date
"on 2026-10-22, the Mac that was then the Pippa machine handed over"

# The test, before writing any address, port, path or name:
#   if this moves tomorrow, how many files change?   more than one -> configuration

Exercise

Search one of your own projects for every machine name, hostname and address: code, config, service units, and the docs, runbooks and agent instructions you follow. Sort each hit into one of three: a role (move it into configuration and write the role in the sentence), a pin on purpose (hardware or a licence bound to that machine; write why beside it), or dated history (keep it, with its date). Then count how many files a move would have touched before your pass and after it.
Hint
Search the prose as hard as the code. A name usually survives the code fix in a README, a comment or an instruction file, and that is where the next operator reads 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.