llm-agents 2026-09-23 (claude-code 2.1.280), drop the SessionEnd harvest hook, shut the coordinator's :8731 wifi door (#460) - #461
Merged
Conversation
…0.96 Tom 2026-09-16: "let's make it the green pistachio recreated 5k monitor my wallpaper", then "make the blur at 0.96 from the niri side for kitty terminal, to appreciate the new colors. this is not transparency". - dot_local/share/wallpapers/imagine-olive-5120x2880.png: the claude.ai/imagine "olive" theme (ground #7c8b62) — the exact recovered source SVG (~/colors/waves/capture/imagine-background.svg) rasterised by Chrome under Anthropic's own CSS recipe (cover/center) at the PA27JCV's native 5120x2880. Not July's design-pass export, whose reconstruction has the crossing-ribbons bug (~/colors/waves/STATE.md). - bin/wallpaper defaults to it; startup.kdl spawns `wallpaper` again (the swaybg spawn was deleted 2026-09-11 for the Zenbook's OLED black). - window-rules.kdl: the kitty blur rule is back, now matching every kitty-derived app-id — kitty, herdr-projector, and the fzf *-prompt popups — so no kitty is see-through without the frost. kitty.conf: background_opacity 0.96, dynamic_background_opacity yes. Same mechanism PR #381 removed: kitty makes only its background translucent, niri blurs the wallpaper behind it, text stays opaque. Shared RAW files: the client laptop gets the wallpaper and the frost too after its next pull. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…erdr, nvim, fish
Tom 2026-09-16: "resurface the light mode and the dark mode, original from
claude. currently i have 'noir' mode. i wonder what it would take to make a
dotfiles-wide switcher … i'm talking kitty, herdr(?), nvim. gtk-noir is fine
to keep it that way for now" — three hand-curated themes, switched in a
nix-native way. Design and option analysis: docs/theme-switcher-2026-09-17.md
(full research report in ~/colors/theme-switcher-design.md).
The themes. noir is the Claude Dark 12-role syntax palette on OLED black;
claude-dark is the same palette on claude.ai's own dark grounds (#1A1A1A
code-block, #20201F page) — home/themes/claude-dark.nix is `noir // {
ground; … }`; claude-light is the Claude Light palette on --bg-100 #F9F9F7,
with a designed 16-slot ANSI mapping (Claude ships none), every slot WCAG AA
on white, flagged for colorlab tuning. Sources: the palettes recovered from
the claude.ai bundle in ~/colors/waves/capture/claude-code-theme/.
The mechanism. home/themes/*.nix are palettes keyed by ROLE; default.nix
renders each app's colour FRAGMENT; home/theme.nix writes every fragment for
every theme under ~/.config/themes/<name>/ and bootstraps/heals the pointer
~/.config/theme, a plain symlink into themes/ that ~/.local/bin/theme
retargets at runtime (HM owns the plural, the switcher the singular). Every
RAW file keeps its body raw and joins its fragment through the app's own
include — the kitty-scrollback-nix.conf / niri-local.kdl move, one level up.
Switching needs no rebuild; only adding a theme or changing a palette does.
- kitty.conf: the colour block becomes `include ${HOME}/.config/theme/kitty.conf`;
opacity stays raw (dynamic_ cannot change on reload). `theme` runs
`kitten @ load-config` per instance socket — a symlink retarget fires no
watcher event, so the explicit reloads are load-bearing.
- niri: config.kdl includes ~/.config/theme/niri.kdl LAST (sections merge,
later wins per property; `optional=true` so a checkout pulled ahead of its
switch cannot take niri down). layout.kdl / misc.kdl lose their colours.
Two stale comments corrected: 26.04 knows both `optional=true` and `~`.
Mod+Shift+T cycles.
- herdr: `[theme] name = "terminal"` — every token an ANSI slot, so kitty
paints herdr, and re-paints it live: herdr enables DEC 2031, kitty reports
the background change as CSI ?997;n on reload, herdr re-queries OSC 10/11/4
(source-verified in the pinned 0.9.0, then seen live: the projector followed
noir -> claude-dark -> noir with no herdr write-back). There was no [theme]
section before, i.e. herdr was on default catppuccin, not noir.
- nvim: RAW lua/theme.lua dofile()s the fragment (noir fallback inline) and
owns the catppuccin setup, bufferline's ground highlights and lualine's two
colours; reload() purges catppuccin, re-setups, :CatppuccinCompile, and is
called over --remote-expr on every running instance. plugins.lua.in's
catppuccin block moves there. Deleted: lua/plugins.lua (a stale tracked
duplicate of the rendered file) and lua/plugins/bufferline.lua (linked,
never required — plugins.lua.in set bufferline up inline).
- fish: conf.d/colors.fish sources the fragment and re-sources it at the next
prompt when the pointer moved. No universal variables — fish_variables is
tracked here and `set -U` would dirty the checkout on every switch.
- starship: the one `#F47B85` becomes `red`; everything else was ANSI-named
and `#DA7756` (accent-brand) is identical in Claude's light and dark tokens.
- Claude Code: `theme` writes the light/dark key into ~/.claude.json
(tmp+rename; next start).
- Not in this PR, by Tom's ruling: GTK/MacTahoe stays Dark (Phase 4 in the
doc; the package already builds the Light variants). cliamp 1.63.2 lists only
built-in themes, so its catppuccin-noir.toml was inert already; zathura and
qt6ct untouched.
Verified: `nix build` of the coordinator Home Manager generation and eval of the
client's; `niri validate`; the noir fragment reproduces the previous kitty.conf
colours key for key (only background_opacity is outside it); live on the
coordinator through a preview render (`THEMES_DIR=~/.cache/theme-preview theme
claude-dark`): 5 kitty instances reloaded, niri reloaded, herdr repainted,
back to noir. After the switch the activation entry re-targets that preview
pointer to ~/.config/themes/noir.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rs, F2 pickers
Tom 2026-09-17: "the hardest thing to make sure that you got right is actually
the gtk theme … for my 'noir' theme i am doing nix replacements on an existing
package. the same can be done for the other gtk themes to follow the
'classic'/vanilla claude light and claude dark themes. i want those as well";
"should be able to switch the 'accent' as well … matching folder colors …
matching exactly the wallpaper color selection"; "F2 should be ONLY for the
dark/light/noir variants … Shift + F2 the switch for the color accent
(wallpaper + icons)".
GTK. pkgs/mactahoe-gtk-theme.nix takes a `variant`: `oled` is the existing
noir build, unchanged output; `claude` recolours BOTH branches of
src/sass/_colors.scss to claude.ai's tokens (bg-000/100/200, text-000/200/400,
cds-text-secondary, links = accent-100) with the `orange` accent slot set to
Anthropic clay #D97757 — MacTahoe-Claude-{Dark,Light}[-solid]-orange[-(x)hdpi].
Each palette carries its GTK theme; home/theme.nix puts every theme's package on
the profile, ships each theme's gtk-4.0/ under ~/.config/themes/<name>/, and
points ~/.config/gtk-4.0/{gtk.css,gtk-dark.css,assets} THROUGH ~/.config/theme
at it. `theme apply` sets gtk-theme, wm theme, icon-theme and color-scheme
with gsettings.
Why that works now and did not before. GTK_THEME (modules/common.nix) is
deleted: an env var cannot change under a running session. It had been the
only thing theming GTK because gsettings-desktop-schemas was never on
XDG_DATA_DIRS — no gschemas.compiled with org.gnome.desktop.interface anywhere
in the session — so GTK3's Wayland backend fell back to settings.ini. With the
schema re-homed to share/glib-2.0/schemas (systemPackages) GTK3 follows the
gsettings value LIVE; verified with gtk-query-settings: unset → Dark (settings.ini),
schema present → the dconf value, changed → follows. `gsettings` itself was on
no session PATH, so startup.kdl's four gsettings lines had been failing
silently; glib is in systemPackages and the four lines become
`spawn-at-startup "theme" "apply"`. dconf.settings no longer pins gtk-theme /
color-scheme (a pin would snap a light session back to Dark on every switch).
Accents. The seven claude.ai/imagine grounds (oat olive cactus sky fig heather
coral), rendered at 5120x2880 from the recovered SVG like the olive one.
bin/wallpaper takes an accent name, remembers it in ~/.local/state/wallpaper/
accent (restored at login), and re-picks the folder colour.
pkgs/mactahoe-icon-theme.nix prebuilds MacTahoe-<accent>{,-light,-dark} for all
seven — the folder SVGs of the grey set with #686868 swapped for the accent's
darker ground (--bg-primary-dark) — so every combination is in the store
before it is picked; 163 MB for 24 dirs after jdupes (was 112 MB for 3).
`theme icons` = MacTahoe[-<accent>]-{dark,light}: accent from the wallpaper,
polarity from the theme, stock blue folders when the variant is missing.
Pickers. F2 → bin/theme-prompt theme (noir / claude-dark / claude-light),
Shift+F2 → theme-prompt accent, both the F1/F9/F10 floating-fzf shape with a
theme-prompt window rule. `theme` also gained a flock and a SIGUSR1 fallback
for kitties without our listen_on (from the Omarchy / DankMaterialShell
research, ~/colors/quickshell-live-theming.md).
Live: kitty, niri, herdr, fish, GTK3, folder icons, Chrome's colour scheme.
Next start: GTK4/libadwaita CSS (colour scheme flips live), agent TUIs.
Not here: fonts (another session owns that spec; proprietary, NAS requireFile
pattern only), cliamp/zathura/qt6ct, Claude Code's `custom:` theme file (the
mechanism is in the 2.1.266 binary, the file format is unverified).
Verified: coordinator home-manager-generation builds; coordinator and client
system toplevels evaluate; both MacTahoe packages build with the expected
hexes in gtk-3.0/gtk-4.0 CSS and the olive hex in MacTahoe-olive folders;
niri validate; `wallpaper olive` / `theme apply` / both picker lists live.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tom 2026-09-17: "i'm not using either of those TUI projects at all. so let's delete them." zathura: the config dir (home/dot_config/zathura), its configDirs entry and the package. Nothing else referenced it; PDFs have no mimeApps binding to it. cliamp: the package (pkgs/cliamp.nix + overlay entry), home/dot_config/cliamp, its two xdg.configFile links, the one-time cliampRealDir activation (safe to drop: a generation with no .config/cliamp path at all lets Home Manager's cleanup remove an old whole-dir link on its own), the cliamp fish wrapper function and the m/music/mshuffle/mp/mn/mb/mnow aliases, the .gitignore block for its runtime files, and bin/media's cliamp preference (it now drives playerctl's default player). Kept: Navidrome itself, `mscan`/navidrome-scan (its Subsonic client id is now `navidrome-scan`), and the navidrome-credentials secret, which navidrome-scan still reads — only the comments in secrets.nix, modules/secrets.nix and hosts/coordinator/services.nix that named cliamp as its consumer change. No rekey. The piri music scratchpad stays; its "later a cliamp-in-kitty pane" notes go. Historical records (DECISIONS.md, the zenbook return doc, a test comment) are left as written. Not touched: ~/.config/cliamp on the coordinator is a real directory holding cliamp's history.toml and resume.json (which carries a Navidrome Subsonic token, DECISIONS.md 2026-09-13); the switch removes only the two links. Verified: coordinator home-manager-generation builds with no cliamp or zathura in home-files or home-path; coordinator, client and nas toplevels evaluate; niri validate; fish -n on the edited fish files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ch works Opening nvim on the coordinator after pulling #417, before its switch: lua/plugins/lualine.lua:144: module 'theme' not found lualine.lua is a per-file RAW link, so it already had the new `require('theme')`; lua/theme.lua was a NEW nvimRaw entry, and per-file links only appear on a switch. Exactly the "checkout ahead of its switch" case the design doc promised would degrade gracefully, and nvim did not. - theme.lua moves to home/dot_local/bin/nvim-lua/. ~/.local/bin is ONE out-of-store link into the checkout, and init.lua appends ~/.local/bin/nvim-lua/?.lua to package.path before plugins load (the same path claude_slash.lua already rides), so require('theme') resolves as soon as the checkout has the file — no rebuild in either direction. - nvim.nix drops the lua/theme.lua nvimRaw entry. - lualine.lua pcalls the require, with noir's two colours as fallback, so a missing module can never cost the status line again. - `theme`'s live reload (`require("theme").reload()` over --remote-expr) is unchanged: same module name, same package.path. Verified on the coordinator's CURRENT (pre-switch) generation: nvim --headless fires VeryLazy, require('theme') returns the noir palette, lualine loads and sets its highlights. home-manager-generation builds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fig dir; drop the dead claude-credentials seed Two Claude Code seats live on the coordinator: cc in ~/.claude (the personal Claude Max login) and cc2 in ~/.claude-work (the leger.run login). Tom re-logged both by hand on 2026-09-22; this makes the arrangement durable. - fish: cc/cac/cc2/cac2 become one launcher. Claude Code never saves workspace trust for $HOME (docs: "trust acceptance is held for the current session only and is not written to disk"), so a seat typed in ~ showed "Accessing workspace" on every launch; the launcher now moves into $CLAUDE_ENVELOPE (default ~/today) when started from ~. `cc` clears CLAUDE_CONFIG_DIR explicitly: a terminal opened from a cc2 session inherits it, and the old alias then silently started cc2 (measured with a stub claude under runtime-test). TALLY_SEAT names the seat to hooks and receipts. cc3 lines are untouched. - secrets: remove the claude-credentials seed and its age file. The token had been dead since the 2026-08-04 rotation, and a seed that fires whenever ~/.claude/.credentials.json is absent could only revert a fresh /login after a rebuild. Verified: nix eval of the coordinator's age.secrets no longer lists it and the other nineteen are intact. - AGENTS.md: the seats paragraph (which login is which directory, the launch rule, where the meters are). Not a rebuild, not a merge; both are Tom's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
home/dot_config/ghostty/config.ghostty mirrors kitty.conf key for key so a cmux terminal pane looks like a kitty window, and names the gap wherever Ghostty has no equivalent key. home/home.nix adds ghostty to configDirs so the out-of-store symlink is declared: ~/.config/ghostty already points into this checkout (created 2026-09-17) but is absent from generation 239's home-manager-files, so it is an orphan today and this is what makes it legitimate. home/themes/default.nix renders a per-theme ghostty fragment alongside kitty.conf. docs/theme-switcher-2026-09-17.md gains the consumer row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comments only, verified: zero non-comment added or removed lines across the three files, so no partition table, device id, size, mountpoint or filesystem change. Records which disks belong to whom and which depart with the worker chassis. Per FRONT-10 no agent invents or enforces the return date. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nvim: load the theme module from nvim-lua/ (fixes "module 'theme' not found" before the switch)
modules/halogen.nix opened `port` on `lanInterface` for every host that set `services.halogen.enable`. modules/strix.nix declares the server on BOTH twins and splits only `autoStart`, so the coordinator — which by design serves no model — was admitting :8731 on wlp192s0, its wifi uplink. The live box had the rule (`-A nixos-fw -i wlp192s0 -p tcp --dport 8731 -j nixos-fw-accept`) with nothing listening behind it: an open door to an empty room, which `halogen-switch flash` would have furnished with an unauthenticated inference endpoint. The "no authentication is fine, it is admitted on the LAN interface only; every client on that segment is a pinned house device" argument at the top of this module is true of the worker's wired enp191s0. It was never a claim about the coordinator's wifi segment. New `openLanPort`, defaulting to `autoStart`, now gates the firewall line. Declaring the server and serving the fleet become separate statements: the worker keeps its door, the coordinator loses one it was never meant to have, and a host that genuinely wants to serve sets one visible option instead of inheriting it from `enable`. Coordinator-local `halogen-switch` use is unaffected — loopback is never filtered. flake.nix:1695 already asserted exactly this and had been failing since 2026-09-16, taking `checks.nas-topology` — and everything evaluated after it — down with it. `nix flake check --offline --no-build` is green again. No change to the assert; it was right all along. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… verb The hook blocked every session close and Claude Code reported the abort as `SessionEnd hook [...] failed: Hook cancelled` each time. The script was not at fault — it always exited 0, always logged its one line, and timed itself out under the hook timeout. The shape is what fails: the session process WAITS for a SessionEnd hook before it exits, and the runs that actually harvested took up to 57 s. Its own ledger is the argument against it. Of the last 101 runs: 46 created a note, 1 updated one, 54 skipped — mostly `one cleaned user/assistant turn exceeds the declared utility context`. Every close paid the latency; fewer than half bought anything. Removed: the script, its home.nix link, the SessionEnd block in home/dot_claude/settings.json, checks.ai-memory-harvest-hook, tests/ai-memory-hook/, tools/mem-2-hook-oracle.sh, tools/mem-2-eval-probe.sh. Kept: ai_memory.py, the `harvest` verb, its --enqueue leg (FIX-E08, #348) and MEM-3's probe. The `drain` skill still runs the verb on demand, so only the automatic leg is gone, not the capability. The removed check asserted there was no SessionStart block, so a hook naming an unshipped script could not come back by accident (the dead herdr-agent-state.sh one, removed 2026-09-13). That guard is not lost — checks.no-claude-code-hooks replaces it and is strictly broader: it fails on ANY hook block in settings.json and on anything delivered into ~/.claude/hooks, which stays a real, writable directory owned by no link. Re-adding a hook is now a deliberate edit to it. docs/local-ai/harvest-on-close.md is kept and banner-marked as removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`nix flake update llm-agents`, the documented way to move the agent catalog without touching the kernel/Mesa pin. From 4a6df59b (2026-09-09) to f54d9ae0 (2026-09-23), carrying its own nixpkgs (2026-09-08 -> 2026-09-22) and bun2nix. claude-code 2.1.266 -> 2.1.280 codex 0.153.4 -> 0.155.1 ccusage 20.0.24 claude-agent-acp 0.81.0 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 23, 2026
Closed
…(DF-6) a775d58 (remove the SessionEnd harvest hook) also carried the home-profiles asserts from f1d689b (#448, parakeet socket activation), which this branch does not contain. The coordinator home here still has parakeet-service with Install.WantedBy = [ "default.target" ] and no socket, so checks.home-profiles failed with `attribute 'parakeet-service' missing` at flake.nix:2036. Restore main's asserts for this check. The socket asserts belong in #448 and return when it merges. No module or host change; only the check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit f7b3423981d66d17e32ff34fc3226ac4dc3eb72e)
Owner
Author
|
Retargeted to main (overnight run 09-23, plan A1, after #466 landed Tom's E13 '8731-close'). Merged main |
mecattaf
added a commit
that referenced
this pull request
Sep 23, 2026
Conflicts in flake.nix (checks), hosts/coordinator and hosts/worker: both sides are additive (ax-fleet checks and myAxFleet vs DF-5/G1 checks and myGvisor.enable = false); kept both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent changes, applied to the coordinator (generation
4vda6fvd…) and verified live.1.
334e5964— halogen: the LAN door followsautoStart, notenable(#460)modules/halogen.nixopenedportonlanInterfacefor every host settingservices.halogen.enable.modules/strix.nixdeclares the server on both twins and splits onlyautoStart, so the coordinator — which by design serves no model — was admitting the unauthenticated:8731API on wlp192s0, its wifi uplink.The module's own "no authentication is fine, it is admitted on the LAN interface only; every client on that segment is a pinned house device" argument is true of the worker's wired
enp191s0. It was never a claim about the coordinator's wifi segment.New
openLanPort(defaultautoStart) gates the firewall line. Declaring the server and serving the fleet are separate statements again.flake.nix:1695had asserted exactly this since 2026-09-16 and was failing, takingchecks.nas-topology— and everything evaluated after it — down with it. The assert is unchanged; it was right all along.2.
a775d58f— remove the SessionEnd harvest hookIt blocked every session close; Claude Code reported the abort as
SessionEnd hook [...] failed: Hook cancelledeach time. The script was not at fault — it always exited 0 and timed itself out under the hook timeout. The shape is: the session process waits for a SessionEnd hook, and harvesting runs took up to 57 s.Its own ledger is the argument: of the last 101 runs, 46 created a note, 1 updated one, 54 skipped.
ai_memory.py, theharvestverb, its--enqueueleg and MEM-3's probe all stay —drainstill runs the verb on demand, so only the automatic leg is gone.checks.no-claude-code-hooksreplaces the removed check and is strictly broader: it fails on any hook block insettings.jsonand on anything delivered into~/.claude/hooks.3.
3a658991— llm-agents to 2026-09-23 HEADVerified on the coordinator after switch
Coordinator
wlp192s0drops to[80, 443]; workerenp191s0keeps[3003, 8731].Unrelated: the switch exited non-zero on
fwupd-refresh.service(PolicyKit daemon is not available), a pre-existing failure independent of these changes. Not suppressed, not addressed here.Closes #460.
🤖 Generated with Claude Code