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

Three Files, Three Fates

~13 min · settings, journal, activity, independent-loads, health

Level 0Idle Hands
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The journal and the settings load independently. Unreadable settings leave emergency release working; an unreadable journal leaves the saved presets visible." — Tinker architecture

What Tinker Keeps on Disk

Tinker's data directory holds three documents with very different stakes. settings.json is Dad's configuration: presets, commands and their shortcuts. journal.json is ownership: which changes Tinker made and must undo. activity.json is history: the last 200 requests, for reading. A fourth thing, a lock file, keeps two Tinker agents from ever owning the same journal at once — a second copy finds the lock taken and reports "Another Tinker agent already owns this account" instead of running.

The Bug That Coupled Them

The first version loaded settings and journal together and gave up if either failed. The review pass found the consequences. An unreadable settings file meant no engine — so Stop and restore everything, the one command that matters most when something is wrong, was gone too. An unreadable journal made the saved presets look deleted. And worst: with the engine unavailable, the Configuration tab showed an empty document that "Validate and save" would happily write over the real settings.

Independent Loads

Now each document loads in its own do/catch. If the journal reads, the engine exists, and emergency release works even when settings could not be read. If settings read, the presets appear even when the journal could not. Problems from either are joined into one loadProblem that is shown in Settings and returned by /health — and, unlike an ordinary error message, it is never cleared by a later successful command, because it describes the files, not the last action. A settingsReady flag stays false while settings are unreadable, and save refuses outright: "Tinker's settings could not be read and are left exactly as they are. Nothing can be saved until that is resolved."

Activity Is Not a Journal

Activity has its own store and its own failure mode. If activity.json cannot be read, Tinker keeps recording in memory for the current session but never saves over the unreadable file. Its architecture line is blunt: losing it changes nothing Tinker will do. It is neither the ownership journal nor the app door's receipts. Keeping that distinction in the types — separate structs, separate files, separate failure handling — is what stops a convenience file from ever becoming load-bearing by accident.

Writes Only When Something Changed

One more small discipline: at launch, settings are completed (missing commands added) and validated, but written back only if completion actually changed them. An unchanged file stays byte-for-byte untouched, which keeps its modification time meaningful and avoids a pointless write on every launch.

Code

Two independent loads; problems joined, never fatal to each other·swift
var problems: [String] = []
do {
  engine = try CapabilityEngine(capabilities: capabilities,
    persistence: DiskJournal(url: root.appendingPathComponent("journal.json")),
    permission: { _ in AXIsProcessTrusted() })
} catch { problems.append(error.localizedDescription) }
do {
  configuration = try Self.loadSettings(store, capabilities: capabilities)
  settingsReady = true
} catch { problems.append(error.localizedDescription) }
do { activity = try activityStore.load(materializeDefaults: false).document }
catch { activityProblem = error.localizedDescription }
if !problems.isEmpty { loadProblem = problems.joined(separator: " ") }
Settings rewritten only when completion added something·swift
let saved = try store.load(materializeDefaults: false).document
var loaded = saved
loaded.completeBindings(capabilities: capabilities)
try loaded.validate(capabilities: known)
if loaded != saved { try store.save(loaded) }   // unchanged file stays untouched
return loaded
Which failure takes away what·text
settings.json unreadable  -> presets hidden from use, save refused,
                             emergency release STILL works
journal.json unreadable   -> commands disabled,
                             saved presets STILL visible
activity.json unreadable  -> history not saved this session,
                             behaviour unchanged
settings/journal problem  -> shown in Settings and in GET /health
activity problem          -> shown on the Activity tab only

External links

Exercise

List the files your own app keeps on disk. For each, write the one-sentence answer to 'what does losing this cost?' Then check: is any file loaded together with a more important one in a way that lets the less important file's corruption disable the more important one's function?
Hint
The usual culprit is a single 'load state' function that reads everything and throws on the first failure. Split it so each file's failure is reported but only disables what genuinely depends on that file — and make sure the save path cannot overwrite a file the load path failed to read.

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.