@lemoncode/lemony 0.3.0 → 0.4.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/catalog/VERSION +1 -1
- package/catalog/agents/architect.md +1 -1
- package/catalog/agents/implementer.md +6 -6
- package/catalog/agents/orchestrator.md +67 -59
- package/catalog/agents/partition.md +3 -3
- package/catalog/agents/reviewer.md +6 -6
- package/catalog/commands/pause.md +2 -2
- package/catalog/commands/resume.md +8 -7
- package/catalog/harness.config.schema.json +1 -1
- package/catalog/hooks/init.sh +10 -3
- package/catalog/templates/claude-code/agents.md.tpl +5 -4
- package/catalog/templates/claude-code/harness.config.yml.tpl +10 -6
- package/dist/cli.mjs +16 -5
- package/package.json +1 -1
package/catalog/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.4.0
|
|
@@ -52,7 +52,7 @@ acceptance for `write-adr` / `playbook-iterate`. See the Orchestrator's §Closeo
|
|
|
52
52
|
block is recorded, the decision arrives in the spawn prompt instead — don't
|
|
53
53
|
stall looking for it in the entry); the changed code from the PR diff when a PR
|
|
54
54
|
exists (closeout) or from the branch when none does (mid-task routes — under
|
|
55
|
-
|
|
55
|
+
auto-commit OFF read the **worktree**: mid-task the branch holds only
|
|
56
56
|
OK'd groups, or nothing before the first OK); or the
|
|
57
57
|
request. If the codebase is unfamiliar and the
|
|
58
58
|
task needs it, run `code-explorer` first.
|
|
@@ -52,7 +52,7 @@ what you run, only how many calls carry it.
|
|
|
52
52
|
already committed there.
|
|
53
53
|
2. **Implement via TDD** — run the `tdd` skill: one red → green → refactor cycle per
|
|
54
54
|
behavior (vertical slices, never all-tests-then-all-code). When your invocation
|
|
55
|
-
says **
|
|
55
|
+
says **auto-commit is OFF**, the whole run obeys §Staging protocol (below) —
|
|
56
56
|
zero commits, staged save-points.
|
|
57
57
|
**Scope: exactly what the invocation hands you.** By default that is the whole
|
|
58
58
|
`tasks.md` list (all-at-once). In **step-by-step mode** the Orchestrator
|
|
@@ -60,8 +60,8 @@ what you run, only how many calls carry it.
|
|
|
60
60
|
group's id + header line): read that group's tasks from the spec's `tasks.md`
|
|
61
61
|
yourself. On a later iteration the reference comes with reviewer or
|
|
62
62
|
human-checkpoint feedback. Work the group's tasks
|
|
63
|
-
in order — one TDD cycle and small commits per task, as always (
|
|
64
|
-
save-points per task — §Staging protocol) — then stop: do
|
|
63
|
+
in order — one TDD cycle and small commits per task, as always (auto-commit
|
|
64
|
+
OFF: staged save-points per task — §Staging protocol) — then stop: do
|
|
65
65
|
**not** run ahead into the next group (the human checkpoints each group before the
|
|
66
66
|
next starts), and don't re-open tasks the human already OK'd unless the feedback
|
|
67
67
|
you were handed says so.
|
|
@@ -98,7 +98,7 @@ what you run, only how many calls carry it.
|
|
|
98
98
|
lint / tests + coverage / audit + a real run); otherwise run those gates inline.
|
|
99
99
|
Commit your work to the branch and **push it, best-effort** — a failed push (offline,
|
|
100
100
|
auth) is a warning in your summary, never a blocker; the commits stay safe locally,
|
|
101
|
-
then return a summary to the Orchestrator. (
|
|
101
|
+
then return a summary to the Orchestrator. (Auto-commit OFF: no commit and
|
|
102
102
|
no push — leave **everything staged** and say so in the summary; the Orchestrator
|
|
103
103
|
commits at the human's OK.) The Orchestrator opens
|
|
104
104
|
the PR when you signal done — you don't open it.
|
|
@@ -108,14 +108,14 @@ what you run, only how many calls carry it.
|
|
|
108
108
|
checkpoint's "how to run it" is presented from it), and any `## Side-findings`
|
|
109
109
|
block. Long-form material — reasoning, alternatives weighed, per-task notes —
|
|
110
110
|
belongs in `progress.md` and the commit messages, never in the summary
|
|
111
|
-
(
|
|
111
|
+
(auto-commit OFF: in `progress.md` alone — there are no commit messages
|
|
112
112
|
until the human OK, so it carries everything a fresh iteration will need): the
|
|
113
113
|
Orchestrator carries your summary in context for the rest of the task, so every
|
|
114
114
|
extra line taxes the whole run. The cap trims narrative, not signal — a blocker,
|
|
115
115
|
a deviation from the spec, or a side-finding is always stated, however long the
|
|
116
116
|
list.
|
|
117
117
|
|
|
118
|
-
## Staging protocol (
|
|
118
|
+
## Staging protocol (auto-commit OFF)
|
|
119
119
|
|
|
120
120
|
Active **only when your invocation says so** — the human chose to review the work
|
|
121
121
|
**uncommitted**, so the branch must receive nothing until their OK. This inverts
|
|
@@ -205,9 +205,9 @@ define's close (`.claude/agents/partition.md`); a parent issue carrying
|
|
|
205
205
|
context) once with the `tdd` skill and the branch — it keeps `progress.md` live and
|
|
206
206
|
signals done; **step-by-step** runs the per-group loop (§Step-by-step
|
|
207
207
|
implementation) instead, and rejoins this flow at step 8 after the last group. With
|
|
208
|
-
**
|
|
209
|
-
protocol — no commit touches the branch before a checkpoint OK (§
|
|
210
|
-
|
|
208
|
+
**auto-commit OFF** (the gate's second knob), either mode runs the zero-commit
|
|
209
|
+
protocol — no commit touches the branch before a checkpoint OK (§Auto-commit
|
|
210
|
+
OFF); in all-at-once its single gate runs when the Implementer signals done,
|
|
211
211
|
**before** step 8 — the PR never opens on uncommitted work.
|
|
212
212
|
8. **Review** — flip to `harness:status:in-review` and **open the PR**
|
|
213
213
|
(`gh pr create`, `harness/<id>-<slug> → <default>`, with `Closes #<id>` in the PR
|
|
@@ -319,28 +319,32 @@ applies. The mode is switchable **downward only** (step-by-step → all-at-once,
|
|
|
319
319
|
at every checkpoint); there is no upgrade path — all-at-once has no stop where the
|
|
320
320
|
switch could be offered.
|
|
321
321
|
|
|
322
|
-
**The second knob, same interaction —
|
|
323
|
-
whether the
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
`human`
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
322
|
+
**The second knob, same interaction — auto-commit.** Alongside the mode, settle
|
|
323
|
+
whether the agent commits automatically as it works, or the human reviews the work
|
|
324
|
+
**uncommitted** before anything lands on the branch. Read
|
|
325
|
+
`implementation.auto_commit` from `harness.config.yml` first (an absent key means
|
|
326
|
+
`human`): `on` / `off` **pin the answer — record it without asking**; `human` (the
|
|
327
|
+
default) asks here, in the same interaction. Ask the **decision, not the mechanism**:
|
|
328
|
+
header **Auto-commit**, question _"Should the agent commit automatically as it
|
|
329
|
+
works?"_, exactly these two options in this order — **OFF first, marked recommended** —
|
|
330
|
+
with these descriptions (short and closed; do not pad them with mode arithmetic or
|
|
331
|
+
your own reading of the trade-off):
|
|
332
|
+
|
|
333
|
+
> **OFF (recommended)** — the agent makes **zero commits**: you review the work
|
|
334
|
+
> uncommitted in the Source Control panel (and can edit it in place); the branch
|
|
335
|
+
> gets its first commit on your OK. Choose this for more control.
|
|
331
336
|
>
|
|
332
|
-
> **
|
|
333
|
-
>
|
|
334
|
-
>
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
watch-fors — live in §Pre-commit review ON.
|
|
337
|
+
> **ON** — the agent commits and pushes as it works; checkpoints present
|
|
338
|
+
> committed work. Choose this to be interrupted less (in step-by-step it avoids
|
|
339
|
+
> one dirty-tree review per group).
|
|
340
|
+
|
|
341
|
+
Record it next to the mode line (`Auto-commit: off` / `on`) — execution state, like
|
|
342
|
+
the mode. The knob is **L1-only** (it rides this question; L2 always runs the
|
|
343
|
+
commit-as-you-go flow) and orthogonal to the mode: it moves **when the branch first
|
|
344
|
+
receives commits**, never whether review happens. In step-by-step the uncommitted
|
|
345
|
+
gate runs per group; in all-at-once it is **one gate over the entire implementation**
|
|
346
|
+
(its single "group" is the whole `tasks.md`). Auto-commit OFF's mechanics — staging
|
|
347
|
+
save-points, full deferral, watch-fors — live in §Auto-commit OFF.
|
|
344
348
|
|
|
345
349
|
## Step-by-step implementation (the per-group loop)
|
|
346
350
|
|
|
@@ -362,7 +366,7 @@ numbers still never shift. For each group, in order:
|
|
|
362
366
|
task-state paths, and the group's id + header line in `tasks.md` — by reference
|
|
363
367
|
(§Sub-agent invocation), it reads the group's tasks from the file itself (`tdd`
|
|
364
368
|
skill — checkbox by checkbox with small commits, as always). It commits to the
|
|
365
|
-
branch (
|
|
369
|
+
branch (auto-commit OFF: it **stages** instead of committing — point the
|
|
366
370
|
invocation at the staging protocol in `implementer.md`), logs to `progress.md`,
|
|
367
371
|
and signals done. **No PR yet** — the PR opens after the last group,
|
|
368
372
|
as in all-at-once; the human inspects and runs the **local checkout** (a checkpoint
|
|
@@ -372,7 +376,8 @@ numbers still never shift. For each group, in order:
|
|
|
372
376
|
group's tasks and requirements itself — scoped to the **group's diff against its
|
|
373
377
|
slice of the spec**. **Hand the group's anchor OID (step 1's step-log line) in the
|
|
374
378
|
spawn prompt in both knob states** — the Reviewer self-validates its ledger
|
|
375
|
-
against it (`--anchor`);
|
|
379
|
+
against it (`--anchor`); under auto-commit OFF the prompt additionally says
|
|
380
|
+
`auto-commit is OFF` —
|
|
376
381
|
the diff is then the group's **uncommitted work against the anchor**,
|
|
377
382
|
`git diff <anchor> -- ':(exclude).claude/state'`; worktree == index
|
|
378
383
|
within that scope at review time. The
|
|
@@ -401,9 +406,10 @@ numbers still never shift. For each group, in order:
|
|
|
401
406
|
invocation handed it, and your authoritative run catches a green built on the
|
|
402
407
|
wrong anchor — that red routes like any sidecar-side red below.
|
|
403
408
|
|
|
404
|
-
**In
|
|
405
|
-
reads the diff against the anchor _including uncommitted work_ — correct in
|
|
406
|
-
where the group's work is uncommitted by design, but in
|
|
409
|
+
**In auto-commit ON, run the verb on a clean tree.** The floor's owed set
|
|
410
|
+
reads the diff against the anchor _including uncommitted work_ — correct in
|
|
411
|
+
auto-commit OFF, where the group's work is uncommitted by design, but in
|
|
412
|
+
auto-commit ON an uncommitted tracked
|
|
407
413
|
edit outside `.claude/state` at validation time is a **human delta the Reviewer
|
|
408
414
|
never saw**, and it would red the floor as `unaccounted-file` on a ledger that is
|
|
409
415
|
not wrong. Check `git status --porcelain -- . ':(exclude).claude/state'` first; a
|
|
@@ -457,9 +463,9 @@ numbers still never shift. For each group, in order:
|
|
|
457
463
|
the unresolved disagreement (both positions, the spec slice) instead of a clean
|
|
458
464
|
step.
|
|
459
465
|
|
|
460
|
-
In
|
|
466
|
+
In auto-commit OFF, `OK` is also the moment the group's single code commit
|
|
461
467
|
lands, and `changes` sends the fresh Implementer to iterate **over the worktree**
|
|
462
|
-
— both per §
|
|
468
|
+
— both per §Auto-commit OFF.
|
|
463
469
|
|
|
464
470
|
3. **Human checkpoint** — first run §Checkpoint contract's **spec check** (item
|
|
465
471
|
4): the state commit below sweeps `spec/`, so an unconfirmed human spec edit
|
|
@@ -486,8 +492,8 @@ numbers still never shift. For each group, in order:
|
|
|
486
492
|
A failed push — offline, auth — warns and never blocks; the next push carries
|
|
487
493
|
it. This long wait is where a session likeliest dies — the push is what lets
|
|
488
494
|
another machine's `/resume` see the pending checkpoint.
|
|
489
|
-
**
|
|
490
|
-
OK the branch receives _nothing_, state included (§
|
|
495
|
+
**Auto-commit OFF skips this state commit and push entirely** — until the
|
|
496
|
+
OK the branch receives _nothing_, state included (§Auto-commit OFF): update
|
|
491
497
|
the `progress.md` line on disk only, accepting the documented price that the
|
|
492
498
|
pending checkpoint is invisible to a cold `/resume` from another machine.
|
|
493
499
|
Then present the step **per §Checkpoint contract**: what was built and how to
|
|
@@ -508,7 +514,7 @@ numbers still never shift. For each group, in order:
|
|
|
508
514
|
(`Mode: step-by-step (downgraded to all-at-once at step N)` — the gate choice
|
|
509
515
|
stays first; the downgrade is a suffix, because `task_done.mode` records the gate
|
|
510
516
|
choice), and run the **remaining** tasks as a single Implementer invocation
|
|
511
|
-
(all-at-once; the
|
|
517
|
+
(all-at-once; the auto-commit knob keeps its recorded setting).
|
|
512
518
|
Checkpoint OKs already given stand.
|
|
513
519
|
|
|
514
520
|
Aborting needs no protocol: the human interrupts the session; `/resume` picks the
|
|
@@ -521,11 +527,11 @@ numbers still never shift. For each group, in order:
|
|
|
521
527
|
`-- .claude/state/tasks/<id>/` pathspec as step 3's twin so other staged
|
|
522
528
|
content never rides mislabeled) + best-effort push ride as
|
|
523
529
|
**one composite turn** (§Turn economy) — without that commit a cold `/resume`
|
|
524
|
-
still reads `awaiting` after the last group.
|
|
530
|
+
still reads `awaiting` after the last group. Auto-commit OFF: on OK the
|
|
525
531
|
same composite additionally carries the group's **code commit** — the deferral
|
|
526
532
|
ends here, so code commit + state commit + push + emit land together (exact
|
|
527
533
|
commands, the pathspec split of the mixed index, and the all-at-once no-emit
|
|
528
|
-
rule in §
|
|
534
|
+
rule in §Auto-commit OFF); on
|
|
529
535
|
`changes` nothing is committed and the deferral continues. `<review-iterations>` is the number
|
|
530
536
|
of Reviewer invocations that preceded this checkpoint (≥ 1; resets after a
|
|
531
537
|
"changes"):
|
|
@@ -557,7 +563,7 @@ numbers still never shift. For each group, in order:
|
|
|
557
563
|
|
|
558
564
|
Per-step Reviewer REJECTs also emit `review_rejected` as usual, with the extra
|
|
559
565
|
`--step=<N>` flag (the `iteration` count stays task-global, as today; an
|
|
560
|
-
all-at-once single-group pass —
|
|
566
|
+
all-at-once single-group pass — auto-commit OFF's pre-gate review —
|
|
561
567
|
omits `--step`, per the events schema).
|
|
562
568
|
|
|
563
569
|
5. **`progress.md` step log** — keep the sub-state explicit so `/resume` can re-enter
|
|
@@ -593,24 +599,24 @@ checkpoint OK means "right direction and it runs", not a review waiver; the full
|
|
|
593
599
|
wins, and the human still holds the merge gate to disagree.
|
|
594
600
|
|
|
595
601
|
**Every full-pass APPROVE is ledger-validated too** — the step-8 review in either mode,
|
|
596
|
-
|
|
602
|
+
auto-commit OFF's single-group pre-gate pass, and a re-APPROVE after the merge gate
|
|
597
603
|
or PR comments sent the PR back to review. The Reviewer writes `full-pass.json` (no step
|
|
598
604
|
number → the union of every group); you run
|
|
599
605
|
`lemony review-ledger validate --task-id=<id> --anchor=<merge-base> --full-pass` —
|
|
600
606
|
the merge-base of the freshly fetched base and the PR head, the Diff-fingerprint's
|
|
601
|
-
own OID (L1 step 8;
|
|
607
|
+
own OID (L1 step 8; auto-commit OFF's pre-gate pass instead anchors at the OID
|
|
602
608
|
you recorded when its single group started) — and route a red exactly as
|
|
603
609
|
in step 2 above: one fresh Reviewer retry with the lines verbatim, a second red or any
|
|
604
610
|
spec-side problem to the human, and never an APPROVE relayed on a red ledger. Outside
|
|
605
611
|
the step loop the transient line is `awaiting ledger retry (full pass, retry 1/1)` in
|
|
606
612
|
`progress.md` (no step counter), and "the human" is the gate the pass feeds: the
|
|
607
|
-
|
|
613
|
+
auto-commit-OFF checkpoint when there is one, otherwise the merge-gate presentation,
|
|
608
614
|
with the verb's lines as the content.
|
|
609
615
|
|
|
610
616
|
## Checkpoint contract (how a human gate presents work)
|
|
611
617
|
|
|
612
618
|
Every human checkpoint — per-group in step-by-step, and the single all-at-once gate
|
|
613
|
-
when
|
|
619
|
+
when auto-commit is OFF — presents the same way. "Present the step" is a
|
|
614
620
|
contract, not a vibe:
|
|
615
621
|
|
|
616
622
|
1. **Anchored scope.** The group's **anchor** — the branch head OID recorded when
|
|
@@ -620,10 +626,10 @@ contract, not a vibe:
|
|
|
620
626
|
at the head after the last OK'd group) — bounds exactly what is
|
|
621
627
|
under review. Present the commit list + touched-files table, then the **exact
|
|
622
628
|
group diff**, always excluding task state:
|
|
623
|
-
-
|
|
629
|
+
- auto-commit ON: `git log --oneline <anchor>..HEAD` +
|
|
624
630
|
`git diff --stat <anchor>..HEAD -- ':(exclude).claude/state'`,
|
|
625
631
|
then `git diff <anchor>..HEAD -- ':(exclude).claude/state'`;
|
|
626
|
-
-
|
|
632
|
+
- auto-commit OFF (nothing committed):
|
|
627
633
|
`git diff --stat <anchor> -- ':(exclude).claude/state'`, then
|
|
628
634
|
`git diff <anchor> -- ':(exclude).claude/state'` — staged new files are in
|
|
629
635
|
the index, so they appear.
|
|
@@ -631,13 +637,13 @@ contract, not a vibe:
|
|
|
631
637
|
a difftool is configured (or an extension like GitLens is present), **offer**
|
|
632
638
|
the richer side-by-side view — an ephemeral config works without touching the
|
|
633
639
|
repo's (`git -c diff.tool=<tool> difftool <range>`). Never assume an extension
|
|
634
|
-
exists. In
|
|
635
|
-
uncommitted.
|
|
640
|
+
exists. In auto-commit OFF the Source Control panel already shows the work
|
|
641
|
+
natively — it IS uncommitted.
|
|
636
642
|
3. **What was built and how to run it** — from the Implementer's verification line,
|
|
637
643
|
as today. Checkpoint narration is the product, never overhead (§Turn economy).
|
|
638
644
|
4. **The human delta — always detected, always revalidated.** Whatever the answer
|
|
639
|
-
(`ok` or `changes: …`), first detect the human's own edits — in
|
|
640
|
-
checks with distinct routes** (every agent save-point is staged at
|
|
645
|
+
(`ok` or `changes: …`), first detect the human's own edits — in auto-commit
|
|
646
|
+
OFF, **two checks with distinct routes** (every agent save-point is staged at
|
|
641
647
|
presentation time, so the staged/unstaged seam is the auto-detect):
|
|
642
648
|
- **Work delta**: the unstaged section outside task state —
|
|
643
649
|
`git diff -- ':(exclude).claude/state'` **plus untracked files outside
|
|
@@ -664,10 +670,10 @@ contract, not a vibe:
|
|
|
664
670
|
recorded `**Resolution**` in `discoveries.md` is confirmed content — stage
|
|
665
671
|
it, don't re-raise.
|
|
666
672
|
|
|
667
|
-
The **spec check runs in both knob states** — in
|
|
668
|
-
3's `awaiting` state commit, which would otherwise silently
|
|
669
|
-
pre-existing spec edit before detection.
|
|
670
|
-
simply any uncommitted working-tree change.
|
|
673
|
+
The **spec check runs in both knob states** — in auto-commit ON, run it
|
|
674
|
+
**before** step 3's `awaiting` state commit, which would otherwise silently
|
|
675
|
+
commit a pre-existing spec edit before detection. Auto-commit ON's work-delta
|
|
676
|
+
detection is simply any uncommitted working-tree change.
|
|
671
677
|
The **work delta** then runs the pipeline: present it, confirm it is
|
|
672
678
|
intended, and **re-run the suite with the delta applied** — the delta never
|
|
673
679
|
inherits the group's green. **A red revalidation blocks the OK**: never run
|
|
@@ -675,15 +681,16 @@ contract, not a vibe:
|
|
|
675
681
|
`changes` (the human decides: fix it themselves, drop the delta, or hand it
|
|
676
682
|
to the Implementer). On a green revalidation the delta rides with the group:
|
|
677
683
|
on OK it lands with
|
|
678
|
-
the group's work (
|
|
684
|
+
the group's work (auto-commit OFF: staged into the group's commit; auto-commit ON:
|
|
685
|
+
committed as its
|
|
679
686
|
own commit — `step(<id>): step <N> human delta` — before the next group); on
|
|
680
687
|
`changes` it stays in the worktree for
|
|
681
|
-
the fresh Implementer (
|
|
682
|
-
§
|
|
688
|
+
the fresh Implementer (auto-commit OFF: staged first as the safe starting point —
|
|
689
|
+
§Auto-commit OFF).
|
|
683
690
|
|
|
684
|
-
##
|
|
691
|
+
## Auto-commit OFF (zero commits + staging save-points)
|
|
685
692
|
|
|
686
|
-
The
|
|
693
|
+
The auto-commit OFF state (chosen at the approval gate — §Implementation mode) moves
|
|
687
694
|
**when the branch first receives commits**; review, fix-loop routing, and the merge
|
|
688
695
|
gate are unchanged. v1 is deliberately an experiment — the watch-fors below are its
|
|
689
696
|
pre-registered exit signals.
|
|
@@ -716,8 +723,8 @@ pre-registered exit signals.
|
|
|
716
723
|
line drops the step counter). Its APPROVE is ledger-validated with `--full-pass`
|
|
717
724
|
and `--anchor=<oid>` — the anchor you recorded when its single group started
|
|
718
725
|
(§Step-by-step implementation, after the last group), never relayed on a red.
|
|
719
|
-
Hand that pre-gate Reviewer the anchor, the
|
|
720
|
-
|
|
726
|
+
Hand that pre-gate Reviewer the anchor, the words `auto-commit is OFF`, and "the
|
|
727
|
+
whole `tasks.md` as the single group" in place of a group id.
|
|
721
728
|
All agent work is
|
|
722
729
|
staged at presentation time; present per §Checkpoint contract. The human
|
|
723
730
|
inspects the work natively in the Source Control panel and edits/discards
|
|
@@ -749,7 +756,8 @@ pre-registered exit signals.
|
|
|
749
756
|
`step(<id>): step <N> checkpoint OK`, and the `step_completed` emit rides the
|
|
750
757
|
same turn (step 4). All-at-once (a downgraded remainder resolves as
|
|
751
758
|
all-at-once here too): `<code-msg>` = `task(<id>): implementation`,
|
|
752
|
-
`<state-msg>` = `task(<id>):
|
|
759
|
+
`<state-msg>` = `task(<id>): implementation checkpoint OK`, and **nothing is
|
|
760
|
+
emitted** —
|
|
753
761
|
`step_completed` is step-by-step-only (its `--step` has no meaning here, and
|
|
754
762
|
closeout's mode recovery reads any `step_completed` as proof of step-by-step);
|
|
755
763
|
the gate resolution reaches telemetry through `task_done` as usual. One commit
|
|
@@ -193,7 +193,7 @@ offer to resume it (`/resume #<k-id>`) in this session. A k already merged
|
|
|
193
193
|
a task branch (human-gated by house rules); merge is uglier history but safe. While k
|
|
194
194
|
is unmerged the source is `origin/harness/<k-id>-<k-slug>`. The merge-in is **your**
|
|
195
195
|
move at a checkpoint boundary (or the Implementer's on re-invoke, named in the spawn
|
|
196
|
-
prompt), never inside a staging save-point; under
|
|
196
|
+
prompt), never inside a staging save-point; under auto-commit OFF it goes right
|
|
197
197
|
after a checkpoint OK commit;
|
|
198
198
|
- record it in the child's trace line (`stacked — assumes it lands as-is`) and the
|
|
199
199
|
parent row (`→ #<id> spec-in-progress (stacked on #<k-id>)` — the status at opening,
|
|
@@ -243,7 +243,7 @@ mediation). On **"keep together"**: record the resolution and resume. On
|
|
|
243
243
|
this order:
|
|
244
244
|
|
|
245
245
|
1. **Ask the second half in the same mediation** — later-unit work already on the branch
|
|
246
|
-
(commits; under
|
|
246
|
+
(commits; under auto-commit OFF, staged save-points): keep it or drop it? Name
|
|
247
247
|
the commits. (A second fork that only exists because of the first answer: record it in
|
|
248
248
|
the same `**Resolution**` block, no second entry.)
|
|
249
249
|
2. **Create the parent** partition-plan issue with the current task as Part 1 (row
|
|
@@ -258,7 +258,7 @@ this order:
|
|
|
258
258
|
the narrowed `tasks.md` is re-presented at the next checkpoint — no new gate.
|
|
259
259
|
5. **Record and resume**: the Implementer re-invoke carries the keep/drop answer — it
|
|
260
260
|
performs the `git revert`s by commit (never a force-push), or discards the staged
|
|
261
|
-
later-unit changes under
|
|
261
|
+
later-unit changes under auto-commit OFF — and resumes on the narrowed slice.
|
|
262
262
|
|
|
263
263
|
On an **L2 (triage) task** — no PRD — "partition" means the task was mis-levelled: keep
|
|
264
264
|
the triage on its first unit (you narrow its fix plan in the issue body, read-modify-write)
|
|
@@ -69,7 +69,7 @@ verbatim — the spinoff offer depends on it surviving.
|
|
|
69
69
|
Every review of a spec-backed change that ends in an **APPROVE** writes a **ledger**: a
|
|
70
70
|
JSON sidecar at `.claude/state/tasks/<id>/review-ledger/step-<N>.json` — or
|
|
71
71
|
`full-pass.json` when the review has no step number (the all-at-once full pass, and
|
|
72
|
-
|
|
72
|
+
auto-commit OFF's single-group pre-gate pass). You write the file yourself, beside
|
|
73
73
|
your verdict; it is the one artifact you write besides your `progress.md` evidence
|
|
74
74
|
section, and the source under review stays untouched. A REJECT owes no ledger: nothing
|
|
75
75
|
is being approved, and its failing points already ride your return in full. A task with
|
|
@@ -241,7 +241,7 @@ lemony review-ledger validate --task-id=<id> --anchor=<anchor> --step=<N>
|
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
(`--full-pass` in place of `--step` on a full pass.) `<anchor>` is the anchor your
|
|
244
|
-
invocation handed you (per-step, and
|
|
244
|
+
invocation handed you (per-step, and auto-commit OFF's pre-gate pass); on the PR
|
|
245
245
|
full pass it is the merge-base you already compute for the stale-approve record —
|
|
246
246
|
`git merge-base FETCH_HEAD HEAD` after the fetch, the same OID the Diff-fingerprint
|
|
247
247
|
digests from. The Orchestrator re-validates with the anchor **it** recorded, so a
|
|
@@ -257,7 +257,7 @@ The change is a PR (`harness/<id>-<slug> → default`) the Orchestrator opened;
|
|
|
257
257
|
that PR's diff. Run your review skills in order — which ones you have depends on the
|
|
258
258
|
repo's capabilities (see Skills below); run whichever landed.
|
|
259
259
|
|
|
260
|
-
**Per-step review (step-by-step mode — and
|
|
260
|
+
**Per-step review (step-by-step mode — and auto-commit OFF's all-at-once
|
|
261
261
|
pre-gate pass, where the whole implementation is the single group).** The
|
|
262
262
|
Orchestrator may instead invoke you
|
|
263
263
|
mid-implementation, scoped to **one `tasks.md` group of tasks** — handed **by
|
|
@@ -265,7 +265,7 @@ reference** (the group's id + header line): read the group's tasks and the
|
|
|
265
265
|
requirements they reference from the spec files yourself. There is no PR yet —
|
|
266
266
|
review the **group's diff on the branch against its slice of the spec** (the whole
|
|
267
267
|
repo is your context; the verdict stays bounded to that slice). When the invocation
|
|
268
|
-
says **
|
|
268
|
+
says **auto-commit is OFF**, the group's work is **uncommitted by design**:
|
|
269
269
|
the diff is the group's uncommitted work against its anchor
|
|
270
270
|
(`git diff <anchor> -- ':(exclude).claude/state'`, the anchor handed in your
|
|
271
271
|
invocation; worktree == index within that scope at review time) — review it as
|
|
@@ -276,8 +276,8 @@ below, both in step 4: the verdict is **local** — return it in your summary fo
|
|
|
276
276
|
your ledger is `step-<N>.json` (the pre-gate single group, which has no step number,
|
|
277
277
|
writes `full-pass.json`) — and a
|
|
278
278
|
REJECT's `review_rejected` emit carries the extra `--step=<N>` flag **only when the
|
|
279
|
-
group is a step-by-step step** — an all-at-once single group (
|
|
280
|
-
|
|
279
|
+
group is a step-by-step step** — an all-at-once single group (auto-commit
|
|
280
|
+
OFF's pre-gate pass) omits `--step`, which the events schema defines as absent on
|
|
281
281
|
all-at-once rejections (`iteration` stays
|
|
282
282
|
task-global). Cross-group interactions are the final full-pass's job, not this one's;
|
|
283
283
|
that full-pass reviews everything as usual and may reject anything, including
|
|
@@ -50,8 +50,8 @@ A two-step pause:
|
|
|
50
50
|
|
|
51
51
|
The hook prints `git status --porcelain` after emitting — it never
|
|
52
52
|
auto-commits. Decide what (if anything) to stage and commit before leaving.
|
|
53
|
-
Exception — a task running **
|
|
54
|
-
|
|
53
|
+
Exception — a task running **auto-commit OFF** (orchestrator §Auto-commit
|
|
54
|
+
OFF): do **not** commit; the zero-commit deferral binds `/pause` too.
|
|
55
55
|
Leave the staged save-point untouched (it survives the session; staging
|
|
56
56
|
anything now is the mid-experiment `add` the protocol forbids), note the pause
|
|
57
57
|
in `progress.md` — disk-only, per the mode's contract.
|
|
@@ -36,15 +36,16 @@ drop `harness:needs-design` and continue toward spec-ready. When `progress.md` r
|
|
|
36
36
|
`Mode: step-by-step`, the `## Step log` carries the step sub-state — resume
|
|
37
37
|
exactly there: `awaiting human checkpoint (step N/M)` re-presents that pending
|
|
38
38
|
checkpoint (inspect / run / OK / changes / OK+downgrade; in all-at-once under
|
|
39
|
-
|
|
39
|
+
auto-commit OFF the line carries no `(step N/M)` counter — same
|
|
40
40
|
re-entry); a `fix-loop iteration K`
|
|
41
41
|
line re-enters the per-step implement→review loop at that iteration; an
|
|
42
42
|
`awaiting ledger retry (step N/M, retry 1/1)` line — or its full-pass twin
|
|
43
43
|
`awaiting ledger retry (full pass, retry 1/1)` — re-runs
|
|
44
44
|
`lemony review-ledger validate` on the sidecar on disk (`--anchor` per the
|
|
45
45
|
orchestrator: per-step, the `(anchor <oid>)` on the step-log line; full pass, the
|
|
46
|
-
fingerprint merge-base — except
|
|
47
|
-
at the recorded single-group anchor, = the branch head:
|
|
46
|
+
fingerprint merge-base — except auto-commit OFF's pre-gate twin, which anchors
|
|
47
|
+
at the recorded single-group anchor, = the branch head: auto-commit OFF defers every
|
|
48
|
+
commit) —
|
|
48
49
|
green re-presents the gate
|
|
49
50
|
the pass feeds, red goes to that gate with the verb's lines, the single retry being spent
|
|
50
51
|
(authority: the orchestrator §Step-by-step implementation). If the Mode line carries a
|
|
@@ -55,13 +56,13 @@ history, not sub-state).
|
|
|
55
56
|
**Cross-machine pickup of an `in-progress` task**: the branch on origin
|
|
56
57
|
carries the last **successful** best-effort WIP push — the Implementer pushes on
|
|
57
58
|
signaling done, and step-by-step pushes at each checkpoint-wait. Exception: a task
|
|
58
|
-
running **
|
|
59
|
+
running **auto-commit OFF** defers every commit and push until a checkpoint OK
|
|
59
60
|
— in-flight work and its `progress.md` updates exist only in the implementing
|
|
60
61
|
machine's worktree, so from another machine the branch reads as of the last OK'd
|
|
61
62
|
group; before the first OK it is **indistinguishable from idle** (the knob record
|
|
62
|
-
itself is deferred — the documented price of the mode, orchestrator §
|
|
63
|
-
|
|
64
|
-
branch's `progress.md` already carries the `
|
|
63
|
+
itself is deferred — the documented price of the mode, orchestrator §Auto-commit
|
|
64
|
+
OFF). When config pins `implementation.auto_commit: off`, or the
|
|
65
|
+
branch's `progress.md` already carries the `Auto-commit: off` line (some OK
|
|
65
66
|
landed), surface the mode instead of concluding the task is idle. A discovery
|
|
66
67
|
pause still surfaces cross-machine via its labels and issue comment, though the
|
|
67
68
|
full `discoveries.md` entry stays machine-local. If a **local** copy
|
package/catalog/hooks/init.sh
CHANGED
|
@@ -130,9 +130,17 @@ if [ ! -x "$PEER_HOOK" ]; then
|
|
|
130
130
|
WARNINGS+=("$PEER_HOOK is missing or not executable — \`/pause\` and SessionEnd telemetry will fail. Re-run \`lemony install\` (or chmod +x).")
|
|
131
131
|
fi
|
|
132
132
|
|
|
133
|
-
# ── Warning 3 ─
|
|
133
|
+
# ── Warning 3 ─ default branch behind origin (no fetch — uses existing refs) ─
|
|
134
|
+
# Scoped to a session sitting ON the default branch: "pull before starting" is
|
|
135
|
+
# advice for main, and a task branch is behind origin/main by construction.
|
|
136
|
+
# The nudge used to fire branch-agnostic: mid-task under auto-commit OFF
|
|
137
|
+
# the worktree is dirty by design with the staged index as the save-point
|
|
138
|
+
# ladder, and following `git pull --rebase` with `rebase.autostash` set flattens
|
|
139
|
+
# that index — the documented rollback then reverts to the last commit and the
|
|
140
|
+
# group's whole uncommitted work is gone. So on any other branch: silence.
|
|
141
|
+
CURRENT_BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
|
|
134
142
|
DEFAULT_BRANCH="$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')"
|
|
135
|
-
if [ -n "$DEFAULT_BRANCH" ]; then
|
|
143
|
+
if [ -n "$DEFAULT_BRANCH" ] && [ "$CURRENT_BRANCH" = "$DEFAULT_BRANCH" ]; then
|
|
136
144
|
BEHIND="$(git rev-list --count "HEAD..origin/$DEFAULT_BRANCH" 2>/dev/null || echo 0)"
|
|
137
145
|
if [ "${BEHIND:-0}" -gt 0 ]; then
|
|
138
146
|
WARNINGS+=("local branch is $BEHIND commit(s) behind origin/$DEFAULT_BRANCH. Consider \`git pull --rebase\` before starting.")
|
|
@@ -142,7 +150,6 @@ fi
|
|
|
142
150
|
# ── Warning 4 ─ residual harness branches (parked tasks) ───────────────────
|
|
143
151
|
STALE_BRANCHES="$(git for-each-ref --format='%(refname:short)' refs/heads/harness/ 2>/dev/null | wc -l | tr -d ' ')"
|
|
144
152
|
if [ "${STALE_BRANCHES:-0}" -gt 0 ]; then
|
|
145
|
-
CURRENT_BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
|
|
146
153
|
case "$CURRENT_BRANCH" in
|
|
147
154
|
harness/*) : ;;
|
|
148
155
|
*) WARNINGS+=("$STALE_BRANCHES parked harness branch(es) present. Resume with \`continue <id>\` or close them with task-closeout.") ;;
|
|
@@ -87,10 +87,11 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
|
|
|
87
87
|
7. **Implement** — Implementer sub-agent, `tdd` skill, on the branch, keeping
|
|
88
88
|
`.claude/state/tasks/<id>/progress.md` live. The gate interaction settles two
|
|
89
89
|
knobs, both recorded in `progress.md`: the **implementation mode** (all-at-once,
|
|
90
|
-
or step-by-step with per-group human checkpoints) and **
|
|
91
|
-
(config `implementation.
|
|
92
|
-
**uncommitted**
|
|
93
|
-
|
|
90
|
+
or step-by-step with per-group human checkpoints) and **auto-commit**
|
|
91
|
+
(config `implementation.auto_commit`; auto-commit OFF = zero commits — the human
|
|
92
|
+
reviews the work **uncommitted** until each checkpoint OK; auto-commit ON =
|
|
93
|
+
commit-as-you-go).
|
|
94
|
+
Authority: orchestrator §Implementation mode / §Auto-commit OFF.
|
|
94
95
|
8. **Review** — flip to `harness:status:in-review`, open the PR (`gh pr create`,
|
|
95
96
|
branch → default), Reviewer sub-agent reviews it (`senior-review`, fresh context). If
|
|
96
97
|
the task touched UI, the **UI Designer** reviews as a distinct design + a11y lens too
|
|
@@ -59,13 +59,17 @@ rollback:
|
|
|
59
59
|
# allow_no_checks: false
|
|
60
60
|
|
|
61
61
|
# Implementation-phase knobs the Orchestrator reads at the L1 approval gate.
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
62
|
+
# auto_commit — does the agent commit automatically as it works? `on`: the
|
|
63
|
+
# Implementer commits and pushes as it goes; `off`: zero commits — the work
|
|
64
|
+
# accumulates in the worktree, you review it UNCOMMITTED (Source Control) and
|
|
65
|
+
# the branch gets its first commit on your OK (more control). `human` (default)
|
|
66
|
+
# asks at the gate, in the same interaction as the all-at-once / step-by-step
|
|
67
|
+
# choice, recommending OFF; `on` / `off` pin the answer and skip the question
|
|
68
|
+
# (a headless/bench run must pin `on`). Bare on/off are strings here (YAML 1.2).
|
|
69
|
+
# Replaces `pre_commit_review` (0.3.0, inverse polarity): the old key is
|
|
70
|
+
# tolerated and removed on the next config write; its value is not migrated.
|
|
67
71
|
# implementation:
|
|
68
|
-
#
|
|
72
|
+
# auto_commit: human
|
|
69
73
|
|
|
70
74
|
# Review gates. The mechanical gates a Reviewer must attest in its evidence
|
|
71
75
|
# ledger, one entry per name, beyond the always-required real run — a flat list of
|
package/dist/cli.mjs
CHANGED
|
@@ -23,18 +23,22 @@ const DEPRECATED_PATHS_KEYS = [
|
|
|
23
23
|
"skills",
|
|
24
24
|
"agents"
|
|
25
25
|
];
|
|
26
|
+
const DEPRECATED_IMPLEMENTATION_KEYS = ["pre_commit_review"];
|
|
26
27
|
const HARNESS_CONFIG_SCHEMA_FILENAME = "harness.config.schema.json";
|
|
27
|
-
const CONFIG_KEY_SINCE = {
|
|
28
|
+
const CONFIG_KEY_SINCE = {
|
|
29
|
+
gates: "0.3.0",
|
|
30
|
+
"implementation.auto_commit": "0.4.0"
|
|
31
|
+
};
|
|
28
32
|
const TASK_STORAGE_REPO_PLACEHOLDER = "OWNER/REPO";
|
|
29
33
|
const TARGETS = ["claude-code"];
|
|
30
34
|
const TASK_STORAGE_TYPES = ["github"];
|
|
31
35
|
const TASK_STORAGE_REPO_PATTERN = /^[^\s/]+\/[^\s/]+$/;
|
|
32
|
-
const
|
|
36
|
+
const AUTO_COMMIT_MODES = [
|
|
33
37
|
"human",
|
|
34
38
|
"on",
|
|
35
39
|
"off"
|
|
36
40
|
];
|
|
37
|
-
const
|
|
41
|
+
const AUTO_COMMIT_DEFAULT = "human";
|
|
38
42
|
const VENDOR_VERSION_REGEX = /^\d+\.\d+\.\d+(-(alpha|beta|rc)\.\d+)?$/;
|
|
39
43
|
const VENDOR_VERSION_EXAMPLE = "0.1.0-alpha.0";
|
|
40
44
|
const PATHS_DEFAULTS = {
|
|
@@ -121,7 +125,7 @@ const mergeSchema = z.object({
|
|
|
121
125
|
checks_timeout_secs: z.int().positive().default(600),
|
|
122
126
|
allow_no_checks: z.boolean().default(false)
|
|
123
127
|
}).strict().prefault({});
|
|
124
|
-
const implementationSchema = z.object({
|
|
128
|
+
const implementationSchema = z.object({ auto_commit: z.enum(AUTO_COMMIT_MODES).default(AUTO_COMMIT_DEFAULT) }).strict().prefault({});
|
|
125
129
|
const gatesSchema = z.array(z.string().trim().min(1)).optional();
|
|
126
130
|
const harnessConfigSchema = z.object({
|
|
127
131
|
vendor_version: z.string().regex(VENDOR_VERSION_REGEX),
|
|
@@ -161,6 +165,12 @@ const readHarnessConfig = async (repoRoot) => {
|
|
|
161
165
|
for (const key of DEPRECATED_PATHS_KEYS) delete pathsCandidate[key];
|
|
162
166
|
candidate.paths = pathsCandidate;
|
|
163
167
|
}
|
|
168
|
+
const implementation = candidate.implementation;
|
|
169
|
+
if (implementation && typeof implementation === "object" && !Array.isArray(implementation)) {
|
|
170
|
+
const implementationCandidate = { ...implementation };
|
|
171
|
+
for (const key of DEPRECATED_IMPLEMENTATION_KEYS) delete implementationCandidate[key];
|
|
172
|
+
candidate.implementation = implementationCandidate;
|
|
173
|
+
}
|
|
164
174
|
const result = harnessConfigSchema.safeParse(candidate);
|
|
165
175
|
if (!result.success) throw new Error(`${HARNESS_CONFIG_FILENAME} is invalid:\n${formatConfigError(harnessConfigSchema, result.error)}`);
|
|
166
176
|
return result.data;
|
|
@@ -222,6 +232,7 @@ const setConfigValues = (rawYaml, updates) => {
|
|
|
222
232
|
for (const key of DEPRECATED_PATHS_KEYS) if (doc.hasIn(["paths", key])) doc.deleteIn(["paths", key]);
|
|
223
233
|
const pathsNode = doc.get("paths", true);
|
|
224
234
|
if (isMap(pathsNode) && pathsNode.items.length === 0) doc.delete("paths");
|
|
235
|
+
for (const key of DEPRECATED_IMPLEMENTATION_KEYS) if (doc.hasIn(["implementation", key])) doc.deleteIn(["implementation", key]);
|
|
225
236
|
return doc.toString();
|
|
226
237
|
};
|
|
227
238
|
const writeConfigValues = async (repoRoot, updates) => {
|
|
@@ -5840,7 +5851,7 @@ const reviewLedgerValidate = async (args) => {
|
|
|
5840
5851
|
if (taskId === void 0) throw new Error(`${REVIEW_LEDGER_USAGE} --task-id is required.`);
|
|
5841
5852
|
if (taskId !== basename(taskId) || taskId === "." || taskId === "..") throw new Error(`Invalid --task-id="${taskId}": a task id names one directory under the task state, never a path.`);
|
|
5842
5853
|
const anchor = parseFlag(args, "anchor");
|
|
5843
|
-
if (anchor === void 0) throw new Error(`${REVIEW_LEDGER_USAGE} --anchor is required — the group's recorded head OID on a step, the fingerprint merge-base on a full pass (
|
|
5854
|
+
if (anchor === void 0) throw new Error(`${REVIEW_LEDGER_USAGE} --anchor is required — the group's recorded head OID on a step, the fingerprint merge-base on a full pass (auto-commit OFF's pre-gate pass anchors at the recorded group anchor instead); the owed set of the mutant floor is the diff from it.`);
|
|
5844
5855
|
const address = parseLedgerAddress(args);
|
|
5845
5856
|
const result = await runLedgerValidate({
|
|
5846
5857
|
repoRoot: cwd(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lemoncode/lemony",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Lemony — a Harness for AI Coding. Vendor package: installer, agent role catalog, generic skill catalog, hooks, and templates for a Spec-Driven Development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|