lobstah 0.5.12 → 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,9 +61,9 @@ Same three keys as the per-repo block. Precedence for every harness setting:
61
61
  | `maxRestartAttempts` | `2` | Bounded restart ladder for dead and wedged runners. |
62
62
  | `wallClockSecs` | `3600` | Initial active-work window. Progress extends it, up to `maxWallClockSecs`; time paused with `report paused --waiting-on` does not count. |
63
63
  | `maxWallClockSecs` | `4 × wallClockSecs` | Hard active-work ceiling across restarts. |
64
- | `pushEarly` | `true` | Push each new committed HEAD to its non-trunk branch on `origin` within 10 seconds. A rejected push is noted and retried only after HEAD moves. |
65
- | `draftPr` | `true` | After first push, adopt an existing PR or open one draft PR when `gh` is available. |
66
- | `checkpointOnStop` | `true` | Before a nonterminal stop, checkpoint eligible tracked and untracked files, then push. Ignored files and secret/build denylist paths are excluded. Set all three switches to `false` for prior runner behavior. |
64
+ | `pushEarly` | `true` | For a new chain, push each new committed HEAD to its non-trunk branch on `origin` within 10 seconds. A rejected push is noted and retried only after HEAD moves. A follow-up whose chain already has a PR does not push automatically; its worker pushes to the existing PR's head branch. |
65
+ | `draftPr` | `true` | For a new chain, after first push, adopt an existing PR or open one draft PR when `gh` is available. A follow-up with a chain PR keeps that PR and its watch; it does not open another. |
66
+ | `checkpointOnStop` | `true` | Before a nonterminal stop, checkpoint eligible tracked and untracked files. A new chain then pushes; a follow-up with a chain PR leaves the checkpoint local for its worker to push. Ignored files and secret/build denylist paths are excluded. Set all three switches to `false` for prior runner behavior. |
67
67
 
68
68
  A runner extends its active-work window when a fresh activity event or new HEAD
69
69
  shows progress at the boundary. The elapsed budget and current window are
package/docs/design.md CHANGED
@@ -702,7 +702,7 @@ with [Preact](https://preactjs.com) and [htm](https://github.com/developit/htm)
702
702
 
703
703
  Components are pure functions of the store. A new snapshot re-renders the
704
704
  page and Preact's reconciliation changes only the DOM whose data changed:
705
- rows, cards, and lobs are keyed, so an unchanged row keeps its node, an open
705
+ rows, cards, stack groups, and lobs are keyed, so an unchanged row keeps its node, an open
706
706
  modal keeps its nodes across ticks, and scroll positions stay where the
707
707
  reader left them — no per-section hashing or manual node preservation. Only
708
708
  the active tab's section renders.
@@ -34,9 +34,9 @@ row, and the session-start brief says when the plugin is behind.
34
34
  | SessionStart hook (`lobstah man brief`) | Prints the session id and a one-line fleet state. A session that is neither helm nor trap gets the two sign-on commands, with the id filled in. |
35
35
  | Stop hook (`lobstah man haul`) | At turn end, blocks with standing attention, or with the command to arm a watcher while work is in flight. Inert unless the session holds the helm or is soaking. |
36
36
  | PostToolUse hook (`lobstah soak beat`) | After every tool call in a soaking session: refreshes the trap's liveness and writes its catch's activity (the tool name and its primary target, redacted). At most once per 30 seconds per trap. No network, no git; always exits 0, errors go to `~/.lobstah/logs/beat.log`. Inert when not soaking or with `[soak].beat = false`. |
37
- | SessionEnd hook (`lobstah stow --quiet`) | Signs a soaking session off when it ends. |
37
+ | SessionEnd hook (`lobstah stow --quiet`) | Signs a soaking session off when it ends and keeps its worktree. |
38
38
  | `man` skill | The orchestrator: the helm, the charter, dispatching, tending, getting woken. |
39
- | `trap` skill | The worker: soaking from a linked worktree, the `wt:` address, the six report verbs, and `paused --waiting-on` before waiting on something external (for ume, the non-blocking push when the await cannot run as a tracked background task). |
39
+ | `trap` skill | The worker: soaking (in a linked worktree, or in one that soak creates), the `wt:` address, the six report verbs, and `paused --waiting-on` before waiting on something external (for ume, the non-blocking push when the await cannot run as a tracked background task). |
40
40
 
41
41
  `lobstah send <id> "<instruction>"` steers live or queued work and wakes a
42
42
  finished dispatch as a follow-up.
@@ -48,7 +48,7 @@ Slash commands, each a shortcut for a CLI verb:
48
48
  | `/lobstah:helm [grounds]` | `lobstah man helm` |
49
49
  | `/lobstah:relieve` | `lobstah man relieve` |
50
50
  | `/lobstah:tend` | `lobstah man tend` |
51
- | `/lobstah:soak` | `lobstah soak` (from a linked worktree) |
51
+ | `/lobstah:soak` | `lobstah soak` |
52
52
  | `/lobstah:stow` | `lobstah stow` |
53
53
 
54
54
  Claude Code also loads the `man` and `trap` skills on its own when you ask
@@ -35,9 +35,9 @@ says when the plugin is behind.
35
35
  | SessionStart hook (`lobstah man brief`) | Prints the session id and a one-line fleet state. A session that is neither helm nor trap gets the two sign-on commands, with the id filled in. |
36
36
  | Stop hook (`lobstah man haul`) | Parks the session at turn end while work is in flight and wakes it when something needs attention. Inert unless the session holds the helm or is soaking. |
37
37
  | PostToolUse hook (`lobstah soak beat`) | After a tool call in a soaking session: refreshes the trap's liveness and writes its catch's activity. Needs Codex 0.117.0+ (see [below](#post-tool-hook-what-codex-has)). Older Codex ignores the event, and a trap's liveness then comes from its reports and its park only. |
38
- | SessionEnd hook (`lobstah stow --quiet`) | Signs a soaking session off when it ends. |
38
+ | SessionEnd hook (`lobstah stow --quiet`) | Signs a soaking session off when it ends and keeps its worktree. |
39
39
  | `man` skill | The orchestrator: the helm, the charter, dispatching, tending, getting woken, and sending a follow-up to finished work. |
40
- | `trap` skill | The worker: soaking from a linked worktree, the `wt:` address, the six report verbs, and `paused --waiting-on` before external waits. |
40
+ | `trap` skill | The worker: soaking (in a linked worktree, or in one that soak creates), the `wt:` address, the six report verbs, and `paused --waiting-on` before external waits. |
41
41
 
42
42
  There are no slash commands: the Codex plugin layout has no commands
43
43
  directory. The skills run the same `lobstah` commands as the README
@@ -104,11 +104,13 @@ brief prints the task id. Pass it on first sign-on:
104
104
 
105
105
  ```bash
106
106
  lobstah man helm --session <task-id> # the helm
107
- lobstah soak --session <task-id> # a trap, from a linked worktree
107
+ lobstah soak --session <task-id> # a trap
108
108
  ```
109
109
 
110
110
  After sign-on, the Stop hook gets the id from Codex on stdin. A trap's
111
- identity is its worktree, so `lobstah soak --wait` needs no flags. Outside
111
+ identity is its worktree, so `lobstah soak --wait` in that worktree needs no
112
+ flags. Outside the trap's worktree, `soak --wait`, `report`, and `stow`
113
+ take `--session <task-id>`. Outside
112
114
  the hook, `man wait`, `man report`, and `man relieve` still take
113
115
  `--session <task-id>`.
114
116
 
package/docs/man.md CHANGED
@@ -17,11 +17,13 @@ you ⇄ liaison (interactive Claude Code / Codex session)
17
17
  The liaison never watches the workers — the daemon does that with no model in
18
18
  the loop. The liaison reads `lobstah status` when you ask, which is the
19
19
  token-efficiency point: supervision is a filesystem read, not a conversation.
20
- Headless runners preserve committed work on `origin` as HEAD moves and open
21
- one draft PR when `gh` is available. On a nonterminal stop they checkpoint
22
- eligible worktree files and push once more; the final status note names the
23
- saved branch, commit, and PR, so the man can send a continuation without
24
- losing work.
20
+ Headless runners preserve new-chain committed work on `origin` as HEAD moves
21
+ and open one draft PR when `gh` is available. On a nonterminal stop they
22
+ checkpoint eligible worktree files and push once more. A follow-up whose
23
+ chain already has a PR keeps that PR and its watch. It does not push or open
24
+ a second PR automatically; its worker pushes to the existing PR's head
25
+ branch. The final status note names the saved branch, commit, and PR, so the
26
+ lobstah man can send a continuation without losing work.
25
27
 
26
28
  **The helm is harness-agnostic: drive the fleet from whichever session you
27
29
  prefer.** The contract is the CLI, not the harness — a Claude Code session,
@@ -217,6 +219,17 @@ attention kinds exactly like a dispatched PR (its dispatch chain column is
217
219
  empty). It stays quiet while it's fine: only a failing check or a changes
218
220
  request surfaces as a watch event; a merge or close arrives as a notice.
219
221
 
222
+ **PR order.** Every PR list uses one order: the glass PRs tab, the On deck
223
+ PR stacks, `lobstah prs`, and the `stack #…` lines and the `work` table of
224
+ `man tend`. Open stacks come first, then finished ones. Within each group, the
225
+ stack whose newest PR was first seen last is on top. Within a stack, PRs are
226
+ in stack position. A PR sorts by its record's `firstSeenAt`, then by number.
227
+ A new observation does not change the order. The order changes when a PR
228
+ opens, merges, closes, or changes its base so that it joins or leaves a
229
+ stack. When two PRs share a head branch, the parent is the one first seen
230
+ last. The attention list is in standing order: the time each condition
231
+ started. `observedAt` is shown as the time of the last check.
232
+
220
233
  An observed PR joins tend's attention list by kind. `pr:ready` needs a
221
234
  mergeable, non-draft PR with no failed, pending, or unknown current checks.
222
235
  `pr:conflict` and `pr:checks` appear for an owned PR only when repair is off,
@@ -354,6 +367,23 @@ must never hide it from the orchestrator that has to answer it. The glass,
354
367
  which has no write endpoint, hides a clicked lob per browser in localStorage
355
368
  instead.
356
369
 
370
+ **The pet's read.** Every six seconds the pet runs `lobstah attention --json`.
371
+ It prints `{ "attention": [...] }`: the same items, with the same fields, that
372
+ `man tend --json` puts under `attention`, and nothing else. If that command
373
+ fails (an older CLI exits 2 on the unknown flag), the pet runs
374
+ `man tend --json` instead. The pet reads the child's output while the child
375
+ runs, so a report of any size works. Each read may take 10 seconds; then the
376
+ pet stops the child and keeps its current windows. After three failed reads in
377
+ a row it writes one line with the reason to `~/.lobstah/logs/pet.log`, and one
378
+ more line when reads work again. After every read it writes
379
+ `~/.lobstah/pet/state.json`. `lobstah doctor` reads that file for its `pet`
380
+ row: installed or not, running or not, and whether the last read worked:
381
+
382
+ ```
383
+ pet ok installed; running (pid 812); last read worked 4s ago (`lobstah attention --json`, 3 walking)
384
+ pet warn installed; running (pid 812); last read failed 2s ago, 3 in a row: `lobstah attention --json` timed out; `lobstah man tend --json` timed out; last worked 5m ago
385
+ ```
386
+
357
387
  **Wrapper loop.** An outer loop blocking on `wait` can spawn one fresh
358
388
  headless turn per event:
359
389
 
@@ -445,23 +475,77 @@ visible terminal, and whatever authenticated tooling a headless spawn can't
445
475
  get.
446
476
 
447
477
  ```bash
448
- lobstah soak # from a worktree — the primary checkout is
449
- # never claimable, so sign on from a linked
450
- # worktree (git worktree add ../side -b side)
478
+ lobstah soak # in a linked worktree: sign it on; in a
479
+ # repo's primary checkout: create a linked
480
+ # worktree and sign that on
451
481
  # (the id: --session, else hook stdin, else
452
482
  # $CLAUDE_CODE_SESSION_ID)
483
+ lobstah soak --repo <key> # outside any configured repo: create a
484
+ # worktree for <key> and sign it on
453
485
  lobstah soak --wait # hookless sessions: listen in the foreground
454
486
  # (re-runs need no flags — identity is the
455
- # worktree); exit 3 = quiet, run it again
487
+ # worktree, else the session id); exit 3 =
488
+ # quiet, run it again
456
489
  lobstah stow # sign off; an open catch requeues, unread
457
- # messages bounce back to the helm
490
+ # messages bounce back to the helm; removes
491
+ # the worktree when soak created it
492
+ lobstah stow --keep # sign off and keep the worktree
493
+ lobstah soak --name amber-gull # choose or change this trap's two-word name
458
494
  ```
459
495
 
460
- **Identity is the worktree.** Sign-on anchors a short trap id in
461
- `.lobstah-trap` and prints the trap's address (`wt:<id>`); the address
496
+ **Soak can create the worktree.** From a repo's primary checkout, or with
497
+ `--repo <key>` from outside any configured repo, soak creates a linked
498
+ worktree the same way a dispatch does. It fetches trunk, adds
499
+ `~/.lobstah/worktrees/soak-<trap>` on a new branch `lobstah/soak-<trap>`
500
+ from `origin/<trunk>`, and runs the repo's `setup` commands. Without
501
+ `--repo`, soak run outside a configured repo fails with an error that names
502
+ `--repo`. In a linked worktree, `--repo` must match that worktree's repo.
503
+ Soak prints `worktree: <path>`, `created: true`, and `branch:`. When the
504
+ session is not inside the worktree, it also prints
505
+ `instruction: cd <path> and work in that directory from now on`, and its
506
+ help lines carry `--session <id>`. The session changes into that directory
507
+ before it takes work. If creation fails (setup fails, the
508
+ `[limits].minFreeGB` check fails, or trunk cannot be fetched), soak signs
509
+ nothing on, removes the partial worktree and its branch, and prints the
510
+ cause. The address is `wt:<trap>`. The registration records
511
+ `createdWorktree: true`, and `.lobstah-trap` records `createdBy: "soak"`,
512
+ the session id, the repo, and the branch.
513
+
514
+ Soak is idempotent. A session that already mans a trap re-uses it when it
515
+ runs `soak` or `soak --wait` outside a linked worktree: the trap is resolved
516
+ from the session id, and no second worktree is created. A worktree that soak
517
+ created for the same session and repo is also re-used after a ghost sweep.
518
+ From outside the worktree, `soak --wait`, `report`, and `stow` find the
519
+ trap by session id (`--session <id>`, or `$CLAUDE_CODE_SESSION_ID`), and
520
+ `send session:<id>` addresses it. With `--session`, or from inside the worktree, `report done` records the
521
+ worktree's HEAD commit and branch in evidence.
522
+
523
+ `stow` removes a worktree only when soak created it and nothing in it exists
524
+ elsewhere. It keeps the worktree and prints `worktree: kept` and a `reason:`
525
+ when soak did not create the worktree, or when the worktree has uncommitted
526
+ changes, untracked files that are not ignored, or commits on no remote
527
+ branch. Ignored files do not block removal. Stow never forces a removal.
528
+ Stow runs the removal from the primary checkout, so it works from inside
529
+ the worktree. On removal it prints `worktree: removed`, `path:`, and
530
+ `returnTo: <primary checkout>`, with a help line `cd <primary>`. It
531
+ deletes the branch only when every commit on it is on its upstream (with no
532
+ upstream: on some remote branch);
533
+ otherwise it prints `branchKept: <branch> (<reason>)`. A deleted branch
534
+ prints as `branchDeleted:`. `stow --wt <id>` follows the same rules. The
535
+ SessionEnd hook (`lobstah stow --quiet`) signs off and keeps the worktree.
536
+
537
+ **Identity is the worktree.** Sign-on anchors a short trap id and two-word
538
+ name in `.lobstah-trap` and prints both, such as `amber-gull (wt:c32a245d)`; the address
462
539
  survives session restarts — a new session in the same worktree resumes the
463
540
  same trap (a *live* foreign session is refused: the session lock). The
464
- session id (from the plugin's session-start brief) lives inside the
541
+ name is reserved across all traps known to this lobstah home, including
542
+ stowed and swept traps. `--name` sets or changes it; malformed and taken
543
+ names are refused. The bare name, `wt:<name>`, and `wt:<id>` all address the
544
+ same live trap in `dispatch --for`, `send`, and `stow --wt`. Unknown names
545
+ list known names and never turn addressed bait into headless work. The id
546
+ remains the key in dispatch and claim records.
547
+
548
+ The session id (from the plugin's session-start brief) lives inside the
465
549
  registration as the liveness principal. The harness (claude or codex) is
466
550
  inferred — from `CLAUDE*` / `CODEX*` in the environment, and when both are
467
551
  set (one harness launched inside the other) from the session id's format:
@@ -485,13 +569,14 @@ stamped, bounced to the helm when undeliverable.
485
569
  A soaking session proves it is alive three ways: its park heartbeat, its
486
570
  reports, and its **beat**. The plugin's post-tool hook runs
487
571
  `lobstah soak beat` after tool calls: at most once per 30 seconds it
488
- refreshes the trap's beat and writes the claimed catch's activity. A trap
489
- that works for an hour without reporting is not swept while it beats.
572
+ refreshes the trap's beat and writes the claimed catch's activity. The hook
573
+ resolves the trap from the working directory, else from the session id. A
574
+ trap that works for an hour without reporting is not swept while it beats.
490
575
  `[soak].beat = false` turns the hook off.
491
576
 
492
577
  Liveness has two failure shapes with two remedies: a registration that
493
578
  parked before and went quiet (no park, report, or beat) past `[soak].ttlSecs` is a **ghost trap** —
494
- swept, catch requeued, noticed; one that **never parked** is a **defective
579
+ swept, catch requeued, noticed, its worktree kept; one that **never parked** is a **defective
495
580
  enlistment** — noticed with its diagnosis (usually a missing Stop hook →
496
581
  `soak --wait`) and left standing so the address keeps protecting its work.
497
582
  Nobody is conscripted: only a worktree whose session ran `soak` ever
@@ -503,7 +588,11 @@ Worktrees are 1 to 8 GB each. `lobstah cull` sweeps what is finished: `done/`
503
588
  entries older than the window (`--older-than <days>`, default 14), worktrees
504
589
  whose dispatch is finished or gone, stale state files, merged or closed PR
505
590
  records, and orphaned acks. It never touches queued or active work, and
506
- `git worktree remove` keeps each dispatch's branch. A worktree that follow-ups
591
+ `git worktree remove` keeps each dispatch's branch. A worktree that soak
592
+ created counts as in use while a trap registration anchors it. After that,
593
+ the cull and the daemon's retention and free-space culls treat it like any
594
+ other worktree that no dispatch owns: it ages out after
595
+ `[limits].retentionDays`. A worktree that follow-ups
507
596
  reused is one worktree shared by the chain: it stays while any dispatch in
508
597
  the chain is queued or active, and it ages from the newest dispatch that
509
598
  used it.
@@ -277,7 +277,7 @@ evidence).
277
277
  | `merged` / `closed` | Terminal; the check sets `done`, and the watch retires once delivered. Emitted only on an open → terminal change, never on the first observation. |
278
278
  | evidence `pr` | `{ url, number, state, draft, reviewDecision, mergeStateStatus, headSha, checks: { total, passed, failed, pending, unknown? }, review: { unresolvedThreads, changesRequested, lastReviewAt }, observedAt }`. Check counts use only the latest run per check name and app/workflow. `CANCELLED` and `STALE` latest runs are unknown, not failed. The PR record also stores repair status, attempts, and reason. `prBadge` shows `repairing: conflict (attempt 1 of 2)` while a repair is in flight; tend, `catch`, and the glass share the badge. |
279
279
  | checks unknown | Without `Checks: read`, the check re-reads the PR without `statusCheckRollup`: the PR state is recorded, `checks.unknown` is `no permission`, and the check's output carries the permission `error`. `pr:ready` never stands on unknown checks. |
280
- | PR record | `~/.lobstah/prs/<owner>__<repo>__<n>.json` — the PR's latest observation keyed by the PR, not by a dispatch: the evidence `pr` object plus `key`, `repo` (`<owner>/<repo>`), and `dispatches` (the ids whose watch observed it; empty for a human's or a culled PR). **Owner:** `packages/core/src/prs.ts` (`upsertPr`, `readPrs`); the one writer is the preset's observation path (`observePr`), on every observation, man-owned or dispatch-owned — a dispatch-owned one also stamps that dispatch's evidence, which stays the per-dispatch view. Tend's `pr:*` kinds and `pr:ready` stack suppression, the glass PRs tab and stacks, the merged/closed notice, and PR acks read records first and fall back to dispatch evidence only for a PR with no record yet. `cull` removes records merged or closed longer than its window, never open ones. |
280
+ | PR record | `~/.lobstah/prs/<owner>__<repo>__<n>.json` — the PR's latest observation keyed by the PR, not by a dispatch: the evidence `pr` object plus `key`, `repo` (`<owner>/<repo>`), `dispatches` (the ids whose watch observed it; empty for a human's or a culled PR), and `firstSeenAt` (the time of the first observation; written once, never rewritten). `firstSeenAt`, then the PR number, is the order of every PR list. A record from before `firstSeenAt` existed sorts by number at the earliest `firstSeenAt` in the set, and its next observation writes that time as its `firstSeenAt`. **Owner:** `packages/core/src/prs.ts` (`upsertPr`, `readPrs`); the one writer is the preset's observation path (`observePr`), on every observation, man-owned or dispatch-owned — a dispatch-owned one also stamps that dispatch's evidence, which stays the per-dispatch view. Tend's `pr:*` kinds and `pr:ready` stack suppression, the glass PRs tab and stacks, the merged/closed notice, and PR acks read records first and fall back to dispatch evidence only for a PR with no record yet. `cull` removes records merged or closed longer than its window, never open ones. |
281
281
 
282
282
  Every event carries `headSha`. A dispatch-owned PR watch records work events,
283
283
  then the repair planner decides whether to follow up. One repair runs per PR
@@ -325,19 +325,21 @@ Identity is **worktree-anchored**: `.lobstah-trap` in the worktree root
325
325
  holds a short stable id, the registration keys on it, and the address
326
326
  (`wt:<id>`) survives session restarts. The session id inside the
327
327
  registration is the liveness principal. **Owner:**
328
- `packages/core/src/soak.ts`. **Enforcement:** sign-on is refused from a
329
- repo's primary checkout, and a live foreign session in an owned worktree is
330
- refused (the session lock); a stale one is adopted.
328
+ `packages/core/src/soak.ts`. **Enforcement:** sign-on from a repo's primary
329
+ checkout, or with `--repo <key>` from outside any configured repo, creates a
330
+ linked worktree (`~/.lobstah/worktrees/soak-<trap>`, branch
331
+ `lobstah/soak-<trap>`) and signs that on; a live foreign session in an owned
332
+ worktree is refused (the session lock); a stale one is adopted.
331
333
 
332
334
  | Word | Meaning |
333
335
  | ---- | ------- |
334
- | `soak` | Sign the worktree's trap on and take matching work. `--session` is needed only on first sign-on. `--one` stows after the first catch. `--wait` registers a watcher and waits for work; a quiet timeout exits 3. Workers never run `man` verbs. |
335
- | `stow` | Sign the trap off (run it in the worktree); an open catch requeues (a cancelled one finalizes as failed) and unread messages bounce to the helm. A trap always stows itself freely; stowing someone else's (`--wt`) is steering — the claimed helm's alone. Stow closes the seat, never the session: an opted-in session can only be asked to stop, and a still-looping worker re-enlists visibly (`trap-signed-on`). |
336
+ | `soak` | Sign the worktree's trap on and take matching work. From a primary checkout, or with `--repo <key>` from outside any configured repo, it first creates a linked worktree like a dispatch does (fetch trunk, new branch from `origin/<trunk>`, `setup` commands), prints `worktree:`, `created: true`, `branch:`, and, when the session is elsewhere, `instruction: cd <path> ...`; the session works in that directory. If creation fails, nothing is signed on and the partial worktree and branch are removed. `--repo` in a linked worktree must match its repo. A session that already mans a trap re-uses it (resolved from the session id) and never gets a second worktree. `--session` is needed only on first sign-on. `--one` stows after the first catch. `--wait` registers a watcher and waits for work; a quiet timeout exits 3. Workers never run `man` verbs. |
337
+ | `stow` | Sign the trap off (in the worktree, or elsewhere by session id: `--session <id>` or `$CLAUDE_CODE_SESSION_ID`); an open catch requeues (a cancelled one finalizes as failed) and unread messages bounce to the helm. A trap always stows itself freely; stowing someone else's (`--wt`) is steering — the claimed helm's alone. Stow closes the seat, never the session: an opted-in session can only be asked to stop, and a still-looping worker re-enlists visibly (`trap-signed-on`). By default stow removes the worktree when soak created it (`createdWorktree: true`) and prints `worktree: removed`, `path:`, and `returnTo:`; `--keep` leaves it. It keeps the worktree (`worktree: kept`, `reason:`) when soak did not create it, or when it holds uncommitted changes, untracked files that are not ignored, or commits on no remote branch; it never forces a removal. The branch is deleted (`branchDeleted:`) only when all its commits are on its upstream (with no upstream: on some remote branch); otherwise `branchKept: <branch> (<reason>)`. `--wt` follows the same rules. The SessionEnd hook (`stow --quiet`) signs off and keeps the worktree. |
336
338
  | address | `--for wt:<trap>` targets one trap; `session:<id>` is an alias resolved to the trap at dispatch time. **Sticky:** addressed work is never the daemon's — it waits for its trap; an orphan (trap gone) surfaces as a `bait-orphaned` notice for the helm to re-address, release, or cancel. Delivery stamps a receipt (`deliveredTo`/`deliveredAt`) into evidence. Unaddressed work defers to a parked matching trap for `[soak].deferSecs`, then the daemon spawns headless. |
337
339
  | message | `send wt:<trap> "<text>"` — a conversational continuation, not work: no branch, no catch, no report obligation. Delivered before bait at the trap's next park, stamped with its sender (`helm` / `session:<id>` / `terminal`); undeliverable messages bounce to the helm as notices. |
338
340
  | catch | The active dispatch a trap claimed (`claim.json`, `by: wt:<id>`). One catch per trap; one active item per worktree. The daemon never spawns or restarts it — the session's reports are its liveness. |
339
- | beat | `lobstah soak beat`, run by the plugin's post-tool hook after every tool call. It resolves the trap from the working directory (files only: no git, no network), refreshes the trap's beat (`soaking/<trap>.beat`, separate from the registration), and writes the claimed catch's [activity](#activity). Throttled to one per 30 seconds per trap. Inert in a session that is not soaking, or with `[soak].beat = false`. Always exits 0; errors go to `logs/beat.log`. |
340
- | ghost trap | A registration whose heartbeat **and** beat lapsed past `[soak].ttlSecs` **after having parked at least once**. The sweep removes it, requeues its catch, and posts a `trap-ghosted` notice; re-soaking the worktree restores the same address. A fresh report or a fresh beat keeps a working session out of the sweep. A fresh beat also holds the session lock. A catch whose last report is `paused` keeps its trap until the pause expires ([Waiting on](#waiting-on)). |
341
+ | beat | `lobstah soak beat`, run by the plugin's post-tool hook after every tool call. It resolves the trap from the working directory, else from the session id (files only: no git, no network), refreshes the trap's beat (`soaking/<trap>.beat`, separate from the registration), and writes the claimed catch's [activity](#activity). Throttled to one per 30 seconds per trap. Inert in a session that is not soaking, or with `[soak].beat = false`. Always exits 0; errors go to `logs/beat.log`. |
342
+ | ghost trap | A registration whose heartbeat **and** beat lapsed past `[soak].ttlSecs` **after having parked at least once**. The sweep removes it, requeues its catch, and posts a `trap-ghosted` notice; the worktree stays, and re-soaking restores the same address. A fresh report or a fresh beat keeps a working session out of the sweep. A fresh beat also holds the session lock. A catch whose last report is `paused` keeps its trap until the pause expires ([Waiting on](#waiting-on)). |
341
343
  | defective enlistment | A stale registration that **never parked** — signed on but never listened (usually no Stop hook). Not swept: the helm gets a `trap-defective` notice with the remedy (`soak --wait`), and the registration stays so the address keeps protecting its work. |
342
344
  | notice | The helm's attention channel for non-status events (`~/.lobstah/notices/`): sign-ons, first parks, sign-offs, ghosts, defective enlistments, orphaned work, bounced messages, PRs merged or closed, watches held over the fork cap (`watch-held`), failing (`watch-failing`), and recovered (`watch-recovered`), free-space holds (`disk-held`, `disk-cleared`), and worktrees released after their PR merged (`worktree-released`, one per cull pass). A trap leaves the registry only through a `trap-stowed` or `trap-ghosted` notice — the end-state is always explicit. Consumed by `man wait`/the park; tend always shows the recent tail. |
343
345
 
@@ -417,6 +419,7 @@ reminder loop apply the same predicate.
417
419
  | Word | Meaning |
418
420
  | ---- | ------- |
419
421
  | ack | `~/.lobstah/acks/<item-key>.json` — `{ key, kind, stateHash, at, by }`, written only by `lobstah attention ack` (removed by `unack`, by the CLI's `man tend` / `attention` when its `stateHash` goes stale, and by `cull` when the item is gone). **Display-only**: it hides the item from the desktop pet and the glass lobs while the item's `stateHash` is unchanged; `man tend --json` keeps the item with `acked: { at, by }`, and `man wait`, the park, reminders, and `notifyCommand` never read acks. Item keys: `<lane>:<uuid>` (question, landed), `pr:<owner>/<repo>#<n>` (all of a PR's `pr:*` kinds — one ack covers them), `watch:<key>`. `stateHash` covers the status entry (question, landed) or the PR's head sha plus every evidence field a `pr:*` kind stands on (not `observedAt`). |
422
+ | pet state | `~/.lobstah/pet/state.json` — `{ pid, at, ok, command, reason, consecutiveFailures, items, lastOkAt }`. The desktop pet's one write: it rewrites the file after each attention read (about every six seconds). `command` is the command that worked (`attention --json`, or `man tend --json` from an older CLI). `reason` says why the last read failed (timed out, non-zero exit, output that does not decode). Only `lobstah doctor` reads it, for its `pet` row: running means `pid` is alive and `at` is less than two minutes old. |
420
423
  | budget stop | A headless runner's `failed` verb with a `budget:` note means its progress-extended active-work window reached the hard ceiling. This is out of time, not a code failure: the runner checkpoints eligible changes, pushes when enabled, names the saved branch/commit/draft PR, and invites `lobstah send <id> "continue"`. Paused `--waiting-on` time is excluded. |
421
424
 
422
425
  ## Exit codes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lobstah",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
4
4
  "description": "Harness-agnostic, token-efficient supervision framework for coding agents",
5
5
  "license": "MIT",
6
6
  "author": "aequitas labs LLC",