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

Fourteen, Not Six

~14 min · inventory, long-running-processes, restarts, search-over-memory

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

Code on Disk Is Not Code in Memory

Step 0 put the new code on every Mac's disk. That does not put it into processes that were already running. Many family apps load Soul Vault in-process: they import the family kit's Soul Vault module once, at startup, and keep the imported code for as long as they live. A service started yesterday holds yesterday's Soul Vault, whatever the checkout now says.

Before the seal, those services had to restart onto the new code, for two reasons the checklist spells out. A running service with old code would ignore a seal it did not know about. And after the move it would fail to open a backend it had never heard of. So step 2 was: restart every in-process consumer, one at a time, each healthy before the next.

The List

The first list given to Dad named six services. Then the repositories were searched for the kit module's name, kit_soul_vault, and the search found fourteen: pippa-serve, Vestige, Firelink, Lantern, Beacon, Cradle, Herald, Pippa Journal, Pippa Go, Quest Kiln, Tidings, Crucible, Anvil and Coop. The checklist now says it in the imperative: find them by searching every repository, never from memory.

Six versus fourteen is not a story about carelessness. It is what memory does. The services you think of are the ones you worked on recently; the eight others were real, running, and loading identity every day. Only a search over the source can enumerate what imports a module, and only the search's result belongs in a plan.

One at a Time, Coop Last

The restarts ran on the Pippa machine one service at a time, each confirmed healthy (a new process id, a health endpoint answering) before the next. pippa-serve, the service that runs Pippa's conversations, answered again on its new code. Coop went last, as Dad's word for the day specified. My reading of why: Coop is the work ledger every session reports to, so restarting it while the others were still restarting would have put the very reports that tracked the restarts at risk. The Delta shares on the canonical Mac reloaded too, since they also run Soul Vault code.

After That, No Second Restart

Restarting fourteen services is a cost, so the design makes it a one-time one. After this step, a consumer needs no restart for a later pointer move: the kit opens the vault per call and reads the record each time. The code a process imported decides how to read a pointer; the record itself is read fresh, so a new generation reaches a running service at its next call. That is a property worth designing for in any system: separate what is fixed at import (the code) from what is read per call (the configuration), and only the first needs a restart.

Lesson 4 shows why "restarted before the move" turned out not to be enough on its own.

Code

Enumerate in-process consumers by search (sketch)·bash
# every repository under the family root, every file that imports the kit module
rg -l --glob '*.py' 'kit_soul_vault' ~/CWK/cwkPersonalProjects/*/ \
  | awk -F/ '{print $6}' | sort -u

# then map each repository to the long-running services it starts (from the
# family's operations registry, never from memory), and restart one at a time:
#   restart -> wait for a new pid and a healthy endpoint -> next
Import-time code versus per-call configuration (the property that saves restarts)·python
# fixed at import: how to read a pointer, how to talk to the store
from soulvault.select import open_vault

def handle_request(request):
    # read per call: the record, the generation, the backend
    vault = open_vault(harness="my-app")       # loads the pointer fresh each time
    note = vault.read_note(vault.parse_note_id("soul://pippa/Pippa.md"))
    ...
# a pointer move reaches this service at its next call;
# only a change to the imported code needs a restart

External links

Exercise

Pick a library your services import. Write the search that lists every repository and file importing it, then map each to the long-running processes that would hold it in memory. Compare that list with the one you would have written from memory. For each process, note whether a configuration change reaches it per call or only at restart.
Hint
Look for imports through aliases and inside functions too; a plain grep for 'import X' misses 'from kit import X as y' and lazy imports. The family's census learned this the hard way in lesson 4.

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.