Skip to content

PRD 0011: teach the herd the agent protocol — hooks-first state, a task ledger, and an A2A surface - #415

Merged
ralyodio merged 1 commit into
mainfrom
worktree-herd-agent-protocol
Aug 16, 2026
Merged

PRD 0011: teach the herd the agent protocol — hooks-first state, a task ledger, and an A2A surface#415
ralyodio merged 1 commit into
mainfrom
worktree-herd-agent-protocol

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Implements PRD 0011, which is added in this branch.

PRD 0009 built the herd; three limits were the daily friction, and all three were the same one: the herd read paint instead of speaking protocol.

Phase 1 — believe the engine, not the paint

  • moshcode herd hooks install claude|all (remove, status, --dry-run, --json) writes Claude Code's own lifecycle hooks so state comes from the engine. ps reads hook in its new last column instead of screen.
  • Merges, never clobbers. Your own hooks survive; remove takes out only what moshcode put in; a settings file that will not parse is refused rather than overwritten.
  • The hook command is guarded: outside a herd session, or on a box with no moshcode on PATH, it does nothing and exits 0. Screen rules stay as the fallback, so a broken hook degrades to today and never below it.
  • Sessions are launched with MOSHCODE_HERD_NAME / MOSHCODE_HERD_DIR through the one env prefix both substrates share.
  • herd doctor checks substrate, manifest drift, stale reports, an unwritable status dir, and finally says what is wrong with rules.json instead of ignoring it.
  • blocked gains sub-kinds (permission / question / menu), in --json and in notifications so an --ask reply can be aimed correctly.

Phase 2 — the task ledger

  • Every prompt mints a task: id, submission, state transitions with timestamps, and the output as a screen delta. 0600 JSONL, capped at 500 tasks per session.
  • herd tasks, herd task <id>, herd log, herd stats read it back. Blocked time is reported as what it is — human latency.
  • The write is inserted where the watcher's notification decision already happens, not in a second poller. A poll (herd tasks, tasks/get) also closes a task whose session has stopped.
  • wait --any/--all — one loop over all members, not N waits raced, so --any returns and exits. moshscript: herdWait([names], { any: true }), herdTasks(), herdTask().

Phase 3 — the A2A surface

  • herd serve speaks A2A v0.3.0: herd card, per-member cards, message/send, tasks/get, tasks/cancel. Streaming and push notifications are declared off in the capability flags.
  • No unauthenticated mode, loopback included; console.mjs's token discipline reused, a loud warning past loopback, and --agent sessions withheld unless --expose-autonomous.
  • herd remote add <name> <url> --kind a2a|run puts a deployed agent on the roster. prompt/read/wait/kill work unchanged on it — the acceptance test is a fan-out with no if (remote) in it. Auth comes from MOSHCODE_REMOTE_<NAME>_TOKEN, never the manifest.

Phase 4 — evals and the DO toolchain

  • herd eval --dataset --engines --judge --threshold scores engines against your dataset, exiting 0 pass / 4 below threshold / 5 harness failure. evals/moshcode.jsonl plus a hand-written (not fleet-managed) workflow that turns itself on when a credential secret exists.
  • gradient joins the tool table with a Python 3.10+ check that names the requirement, plus screen rules and a template pointer.

Verification

  • Full suite green: 1916 passing, 0 failing (119 new tests).
  • Hooks install/remove exercised against a real settings file that already had someone else's hooks in it.
  • Ledger, sub-kinds, doctor and fan-in exercised against a live tmux herd.
  • Remote members and herd serve exercised against a fake A2A agent and a bare POST endpoint: discover → send → get → cancel, including the 401 on every unauthenticated path.

Deliberate deviations

Recorded at the end of the PRD under Decisions taken while building, not edited into its requirements. The two worth reading before review: tasks/cancel interrupts but stops one rung short of killing the member, and a closed task reports completed rather than inheriting the session's idle — without that, an A2A client polls a finished job forever.

Draft: the PRD is status: Draft and these are its decisions to accept or send back.

🤖 Generated with Claude Code

The herd read paint instead of speaking protocol. Three symptoms, one cause:
state came from regex against a screen capture, a prompt left no evidence it
was ever submitted, and the roster stopped at the edge of the box.

Phase 1 — believe the engine. `herd hooks install claude` writes Claude Code's
own lifecycle hooks so state comes from the engine (`authority: hook`), merging
into the user's settings file rather than clobbering it. Sessions are launched
with MOSHCODE_HERD_NAME/DIR so a hook can name what it is reporting for, and a
hook fired outside a herd session does nothing and exits 0. `herd doctor`
checks substrate, manifest drift, stale reports, and — for the first time —
says what is wrong with rules.json instead of ignoring it. blocked gains
sub-kinds (permission/question/menu) that ride in --json and notifications.

Phase 2 — the ledger. Every prompt mints a task: id, transitions with
timestamps, and the output captured as a screen delta, in 0600 JSONL capped at
500 tasks per session. `herd tasks`, `herd task`, `herd log`, `herd stats` read
it back; blocked time is reported as what it is, human latency. The write goes
where the watcher's notification decision already happens, not in a second
poller. `wait --any/--all` replaces the polling loop every fan-out script had.

Phase 3 — the protocol. `herd serve` exposes the herd over A2A v0.3.0
(discovery, message/send, tasks/get, tasks/cancel) behind the moshcode login,
with no unauthenticated mode, loopback included, and --agent sessions withheld
unless --expose-autonomous. `herd remote add` puts a deployed agent on the
roster — a2a or a bare POST endpoint — and prompt/read/wait/kill work on it
unchanged, with auth from MOSHCODE_REMOTE_<NAME>_TOKEN and never in the
manifest.

Phase 4 — `herd eval` runs a dataset across engines with either the dataset's
own patterns or an engine as judge, exiting 0/4/5 so CI can tell a worse agent
from a broken box. gradient joins the tool table with its own runtime check.

119 new tests. Decisions taken while building are recorded at the end of the
PRD rather than edited into its requirements.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

3 finding(s) in the 24 file(s) this pull request changes.

MEDIUM: 3

Severity Rule Location
MEDIUM sql-string-concatenation src/cli-schema.mjs:158
MEDIUM sql-string-concatenation src/cli-schema.mjs:457
MEDIUM sql-string-concatenation src/cli-schema.mjs:583
52 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 5 | **MEDIUM**: 41 | **LOW**: 6

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:79
HIGH sh-remote-script-execution install.sh:83
HIGH tls-verification-disabled src/dns.mjs:741
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:68
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:82
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:108
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:295
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:299
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:344
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:568
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:749
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:751
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:810
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:856
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:926
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1029
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1052
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1074

…and 32 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio marked this pull request as ready for review August 16, 2026 12:57
@ralyodio
ralyodio merged commit 8191e93 into main Aug 16, 2026
5 checks passed
@ralyodio ralyodio mentioned this pull request Aug 17, 2026
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