Skip to content

Lot 9(a): reload the configuration as it changes, and show a fault in the icon - #9

Merged
geeooff merged 5 commits into
mainfrom
lot-9a-configuration-faults
Sep 20, 2026
Merged

geeooff merged 5 commits into
mainfrom
lot-9a-configuration-faults

Conversation

@geeooff

@geeooff geeooff commented Sep 20, 2026

Copy link
Copy Markdown
Owner

The watcher reads config.toml again whenever it changes, and a file it cannot use no longer makes it exit: the icon turns red, a notification says why, and nothing runs until it is fixed. Lot 9's configuration item, built 2026-09-19 and verified in the field on 2026-09-20.

What changed for the person running it

  • Live reload. Save the file and the watcher reads it within a second — Configuration reloaded in the log. A game in progress is not disturbed: the engine hands the session to its successor the way an update does, and the commands that run when the game ends are the ones just saved. log_level follows live; log_dir waits for the next start and says so.
  • Faults. An unusable file — a typo, a value the program refuses, a missing file — is a red, slashed icon; a silent notification with the error glyph carrying the whole summary (line 3: unknown field 'log_levl', expected one of …); a menu line with the short of it; an ERROR line in the log. Nothing runs until it is fixed, not even the stop half — frozen, deliberately, never a fallback to the last good file. Saving a good one brings the icon back and a notification says the watcher is watching again, also after a restart on a file fixed while it was stopped.
  • Fixed: log_level = "debg" used to be accepted and to leave a log with nothing but errors in it, silently. Now a fault like any other.

How it is built

  • Supervisor, not RwLock. The plan named an Arc<RwLock<Config>> read at each use; that would have covered a valid change and left the invalid case — frozen idle, frozen mid-game, a recovery afterwards — to new engine states. Lot 13's handover already was those three, so a change to the file is a handover from one engine to the next in the same process, and the engine changed by one match arm. docs/design/09-robustness.md records the plan and what was built, and why.
  • StopSignal gains a Reload reason and a child signal: the engine runs on a child of the process-wide stop; the child's own event carries the reload and is reset between engines, the parent is never reset, so a Quit during a reload cannot be lost.
  • config::watch: FindFirstChangeNotificationW on the folder, a 250 ms settle after the last notification, bytes compared before signalling. LoadError with summary() and headline() replaces the two marker structs; exit codes 3 and 4 are now the other commands'.
  • The tray reads the session and the fault together and draws both; the supervisor reports which transition each read is (Report::Faulty | Restored | Usable) so the tray can word the two notices without knowing the history. A configuration-fault file beside the session marker carries a fault across processes; status reports it, purge removes it.
  • Eight new tests (181 with the client ones): the summaries and the level validation, the folder watch on a real folder, the reload scenario in the engine, the fault on every tray surface, the fault marker.

Measured

  • 2026-09-19, scratch configuration, no game: six changes, six reloads, the error line and the icon at T+250 ms each time.
  • 2026-09-20, the maintainer on the installed copy: a reload during a Starfield session (no command run, the session resumed, the renamed stop action ran at the end); a fault during a session (the game quit into a frozen watcher, the fix ran the recovery and the idle configuration came back); the notifications for a fault, a fix, a fault at start, and a fix after a restart.

For the reviewer

  • AGENTS.md's Discreet principle now names the two things the program says unasked; the tray convention names the supervisor's verdict.
  • The user pages no longer tell anyone to restart the watcher after an edit; the recipes keep install-task for the zip's first install.
  • CI runs the same checklist; the three registry tests stay ignored on the runner.

🤖 Generated with Claude Code

geeooff and others added 5 commits September 19, 2026 02:03
The watcher used to read config.toml once, and a file it could not use
made it exit with a code and no icon. Now `serve` loads the file itself
and a supervisor on the worker thread runs one engine per usable
configuration: a thread parked on the folder's change notification
(`config::watch`, 250 ms settle, bytes compared) signals a reload, the
engine stops through the handover path with the session left open in
the marker, and the next engine resumes it -- a game in progress is not
disturbed, and no new engine state was needed. A file that cannot be
used freezes the program instead: `LoadError` carries a one-line summary
(`line 3: unknown field ...`, `the file is missing`, the validation's
sentence), a `FaultSink` beside the session sink hands it to the tray,
and the tray draws the reserved error state with the summary as the
menu's first line. `log_level` follows live through a reload layer;
`log_dir` waits for the next start and says so.

`StopSignal` gains a `Reload` reason and a child signal: the engine runs
on a child of the process-wide stop, whose own event carries the reload
and is reset between engines while the parent is never reset, so a Quit
during a reload cannot be lost. Exit codes 3 and 4 are now the other
commands'. Six new tests: the summaries, the watch on a real folder, the
reload scenario in the engine, the fault on every tray surface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Getting started no longer tells people to stop and restart the watcher
after an edit; the recipes say the same, with install-task kept for the
zip's first install. The icon table and the troubleshooting section gain
the red, slashed icon; How it works explains the reload, the handover
it rides on and why an unusable file freezes the program rather than
falling back; the reference notes what applies live and that the exit
codes are the commands'. The changelog's Unreleased section carries the
three lines for the person running it. Lot 9's page records the
mechanics as built -- the engine restarted through the handover, not the
RwLock the plan named, and why -- and the measurement of 2026-09-19;
Lot 6's reserved state has its meaning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two things the field run of 2026-09-20 found. `log_level = "debg"`
passed validation, gave the filter no directive it knew, and the log fell
back to errors alone -- quiet for ninety seconds, the reload line that
would have explained it filtered out with the rest. The five levels are
now validated, case-insensitively, and a sixth word is a fault with the
icon and the menu line. And the debug line at the writer's exit, after a
resume, read "the game was never named" with a 34 s session: the resumed
signal has a name and no process id, and the engine's clock starts at
the resume. It now says the game was known by name only, from the
resumed session, with the time since the resume. Lot 9's page records
the run -- a reload and a fault, each during a Starfield session -- and
closes the item.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On the maintainer's remark after the field run: the red icon is easy to
miss at logon and the menu line was too long to read. A fault is now
said with a silent notification carrying the shell's error glyph and the
whole summary, at start and at every reload that fails, and the end of a
fault is said too, so the person knows the watcher is watching again; a
reload that stays usable says nothing. The menu keeps a headline, the
summary cut before the parser's list of expected fields. The supervisor
reports which transition each read is -- faulty, restored, usable --
because the notice depends on what came before; the tray owns the
wording. The Discreet principle in AGENTS.md now names the two things
the program says unasked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The maintainer broke the file, stopped the watcher, fixed the file and
started it again: no word, since the new process had never seen the
fault. A fault is now noted in a second file beside the session marker,
`configuration-fault`, removed when a usable file is read; a start that
removes one says the fault is over, and a start on a file that was
usable all along stays silent as before. `status` reports the file and
`purge` removes it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@geeooff
geeooff merged commit a04e804 into main Sep 20, 2026
1 check passed
@geeooff
geeooff deleted the lot-9a-configuration-faults branch September 20, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant