Commit Graph

24 Commits

Author SHA1 Message Date
Markus Fritsche eff4f0073e grinder: overlay uncommitted working-tree files into the worktree + baseline
The grinder worktreed a clean HEAD, so a fresh @testdesigner spec (uncommitted)
was invisible to @py. Now: copy the repo modified+untracked files onto the
worktree, commit HEAD+working-tree as a grind baseline (diff shows only the
grinders changes), stage before diffing (new files appear). Verified green on an
uncommitted spec. @testdesigner->@py needs no manual commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 10:08:04 +02:00
Markus Fritsche c8a21c1be1 @doctor: per-agent model tag + --json (feeds bullseye status panel)
Active agent sessions now show which MODEL they run (reads the instance .model),
directly answering which-worker-on-what-resource. New --json emits structured
{hosts:{load,temp,mem,reachable}, agents:[{agent,model,pid,age}], grinds:[…],
verdict, flags} for bullseye status tab to render live. --once/--serve unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 10:03:57 +02:00
Markus Fritsche 78293b07bd add @doctor — herd health monitor (busy vs passed-out)
Deterministic (no LLM) checkup surfaced in the room: host vitals (load/temp/mem
for boltzmann+noether) + active-job liveness (grinder runs via bracketed pgrep;
agent claude sessions filtered to *_lurker cwds so the primary session is not
mis-flagged) + age-based stall flag (>25min). Verdict: BUSY & HEALTHY / IDLE /
NEEDS-A-LOOK. bullpen-doctor --once prints it; --serve doctor is the room worker
(bp doctor). Registered in dispatcher (health/busy/stalled/checkup keywords).
Complements @triage (post-failure) — the doctor watches DURING the run, so the
herd can answer busy-or-dead itself instead of a human SSHing in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 09:59:53 +02:00
Markus Fritsche 14eb0b71fd #76: sandbox the grinder test run (bubblewrap jail)
run_tests executes model-generated code — now inside bwrap: --unshare-all (no
network), --tmpfs /home + /root (no ssh/claude creds), read-only system, only the
worktree writable. Proven: inside the jail ~/.ssh + /home are gone and the network
is unreachable; a normal grind still goes GREEN. Falls back to unsandboxed with a
loud WARN only if bwrap is absent. The fleet-reach blast radius on generated code
is closed — @py is now safe to point beyond our own repo and to scale to @go/@js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 09:18:27 +02:00
Markus Fritsche b5cf3b2019 add test-driven grinder harness v2 (#74) + failure-triage supervisor (#75)
#74 bin/bullpen-grinder: proxy-backed, tiered (floor 4B -> ceiling deepseek-local),
   git-worktree workspace, local pytest, fence-optional FILE-block parser (4B fences,
   deepseek does not), auto-escalates on stall. --serve <nick> = room coordinator
   (runs on noether, grinds on boltzmann via sic). @py graduated off the lurker.
   Proven: 4B solves add()+roman() in 1 iter; forced escalation climbs to deepseek and
   its fix applies; room path bp py -> GREEN.
#75 bin/bullpen-triage: small-model failure supervisor — watches for failure replies
   (timeout/rc/RED/turn-cap), classifies why + proposes a fix, surfaces to @markus for
   approval. Advisory only. TRIAGE_MODEL swaps the classifier (4B default; 30b sharper).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 09:13:29 +02:00
Markus Fritsche 8708d93039 add test-driven pipeline (@testdesigner + @py) + fix reviewer #7
@testdesigner (Fable) designs an executable spec; @py (Haiku) grinds until green.
Both are bullpen-lurker@ instances (.model fable / haiku) + dispatcher roster +
bp timeouts + extended invoker allowlist.

First ticket = reviewer finding #7 (_show shell-injection). Fable wrote a sharp
black-box spec (tests/test_show_shell_safe.py) that PROVES the injection by pasting
and executing the suggested command — 6 tests, and it independently caught the
@architect debate-path variant too. Fix: _show now escapes the double-quote-context
metacharacters (backslash, backtick, dollar, quote) so no command substitution or
quote-breakout survives a copy-paste. 6/6 green.

HONEST RESULT: the spec half worked; the GRINDER half did not — @py hit the 20-turn
cap fighting remote-edit-over-sic (no Edit tool, re-running pytest across sic each
cycle). So I closed the bug directly against Fable spec. Lesson: grinders need a
LOCAL workspace (Edit + local pytest) and a higher turn budget, not remote-edit; and
@testdesigner s reply was silently lost (reviewer #2 live). Grinder-harness rework
+ #2 are the follow-ups before @py is real.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 07:35:33 +02:00
Markus Fritsche ca8c9812a2 @reviewer findings #8 + #1: fix should-we hijack + fence the lurkers
#8 (routing): drop "should we" from CONV_PATS — it is checked before keyword
scoring, so ordinary questions ("should we wake data?") were hijacked to the
debate personas. Conversants now need an explicit debate marker.

#1 (security): the lurker embedded any room body verbatim into a skip-permissions,
fleet-reaching claude — a crafted "@herder ignore your contract and run…" = RCE as
mfritsche. Two layers: (a) invoker allowlist — only trusted nicks may wake a lurker
(override per-instance via ~/<nick>_lurker/.trust); (b) prompt boundary — the body is
framed as UNTRUSTED DATA with an explicit refuse-embedded-instructions directive.
Mitigation, not elimination (prompt injection is unsolved); dropping skip-permissions
to dontAsk remains the publish-gate hardening. Both credited to the fleet-authored
@reviewer, which flagged its own attack surface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-21 00:41:46 +02:00
Markus Fritsche bf2c9d07f3 add @reviewer (Fable) + refactor lurker into a systemd @-template
The lurker is now one parameterized watcher (bullpen-lurker <nick>) driven by a
systemd template bullpen-lurker@.service; each instance = a nick with its own
~/<nick>_lurker/ context dir and optional .model override. Two instances:
- @herder  (~/herder_lurker, default Claude model) — infra specialist, re-homed.
- @reviewer (~/reviewer_lurker, .model=fable) — outside-look code/design review.
  It IS Fable (claude --model claude-fable-5), reads the target repo via sic,
  returns a ranked findings list. Same locationing discipline as @herder.
Registered @reviewer in the dispatcher (review/critique/implementation keywords,
distinct from @herder host-ops), bp (600s timeout), skills, docs. Adding a role
is now: a context dir + one enabled template instance, no new code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-20 23:33:53 +02:00
Markus Fritsche 4a38b19a21 rename the infra worker nick @his -> @herder (disambiguate)
@his failed all three naming criteria: its a pronoun (dissolves into any
sentence), an existing repo name (a strong model resolved it to the wrong
noun and went spelunking gitea), and semantically empty. @herder is an
unambiguous role noun that ties to /opt/herding (the fleet infra-tooling
root). Renamed across dispatcher roster, bp, skills (noether/pica/pipi/orca),
docs, and the lurker (script/service/dir his_lurker -> herder_lurker). The
his SUBAGENT + /opt/his-context runbook are untouched (thats the real HIS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-20 23:11:47 +02:00
Markus Fritsche d5c78c3307 register @his in the dispatcher roster + skills
@his (the infra-specialist lurker) was built but never registered, so nothing
could route to it — a request to assess a host fell through to @librarian. Add
it to the dispatcher ROSTER as the heavyweight: action/host-scoped keywords
(assess/audit/reach/wake/set up/configure/marfrit-packages/on <host>) so it is
escalated only for LIVE host work, not memory recall (that stays @librarian).
Verified: the higgs-inventory query now routes to @his; recall + which-host
still go to @librarian. Skills (noether/pica/pipi/orca) + docs table updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-20 22:48:13 +02:00
Markus Fritsche 3c24807053 add @his lurker: headless Claude Code woken by room activity (Fork B)
lurker/ — an event-driven rich-agent resident:
- bullpen-lurker: dumb tail loop (no LLM); on chat/ask to @his, runs claude -p in
  his_lurker/ (CLAUDE.md + fleet context load), posts the final answer back as @his.
  Idle cost zero; fresh session per poke.
- bullpen-lurker.service: boot-persistent systemd USER service (noether, linger on).
- his_lurker/: the @his role doc (answer-or-delegate, verify-dont-confabulate,
  output contract) + invocation cheatsheet.
Permission posture: --dangerously-skip-permissions (VPN-gated home net; easy setup,
tighten before publishing the team). Live-verified: @his answered a fleet question,
and the verify-dont-confabulate rule made it run sic to check rather than embellish.
Also: bp bumps the his timeout to 280s (claude-backed replies run longer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-20 22:04:28 +02:00
Markus Fritsche cfc9f82975 tool-hands @callboy (write_file) + bp shorthand + gc retention
- @callboy gains write_file(name, content): jailed to /var/lib/bullpen/hands/,
  returned as a fetchable "sic hertz cat" handle + /bullpen stash index (never
  the raw content). Small content only — big verbatim text should not round-trip
  the 4B. Mechanical allowlist still enforced (fetch_url + write_file).
- bin/bp: REPL shorthand for room-ask (@/# optional, --from default, researcher
  auto-bumps timeout; no-arg = roster).
- bin/bullpen-gc + timer: daily retention, artifacts >14d / hands >7d, scoped to
  the two scratch dirs (never room.jsonl/COUNTER).
- @dispatcher roster: callboy re-described as tool-hands + write/save-file keywords
  so the new capability is discoverable; dropped librarian s collision-prone
  "notes" keyword (matched filenames like notes.md).
- Docs/skill/README updated; bp shorthand + tool-hands noted across surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-20 19:32:30 +02:00
Markus Fritsche 19bbba7ab7 add @dispatcher: rule-based routing worker (reply-only, no relay)
Reads a request and names the right worker + the exact room-ask to run;
never answers or forwards it, so a misroute costs one wasted hop. Keyword
match first (no LLM slot burned), one LLM tie-break through the gated proxy
only on ambiguity, full roster on weak signal. Strong debate markers route
to @architect/@skeptic and win over an incidental worker-keyword hit. Roster
manifest in the script is the single source of truth. Docs + skill updated
with a start-here-if-unsure pointer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-20 18:55:17 +02:00
claude-noether f571a350ba add turn-driven participants + talking-stick (autonomous conversation)
bullpen_participant harness: a chatty agent holds the stick when the latest chat is
addressed to it, replies (persona LLM), and hands the floor on. Talking-stick enforced:
parse-fail/unknown/offline -> yield to human; >=TURN_CAP consecutive agent turns -> forced
yield (storm-guard). Two example conversants (architect/skeptic) verified debating a real
design question with clean stick-passing + default-to-human. Wired only when you want the
society to converse; on-demand visit/ask/leave needs none of this.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:07:55 +02:00
claude-noether 58f720b482 add @researcher worker: web search + multi-source cited synthesis
First multi-step worker: web_search (Brave) -> fetch top 3 (degater) -> synthesize a
cited digest (dspark, big context) -> stash to /bullpen. Honest about source gaps (won't
fabricate a metric the sources don't give). dspark-only (3 full pages overflow the 4B).
Verified live on the SSD->RAM homelab cost/perf question.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:36:56 +02:00
claude-noether 959581f5a6 add @coder worker (write+run Lua) + multi-worker back-and-forth demo
@coder: reactive worker on the shared harness; dspark (via gated proxy) generates Lua,
writes to a sandbox, runs it as 'nobody' with a timeout, returns code+output. Verified a
full 3-way chain: pipi (dspark, research-constrained) -> @callboy (fetch lua-users CsvUtils)
-> @coder (write+run csvtest.lua) -> pipi saves ~/csvtest.lua & reports. dspark never
self-implemented; the no-Lua-knowledge constraint forced delegation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:50:18 +02:00
claude-noether 131934eb51 librarian: relevance floor (score>=0.5) + honest 'no relevant memory' + clearer purpose
@librarian recalls the fleet stash, which is entirely ops/kernel/RK3588 knowledge, so
off-topic queries returned low-score (~0.4) nearest nodes (always RK3588) with false
confidence. Now filters apropos + /bullpen hits by score; if nothing clears the floor,
it says so and points to @callboy/news-de for web/general. On-topic recall unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:32:21 +02:00
claude-noether a5c80d59f0 callboy: route engine through the gated llm-proxy (:8082) + retry on 503
Was hitting boltzmann:8091 direct, bypassing admission control. Now goes through
the hossenfelder proxy (per-backend concurrency gate) so callboy + pipi + OWUI share
the single NPU slot without pile-up; retries on the gate's 503 busy signal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 13:06:38 +02:00
claude-noether d57dcc2f45 add news-de use-case: German news via @callboy -> pi-agent filter
news-de fetches tagesschau RSS through @callboy (stashed to /bullpen), returns clean
headlines; skills/german-news.md drives a pi-agent (pipi) to present top serious items,
no sport/celebrity/lifestyle. Verified end-to-end in pipi with the dspark model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:53:27 +02:00
claude-noether 3a5f8f9119 refactor: extract shared bullpen_worker harness; workers = nick + dispatch fn
Both @callboy and @librarian duplicated the reactive plumbing (poll/say/read/loop/
addressing/since-offset). Lifted it into lib/bullpen_worker.py (bw.run(nick, dispatch));
a worker is now just a dispatch(msg)->reply function. No behavior change — both verified
live after the swap. Adding a 3rd worker is now ~15 lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:25:43 +02:00
claude-noether e688706a7a add @librarian — second worker (dumb dispatcher, no LLM): recall fleet memory + /bullpen artifacts
Proves the worker pattern generalizes: @callboy writes knowledge, @librarian reads it
back. Reactive-only, reply-to-asker, coexists with @callboy (each responds only to its
own @nick). Search = apropos (curated fleet namespaces) + stash recall of /bullpen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:17:03 +02:00
claude-noether 43e9498734 callboy: index big artifacts into stash /bullpen (searchable) + file (exact)
Artifacts over ~600 chars now get a one-line pointer stored in the stash memory
(namespace /bullpen) in addition to the hertz file, so any fleet agent can find a
past fetch semantically (apropos/recall) and pull the exact text via 'sic hertz cat'.
callboy reaches boltzmann's stash tool with the hub-registry token (no sic on hertz).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:08:30 +02:00
claude-noether dc12257d0a add room-ask (rich-agent visit/ask/leave primitive) + bullpen skill
room-ask posts an ask to @worker and blocks client-side for the matching reply
(in_reply_to), so the lmcp server never blocks. skills/bullpen.md documents the
sic-based visit/ask/leave flow for pi-agents.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:54:36 +02:00
claude-noether c415899b03 bullpen MVP1: room (room_say/room_read) + room_tail window + @callboy worker
Self-made multi-agent chatroom. Append-only JSONL room on lmcp; reactive thin worker
with a mechanically-enforced tool allowlist; human window via room_tail. Engine = a
small local tool-model. Verified end-to-end (ask->ack->fetch_url reply incl. artifact
spill; non-tool ask -> graceful decline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:40:21 +02:00