lobstah 0.5.13 → 0.6.0
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/dist/main.js +7044 -3118
- package/dist/runner.js +1513 -1113
- package/docs/configuration.md +15 -9
- package/docs/harness/claude-code.md +14 -0
- package/docs/harness/codex.md +12 -0
- package/docs/man.md +471 -39
- package/docs/pickup.md +11 -3
- package/docs/vocabulary.md +120 -22
- package/package.json +1 -1
package/docs/configuration.md
CHANGED
|
@@ -14,7 +14,7 @@ keys of that section.
|
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| `notifyCommand` | — | Exec'd by the daemon on wake-worthy status transitions with `LOBSTAH_ID`, `LOBSTAH_LANE`, `LOBSTAH_VERB`, `LOBSTAH_NOTE`, `LOBSTAH_AT` in the environment. Fire-and-forget; point it at ntfy, a Slack helper, anything. |
|
|
16
16
|
| `notifyVerbs` | `["needs-decision", "blocked", "done", "failed"]` | Which verbs fire `notifyCommand`. |
|
|
17
|
-
| `attentionKinds` | `["question", "pr:ready", "pr:review", "pr:conflict", "pr:checks"]` | Which kinds `man tend`, the glass, and the desktop pet show. `pr:draft` and `
|
|
17
|
+
| `attentionKinds` | `["question", "decision", "pr:ready", "pr:review", "pr:conflict", "pr:checks"]` | Which kinds `man tend`, the glass, and the desktop pet show. `decision` is a question the helm put to the human with `lobstah man ask`; the glass shows it as a card to answer, and it hides the raw `question` it frames. Without `decision`, framed decisions do not show and raw questions do. `pr:draft`, `landed`, and `report` are valid opt-in kinds. An explicit list is used unchanged. Unknown kinds are errors. See the [attention contract](vocabulary.md#attention-contract). |
|
|
18
18
|
| `remindSecs` | `900` | An unanswered `needs-decision`/`blocked` re-fires to `man wait`/`man haul` on this interval until answered. `0` = report once only. |
|
|
19
19
|
|
|
20
20
|
## `[repos.<key>]` — workspace definitions
|
|
@@ -31,6 +31,7 @@ The descriptor's `repo` field resolves here; the key is what dispatchers name.
|
|
|
31
31
|
| `env` | no | Environment merged into every dispatch for this repo. |
|
|
32
32
|
| `pickup` | no (`false`) | Opt this repo into `[pickup.github]` multi-repo mode. Explicit per repo — nothing becomes pickable by being configured. |
|
|
33
33
|
| `pushEarly`, `draftPr`, `checkpointOnStop` | no (inherit `[limits]`) | Override remote preservation for this repo's headless dispatches. |
|
|
34
|
+
| `humanGateChecks` | no | Check names that fail until a person approves the change (e.g. `["owner approval"]`). `*` matches any run of characters. On a PR of a dispatch in this repo, a failed human gate never starts a PR repair or a CI-fix continuation. A PR whose only failing checks are human gates shows `repair.status: waiting` with `heldBy: human-gate`. A worker adds gates for one PR with `lobstah report --human-gate <check>`. |
|
|
34
35
|
|
|
35
36
|
`[repos.<key>.harness]` — per-repo harness defaults: `default` (`claude` \|
|
|
36
37
|
`codex`), `model`, `effort`.
|
|
@@ -55,14 +56,14 @@ Same three keys as the per-repo block. Precedence for every harness setting:
|
|
|
55
56
|
|
|
56
57
|
| Key | Default | Meaning |
|
|
57
58
|
|---|---|---|
|
|
58
|
-
| `maxConcurrent` | `2` | Headless work-lane runners the daemon may run at once. Trap-claimed catches use their own sessions and do not spend these slots. |
|
|
59
|
+
| `maxConcurrent` | `2` | Headless work-lane runners the daemon may run at once. Trap-claimed catches use their own sessions and do not spend these slots. A dispatch whose worker reported `done` or `failed` does not spend a slot, even while its runner is still exiting. A dispatch whose worker reported `paused` is parked: its runner ends the session and exits, and it spends no slot. An operator message, or the time given with `--until`, wakes it into the same session when a slot is free; a waking dispatch takes the slot before queued work. `man tend`, `daemon status`, `doctor`, and the glass list parked dispatches. |
|
|
59
60
|
| `choreConcurrent` | `1` | Headless chore-lane runner ceiling (rebases and other machine-originated runs). |
|
|
60
61
|
| `wedgeThresholdSecs` | `600` | No tool activity for this long while alive = wedged → killed and forked with a nudge. Also the age past which `status`, `ls`, `man tend`, and the glass show a dispatch's activity line as stale. |
|
|
61
62
|
| `maxRestartAttempts` | `2` | Bounded restart ladder for dead and wedged runners. |
|
|
62
63
|
| `wallClockSecs` | `3600` | Initial active-work window. Progress extends it, up to `maxWallClockSecs`; time paused with `report paused --waiting-on` does not count. |
|
|
63
64
|
| `maxWallClockSecs` | `4 × wallClockSecs` | Hard active-work ceiling across restarts. |
|
|
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. |
|
|
65
|
+
| `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, and a dispatch whose descriptor names a PR (a repair or a rebase chore), does not push automatically; its worker pushes to the existing PR's head branch. |
|
|
66
|
+
| `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, and a dispatch whose descriptor names a PR, keeps that PR and its watch; it does not open another. |
|
|
66
67
|
| `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
68
|
|
|
68
69
|
A runner extends its active-work window when a fresh activity event or new HEAD
|
|
@@ -72,8 +73,9 @@ time. At the hard ceiling, the status verb remains `failed` for compatibility,
|
|
|
72
73
|
but its note starts `budget:` and tells the man what work was saved and to
|
|
73
74
|
send a continuation.
|
|
74
75
|
| `backgroundWaitSecs` | `1800` | A turn that ends without a report is held open this long while background work the worker started is still running (a push behind a slow pre-push gate); the harness wakes the worker when it settles. Heartbeats keep the wedge detector off the wait. Keep it below `wallClockSecs`, which still ends the run. |
|
|
76
|
+
| `exitGraceSecs` | `30` | After the worker reports `done` or `failed` at the end of a turn, the runner ends the session and waits this long for the harness to close its event stream. If the stream is still open, the runner kills the harness, then stops every process the runner started (its process group on Linux and macOS, its process tree on Windows), and finishes. The report stands: the status stays `done` or `failed`, and evidence records `harnessStopped` with how many seconds after the report the harness was stopped. When the stream closes in time, the runner stops any background processes the harness left running on its way out. |
|
|
75
77
|
| `choreRetentionDays` | `7` | Completed chores age out of `chores/done/`. |
|
|
76
|
-
| `attachmentMaxBytes` | `26214400` (25 MiB) | Maximum size of each file supplied with repeatable `dispatch --attach
|
|
78
|
+
| `attachmentMaxBytes` | `26214400` (25 MiB) | Maximum size of each file supplied with repeatable `dispatch --attach`, `send --attach`, or a decision answer. Picked files and images pasted into a glass answer box use this limit. |
|
|
77
79
|
| `retentionDays` | `0` (off) | The daemon culls finished dispatches (done and failed) older than this many days: their `done/` entries, worktrees, state files, and stale PR records and acks. Branches are kept. A dispatch whose PR is still open is kept. Queued and active dispatches are never culled. The pass runs at most once per hour and culls at most 10 dispatches per pass, so it cannot stall the claim loop. Suggested: `14`, the same window as `lobstah cull`. |
|
|
78
80
|
| `releaseOnMerge` | `false` (off) | When a PR watch records `merged` for a PR, the daemon's next cull pass removes the worktree of the dispatch that owns the PR, and of every dispatch in its follow-up chain that ran on that PR (its own worktree or a shared one). It uses the retention cull's removal path, its hourly throttle, and its limit of 10 per pass, and runs even when `retentionDays` is `0`. It releases a worktree only when every dispatch in the chain is finished (done or failed) and none is queued or active, `git status --porcelain` is empty, and after a fetch `git branch -r --contains HEAD` is not empty. Otherwise it keeps the worktree, records why, and `lobstah doctor`'s `disk` row shows `kept: unpushed work`; the next pass checks again. Uncommitted changes and unpushed commits are never deleted. A PR closed without merge releases nothing. A trap's worktree is never released. Branches (local and remote), `done/` entries, state, and evidence are kept; `lobstah catch` prints `worktree: released on merge (<time>)`. One `worktree-released` notice per pass lists what was released. |
|
|
79
81
|
| `reuseWorktree` | `true` | A follow-up (`--follow-up <id>`) runs in the worktree of the newest dispatch in its chain whose worktree still exists, on the branch and HEAD where that dispatch stopped. Trunk is fetched; `setup` runs again only if a lockfile changed. It reuses only when the worktree is clean (untracked files under the repo's `scratch` paths excepted), belongs to the same repo, and no other dispatch runs in it; otherwise it allocates a fresh worktree as usual. A dirty worktree is never cleaned or reset. The first status note says which: `reusing worktree of <origin>` or `fresh worktree (<reason>)`. A lock file in the worktree's git dir (`lobstah.lock`) keeps a second runner out; a lock whose dispatch has finished is stale. A shared worktree is kept by the cull and the free-space guard while any dispatch in the chain is queued or active, and it ages from the newest dispatch that used it. `false` allocates a fresh worktree for every dispatch. Headless dispatches only: a trap works in its own worktree. |
|
|
@@ -84,7 +86,7 @@ send a continuation.
|
|
|
84
86
|
| Key | Default | Meaning |
|
|
85
87
|
|---|---|---|
|
|
86
88
|
| `deferSecs` | `90` | A soaking session whose park heartbeat is this fresh holds unaddressed matching bait — the daemon waits instead of spawning. Addressed bait (`--for session:<id>`) waits regardless, until the registration is gone. |
|
|
87
|
-
| `ttlSecs` | `1800` | Heartbeat age past which a registration is a ghost trap: the sweep removes it and requeues
|
|
89
|
+
| `ttlSecs` | `1800` | Heartbeat age past which a registration is a ghost trap: the sweep removes it and requeues an unfinished catch, finalizes done/failed in `done/`, or finalizes a cancelled catch as failed. A fresh `lobstah report` or beat counts as liveness. If the daemon's own tick gap exceeds this TTL, it grants a full TTL after resume before sweeping any traps. |
|
|
88
90
|
| `beat` | `true` | The post-tool hook (`lobstah soak beat`) refreshes a soaking session's liveness and writes its catch's activity, at most once per 30 seconds per trap. With `false` the hook does nothing, and a trap's liveness comes from its reports and its park only. |
|
|
89
91
|
| `pausedTtlSecs` | `86400` (24 hours) | A trap whose catch last reported `paused` is kept out of the ghost sweep this long after the report. `report paused --until <iso|duration>` sets the expiry instead. After it, the sweep removes the trap as usual, and the notice says the pause expired. |
|
|
90
92
|
|
|
@@ -106,11 +108,13 @@ send a continuation.
|
|
|
106
108
|
|
|
107
109
|
| Key | Default | Meaning |
|
|
108
110
|
|---|---|---|
|
|
109
|
-
| `maxForksPerCycle` | `3` | The most continuation (CI-fix) dispatches one watch cycle of `lobstah pick` may fork. Each watch over the cap is held: its events stay buffered, `man tend` and `lobstah watch` list it as `held`, one `watch-held` notice names the held watches, and it forks nothing until `lobstah watch release <key>` (or `--all`). |
|
|
110
|
-
| `autoRepair` | `true` | On a dispatch-owned PR,
|
|
111
|
+
| `maxForksPerCycle` | `3` | The most continuation (CI-fix) dispatches one watch cycle of `lobstah pick` may fork. Each watch over the cap is held: its events stay buffered, `man tend` and `lobstah watch` list it as `held`, one `watch-held` notice names the held watches, and it forks nothing until `lobstah watch release <key>` (or `--all`). A held PR watch also queues no repair; see [the man page](man.md) for `watch hold` and cancelled repairs. |
|
|
112
|
+
| `autoRepair` | `true` | On a dispatch-owned PR, queue a repair chore for a conflict, failed current check, or requested review changes. `false` leaves check-event delivery and attention as before. |
|
|
111
113
|
| `conflicts` | `true` | Repair conflicts when `autoRepair` is on. Set `false` to show conflict attention without a repair. |
|
|
112
114
|
| `checks` | `true` | Repair failed current checks when `autoRepair` is on. Set `false` to show check attention without a repair. |
|
|
113
|
-
| `maxRepairsPerPr` | `2` | Maximum repair follow-ups for one PR head SHA. When the limit is reached and the issue remains, `pr:conflict`, `pr:checks`, or `pr:review` attention names the limit. |
|
|
115
|
+
| `maxRepairsPerPr` | `2` | Maximum repair follow-ups for one PR head SHA. When the limit is reached and the issue remains, `pr:conflict`, `pr:checks`, or `pr:review` attention names the limit. A repair that waits is not an attempt. Below this limit, each failing check gets at most one repair round per PR and head SHA: a check that had its round and still fails on the same head waits for a new commit, with `repair.status: waiting` and `heldBy: repaired`, and raises `pr:checks` attention. A CI-fix continuation from `lobstah pick` (`autoRepair = false`) follows the same rule. Human gates (`[repos.<key>].humanGateChecks`, `report --human-gate`) get no round. |
|
|
116
|
+
| `repairSettleSecs` | `600` | A repair is queued only after the PR's head, its base branch's head, and its failing checks have been unchanged for this many seconds. Until then the PR record shows `repair.status: waiting` with `heldBy: settle` and `until`. |
|
|
117
|
+
| `repairTrapWaitSecs` | `600` | A daemon repair chore addressed to the PR-owning trap waits this many seconds for that trap. If still queued, it becomes headless. A person's addressed work never falls back. |
|
|
114
118
|
|
|
115
119
|
## `[grounds.*]` — helm territories
|
|
116
120
|
|
|
@@ -195,3 +199,5 @@ See [pickup.md](pickup.md) for the loop semantics these keys drive.
|
|
|
195
199
|
|---|---|
|
|
196
200
|
| `LOBSTAH_HOME` | The instance root (default `~/.lobstah`). Multiple instances = multiple homes; one daemon per home, enforced. |
|
|
197
201
|
| `LOBSTAH_MAN` | `=1` designates a session as the lobstah man for the `man haul` Stop hook. |
|
|
202
|
+
| `LOBSTAH_TRAP_TICKET` | A `trap reserve` ticket. `lobstah soak` redeems it and signs the session on as the reserved trap. `--ticket` takes precedence. |
|
|
203
|
+
| `LOBSTAH_TERMINAL_TITLE` | `=0` stops soak from naming the terminal tab after the trap, and stow from clearing it. |
|
|
@@ -61,6 +61,14 @@ reads it, so no `--session` flag is needed. The session-start brief still
|
|
|
61
61
|
prints the id, and the sign-on commands with the id filled in, if you want
|
|
62
62
|
to pass `--session` explicitly. Claude Code session ids are UUIDv4.
|
|
63
63
|
|
|
64
|
+
For the glass's ↗ open button, give the trap its own session link with
|
|
65
|
+
`lobstah soak --link <url>`. A Claude desktop session exposes its own
|
|
66
|
+
`claude://claude.ai/...` link in the app; copy that link from the session.
|
|
67
|
+
Its app id is not the CLI session id. In the VS Code extension, use
|
|
68
|
+
`vscode://anthropic.claude-code/open?session=<session-id>` with the id above.
|
|
69
|
+
The glass checks the stored link before showing it. `lobstah focus <trap>`
|
|
70
|
+
uses the same focus steps from the terminal.
|
|
71
|
+
|
|
64
72
|
## Getting woken: arm the watcher
|
|
65
73
|
|
|
66
74
|
In Claude Code the Stop hook runs in **arm** mode by default. At turn end
|
|
@@ -74,6 +82,12 @@ with work in flight:
|
|
|
74
82
|
question.
|
|
75
83
|
|
|
76
84
|
A trap arms `lobstah soak --wait --timeout 900` the same way.
|
|
85
|
+
|
|
86
|
+
`lobstah soak` prints a title at sign-on, `soak --wait` prints one with
|
|
87
|
+
the claimed work, and `report done` or `report failed` prints the trap
|
|
88
|
+
name again. The trap skill uses `set_session_title` for literal `self`
|
|
89
|
+
in the desktop Code tab when available. It does not retry a refusal or
|
|
90
|
+
approval request. The CLI title hook does not rename a running CLI session.
|
|
77
91
|
`lobstah man haul --park` or `[helm].park = "block"` in `config.toml`
|
|
78
92
|
switches to the blocking park instead: the hook itself waits (up to its
|
|
79
93
|
4-hour timeout) for something to need attention.
|
package/docs/harness/codex.md
CHANGED
|
@@ -39,6 +39,12 @@ says when the plugin is behind.
|
|
|
39
39
|
| `man` skill | The orchestrator: the helm, the charter, dispatching, tending, getting woken, and sending a follow-up to finished work. |
|
|
40
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
|
+
`lobstah soak` prints a title at sign-on, `soak --wait` prints one with
|
|
43
|
+
the claimed work, and `report done` or `report failed` prints the trap
|
|
44
|
+
name again. The trap skill uses `set_thread_title` for the current thread
|
|
45
|
+
in the Codex app when available. It does not retry a refusal or approval
|
|
46
|
+
request. The CLI has no live title setter.
|
|
47
|
+
|
|
42
48
|
There are no slash commands: the Codex plugin layout has no commands
|
|
43
49
|
directory. The skills run the same `lobstah` commands as the README
|
|
44
50
|
quickstart.
|
|
@@ -118,6 +124,12 @@ Codex task ids are UUIDv7 (for example `01a0ceb8-b9bd-7d42-…`); Claude Code
|
|
|
118
124
|
session ids are UUIDv4. When both harnesses' variables are set, lobstah uses
|
|
119
125
|
this format to tell which one signed on.
|
|
120
126
|
|
|
127
|
+
For the glass's ↗ open button, form `codex://threads/<task-id>` from the
|
|
128
|
+
task id in the session-start brief and pass it with
|
|
129
|
+
`lobstah soak --link <url>`. The glass checks the stored link before
|
|
130
|
+
showing it. `lobstah focus <trap>` uses the same focus steps from the
|
|
131
|
+
terminal.
|
|
132
|
+
|
|
121
133
|
## Getting woken: the blocking park
|
|
122
134
|
|
|
123
135
|
In Codex the Stop hook runs in **block** mode by default. At turn end with
|