lobstah 0.1.2 → 0.2.1

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.
@@ -86,7 +86,11 @@ The verdict distinguishes states that look identical from the outside:
86
86
  Below the verdict: counts (queued, active, chores, done/failed last 24h), the
87
87
  unanswered questions with how long they have waited, and one row per work
88
88
  item — tracker key, its dispatch chain (original → swaps → review follow-ups),
89
- its PR, and the PR's merge-gate status. Gate status comes from the [merge
89
+ its PR, the PR's merge-gate status, and any external source the chain is
90
+ watching (a ume review session, a CI run). Watches join the same way the
91
+ merge view does — from disk. A man-owned watch with unconsumed events counts
92
+ as `needs-attention` with its age; a dispatch-owned one annotates its story
93
+ (the wake is machinery's job, not the human's). Gate status comes from the [merge
90
94
  view](pickup.md#merge-view) pickup persists each tick, so PR state is at most
91
95
  one poll interval stale without tend making a single network call. `--json`
92
96
  emits the full report for dashboards and scripts to render.
@@ -94,11 +98,14 @@ emits the full report for dashboards and scripts to render.
94
98
  ## Getting woken instead of asked
95
99
 
96
100
  Three escalation tiers, least to most invasive. All are built on
97
- `lobstah man wait`: block until a dispatch needs attention, print the event and
98
- what to do next, exit. It is **level-triggered for attention states** — if a
99
- `needs-decision` is already standing when it starts, it returns immediately —
100
- so a gap between one watcher exiting and the next arming can never lose an
101
- event.
101
+ `lobstah man wait`: block until a dispatch — or a watched external source
102
+ (`lobstah watch add`, e.g. a ume review session) — needs attention, print the
103
+ event and what to do next, exit. It is **level-triggered for attention
104
+ states** — if a `needs-decision` or an unconsumed watch event is already
105
+ standing when it starts, it returns immediately — so a gap between one
106
+ watcher exiting and the next arming can never lose an event. When no pick
107
+ process is running, `man wait` runs due watch checks itself, so watching
108
+ works with every service stopped.
102
109
 
103
110
  **Tier 1 — a push for the human.** Set the daemon's hook and forget it:
104
111
 
@@ -134,6 +141,9 @@ trapline; every orchestrator-facing command lives under `lobstah man`).
134
141
  Install it from the project you'll run the lobsterman in:
135
142
 
136
143
  ```bash
144
+ # Easiest: the Claude Code plugin ships the hook + the lobsterman skill +
145
+ # /lobstah, no settings edits — /plugin marketplace add aequitas-labs/lobstah,
146
+ # /plugin install lobstah@lobstah. Or wire the hook by hand:
137
147
  lobstah man init # merges the Stop hook into .claude/settings.local.json
138
148
  lobstah man init --shared # …or the committed .claude/settings.json
139
149
  lobstah man init --global # …or once into ~/.claude/settings.json — any
package/docs/pickup.md CHANGED
@@ -156,6 +156,45 @@ with none of the webhook plumbing.
156
156
 
157
157
  ---
158
158
 
159
+ ## Watch loop
160
+
161
+ Pick's third loop family: standing outbound polls on anything external with
162
+ a CLI that can answer "anything new since cursor N?" — a ume review session,
163
+ a CI run. Registration goes through `lobstah watch add <key> --check <cmd>`
164
+ (the validated write path; the check contract and word set live in
165
+ [vocabulary.md](vocabulary.md#watch-contract)):
166
+
167
+ ```bash
168
+ # an interactive session pushes a plan for review, then stops blocking on it
169
+ lobstah watch add ume:9f2c --check 'ume events 9f2c --since {cursor} --json'
170
+
171
+ # a worker pauses on a review round; events fork a continuation of its chain
172
+ lobstah watch add ume:9f2c --check '...' --for <dispatch-uuid>
173
+ ```
174
+
175
+ Each cycle: run due checks (cadence = `[pickup].pollSecs`, or `--every` per
176
+ watch), append events, deliver by owner. Man-owned events surface through
177
+ `man wait`/`man haul` and fire `notifyCommand` with verb `watch`;
178
+ dispatch-owned events enqueue a continuation that forks the latest session
179
+ in the owning chain — one in flight per watch, later rounds buffer. With no
180
+ tracker sources configured, `lobstah pick` still runs in watch-only mode.
181
+ `man wait` runs due checks itself when no pick process is stamping them, so
182
+ watches work with every service stopped.
183
+
184
+ **Streams — the latency optimization.** `--stream <cmd>` names a long-lived
185
+ process (spawned with `{cursor}` substituted) that emits the same event
186
+ objects as NDJSON lines, plus bare `{"cursor": "N"}` checkpoints. Pick holds
187
+ one child per streaming watch and delivers each line the moment it arrives —
188
+ milliseconds instead of the poll interval. The queue contract's rule applies
189
+ one layer up: **watch as an optimization, poll as the guarantee** — appends
190
+ dedupe by `seq`, so the cadence check re-seeing a streamed event is a no-op,
191
+ and a dead stream just means cadence-only until the next cycle respawns it.
192
+ Stream and cadence events feed one serialized executor, so pick's state stays
193
+ single-writer. The daemon completes the fast path: it fs.watches the queue
194
+ directories, so a continuation enqueued by a stream event is claimed in
195
+ milliseconds too — end to end, an external event reaches a spawning session
196
+ in roughly harness start-up time.
197
+
159
198
  ## Merge loop
160
199
 
161
200
  Opt-in, per repo, off by default. Merging is policy, not mechanics, so it ships
@@ -116,3 +116,24 @@ Bucket transitions are atomic renames; the directory *is* the state.
116
116
  | `ok` | Works as configured. |
117
117
  | `warn` | Degraded or optional — dispatches may still run (e.g. one harness missing, daemon not running). |
118
118
  | `fail` | Broken configuration or missing requirement — fix before relying on lobstah. |
119
+
120
+ ## Watch contract
121
+
122
+ A **watch** is a standing outbound poll on something external (a ume review
123
+ session, a CI run) registered through `lobstah watch add` — the validated
124
+ write path; nothing else touches `watches/`. **Owner:**
125
+ `packages/core/src/watch.ts`. **Enforcement:** check output that doesn't
126
+ parse records `lastError` and advances nothing.
127
+
128
+ | Word | Meaning |
129
+ | ---- | ------- |
130
+ | `check` | Shell command exec'd with `{cursor}` substituted; prints `{ "cursor", "events"?, "done"? }` JSON. Read-only and idempotent — pick and an inline `man wait` coordinate only by the `lastCheckedAt` stamp. |
131
+ | `cursor` | Opaque progress marker, advanced only from successful check output. The stream of record: a crashed watcher resumes from it losslessly. |
132
+ | `owner` | Who the events belong to: `man` (surface via `man wait`/`man haul` + notify) or `dispatch:<uuid>` (fork a continuation of that chain). Events are never unowned work. |
133
+ | `done` | The source is finished (session closed, run complete); the watch retires after its last events are consumed. |
134
+ | `stream` | Optional long-lived NDJSON command held by pick for ms-latency delivery; a pure optimization — appends dedupe by `seq`, the check remains the guarantee. |
135
+
136
+ Delivery is level-triggered and at-least-once, like dispatch attention:
137
+ events stand until the owner consumes them. One continuation dispatch in
138
+ flight per watch; later events buffer and fork from the latest session in
139
+ the chain.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lobstah",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Harness-agnostic, token-efficient supervision framework for coding agents",
5
5
  "license": "MIT",
6
6
  "author": "aequitas labs LLC",