|
2 | 2 | openprd: "0.2" |
3 | 3 | id: "0009" |
4 | 4 | title: "Keep the herd alive — a persistent runtime, semantic agent state, and one control surface for humans and agents" |
5 | | -status: Draft |
| 5 | +status: Accepted |
6 | 6 | authors: |
7 | 7 | - anthony@profullstack.com |
8 | 8 | created: 2026-08-09 |
9 | 9 | updated: 2026-08-09 |
10 | 10 | repo: https://github.com/moshcoder/moshcode |
11 | | -discussion: |
12 | | -implementation: |
| 11 | +discussion: https://github.com/moshcoder/moshcode/pull/341 |
| 12 | +implementation: src/herd.mjs, src/herd-state.mjs, src/herd-cli.mjs |
13 | 13 | tags: [runtime, sessions, agents, tui, notify] |
14 | 14 | supersedes: |
15 | 15 | superseded-by: |
@@ -203,17 +203,26 @@ entries in a new `runtime` group: |
203 | 203 |
|
204 | 204 | | command | what it does | |
205 | 205 | |---|---| |
206 | | -| `moshcode runtime` | start / inspect / stop the background runtime | |
207 | | -| `moshcode ps` | list live sessions with state | |
| 206 | +| `moshcode herd` | the namespace: status, start, prompt, read, send-keys, report, notify, watch, prune, stop | |
| 207 | +| `moshcode ps` | list sessions with state | |
208 | 208 | | `moshcode attach <name>` | attach to a session | |
209 | 209 | | `moshcode kill <name>` | end a session | |
210 | 210 | | `moshcode wait <name>` | block until a state transition | |
211 | 211 | | `moshcode restore` | rebuild sessions from the manifest | |
212 | | -| `moshcode agent <verb>` | start / prompt / read / send-keys / stop | |
213 | 212 |
|
214 | | -TUI equivalents follow the existing convention: `/ps`, `/attach <name>`, |
215 | | -`/kill <name>`, `/restore`. `/agents <engine>` keeps its meaning and simply |
216 | | -gains `--name` and a detachable session underneath it. |
| 213 | +The namespace is `herd`, not the `runtime` / `agent <verb>` this document first |
| 214 | +proposed. Two reasons, both found while building it. `agent` is already a |
| 215 | +registered alias of `agents` in `PIT_COMMANDS`, and a test pins |
| 216 | +`suggest("agent") === "agents"` — so `moshcode agent start` would have meant two |
| 217 | +different things depending on where it was typed. And `runtime` is what |
| 218 | +`src/runtime.mjs` already calls the moshscript interpreter. The five verbs |
| 219 | +people reach for most are top-level anyway, which is what the original table was |
| 220 | +really asking for: nobody should have to learn a namespace to ask what is |
| 221 | +running. |
| 222 | + |
| 223 | +TUI equivalents follow the existing convention: `/herd`, `/ps`, `/attach <name>`, |
| 224 | +`/kill <name>`, `/wait`, `/restore`. `/agents <engine>` and `/start <engine>` |
| 225 | +keep their meaning and simply gain `-d` / `--name`. |
217 | 226 |
|
218 | 227 | **The pit's front door changes.** Today `moshcode` prints a banner and a prompt. |
219 | 228 | With anything running it prints the herd first: |
@@ -301,3 +310,53 @@ exactly like today. No repeated nagging, no failure. |
301 | 310 | - **Scope.** Phases 1–3 are independently shippable and should ship that way. |
302 | 311 | Phase 1 alone — sessions that survive the terminal — is the bulk of the value |
303 | 312 | and does not require a single line of state detection. |
| 313 | + |
| 314 | +## Implementation Notes |
| 315 | + |
| 316 | +Written after the build, so the document and the code agree. |
| 317 | + |
| 318 | +**A second substrate, which this PRD did not ask for.** R2 promised only to |
| 319 | +degrade gracefully without tmux. That was not good enough: `/new` already |
| 320 | +required tmux and it is the wart people notice. So there are two substrates |
| 321 | +behind one interface — tmux when the box has it, and otherwise `script(1)` with |
| 322 | +the session's stdin on a FIFO, reusing the capability detection `pty.mjs` |
| 323 | +already does. The FIFO is opened `O_RDWR` before the spawn so the child is its |
| 324 | +own writer and never sees EOF when the pit exits, which is the whole trick. Its |
| 325 | +one real limit: nothing outside a pty can ioctl its master, so the size is fixed |
| 326 | +at launch (set from inside by `stty`) and a later resize does not reach it. |
| 327 | +`MOSHCODE_HERD=pty` forces it, which is how the fallback is tested on a box that |
| 328 | +has tmux. |
| 329 | + |
| 330 | +**Two bugs the survival test caught**, both of which would have shipped as |
| 331 | +"finished agents report `gone`". tmux's `remain-on-exit` was being set in a |
| 332 | +second call, and a fast command finishes before that process starts — fixed by |
| 333 | +making the session and its option one invocation using tmux's `;` argument. And |
| 334 | +the pty substrate could not tell "the agent finished" from "the box rebooted", |
| 335 | +since both are a dead pid — fixed by having the session's own shell record its |
| 336 | +exit code on the way out. |
| 337 | + |
| 338 | +**Delivered:** R1–R12 and R14. Both substrates are covered by an integration |
| 339 | +test that starts a session in one process, exits it, and talks to the session |
| 340 | +from another. |
| 341 | + |
| 342 | +**Not delivered, deliberately:** |
| 343 | + |
| 344 | +- **R7 tier-1 hook installation.** The protocol ships and works — |
| 345 | + `moshcode herd report <name> <state>` takes authority, suppresses screen |
| 346 | + classification entirely while it is live, and expires so a crashed agent |
| 347 | + cannot read `working` forever. What is not built is auto-installing that call |
| 348 | + into each engine's hook config via the `plugins.mjs` / `skills.mjs` fan-out. |
| 349 | + Until then tier 1 is opt-in and tier 2 carries the roster. |
| 350 | +- **R13, the browser as a real client.** `console.mjs` still points ttyd at a |
| 351 | + shell rather than at `moshcode attach <name>`, and `mirror.mjs` keeps its |
| 352 | + documented blind spot. The runtime it would attach to now exists, so this is a |
| 353 | + small follow-up rather than a design question. |
| 354 | +- **R15, scrollback replay.** P2 and opt-in in this document; still the right |
| 355 | + call not to write engine output across a reboot by default. |
| 356 | + |
| 357 | +**Rules will rot, and that is planned for.** The shipped patterns are |
| 358 | +conservative and anchored to things a terminal draws — brackets, selectors, line |
| 359 | +anchors — never bare English words, and a test asserts that. `unknown` is |
| 360 | +common and safe. `~/.moshcode/herd/rules.json` lets a rotted pattern be fixed on |
| 361 | +the box it rots on, and a malformed entry there loses that pattern rather than |
| 362 | +the file. |
0 commit comments