Persistent VM hostname status indicator in Claude and Pi harnesses #8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "agent/agent-vm-status"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes allod/profiles#7
Every
piandclaudesession on a dev VM now shows a persistentVM: <hostname>indicator in its own footer/status line, sourced once fromosConfig.networking.hostNameand fanned out to both harnesses so they render the identical string.What changed
modules/ai-agents.nixgainsosConfigin the inner module args and bindshostName = osConfig.networking.hostName. Four Nix store artifacts are added (immutable, content-addressed, directly testable):claudeStatusLine(astatusLinecommand script that discards stdin and printsVM: <hostname>),claudeStatusMerge(ajqmerge that sets only the.statusLinekey of~/.claude/settings.json, guarded by a single-JSON-object preflight and an atomicmv),piStatusExtension(an auto-discovered Pi.tsthat re-applies the footer status onsession_start), andpiStatusInstall(an installer that refuses to replace any non-managed file, directory, or symlink at the extension target). A dedicatedhome.activation.agentVmStatusentry (entryAfter ["writeBoundary"], kept separate fromllmMemoryLinks) invokes the Pi installer and the Claude merge, each NON-FATALLY via|| warnEcho, so a refusal prints a loud error and leaves operator state byte-unchanged without aborting the sharedset -euactivation script.flake.nixgains anagent-vm-statuscheck that mirrorspi-integration, embeds the activation.dataviapkgs.writeText(whose string context realizes all four artifacts into the check closure), and exercises the artifacts plus their negative/sabotage paths.Risk
Residual risk R2 (Medium), unchanged from the plan. Blast radius is dev VMs only (
ai-agents.nixis imported solely by the dev-VM builder); no secret, provisioning phase, privacy VM, host authority, or cross-repo sequencing is touched. The only persistent, operator-authoritative state is~/.claude/settings.json(merged, not owned; every non-statusLinekey preserved) and the exact Pi extension target~/.pi/agent/extensions/vm-status.ts(refused if it is a non-managed file, directory, or symlink). Both mutable-state helpers are invoked non-fatally, so a target collision or malformed operatorsettings.jsoncannot skip a later activation entry — containment comes from non-fatality, not from DAG ordering, becauseinstallPackagesandsyncPrBranchProtectionsort after this entry. The R2 score rests on the behavioral fixture coverage below; without it the honest score would drift to R3. Rollback is a straight revert plus a human rebuild; the residual runtime symlink and.statusLinekey are inert if left and trivially removable.Validation
All agent-runnable acceptance tests from the plan (steps 1-6) were run here on
allod-dev(x86_64-linux) and passed. Step 1:nix build .#checks.x86_64-linux.agent-vm-statuspasses, and the check asserts both helpers are named in the activation text, both are invoked non-fatally viawarnEcho, and the Pi extension bakesVM: allod-devwithctx.ui.setStatus("vm-status"onsession_start. Step 1a: the Pi installer fixtures pass (fresh install creates the managed symlink; an old managed store symlink is replaced; operator file, directory, and custom symlink are each refused byte-unchanged). Step 1b: the Claude merge fixtures pass (operator keys preserved,.statusLine.commandpoints at the realized script, idempotent byte-for-byte; empty, scalar, array, multi-document, and malformed inputs are all refused with the file byte-unchanged). Step 2: the Claude render pipes a representative stdin object and stdout carriesVM: allod-dev. Step 3: a hostname-mutated statusline script fails closed. Step 4:pi --mode rpc --offlineon the real generated extension emits{"type":"extension_ui_request",...,"method":"setStatus","statusKey":"vm-status","statusText":"VM: allod-dev"}with exit 0. Step 5: a syntactically broken extension exits non-zero and emits nosetStatus. Step 6: the fullallod-devtoplevel closure still evaluates.warnEchowas confirmed defined in the built home-manageractivatescript (sourced fromlib/bash/home-manager.sh).One deviation from the plan text: the acceptance-test store-path extraction uses the character class
[^"* ]+rather than[^" ]+. The plan's[^" ]+-pi-vm-status.tsalso matches the installer's owncase-guard glob/nix/store/*-pi-vm-status.ts(which likewise ends in-pi-vm-status.ts), andhead -1selected that glob instead of the reallntarget, sopiextbecame a literal/nix/store/*-pi-vm-status.tsand the render assertions failed on a nonexistent file. Excluding*(a real store path never contains*,", or a space) selects the reallntarget; the generatedpiStatusInstallartifact itself is exactly per the plan's Interface Contract and was not changed.Remaining human-gated follow-ups (cannot run without a rebuild): a human runs
nixos-rebuildonallod-devto make the indicator live, then eyeballs a realpiandclaudeTUI to confirm the footer/status row persistently showsVM: allod-devacross turns and after/new(pi) / new messages (claude). Claude's row needs an accepted workspace-trust prompt and is transiently hidden during autocomplete/permission prompts — those UI caveats are expected, not defects. If a row is absent after rebuild, checkjournalctl -u home-manager-allod.servicefor the helper ERROR/warning first.