@pmelab/gtd 7.1.0 → 7.2.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/docs/cli.md CHANGED
@@ -25,6 +25,17 @@ Commands:
25
25
  state (fixEntry: true) that goes straight into repairing the
26
26
  current failing tests. Requires a clean tree resting at the
27
27
  workflow's initial state
28
+ abandon End the process currently underway without completing it:
29
+ close any open review checkout window, then rewind HEAD to
30
+ the commit the process started from, keeping everything it
31
+ produced as uncommitted changes. A no-op when no process is
32
+ underway
33
+ restore Hard-reset HEAD back to the pre-squash tip retained by the
34
+ last squash/abandon (refs/worktree/gtd/history), undoing a
35
+ squash or bringing back an abandoned process's turns.
36
+ Refuses on a dirty working tree, when there is no retained
37
+ history, or when HEAD has advanced past the squash with
38
+ commits that would be lost
28
39
  next Print the resolved rest's rendered script/prompt/message
29
40
  (no mutation)
30
41
  status Print the resolved rest's state/actor and which declared
@@ -167,7 +178,8 @@ written):
167
178
 
168
179
  - the machine currently resting at the workflow's **initial state** (a plain
169
180
  non-gtd branch resolves there via the inert-subject rule — the normal case; a
170
- process already underway refuses);
181
+ process already underway refuses — finish it, or
182
+ [`gtd abandon`](#gtd-abandon---json) it);
171
183
  - a **clean working tree**;
172
184
  - the active workflow declaring a **`reviewEntry: true`** state (otherwise:
173
185
  `gtd review: the active workflow declares no review entry state`);
@@ -213,7 +225,8 @@ Requires, in order (any failure is a plain refusal — exit non-zero, nothing
213
225
  written):
214
226
 
215
227
  - the machine currently resting at the workflow's **initial state** (a process
216
- already underway refuses);
228
+ already underway refuses — finish it, or [`gtd abandon`](#gtd-abandon---json)
229
+ it);
217
230
  - a **clean working tree**;
218
231
  - the active workflow declaring a **`fixEntry: true`** state (otherwise:
219
232
  `gtd fix: the active workflow declares no fix entry state`).
@@ -232,6 +245,103 @@ unified template the fix-entry state is `fix-check`: a red suite drops into the
232
245
  shared `fixing` loop and out through the review + squash tail; a green suite is
233
246
  a no-op back to `idle`.
234
247
 
248
+ ## `gtd abandon [--json]`
249
+
250
+ Ends the process currently underway **without completing it** — the way out of a
251
+ process nobody is going to finish, and the command `gtd review`/`gtd fix` name
252
+ when they refuse ("finish it, or run `gtd abandon`, before starting a review").
253
+ A workflow's own exit is its squash finale
254
+ ([STATES.md §8](../STATES.md#8-the-squash-lifecycle)); this is the human's, and
255
+ it targets the same boundary:
256
+
257
+ 1. any open **review checkout window** is closed first (the bracket every state
258
+ subcommand runs — see
259
+ [STATES.md §11](../STATES.md#11-the-review-checkout-window)), so the rewind
260
+ starts from the real head, not the rewound base;
261
+ 2. HEAD is `git reset --mixed`ed to the commit the process started from (its
262
+ start parent — the same target a squash resets to).
263
+
264
+ **Nothing is discarded.** Every turn commit the process wrote is dropped, and
265
+ everything they carried — the code, the `.gtd/` steering files — stays in the
266
+ working tree as uncommitted changes for you to keep, re-commit, or throw away
267
+ with ordinary git:
268
+
269
+ ```
270
+ abandoned the process resting at "await-review" — HEAD is back at 1a2b3c4
271
+ ("feat: add calculator"), resting at "idle".
272
+ Everything the process produced is kept as uncommitted changes (`git status`);
273
+ discard them with `git checkout -- . && git clean -fd .gtd` for a clean tree.
274
+ ```
275
+
276
+ Takes no positional argument (extra arguments are a usage error). Resting at the
277
+ workflow's initial state is a **no-op success**, not a refusal — a recovery
278
+ command that fails when there is nothing to recover is a worse tool:
279
+
280
+ ```
281
+ no gtd process is underway (resting at "idle") — nothing to abandon
282
+ ```
283
+
284
+ The one refusal is a process whose first commit is the repository's own root
285
+ commit: there is no earlier commit to rewind to, so its commits have to be
286
+ removed by hand.
287
+
288
+ `--json` emits `{state, abandoned, from, head}` — `state` is the initial state
289
+ the machine returns to, `abandoned` whether anything was rewound, and (only when
290
+ it was) `from` the state the abandoned process rested at plus `head` the full
291
+ hash HEAD now points at:
292
+
293
+ ```json
294
+ {
295
+ "state": "idle",
296
+ "abandoned": true,
297
+ "from": "await-review",
298
+ "head": "1a2b3c4…"
299
+ }
300
+ ```
301
+
302
+ The no-op reports `{"state": "idle", "abandoned": false}` and still exits 0.
303
+
304
+ ## `gtd restore [--json]`
305
+
306
+ Undoes the last squash or `gtd abandon` by hard-resetting HEAD back to the
307
+ pre-squash tip that either one retains, before acting, on the per-worktree
308
+ `refs/worktree/gtd/history` ref
309
+ ([STATES.md §8](../STATES.md#8-the-squash-lifecycle)):
310
+
311
+ ```
312
+ restored the retained history — HEAD is back at 1a2b3c4 ("gtd(agent): drafting
313
+ → working"), resting at "await-review". Resume with the loop, or `git reset`
314
+ to any earlier turn to restart from there.
315
+ ```
316
+
317
+ Refuses on a dirty working tree — commit, stash, or discard your changes first.
318
+ Refuses when there is no retained history to restore (nothing has been squashed
319
+ or abandoned yet, or a previous `restore` already consumed it). Refuses when
320
+ HEAD has advanced past the retained tip with commits that would be discarded by
321
+ resetting:
322
+
323
+ ```
324
+ gtd restore: HEAD has advanced past the squash — restoring would discard
325
+ commits built on top of it — HEAD 4d5e6f7 is ahead of the retained tip
326
+ 1a2b3c4.
327
+ ```
328
+
329
+ Takes no positional argument (extra arguments are a usage error).
330
+
331
+ `--json` emits `{state, restored, to, from}` — `state` is the resolved rest
332
+ after the reset, `restored` always `true` (a refusal exits non-zero instead),
333
+ `to` the full hash HEAD was reset to, and `from` the state the machine rested at
334
+ before the reset:
335
+
336
+ ```json
337
+ {
338
+ "state": "await-review",
339
+ "restored": true,
340
+ "to": "1a2b3c4…",
341
+ "from": "idle"
342
+ }
343
+ ```
344
+
235
345
  ## `gtd next [--json]`
236
346
 
237
347
  Pure emitter of the resolved rest's rendered content — it **never mutates** the
@@ -495,3 +605,11 @@ the plain-text one.
495
605
  `--help`/`--version` (and the `help`/`version` subcommands), `lsp`, and
496
606
  `visualize` skip this guard entirely (`visualize` still reads the `.gtdrc`
497
607
  workflow, but needs no git state).
608
+ - **Linked worktrees are independent.** N `git worktree` worktrees of one
609
+ repository (sharing a single `.git`) each run their own gtd process: state is
610
+ derived from that worktree's own HEAD, and the review checkout window's refs
611
+ live in git's per-worktree `refs/worktree/gtd/*` namespace, so a review
612
+ resting in one worktree neither blocks nor rewrites any other
613
+ ([STATES.md §11](../STATES.md#11-the-review-checkout-window)). This holds from
614
+ 7.2 on; gtd ≤ 7.1 kept the window under the shared `refs/gtd/*` refs, where
615
+ the first worktree to open one clobbered every sibling's branch.
@@ -261,6 +261,38 @@ value) only when the resolved state declares one — **omitted entirely**, never
261
261
  `null`, when unset. Plain `gtd status` prints a `Memory:` line (right after
262
262
  `Model:`, when present).
263
263
 
264
+ ### `label:` — the opaque display name, template-rendered
265
+
266
+ A state may declare `label: <string>` — an OPAQUE display name gtd never
267
+ interprets; it is only passed through so a driver/viewer can show something
268
+ friendlier than the raw state name. Unset means the state has no display name.
269
+ Forbidden on a commit state (never at rest, emits nothing):
270
+
271
+ ```yaml
272
+ workflow:
273
+ states:
274
+ working:
275
+ actor: agent
276
+ label: Implement the feature
277
+ prompt: do the thing
278
+ on:
279
+ "* **": done
280
+ ```
281
+
282
+ Like `model`/`memory`, `label:` is rendered as an Eta template through the exact
283
+ same context as the state's content — a plain string with no Eta tags
284
+ (`Implement the feature` above) passes through unchanged, but
285
+ `label: "<%= it.vars.stateLabel %>"` resolves against the merged `it.vars` (see
286
+ ["Variables"](#variables)). A render failure behaves exactly like a content
287
+ render failure at the same call site: `gtd next`/`gtd status` error out, nothing
288
+ committed.
289
+
290
+ `gtd next --json` and `gtd status --json` include a `"label"` key (the RENDERED
291
+ value) only when the resolved state declares one — it is **omitted entirely**,
292
+ never emitted as `null`, when unset. A driver/viewer choosing to fall back to
293
+ the raw state name when `label` is absent is that consumer's own fallback, not
294
+ something gtd itself does.
295
+
264
296
  ### `file:`/`mode:` — the steering-file association
265
297
 
266
298
  A state may additionally declare `file:` — an Eta template naming THE steering
@@ -449,9 +481,10 @@ The unified template enables `reviewWindow: true` on `await-review` and marks
449
481
  `review-deciding` `reviewBase: true`, so the first review covers the whole cycle
450
482
  but each subsequent feedback round's window narrows to `last-review..HEAD`. The
451
483
  pure engine never observes an open window — it is opened/closed entirely at the
452
- edge; the real head is preserved under `refs/gtd/review-head` (the base under
453
- `refs/gtd/review-base`) for the window's lifetime. See
454
- [STATES.md §11](../STATES.md) for the full lifecycle.
484
+ edge; the real head is preserved under `refs/worktree/gtd/review-head` (the base
485
+ under `refs/worktree/gtd/review-base`) for the window's lifetime — git's
486
+ per-worktree ref namespace, so linked worktrees of one repository each get their
487
+ own window. See [STATES.md §11](../STATES.md) for the full lifecycle.
455
488
 
456
489
  ### `reviewEntry:` — the review entry point (`gtd review <commitish>`)
457
490
 
package/docs/loop.md CHANGED
@@ -18,13 +18,18 @@ DRIVER runs the emitted `content` and then steps that state's own actor.
18
18
  A loop is a simple cycle:
19
19
 
20
20
  1. Run `gtd next --json` — see [`cli.md`](cli.md#gtd-next---json) for the exact
21
- `{state, actor, kind, content, model?, memory?}` field shape. `kind` is the
22
- dispatch key: `"message"` → halt (a human rest); `"script"` → execute
21
+ `{state, actor, kind, content, model?, memory?, label?}` field shape. `kind`
22
+ is the dispatch key: `"message"` → halt (a human rest); `"script"` → execute
23
23
  `content` verbatim (ignoring its exit code), then `gtd step <actor>` to
24
24
  capture the outcome; `"prompt"` → feed `content` to the agent, then
25
25
  `gtd step <actor>` yourself once it's done acting. The optional `model` is an
26
26
  opaque string the workflow author chose, which you map onto your own
27
- harness's model selection if you use one.
27
+ harness's model selection if you use one. The optional `label` is a
28
+ human-readable display name for the resolved state, present only when that
29
+ state declares `label:` in the workflow; when it's absent, a driver that
30
+ wants to show something falls back to `state` itself (that fallback lives in
31
+ the driver, not gtd core — see `bin/gtd-loop`'s
32
+ `label="$(jq -r '.label // .state' <<<"$next_json")"`).
28
33
  2. Repeat until a `"message"` rest halts the loop, or a zero-commit script step
29
34
  at idle settles it (the green terminal signal).
30
35
 
@@ -34,6 +39,19 @@ capture for the human as its opening move (see below), so re-launching the loop
34
39
  is the only thing the human ever does — at a gate they edit and re-run, and the
35
40
  loop picks their change up.
36
41
 
42
+ **A `"message"` rest is a real handoff, not a slow beat.** No driver signs off
43
+ for the human: in the bundled workflow the loop runs the whole cycle unattended
44
+ up to `await-review` and then STOPS there with the review checkout window open
45
+ (the reviewable diff surfaced as uncommitted changes — see
46
+ [STATES.md §11](../STATES.md#11-the-review-checkout-window)). Advancing takes a
47
+ human edit to `.gtd/REVIEW.md`: ticking every box with **no** comment signs off
48
+ into the squash finale, while any comment (a note, or a code edit of your own)
49
+ is feedback that routes into another build + re-review round. Re-launching the
50
+ loop after that edit picks the decision up. Until then the loop halts on every
51
+ run — that is the contract, not a stall. A review nobody is going to finish is
52
+ ended with [`gtd abandon`](cli.md#gtd-abandon---json), which drops the process's
53
+ commits and keeps their content as uncommitted changes.
54
+
37
55
  ```bash
38
56
  gtd next --json # ask who's up and what they should do
39
57
  ```
@@ -149,3 +167,35 @@ example, to drive a different agent CLI:
149
167
  ```bash
150
168
  GTD_LOOP_AGENT_CMD='my-agent-cli --prompt "$GTD_LOOP_PROMPT"' gtd-loop
151
169
  ```
170
+
171
+ ## Herdr integration (optional)
172
+
173
+ `bin/gtd-loop` optionally reports its lifecycle to [Herdr](https://herdr.dev) (a
174
+ terminal multiplexer for coding agents) via a `herdr` CLI binary, entirely at
175
+ the driver's edge — gtd core never talks to Herdr. Every call is best-effort
176
+ (guarded, output discarded, `|| true`), so a missing/failing `herdr` binary
177
+ never blocks, slows, or fails the loop.
178
+
179
+ The reporting is a no-op unless all three guard conditions hold: `HERDR_ENV=1`,
180
+ a non-empty `$HERDR_PANE_ID`, and `herdr` on `$PATH`. When they do, `gtd-loop`
181
+ makes three kinds of calls:
182
+
183
+ - `herdr pane report-agent --source herdr:gtd --agent gtd --state <state> --message <label> "$HERDR_PANE_ID"`
184
+ - `herdr pane release-agent --source herdr:gtd --agent gtd "$HERDR_PANE_ID"`
185
+ - `herdr notification show <title> --body <label> --sound request`
186
+
187
+ mapped onto the loop's states like this:
188
+
189
+ | Loop moment | Call(s) |
190
+ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
191
+ | Top of every non-`"message"` iteration | `report-agent --state working` |
192
+ | Right before halting at a `"message"` (human) gate | `report-agent --state blocked` + `notification show` |
193
+ | Right before a clean settle (a script rest, zero new commits) | `report-agent --state idle` + `release-agent` |
194
+ | Any non-zero exit (stall guard, validate-cap, `gtd next` failure, unhandled failure) | `report-agent --state blocked` + `notification show`, via an `EXIT` trap |
195
+
196
+ `working`/`blocked` reports use the resolved rest's `label` (falling back to
197
+ `state`, per the field above); the trap falls back to `$GTD_LAST_LABEL`, the
198
+ label tracked from the last completed iteration, when it fires outside the
199
+ normal per-iteration flow. `report-agent` claims display authority for the
200
+ `herdr:gtd` source, so `working` is re-reported every lap rather than once, to
201
+ stay fresh against Herdr's own heuristic detection of the inner agent process.
package/docs/upgrading.md CHANGED
@@ -159,6 +159,42 @@ loudly at load time rather than silently misinterpreting.
159
159
  or agent harness, upgrading the `gtd` binary also means re-copying that skill
160
160
  from this release.
161
161
 
162
+ ## 7.2: the review checkout window is now per worktree
163
+
164
+ The review checkout window's two refs moved from the SHARED `refs/gtd/*`
165
+ namespace to git's PER-WORKTREE `refs/worktree/*` one:
166
+
167
+ | before (≤ 7.1) | from 7.2 |
168
+ | ---------------------- | ------------------------------- |
169
+ | `refs/gtd/review-head` | `refs/worktree/gtd/review-head` |
170
+ | `refs/gtd/review-base` | `refs/worktree/gtd/review-base` |
171
+
172
+ Nothing in a `.gtdrc` or a workflow definition references those names, so **no
173
+ config change is needed**. What changes is behaviour in a repository checked out
174
+ as several linked worktrees (`git worktree add`, one `.git` shared between
175
+ them): each worktree now has its own window, so a review resting in one no
176
+ longer "closes" from another — which, on the shared refs, mixed-reset that other
177
+ worktree's branch onto the reviewing worktree's saved head and left every
178
+ sibling refusing with "a process is already underway".
179
+
180
+ A window an OLDER gtd left open across the upgrade still closes from the legacy
181
+ refs, so an in-flight review in a single-worktree repo needs no attention. In a
182
+ multi-worktree repo gtd cannot tell which worktree a shared ref belongs to, so
183
+ it closes only when HEAD is contained in the saved head and otherwise refuses
184
+ with the exact recovery commands — run them in the worktree that owns the
185
+ window:
186
+
187
+ ```bash
188
+ git reset --mixed refs/gtd/review-head
189
+ git update-ref -d refs/gtd/review-head
190
+ git update-ref -d refs/gtd/review-base
191
+ ```
192
+
193
+ 7.2 also adds [`gtd abandon`](cli.md#gtd-abandon---json), the supported way out
194
+ of a process that will never be finished (it closes the window, then rewinds
195
+ HEAD to the process's start parent, keeping everything the process produced as
196
+ uncommitted changes) — no hand-editing of refs required.
197
+
162
198
  ## Prior breaking change: the v1 → v2 label grammar (historical)
163
199
 
164
200
  v2 replaced v1's undifferentiated turn labels with six labels carrying the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "7.1.0",
3
+ "version": "7.2.0",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {