infinity-harness 2.0.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/LICENSE +21 -0
  3. package/README.md +266 -0
  4. package/extensions/infinity-harness/index.ts +870 -0
  5. package/harness/docs/ARCHITECTURE.md +159 -0
  6. package/harness/docs/CONSTRAINTS.md +19 -0
  7. package/harness/docs/DECISIONS.md +107 -0
  8. package/harness/docs/DOMAIN.md +13 -0
  9. package/harness/docs/agents/evaluator.md +14 -0
  10. package/harness/docs/agents/generator.md +13 -0
  11. package/harness/docs/agents/planner.md +13 -0
  12. package/harness/docs/agents/simplifier.md +13 -0
  13. package/harness/docs/api-patterns.md +23 -0
  14. package/harness/docs/phases/build.md +47 -0
  15. package/harness/docs/phases/define.md +58 -0
  16. package/harness/docs/phases/plan.md +50 -0
  17. package/harness/docs/phases/review.md +47 -0
  18. package/harness/docs/phases/ship.md +43 -0
  19. package/harness/docs/phases/simplify.md +45 -0
  20. package/harness/docs/phases/verify.md +46 -0
  21. package/harness/model-router.json +28 -0
  22. package/harness/skills/README.md +60 -0
  23. package/harness/skills/auth-security.md +56 -0
  24. package/harness/skills/building-mcp-servers.md +70 -0
  25. package/harness/skills/building-tools.md +60 -0
  26. package/harness/skills/capability-acquisition.md +72 -0
  27. package/harness/skills/cli-design.md +55 -0
  28. package/harness/skills/code-review.md +57 -0
  29. package/harness/skills/codebase-design.md +70 -0
  30. package/harness/skills/concurrency-async.md +61 -0
  31. package/harness/skills/config-and-secrets.md +52 -0
  32. package/harness/skills/context-hygiene.md +51 -0
  33. package/harness/skills/databases.md +63 -0
  34. package/harness/skills/diagnosing-bugs.md +84 -0
  35. package/harness/skills/domain-modeling.md +65 -0
  36. package/harness/skills/error-handling-logging.md +56 -0
  37. package/harness/skills/frontend-ui.md +56 -0
  38. package/harness/skills/grilling.md +48 -0
  39. package/harness/skills/http-apis.md +60 -0
  40. package/harness/skills/performance.md +53 -0
  41. package/harness/skills/pi-todo-adapted.md +41 -0
  42. package/harness/skills/planning-tasks.md +86 -0
  43. package/harness/skills/prototype.md +39 -0
  44. package/harness/skills/research.md +32 -0
  45. package/harness/skills/resolving-merge-conflicts.md +30 -0
  46. package/harness/skills/scope-discipline.md +49 -0
  47. package/harness/skills/self-review.md +45 -0
  48. package/harness/skills/stuck-protocol.md +51 -0
  49. package/harness/skills/tdd.md +80 -0
  50. package/harness/skills/testing-infra.md +57 -0
  51. package/harness/skills/writing-skills.md +60 -0
  52. package/package.json +61 -0
  53. package/src/core/brief.ts +242 -0
  54. package/src/core/config.ts +265 -0
  55. package/src/core/exec.ts +130 -0
  56. package/src/core/featureList.ts +286 -0
  57. package/src/core/fsx.ts +119 -0
  58. package/src/core/gates.ts +444 -0
  59. package/src/core/lock.ts +192 -0
  60. package/src/core/paths.ts +95 -0
  61. package/src/core/phases.ts +143 -0
  62. package/src/core/settings.ts +445 -0
  63. package/src/core/types.ts +245 -0
  64. package/src/goalLoop.ts +628 -0
  65. package/src/goalSpec.ts +679 -0
  66. package/src/goalState.ts +338 -0
  67. package/src/loop.ts +355 -0
  68. package/src/modelRouter.ts +184 -0
  69. package/src/remote.ts +244 -0
  70. package/src/replan.ts +300 -0
  71. package/src/review.ts +53 -0
  72. package/src/rework.ts +274 -0
  73. package/src/taskList.ts +355 -0
  74. package/src/ui/config.ts +286 -0
  75. package/src/ui/dashboard.ts +1066 -0
  76. package/src/ui/theme.ts +317 -0
  77. package/src/ui/widget.ts +370 -0
  78. package/src/unstuck.ts +214 -0
  79. package/src/worker.ts +351 -0
  80. package/types/proper-lockfile.d.ts +19 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,114 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [2.0.0] — 2026-08-23
8
+
9
+ Renamed from `pi-harness` to **infinity-harness**, and rebuilt from a working prototype into
10
+ something shippable. This is a breaking release: tool names, command names and the package name all
11
+ changed, and the extension no longer depends on an external repository.
12
+
13
+ ### Breaking
14
+
15
+ - **Package renamed** `pi-harness` → `infinity-harness`.
16
+ - **Tools renamed.** `harness_task_list` → `infinity_plan`, `pi_goal_task`/`harness_goal_loop` →
17
+ `infinity_goal`, `pi_harness_remote`/`harness_remote` → `infinity_dashboard`,
18
+ `harness_spawn_worker` → `infinity_spawn_worker`. New: `infinity_brief`, `infinity_validate`,
19
+ `infinity_advance`.
20
+ - **Commands renamed** to the `/infinity:*` namespace, and `/infinity:run` / `/infinity:halt` added.
21
+ - **The `cli`, `prompts` and `skills` symlinks are gone.** They pointed at absolute paths inside a
22
+ sibling `dev-harness` checkout, which made the package impossible to install anywhere else. The
23
+ logic they reached is now owned TypeScript in `src/core/`. Skills ship from `harness/skills/`.
24
+ - **Node 22+ required** (the test and E2E runners use native TypeScript stripping).
25
+ - **Dependencies trimmed** to `proper-lockfile` and `string-width`; `ajv` and `simple-git` were
26
+ declared but unused.
27
+
28
+ ### Fixed
29
+
30
+ - **Concurrent plan writes could lose edits.** `writeTaskList` read the plan, checked `baseRevision`,
31
+ then wrote — three steps with no mutual exclusion, so two writers that both read revision N both
32
+ passed the check and both wrote N+1. Measured at 2 lost updates in a 6-way fan-out. The whole
33
+ read-apply-write now happens under an exclusive lock, and fails closed rather than racing.
34
+ - **Plan writes silently dropped task fields.** Round-tripping through the extension rebuilt each
35
+ task from a fixed shape, discarding `difficulty`, `modelHint`, `criteria` and anything else. Updates
36
+ now merge onto the stored task, so unknown fields survive.
37
+ - **The tested code was not the shipped code.** The extension carried inlined copies of the plan
38
+ engine and the widget; the test suite exercised `src/`, which the extension never called. One
39
+ implementation now, in `src/`.
40
+ - **Status aliases failed dependency validation.** `rework.ts` and `replan.ts` parsed the plan raw, so
41
+ a task stored as `"done"` never compared equal to `"complete"` and every amendment to such a plan
42
+ was rejected — with a message claiming a task in flight had unresolved dependencies.
43
+ - **A nested lock deadlocked against itself.** The new sync lock used `<path>.lock`, the same
44
+ directory `proper-lockfile` uses, so a caller holding the async lock blocked the event loop waiting
45
+ for a directory it already owned. The sync lock now uses `<path>.ilock`.
46
+ - **Locks were held across whole agent turns** with an 8-second staleness timeout, meaning any turn
47
+ longer than eight seconds left a lock another process could steal. Critical sections are now
48
+ milliseconds.
49
+ - **`require()` in an ESM module** meant the rich tool renderers silently never loaded.
50
+ - **`process.cwd()` in the context hook** instead of the session's project directory.
51
+ - **The widget overran its frame below 40 columns.** The progress row now degrades instead of padding
52
+ past the edge, and unboxed output is clamped to the requested width.
53
+ - **Ambiguous-width glyphs misaligned the widget.** `string-width` reports `⚠ ↷ ▸ ✓ ·` as two columns;
54
+ terminals draw them in one. Width measurement now pins them.
55
+ - **Truncation destroyed ANSI styling**, bleeding colour into the rest of the line.
56
+ - **`gateHistory` grew without bound** over a long run. Capped at 500 entries.
57
+ - **A type-only import of `AddressInfo`** made the dashboard module fail to load under type stripping.
58
+ - **A raw NUL byte in a source file** made `grep` and `file` treat the module as binary.
59
+ - **A model reference reached a shell unvalidated.** It is interpolated into the worker command, so it
60
+ is now checked against a strict character set and refused rather than escaped — a typo cannot
61
+ become a command substitution.
62
+
63
+ ### Added
64
+
65
+ - **Continuous run driver** (`src/loop.ts`). `/infinity:run` validates, advances, re-briefs and keeps
66
+ going until the pipeline completes or a guard fires: no-progress detection, wall-clock and
67
+ iteration ceilings, retry budgets, and a human brake (`/infinity:halt`, `paused`, `harness/STOP`).
68
+ Every stop names its reason.
69
+ - **New terminal widget.** Phase rail, progress meter, task window centred on the active task,
70
+ dependency references, subtasks under the task actually being worked. Responsive to ~58 columns,
71
+ ASCII fallback on non-UTF-8 locales, colour degradation from truecolor to none.
72
+ - **New web dashboard.** Same information design for the browser: stacked progress meters that show
73
+ stuck work as colour rather than absence, gate panel, alerts strip, 5-second self-refresh with
74
+ backoff. Read-only, loopback-only, and it never runs the gate.
75
+ - **Deterministic gate suite** with per-phase checks, task-scoped validation, and advisory checks that
76
+ report without blocking.
77
+ - **E2E suite** (`npm run e2e`): 11 scenarios, 79 assertion groups, over real temp projects, real git
78
+ repos and real child processes — including a concurrency fan-out with an unlocked control that
79
+ demonstrates why the lock is load-bearing.
80
+ - **Test runner** (`npm test`): 21 files, plain `node:assert`, no framework.
81
+ - **Full configuration from inside pi.** `/infinity:config` opens an interactive menu covering models,
82
+ pipeline, project commands, gates, loop budgets and retry budgets; `/infinity:config show` prints
83
+ the lot as text and is what runs automatically when the mode has no dialogs. The menu is generated
84
+ from one schema (`src/core/settings.ts`), so an option cannot exist in the file format and be
85
+ missing from the UI.
86
+ - **Model tiers picked from pi's own models.** Config → Models offers the models pi has configured
87
+ and can authenticate — session-scoped ones when scoping is set — for each difficulty tier, the
88
+ master model and the default. Any tier can be handed back to "pi's current model".
89
+ `/infinity:models` shows the available list beside the current routing.
90
+ - Values are bounds-checked before they are written, durations accept `24h` / `90m`, and the phase
91
+ list is toggled item by item and stored in pipeline order rather than click order.
92
+ - `.bak` recovery for a corrupt plan file.
93
+ - A `LockTimeoutError` a caller can actually act on.
94
+
95
+ ### Changed
96
+
97
+ - **Model routing ships vendor-neutral.** Every slot is empty by default, meaning "use whatever model
98
+ pi is already configured with". Installing the harness no longer silently redirects work to a
99
+ specific third-party model.
100
+ - **Pi-only.** Support and references for other agent systems were removed; this is a pi extension
101
+ and nothing else.
102
+ - The dashboard refuses to bind to a non-loopback interface without an explicit opt-in, and serves a
103
+ CSP tight enough that an escaping slip cannot become script execution.
104
+ - Documentation rewritten: README, AGENTS.md, and `harness/docs/ARCHITECTURE.md`.
105
+ - **`strict: true`.** The whole tree typechecks under TypeScript strict mode, with ambient types added
106
+ for `proper-lockfile`, which ships none.
107
+ - **Verified installable from npm** (`pi install infinity-harness`). pi loads extensions through
108
+ `jiti`, which transpiles TypeScript at runtime, so the package ships `.ts` with no build step.
109
+ Plain `node` refuses to type-strip inside `node_modules`; pi does not use plain `node` for this.
110
+
111
+ ---
112
+
113
+ Earlier releases (0.2.0 – 1.2.0) were developed under the `pi-harness` name and are not carried
114
+ forward here; that history is in the git log.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bakr Bagaber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # infinity-harness
2
+
3
+ **A pi extension that keeps an agent building — correctly — for hours or days without you.**
4
+
5
+ Point it at a project, describe the goal, and walk away. infinity-harness drives the agent through a
6
+ gated pipeline, checks the work with deterministic gates rather than the model's own judgement, and
7
+ stops with a clear reason when it genuinely needs you.
8
+
9
+ ```
10
+ ╭──────────────────────────────────────────────────────────────────────────╮
11
+ │ ∞ INFINITY ──────────────────────────────────────────────── BUILD rev 42 │
12
+ │ ▸ Ship the payments rewrite behind a flag │
13
+ │ │
14
+ │ ● define ─ ● plan ─ ◉ BUILD ─ ○ verify ─ ○ review ─ ○ ship │
15
+ │ │
16
+ │ ▰▰▰▰▰▰▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱▱ 45% 5/11 tasks · 1/2 features │
17
+ │ ⚠ 1 blocked · ↷ 1 rework · retry 2/10 │
18
+ │ │
19
+ │ ▸ feature-002 · Checkout flow │
20
+ │ ● 4 validate cart totals against catalogue prices │
21
+ │ ● 5 apply stacked discount codes with precedence rules ← #4 │
22
+ │ ◐ 6 handle partial refunds across split tenders ← #4, #5 │
23
+ │ ✓ unit tests for tender split │
24
+ │ ▸ integration test against sandbox │
25
+ │ · audit log entries │
26
+ │ ○ 7 emit refund webhook ← #6 │
27
+ │ ⚠ 8 reconcile ledger nightly ← #6 │
28
+ │ ⋯ 3 more │
29
+ ╰──────────────────────────────────────────────────────────────────────────╯
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Why it exists
35
+
36
+ Left alone, a coding agent drifts. It declares work finished that isn't, forgets what it already
37
+ did, re-solves the same problem three different ways, and — worst of all — keeps going long after it
38
+ stopped making progress. Pair it with a small or cheap model and all of that gets worse.
39
+
40
+ infinity-harness fixes that by taking two decisions away from the model:
41
+
42
+ 1. **When work is done.** A deterministic gate decides, not the agent. Same tree, same verdict, every
43
+ time. No agent marks its own homework.
44
+ 2. **What happens next.** The phase pipeline is forward-only and one step at a time. The agent cannot
45
+ decide it's bored of BUILD and jump to SHIP.
46
+
47
+ Everything else — the plan, the retries, the model routing — exists to keep those two decisions
48
+ honest over a very long run.
49
+
50
+ ## Install
51
+
52
+ Requires **Node 22+** and [pi](https://github.com/earendil-works/pi-coding-agent).
53
+
54
+ ```bash
55
+ # from npm — project-local, so the team shares it via .pi/settings.json
56
+ pi install npm:infinity-harness -l
57
+
58
+ # or globally
59
+ pi install npm:infinity-harness
60
+
61
+ # straight from the repo
62
+ pi install git:github.com/bakr-bagaber/infinity-harness
63
+
64
+ # or from a checkout
65
+ pi install ./infinity-harness -l
66
+
67
+ pi list # confirm it loaded
68
+ ```
69
+
70
+ The package ships TypeScript. pi loads extensions through `jiti`, which
71
+ transpiles at runtime, so there is no build step and nothing to compile.
72
+
73
+ ## Use it
74
+
75
+ ```bash
76
+ cd your-project
77
+ pi
78
+ ```
79
+
80
+ On session start the harness injects a brief — phase, role, current task, acceptance criteria, and
81
+ what to do next. Do the work, then:
82
+
83
+ ```
84
+ /infinity:validate run the gate for this phase
85
+ /infinity:run hand it the wheel: validate → advance → re-brief, until done or stuck
86
+ /infinity:config change any setting, including which model runs which tier
87
+ /infinity:models what models pi has, and how they are being routed
88
+ /infinity:dashboard open the live web view
89
+ /infinity:halt take the wheel back
90
+ ```
91
+
92
+ `/infinity:run` is the point of the tool. It keeps the loop turning without you.
93
+
94
+ ## Configuration
95
+
96
+ Everything is configurable from inside pi:
97
+
98
+ ```
99
+ /infinity:config interactive menu
100
+ /infinity:config show print the whole configuration as text
101
+ ```
102
+
103
+ The menu is generated from a single schema, so every option the file format
104
+ supports is reachable from the UI — the two cannot drift. Editing
105
+ `harness/config.json` and `harness/model-router.json` by hand stays entirely
106
+ valid; the menu is the same data with prompts and bounds checking attached.
107
+
108
+ | Group | Covers |
109
+ |---|---|
110
+ | **Models** | Which model runs each difficulty tier, the master model, consultation budget |
111
+ | **Pipeline** | Which phases run, copilot vs autopilot, role strictness, pause |
112
+ | **Project commands** | lint / test / coverage / build — what the gate actually runs |
113
+ | **Gates** | Enable, coverage threshold, placeholder rejection |
114
+ | **Continuous run** | Iteration ceiling, wall-clock budget, no-progress strikes |
115
+ | **Retry budgets** | Attempts per task, feature and phase |
116
+
117
+ ## The pipeline
118
+
119
+ ```
120
+ define → plan → build → verify → [simplify] → review → ship
121
+ ```
122
+
123
+ | Phase | What it's for | Gate opens when |
124
+ |---|---|---|
125
+ | **define** | Write down what's being built and how you'll know it's done | Every feature has acceptance criteria |
126
+ | **plan** | Break features into ordered, dependency-aware tasks | Tasks exist and criteria are set |
127
+ | **build** | Implement, one task at a time, tests alongside | Lint, tests, coverage pass; no placeholders; every task complete |
128
+ | **verify** | Prove behaviour; hunt what the tests miss | Tests and coverage pass on a clean tree |
129
+ | **simplify** | Delete more than you add *(opt-in)* | Tests pass, no empty dirs, clean tree |
130
+ | **review** | Judge it as if someone else wrote it | Rubric, README, architecture doc and decisions are real; branch level with upstream |
131
+ | **ship** | Tag, changelog, leave it clean | Clean tree, tagged, changelog, README, licence, no placeholders |
132
+
133
+ Enable or disable phases in `harness/config.json` under `phases.enabled`. SIMPLIFY is off by default.
134
+
135
+ ## Knowing when to stop
136
+
137
+ This is the part that makes an unattended run safe. `/infinity:run` halts on any of:
138
+
139
+ | Guard | Default | What it catches |
140
+ |---|---|---|
141
+ | **No progress** | 3 strikes | Gate keeps failing and the working tree hasn't moved — the agent is spinning, not working |
142
+ | **Wall clock** | 24h | A run you forgot about |
143
+ | **Iterations** | 2000 | Runaway loops that stay under the clock |
144
+ | **Retry budget** | 10/task | One impossible task eating the whole run |
145
+ | **Pipeline complete** | — | Final phase passed with every task done |
146
+ | **Human brake** | — | `/infinity:halt`, `/infinity:pause`, or `touch harness/STOP` |
147
+
148
+ Every stop names its reason. You come back to an explanation, not a mystery.
149
+
150
+ Tune the budgets in `harness/config.json`:
151
+
152
+ ```json
153
+ {
154
+ "loop": {
155
+ "maxIterations": 2000,
156
+ "maxWallClockMs": 86400000,
157
+ "noProgressLimit": 3
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## The plan is a file
163
+
164
+ `harness/features/feature-list.json` is the single source of truth. The widget, the dashboard and the
165
+ brief all read it; nothing caches a second copy.
166
+
167
+ The agent edits it by submitting the **complete** task list through the `infinity_plan` tool:
168
+
169
+ - **Omission means deletion.** One unambiguous rule beats incremental edits a model loses track of.
170
+ - **`baseRevision` guards every write.** A stale revision is rejected, so parallel workers can't
171
+ clobber each other.
172
+ - **Unknown fields survive.** An update merges onto the stored task, so `difficulty`, `modelHint`,
173
+ `criteria` and anything added later are never silently dropped.
174
+ - **The dependency graph stays sane.** Cycles and dangling references are rejected at write time; a
175
+ task can't be `complete` while something it depends on isn't.
176
+
177
+ ## Watching it work
178
+
179
+ **In the terminal** — the widget above updates on every turn. It's responsive down to ~58 columns,
180
+ degrades to ASCII when the locale isn't UTF-8, and drops colour under `NO_COLOR`.
181
+
182
+ **In a browser** — `/infinity:dashboard` serves a live page on loopback: phase rail, stacked progress
183
+ meters that show stuck work as colour rather than absence, the full task tree, and the last gate
184
+ verdict. It refreshes itself every 5 seconds and reconnects with backoff if the run ends.
185
+
186
+ The dashboard is strictly read-only and binds to `127.0.0.1`. It never writes, and never bumps
187
+ `baseRevision` — opening it can't perturb the run you're watching.
188
+
189
+ ## Model routing (optional)
190
+
191
+ Send cheap tasks to a small model and hard ones to a large one. Pick them with `/infinity:config` →
192
+ **Models**: the list offered is the models **pi itself has configured and can authenticate**, so you
193
+ choose from what you already have rather than typing ids from memory. `/infinity:models` shows that
194
+ list alongside the current routing.
195
+
196
+ Routing ships **disabled with every slot empty**, meaning "use whatever model pi is already
197
+ configured with" — installing the harness never silently redirects your work to someone else's
198
+ model. Any tier can be handed back to that default at any time.
199
+
200
+ Resolution order:
201
+
202
+ ```
203
+ task.modelHint → byTask → byDifficulty → byFeature → bySprint → byPhase → byRole → default
204
+ ```
205
+
206
+ `master` is never assigned directly; it's reachable only through one-step consultation after the
207
+ normal ladder is exhausted.
208
+
209
+ **Reasoning models need headroom.** A reasoning model emits nothing on the content channel until it
210
+ has finished thinking — measured at ~370 reasoning tokens to answer "reply with one word". If you
211
+ route a tier to one, give its workers a generous timeout; a tight budget returns an empty completion
212
+ that looks like a broken endpoint but is only a small cap.
213
+
214
+ ## Tools the agent gets
215
+
216
+ | Tool | Purpose |
217
+ |---|---|
218
+ | `infinity_brief` | What am I supposed to be doing right now? |
219
+ | `infinity_plan` | Read or rewrite the task list |
220
+ | `infinity_validate` | Run the gate for this phase |
221
+ | `infinity_advance` | Move to the next phase (refuses on a failing gate) |
222
+ | `infinity_dashboard` | Start/stop/query the web view |
223
+
224
+ ## Layout
225
+
226
+ ```
227
+ infinity-harness/
228
+ ├── extensions/infinity-harness/ pi lifecycle adapter — thin, no logic of its own
229
+ ├── src/
230
+ │ ├── core/ types · paths · fsx · config · phases · gates · brief
231
+ │ │ · featureList (the SSOT) · lock · exec
232
+ │ ├── ui/ theme · widget (terminal) · dashboard (web)
233
+ │ ├── loop.ts the continuous-run driver and its stop conditions
234
+ │ ├── taskList.ts atomic plan editor
235
+ │ ├── worker.ts isolated per-task workers
236
+ │ ├── modelRouter.ts difficulty ladder + consultation
237
+ │ ├── rework.ts · replan.ts backward rework with BFS impact · mid-build amendment
238
+ │ ├── unstuck.ts · review.ts escalation strategy matrix · review bounce guard
239
+ │ └── goalLoop.ts · goalState.ts · goalSpec.ts
240
+ ├── harness/
241
+ │ ├── features/feature-list.json the plan
242
+ │ ├── config.json pipeline state and settings
243
+ │ ├── model-router.json optional routing
244
+ │ ├── docs/ architecture · decisions · phase and role docs
245
+ │ └── skills/ 29 craft skills the brief points at
246
+ ├── tests/ 20 files, plain node:assert
247
+ └── scripts/run-tests.mjs
248
+ ```
249
+
250
+ The extension is deliberately thin. Every decision lives in `src/`, where it's typed and tested —
251
+ there is one implementation, and the adapter calls it.
252
+
253
+ ## Development
254
+
255
+ ```bash
256
+ npm install
257
+ npm run check # tsc --noEmit
258
+ npm test # 20 test files
259
+ npm run e2e # end-to-end against a live model
260
+ ```
261
+
262
+ Tests are plain `node:assert` run under `--experimental-strip-types`. No framework, no build step.
263
+
264
+ ## Licence
265
+
266
+ MIT