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.
- package/README.md +2 -1
- package/dist/main.js +2678 -1991
- package/dist/runner.js +238 -139
- package/docs/configuration.md +3 -3
- package/docs/design.md +1 -1
- package/docs/harness/claude-code.md +3 -3
- package/docs/harness/codex.md +6 -4
- package/docs/man.md +106 -17
- package/docs/vocabulary.md +11 -8
- package/package.json +1 -1
package/docs/configuration.md
CHANGED
|
@@ -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` |
|
|
65
|
-
| `draftPr` | `true` |
|
|
66
|
-
| `checkpointOnStop` | `true` | Before a nonterminal stop, checkpoint eligible tracked and untracked files
|
|
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
|
|
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`
|
|
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
|
package/docs/harness/codex.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
21
|
-
one draft PR when `gh` is available. On a nonterminal stop they
|
|
22
|
-
eligible worktree files and push once more
|
|
23
|
-
|
|
24
|
-
|
|
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 #
|
|
449
|
-
#
|
|
450
|
-
# worktree
|
|
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 =
|
|
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
|
-
**
|
|
461
|
-
|
|
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
|
-
|
|
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.
|
|
489
|
-
|
|
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
|
|
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.
|
package/docs/vocabulary.md
CHANGED
|
@@ -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>`),
|
|
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
|
|
329
|
-
|
|
330
|
-
|
|
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 (
|
|
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
|
|
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
|