6 Commits

Author SHA1 Message Date
marfrit 162d5cdd10 deploy: install every executable in bin/, not just bullpen-* (#98 follow-up)
install.sh promises "the running fleet == the repo, always; editing the live file IS
editing the tracked file". Its bin/ loop globbed `bin/bullpen-*`, so five entrypoints were
never deployed by it at all: `bp`, `room-ask`, `room_tail`, `news-de` and `deus-seize` —
the commands a human types, plus the emergency stop. They had been hand-copied once and
then drifted silently. Measured on 2026-08-02: the room host was running a `bp` from
2026-07-21 (61 lines behind the repo, which is how `bp --route` came back as "no reply
from @--route"), and deus-seize had never reached any host, so tonight's portability work
on it was live nowhere.

The hole is invisible to inspection: the glob reads as "the bullpen commands", and every
file it misses is sitting right beside the ones it catches. tests/test_install_covers_bin.py
compares the two sets instead of reading the pattern — red under the old glob, green now.

Directories and non-executables are skipped explicitly, so bin/__pycache__ (which appears
the moment anything in bin/ is imported) cannot be symlinked into /usr/local/bin.
2026-08-02 09:58:00 +02:00
marfrit c2af7b6882 systemd: units carry no username — one file, correct in both install layouts (#100)
deploy/install.sh symlinks the SAME tracked unit into ~/.config/systemd/user OR
/etc/systemd/system, so any literal ExecStart path is wrong in one of them. That
is why the path flip-flopped: 6905f4e moved %h/.local/bin -> /usr/local/bin ("--
system needs it"), 933dd86 moved it back but as /home/mfritsche/.local/bin,
dropping the specifier. bullpen-triage crash-looped 27000+ times on 203/EXEC.

Use a pair that resolves correctly in both scopes instead:
  Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin
  ExecStart=/usr/bin/env <prog>
Measured on hertz, not recalled: in a system unit %h expands to /root, so
/root/.local/bin misses and /usr/local/bin wins; in a user unit it expands to the
user's home and ~/.local/bin wins first. /usr/bin/env resolves the program
through the unit's own PATH in both scopes.

The two layouts stay as they are — DEPLOY.md justifies them with a trust
boundary (agent tier without fleet credentials vs. coordinator tier with sic
reach), so collapsing them would trade a security property for deploy comfort.

tests/test_units_portable.py fails on the pre-fix tree (exactly these 7 units)
and passes after. Verified live: daemon-reload + restart of every affected unit
on noether, all active/running, NRestarts=0.
2026-08-01 23:49:25 +02:00
Markus Fritsche 9582236821 install.sh: deploy systemd units from repo; add grinder-godev unit
Extends install.sh to symlink systemd/ + lurker/ unit files into place (/etc/systemd/system
for --system, ~/.config/systemd/user otherwise) + daemon-reload, so unit files are also
git-pull-updatable rather than hand-copied. Captures the live-only bullpen-grinder-godev
unit into the repo (sibling of grinder-py, --serve godev). Verified: every live unit on
hertz + noether is byte-identical to repo, so converting the copies to symlinks is a no-op
for the running services.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-24 13:33:19 +02:00
Markus Fritsche 0383a8f5b2 Add deploy/bullpen-up — verified one-shot Incus room bring-up (#100)
The road to `bullpen up`: launch a bridged Debian system container, install lmcp
from the marfrit apt repo + this repo, seed config + a room lmcp, then round-trip
a room_say/room_read INSIDE the container so you get a proven box, not a hopeful
one. Env-overridable; defaults target the origin fleet's public repo + apt repo.
--verify re-runs just the probe; --destroy tears a throwaway down.

Verified end-to-end on a throwaway Incus container (boltzmann): launch -> apt+lmcp
-> install.sh -> seed -> room service -> probe round-tripped (real room.jsonl
entry), idempotent on re-run, clean --destroy.

Also:
- install.sh --system: /usr/local layout for the room/worker host (bin ->
  /usr/local/bin, lib -> /usr/local/lib/bullpen, contrib/lmcp-tool onto PATH) —
  matches how hertz is wired; the plain user mode is unchanged.
- contrib/lmcp-tool: vendor the lmcp client the coordinators call by name. It was
  a loose unpackaged script on the room host (portability gap for a new fleet).
- DEPLOY.md: bullpen-up as the fastest path; manual recipe fixed to actually
  install lmcp + note --system.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-24 12:28:46 +02:00
Markus Fritsche b81ec781d0 Add deploy/DEPLOY.md — two-tier deployment guide (Incus recommended)
Captures the packaging design: bullpen.conf carries fleet-specifics (defaults =
origin fleet). Recommended path = an Incus system container (bridged, systemd,
LAN IP — behaves like metal, no snap/NAT/PID-1 confusion for agents), with the
untrusted-input agent + code-exec tier isolated (no fleet creds, egress-filtered)
per tonights
2026-07-24 11:52:25 +02:00
Markus Fritsche d391a76354 Add deploy/install.sh — symlink live entrypoints to the working copy (anti-drift)
Deployment now flows FROM the repo: install.sh replaces the loose hand-edited
~/.local/bin/bullpen-* copies with symlinks into the checked-out tree, so the
running fleet == the repo and editing a "live copy" is editing the tracked file.
Idempotent, reversible (pre-existing files -> .predeploy-bak). To update a host:
git pull in the working copy. Closes the drift vector that hid a whole session
of edits on one host.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-24 01:15:54 +02:00