infinity-harness 2.3.0 → 2.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/CHANGELOG.md +85 -1
- package/README.md +87 -22
- package/extensions/infinity-harness/index.ts +223 -39
- package/package.json +2 -2
- package/src/approval.ts +15 -5
- package/src/core/config.ts +44 -1
- package/src/core/init.ts +22 -1
- package/src/core/paths.ts +25 -0
- package/src/core/settings.ts +141 -12
- package/src/core/types.ts +49 -1
- package/src/intake.ts +124 -115
- package/src/remote.ts +7 -1
- package/src/ui/config.ts +6 -0
- package/src/ui/dashboard.ts +90 -27
- package/src/ui/display.ts +267 -0
- package/src/ui/planTree.ts +38 -16
- package/src/ui/widget.ts +50 -18
- package/src/ui/wizard.ts +257 -90
- package/src/workflow.ts +309 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,74 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [2.
|
|
7
|
+
## [2.4.0] — 2026-08-24
|
|
8
|
+
|
|
9
|
+
Two settings that were one switch each, and one switch turned out to be the wrong shape for both
|
|
10
|
+
questions.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **A mode per phase, not a mode per run.** "copilot" and "autopilot" could not say "let it define
|
|
15
|
+
and plan on its own but show me the review", and that is a thing people want. Every phase now
|
|
16
|
+
carries its own mode: `autopilot` advances when the gate passes, `copilot` stops and waits for
|
|
17
|
+
your signature. The two familiar words survive as two named points in that space rather than the
|
|
18
|
+
only two points in it, and every phase except INIT can be a checkpoint — the three that decide
|
|
19
|
+
*what gets built* are the ones that usually pay for themselves, but that is a default rather than
|
|
20
|
+
a limit.
|
|
21
|
+
|
|
22
|
+
- **Five workflows ship, and you can build your own.** `copilot`, `autopilot`, `spec and ship`
|
|
23
|
+
(you sign the scope going in and the release coming out), `research first`, and `every gate`.
|
|
24
|
+
Building one is its own short flow: pick the phases, then say for each whether it stops for you.
|
|
25
|
+
Name it and it is kept — in `~/.pi/agent/infinity-harness/`, with *you* rather than with the
|
|
26
|
+
project, because a workflow you designed is worth as much on the next one. It is then the first
|
|
27
|
+
thing offered there.
|
|
28
|
+
|
|
29
|
+
Built-ins are read-only, and their names cannot be taken. "copilot" has to mean the same thing in
|
|
30
|
+
every conversation about this tool; someone who wants a different copilot makes their own and
|
|
31
|
+
gives it their own name.
|
|
32
|
+
|
|
33
|
+
- **`/infinity:workflow`** — choose one, build one, switch by name (`/infinity:workflow
|
|
34
|
+
spec-and-ship`), or `list` what is available and what you are on. `/infinity:config` →
|
|
35
|
+
**Workflow** edits one phase at a time. All of it changes at any time and takes effect at the next
|
|
36
|
+
gate: three phases into a run is exactly when someone realises they do want to see the review.
|
|
37
|
+
|
|
38
|
+
- **Display templates.** Shipping all five plan levels to everyone was the wrong answer for the same
|
|
39
|
+
reason shipping two was: one person works in sprints and never opens a subtask, the next has no
|
|
40
|
+
sprints and lives in the subtask list. Four ship — `focus`, `everything`, `overview` (the shape,
|
|
41
|
+
no tasks), `worklist` (tasks only, no rail) — and anything else is chosen level by level: the
|
|
42
|
+
five plan levels, the `done/total` counts, the `← #3` dependency labels, the acceptance criteria,
|
|
43
|
+
the phase rail, the progress meter, the alert strip, and how many rows the terminal shows before
|
|
44
|
+
it scrolls. Name what you end up with and it is saved with you like a workflow.
|
|
45
|
+
|
|
46
|
+
- **`/infinity:display`** — pick a template, choose level by level, switch by name, or `list`.
|
|
47
|
+
`/infinity:config` → **Display** edits the same things one at a time.
|
|
48
|
+
|
|
49
|
+
- **The widget and the dashboard read the same setting.** Configure how you like to read a plan
|
|
50
|
+
once, not twice.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- `harness/config.json` gains `phaseModes`, `workflow` and `display`. A 2.3 config is migrated on
|
|
55
|
+
read: its three-phase `approvals` becomes the equivalent modes and is labelled with the workflow
|
|
56
|
+
it amounts to, so a project mid-run keeps exactly the approvals it was configured with and nobody
|
|
57
|
+
has to edit JSON to upgrade. The legacy field is kept in step on write.
|
|
58
|
+
- Hiding a plan level hides the row, never the work beneath it: turn off sprints on a plan organised
|
|
59
|
+
into sprints and the features move up one indent rather than vanishing, and task numbers do not
|
|
60
|
+
shift, so `← #3` still points at the same task. A task nobody can see is a task that gets stuck
|
|
61
|
+
forever.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- **`initHarness` ignored a caller that passed the old `approvals` shape** once `phaseModes`
|
|
66
|
+
existed, silently producing an autopilot run. It falls back to `approvals` when no modes are
|
|
67
|
+
given — the same rule `loadConfig` applies to an older file.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## [2.3.1] — 2026-08-24
|
|
72
|
+
|
|
73
|
+
*(2.3.0 was staged at the registry and never completed; 2.3.1 is that release plus the last two
|
|
74
|
+
fixes below, and is the first version of this work anyone can install.)*
|
|
8
75
|
|
|
9
76
|
Five bugs were reported against 2.1.0 by someone actually using the thing. Every one of them was
|
|
10
77
|
real, none of them could be seen by a test that mocks pi, and finding out why led to the change
|
|
@@ -106,6 +173,23 @@ that matters most in this release: **the suite now drives a real `pi` process.**
|
|
|
106
173
|
|
|
107
174
|
- **The phase picker did not offer RESEARCH** — a feature nobody could find.
|
|
108
175
|
|
|
176
|
+
- **A handoff in a one-shot `pi -p` run replaced the session out from under the instance that
|
|
177
|
+
asked for it**, so every handler afterwards touched a torn-down context and pi reported
|
|
178
|
+
"This extension ctx is stale after session replacement" on every turn. A headless run has no
|
|
179
|
+
next turn to hand anything to, so it does not hand off — and the extension now stops touching
|
|
180
|
+
pi the moment its session is shut down, whatever the reason.
|
|
181
|
+
|
|
182
|
+
- **A new phase counted as a stall.** The first failure of a phase was compared against the
|
|
183
|
+
fingerprint taken when the *previous* phase passed — identical, because nothing had happened
|
|
184
|
+
yet — so the run spent `retry` and `reframe` on the opening turn of every single phase, and
|
|
185
|
+
arrived at the rungs that matter with the cheap ones already gone. A stall is the agent
|
|
186
|
+
producing nothing when asked; a fresh brief has not asked yet.
|
|
187
|
+
|
|
188
|
+
- **The gate history counted every pass twice.** `runChecks` recorded the verdict and
|
|
189
|
+
`transitionPhase` recorded it again, so the history read `define:pass → define:pass`, which says
|
|
190
|
+
a phase had to be attempted twice — the opposite of what happened. Repeated *failures* are still
|
|
191
|
+
every one of them: that is the fact a human comes back to read.
|
|
192
|
+
|
|
109
193
|
### Changed
|
|
110
194
|
|
|
111
195
|
- The dashboard shows every subtask, not only the active task's. The widget has nine rows and a job
|
package/README.md
CHANGED
|
@@ -100,11 +100,10 @@ It detects your stack and its lint/test/build commands, then asks you five quest
|
|
|
100
100
|
|
|
101
101
|
| | |
|
|
102
102
|
|---|---|
|
|
103
|
-
| **
|
|
104
|
-
| **What are you building?** | One or two sentences. Asked
|
|
105
|
-
| **Research it first?** | Adds an optional RESEARCH phase before DEFINE: prior art, constraints, options with costs, a recommendation, and the questions only you can answer. |
|
|
106
|
-
| **Which phases do you sign?** *(autopilot only)* | RESEARCH, DEFINE, PLAN — tick any, all or none. None is the walk-away setting. |
|
|
103
|
+
| **Which workflow?** | A built-in, one you saved, or *build one* — pick the phases, then say for each whether it stops for you |
|
|
104
|
+
| **What are you building?** | One or two sentences. Asked whatever the workflow, because a run with no goal has no business inventing one. |
|
|
107
105
|
| **When should it start a fresh session?** | Every phase (default) · every task · never |
|
|
106
|
+
| **How much of the plan on screen?** | A display template — `focus`, `everything`, `overview`, `worklist`, or level by level |
|
|
108
107
|
|
|
109
108
|
Then it writes `harness/` — the config, an empty plan, the phase and role docs, and starters
|
|
110
109
|
for the documents the review gate will demand — and hands the model its first brief. It never
|
|
@@ -134,14 +133,45 @@ the craft skills that match the work, and what to do next. Do the work, then:
|
|
|
134
133
|
|
|
135
134
|
## Who decides what
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
"copilot" and "autopilot" used to be one switch, and one switch is the wrong shape for the
|
|
137
|
+
question. What people actually want is per-phase: let the model define and plan on its own but
|
|
138
|
+
show me the review; or grill me on the definition and then leave me alone until it ships. Two
|
|
139
|
+
words cannot say that.
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
|
144
|
-
|
|
141
|
+
So the setting is a **mode per phase**, and the two familiar words are two named points in that
|
|
142
|
+
space rather than the only two points in it.
|
|
143
|
+
|
|
144
|
+
| Mode | When a phase's gate passes |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `autopilot` | it advances |
|
|
147
|
+
| `copilot` | it stops and waits for your signature |
|
|
148
|
+
|
|
149
|
+
Five workflows ship with the package, and none of them can be edited — `copilot` has to mean the
|
|
150
|
+
same thing in every conversation about this tool:
|
|
151
|
+
|
|
152
|
+
| Workflow | Stops at |
|
|
153
|
+
|---|---|
|
|
154
|
+
| **copilot** | DEFINE, PLAN |
|
|
155
|
+
| **autopilot** | nothing |
|
|
156
|
+
| **spec and ship** | DEFINE, SHIP — you sign the scope going in and the release coming out |
|
|
157
|
+
| **research first** | adds a RESEARCH phase, and stops on all three thinking phases |
|
|
158
|
+
| **every gate** | every phase. Slowest, and the one you want on something that matters |
|
|
159
|
+
|
|
160
|
+
Anything else you build yourself: pick the phases, then say for each whether it stops for you. Give
|
|
161
|
+
it a name and it is saved with *you*, not with the project, so it is the first thing offered on your
|
|
162
|
+
next one.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
/infinity:workflow choose one, or build one
|
|
166
|
+
/infinity:workflow spec-and-ship switch to a named one, no menu
|
|
167
|
+
/infinity:workflow list what is available, and what you are on now
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Any of it changes at any time and takes effect at the next gate — three phases into a run is
|
|
171
|
+
exactly when someone realises they do want to see the review after all. `/infinity:config` →
|
|
172
|
+
**Workflow** edits one phase at a time.
|
|
173
|
+
|
|
174
|
+
### Signing a phase
|
|
145
175
|
|
|
146
176
|
When a phase you signed up for passes its gate, the run **stops and asks you** rather than
|
|
147
177
|
advancing. Approving continues it; answering with a sentence sends the phase back carrying your
|
|
@@ -152,9 +182,9 @@ words, so it is redone against your objection rather than redone identically:
|
|
|
152
182
|
/infinity:approve the criteria say nothing about refunds
|
|
153
183
|
```
|
|
154
184
|
|
|
155
|
-
A rejection is pinned to the state of the project when you made it, so the run will not ask you
|
|
156
|
-
|
|
157
|
-
|
|
185
|
+
A rejection is pinned to the state of the project when you made it, so the run will not ask you the
|
|
186
|
+
same question again until the agent has actually changed something in response. If it never does,
|
|
187
|
+
the run stops and says so instead of nagging forever.
|
|
158
188
|
|
|
159
189
|
## One run, many sessions
|
|
160
190
|
|
|
@@ -224,7 +254,8 @@ valid; the menu is the same data with prompts and bounds checking attached.
|
|
|
224
254
|
|---|---|
|
|
225
255
|
| **Models** | Which model runs each difficulty tier, the master model, consultation budget |
|
|
226
256
|
| **Pipeline** | Which phases run, copilot vs autopilot, role strictness, pause |
|
|
227
|
-
| **
|
|
257
|
+
| **Workflow** | The mode for each phase — which of them stop and wait for your signature |
|
|
258
|
+
| **Display** | Which plan levels and which chrome the widget and the dashboard draw |
|
|
228
259
|
| **Sessions** | Fresh session per phase or per task, the context threshold, the carry note |
|
|
229
260
|
| **Project commands** | lint / test / coverage / build — what the gate actually runs |
|
|
230
261
|
| **Gates** | Enable, coverage threshold, placeholder rejection |
|
|
@@ -349,7 +380,7 @@ The agent edits it by submitting the **complete** task list through the `infinit
|
|
|
349
380
|
|
|
350
381
|
## Watching it work
|
|
351
382
|
|
|
352
|
-
**In the terminal** — the widget
|
|
383
|
+
**In the terminal** — the widget updates on every turn, showing the plan as deep as you asked for:
|
|
353
384
|
goal, sprint, feature, task, subtask. It is a *window*, not a truncation — the rows above and
|
|
354
385
|
below are counted, and one keypress away:
|
|
355
386
|
|
|
@@ -362,14 +393,43 @@ below are counted, and one keypress away:
|
|
|
362
393
|
It is responsive down to ~58 columns, degrades to ASCII when the locale isn't UTF-8, and drops
|
|
363
394
|
colour under `NO_COLOR`.
|
|
364
395
|
|
|
365
|
-
**In a browser** — `/infinity:dashboard` serves a live page on loopback: phase rail, stacked
|
|
366
|
-
meters that show stuck work as colour rather than absence, the
|
|
367
|
-
|
|
368
|
-
|
|
396
|
+
**In a browser** — `/infinity:dashboard` serves a live page on loopback: phase rail, stacked
|
|
397
|
+
progress meters that show stuck work as colour rather than absence, the plan as a collapsible tree
|
|
398
|
+
with counts at every level, and the last gate verdict. It refreshes itself every 5 seconds and
|
|
399
|
+
reconnects with backoff if the run ends.
|
|
369
400
|
|
|
370
401
|
The dashboard is strictly read-only and binds to `127.0.0.1`. It never writes, and never bumps
|
|
371
402
|
`baseRevision` — opening it can't perturb the run you're watching.
|
|
372
403
|
|
|
404
|
+
### What they show is yours to choose
|
|
405
|
+
|
|
406
|
+
Two people watching the same run want different things on screen. One works in sprints and never
|
|
407
|
+
opens a subtask; the next has no sprints and lives in the subtask list. So it is a setting, and
|
|
408
|
+
**the widget and the dashboard read the same one** — configure how you like to read a plan once,
|
|
409
|
+
not twice.
|
|
410
|
+
|
|
411
|
+
| Template | |
|
|
412
|
+
|---|---|
|
|
413
|
+
| **focus** *(default)* | every level, with subtasks on the task being worked |
|
|
414
|
+
| **everything** | all five levels, every subtask on every task, a taller window |
|
|
415
|
+
| **overview** | goals, sprints and features with their counts. No tasks — the shape, not the work |
|
|
416
|
+
| **worklist** | tasks only. No grouping rows, no rail — for when you already know the plan |
|
|
417
|
+
|
|
418
|
+
```
|
|
419
|
+
/infinity:display pick a template, or choose level by level
|
|
420
|
+
/infinity:display overview switch straight to one
|
|
421
|
+
/infinity:display list what is available, and what you are on now
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Choosing level by level also lets you turn off the phase rail, the progress meter, the alert strip,
|
|
425
|
+
the `done/total` counts, the `← #3` dependency labels and the acceptance criteria, and set how many
|
|
426
|
+
rows the terminal shows before it scrolls. Name what you end up with and it is saved with you,
|
|
427
|
+
offered on your next project. `/infinity:config` → **Display** edits the same things one at a time.
|
|
428
|
+
|
|
429
|
+
Hiding a level never hides what is under it: turn off sprints on a plan organised into sprints and
|
|
430
|
+
the features move up one indent, they do not vanish. A task nobody can see is a task that gets
|
|
431
|
+
stuck forever.
|
|
432
|
+
|
|
373
433
|
## Craft skills
|
|
374
434
|
|
|
375
435
|
28 short documents on how to do the work well — how to write a test worth
|
|
@@ -447,10 +507,12 @@ infinity-harness/
|
|
|
447
507
|
│ │ · featureList (the SSOT) · lock · exec
|
|
448
508
|
│ │ · skills (match) · skillsAudit (guard)
|
|
449
509
|
│ ├── ui/ theme · planTree (the five levels, once)
|
|
510
|
+
│ │ · display (what to draw, and the templates)
|
|
450
511
|
│ │ · widget (terminal) · dashboard (web) · wizard · config
|
|
451
512
|
│ ├── loop.ts the continuous-run driver and its stop conditions
|
|
452
513
|
│ ├── runState.ts is a run armed, and which run is it — on disk, across sessions
|
|
453
514
|
│ ├── handoff.ts when to continue in a fresh session, and what to tell it
|
|
515
|
+
│ ├── workflow.ts a mode per phase, the built-ins, and the ones you save
|
|
454
516
|
│ ├── approval.ts · intake.ts human sign-off · what the start-up wizard's answers mean
|
|
455
517
|
│ ├── escalate.ts the ladder's actuator: chooses a rung and takes it
|
|
456
518
|
│ ├── goal.ts the outer loop: is the thing asked for actually done?
|
|
@@ -467,7 +529,10 @@ infinity-harness/
|
|
|
467
529
|
│ ├── model-router.json optional routing
|
|
468
530
|
│ ├── docs/ architecture · decisions · phase and role docs
|
|
469
531
|
│ └── skills/ 28 craft skills the brief points at
|
|
470
|
-
|
|
532
|
+
│
|
|
533
|
+
│ ~/.pi/agent/infinity-harness/ the workflows and display templates you saved —
|
|
534
|
+
│ they belong to you, not to a project
|
|
535
|
+
├── tests/ 33 files, plain node:assert
|
|
471
536
|
└── scripts/
|
|
472
537
|
├── run-tests.mjs
|
|
473
538
|
├── e2e.mjs 16 scenarios, including one against a real pi process
|
|
@@ -482,7 +547,7 @@ there is one implementation, and the adapter calls it.
|
|
|
482
547
|
```bash
|
|
483
548
|
npm install
|
|
484
549
|
npm run check # tsc --noEmit, strict
|
|
485
|
-
npm test #
|
|
550
|
+
npm test # 33 test files
|
|
486
551
|
npm run e2e # 16 end-to-end scenarios
|
|
487
552
|
npm run e2e -- --only realpi # just the ones that drive a real pi process
|
|
488
553
|
npm run e2e -- --list # what the scenarios are
|