infinity-harness 2.8.9 → 2.8.11

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 (192) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/extensions/infinity-harness/index.js +3614 -0
  3. package/dist/extensions/infinity-harness/index.js.map +1 -0
  4. package/dist/src/approval.js +154 -0
  5. package/dist/src/approval.js.map +1 -0
  6. package/dist/src/core/brief.js +309 -0
  7. package/dist/src/core/brief.js.map +1 -0
  8. package/dist/src/core/config.js +539 -0
  9. package/dist/src/core/config.js.map +1 -0
  10. package/dist/src/core/exec.js +106 -0
  11. package/dist/src/core/exec.js.map +1 -0
  12. package/dist/src/core/featureList.js +365 -0
  13. package/dist/src/core/featureList.js.map +1 -0
  14. package/dist/src/core/fsx.js +123 -0
  15. package/dist/src/core/fsx.js.map +1 -0
  16. package/dist/src/core/gates.js +490 -0
  17. package/dist/src/core/gates.js.map +1 -0
  18. package/dist/src/core/init.js +480 -0
  19. package/dist/src/core/init.js.map +1 -0
  20. package/dist/src/core/lock.js +183 -0
  21. package/dist/src/core/lock.js.map +1 -0
  22. package/dist/src/core/modelRouter.js +185 -0
  23. package/dist/src/core/modelRouter.js.map +1 -0
  24. package/dist/src/core/paths.js +129 -0
  25. package/dist/src/core/paths.js.map +1 -0
  26. package/dist/src/core/phases.js +254 -0
  27. package/dist/src/core/phases.js.map +1 -0
  28. package/dist/src/core/plan.js +15 -0
  29. package/dist/src/core/plan.js.map +1 -0
  30. package/dist/src/core/runState.js +126 -0
  31. package/dist/src/core/runState.js.map +1 -0
  32. package/dist/src/core/settings.js +877 -0
  33. package/dist/src/core/settings.js.map +1 -0
  34. package/dist/src/core/skills.js +319 -0
  35. package/dist/src/core/skills.js.map +1 -0
  36. package/dist/src/core/skillsAudit.js +194 -0
  37. package/dist/src/core/skillsAudit.js.map +1 -0
  38. package/dist/src/core/types.js +77 -0
  39. package/dist/src/core/types.js.map +1 -0
  40. package/dist/src/daemon/budget.js +85 -0
  41. package/dist/src/daemon/budget.js.map +1 -0
  42. package/dist/src/daemon/guard.js +122 -0
  43. package/dist/src/daemon/guard.js.map +1 -0
  44. package/dist/src/daemon/index.js +461 -0
  45. package/dist/src/daemon/index.js.map +1 -0
  46. package/dist/src/daemon/isolation.js +84 -0
  47. package/dist/src/daemon/isolation.js.map +1 -0
  48. package/dist/src/daemon/preflight.js +131 -0
  49. package/dist/src/daemon/preflight.js.map +1 -0
  50. package/dist/src/daemon/server.js +174 -0
  51. package/dist/src/daemon/server.js.map +1 -0
  52. package/dist/src/daemon/supervisorState.js +42 -0
  53. package/dist/src/daemon/supervisorState.js.map +1 -0
  54. package/dist/src/daemon/worker.js +239 -0
  55. package/dist/src/daemon/worker.js.map +1 -0
  56. package/dist/src/daemon/worktree.js +102 -0
  57. package/dist/src/daemon/worktree.js.map +1 -0
  58. package/dist/src/escalate.js +312 -0
  59. package/dist/src/escalate.js.map +1 -0
  60. package/dist/src/exec/piWorker.js +630 -0
  61. package/dist/src/exec/piWorker.js.map +1 -0
  62. package/dist/src/goal.js +317 -0
  63. package/dist/src/goal.js.map +1 -0
  64. package/dist/src/goalLoop.js +374 -0
  65. package/dist/src/goalLoop.js.map +1 -0
  66. package/dist/src/goalSpec.js +397 -0
  67. package/dist/src/goalSpec.js.map +1 -0
  68. package/dist/src/goalState.js +255 -0
  69. package/dist/src/goalState.js.map +1 -0
  70. package/dist/src/handoff.js +205 -0
  71. package/dist/src/handoff.js.map +1 -0
  72. package/dist/src/intake.js +203 -0
  73. package/dist/src/intake.js.map +1 -0
  74. package/dist/src/loop.js +592 -0
  75. package/dist/src/loop.js.map +1 -0
  76. package/dist/src/modelRouter.js +216 -0
  77. package/dist/src/modelRouter.js.map +1 -0
  78. package/dist/src/remote.js +246 -0
  79. package/dist/src/remote.js.map +1 -0
  80. package/dist/src/replan.js +262 -0
  81. package/dist/src/replan.js.map +1 -0
  82. package/dist/src/review.js +54 -0
  83. package/dist/src/review.js.map +1 -0
  84. package/dist/src/rework.js +241 -0
  85. package/dist/src/rework.js.map +1 -0
  86. package/dist/src/runState.js +4 -0
  87. package/dist/src/runState.js.map +1 -0
  88. package/dist/src/scheduler.js +270 -0
  89. package/dist/src/scheduler.js.map +1 -0
  90. package/dist/src/supervisor.js +787 -0
  91. package/dist/src/supervisor.js.map +1 -0
  92. package/dist/src/taskList.js +430 -0
  93. package/dist/src/taskList.js.map +1 -0
  94. package/dist/src/ui/config.js +268 -0
  95. package/dist/src/ui/config.js.map +1 -0
  96. package/dist/src/ui/dashboard.js +1288 -0
  97. package/dist/src/ui/dashboard.js.map +1 -0
  98. package/dist/src/ui/display.js +229 -0
  99. package/dist/src/ui/display.js.map +1 -0
  100. package/dist/src/ui/planTree.js +219 -0
  101. package/dist/src/ui/planTree.js.map +1 -0
  102. package/dist/src/ui/theme.js +298 -0
  103. package/dist/src/ui/theme.js.map +1 -0
  104. package/dist/src/ui/viewState.js +65 -0
  105. package/dist/src/ui/viewState.js.map +1 -0
  106. package/dist/src/ui/widget.js +721 -0
  107. package/dist/src/ui/widget.js.map +1 -0
  108. package/dist/src/ui/wizard.js +540 -0
  109. package/dist/src/ui/wizard.js.map +1 -0
  110. package/dist/src/unstuck.js +210 -0
  111. package/dist/src/unstuck.js.map +1 -0
  112. package/dist/src/worker.js +267 -0
  113. package/dist/src/worker.js.map +1 -0
  114. package/dist/src/workflow.js +250 -0
  115. package/dist/src/workflow.js.map +1 -0
  116. package/dist/tests/approval.test.js +144 -0
  117. package/dist/tests/approval.test.js.map +1 -0
  118. package/dist/tests/brief.test.js +271 -0
  119. package/dist/tests/brief.test.js.map +1 -0
  120. package/dist/tests/config.test.js +320 -0
  121. package/dist/tests/config.test.js.map +1 -0
  122. package/dist/tests/dashboard.test.js +277 -0
  123. package/dist/tests/dashboard.test.js.map +1 -0
  124. package/dist/tests/display.test.js +229 -0
  125. package/dist/tests/display.test.js.map +1 -0
  126. package/dist/tests/escalate.test.js +272 -0
  127. package/dist/tests/escalate.test.js.map +1 -0
  128. package/dist/tests/featureList.test.js +429 -0
  129. package/dist/tests/featureList.test.js.map +1 -0
  130. package/dist/tests/gates.test.js +333 -0
  131. package/dist/tests/gates.test.js.map +1 -0
  132. package/dist/tests/goal.test.js +229 -0
  133. package/dist/tests/goal.test.js.map +1 -0
  134. package/dist/tests/goalLoop.test.js +170 -0
  135. package/dist/tests/goalLoop.test.js.map +1 -0
  136. package/dist/tests/goalSpec.test.js +146 -0
  137. package/dist/tests/goalSpec.test.js.map +1 -0
  138. package/dist/tests/goalState.test.js +202 -0
  139. package/dist/tests/goalState.test.js.map +1 -0
  140. package/dist/tests/handoff.test.js +127 -0
  141. package/dist/tests/handoff.test.js.map +1 -0
  142. package/dist/tests/init.test.js +264 -0
  143. package/dist/tests/init.test.js.map +1 -0
  144. package/dist/tests/intake.test.js +288 -0
  145. package/dist/tests/intake.test.js.map +1 -0
  146. package/dist/tests/loop.test.js +453 -0
  147. package/dist/tests/loop.test.js.map +1 -0
  148. package/dist/tests/modelRouter.test.js +131 -0
  149. package/dist/tests/modelRouter.test.js.map +1 -0
  150. package/dist/tests/phases.test.js +244 -0
  151. package/dist/tests/phases.test.js.map +1 -0
  152. package/dist/tests/planTree.test.js +190 -0
  153. package/dist/tests/planTree.test.js.map +1 -0
  154. package/dist/tests/remote.test.js +380 -0
  155. package/dist/tests/remote.test.js.map +1 -0
  156. package/dist/tests/replan.test.js +113 -0
  157. package/dist/tests/replan.test.js.map +1 -0
  158. package/dist/tests/review.test.js +77 -0
  159. package/dist/tests/review.test.js.map +1 -0
  160. package/dist/tests/rework.test.js +109 -0
  161. package/dist/tests/rework.test.js.map +1 -0
  162. package/dist/tests/routing-live.test.js +121 -0
  163. package/dist/tests/routing-live.test.js.map +1 -0
  164. package/dist/tests/runState.test.js +73 -0
  165. package/dist/tests/runState.test.js.map +1 -0
  166. package/dist/tests/settings.test.js +311 -0
  167. package/dist/tests/settings.test.js.map +1 -0
  168. package/dist/tests/skills.test.js +387 -0
  169. package/dist/tests/skills.test.js.map +1 -0
  170. package/dist/tests/supervisor.test.js +437 -0
  171. package/dist/tests/supervisor.test.js.map +1 -0
  172. package/dist/tests/surface.test.js +203 -0
  173. package/dist/tests/surface.test.js.map +1 -0
  174. package/dist/tests/taskList.test.js +376 -0
  175. package/dist/tests/taskList.test.js.map +1 -0
  176. package/dist/tests/theme.test.js +149 -0
  177. package/dist/tests/theme.test.js.map +1 -0
  178. package/dist/tests/unstuck.test.js +268 -0
  179. package/dist/tests/unstuck.test.js.map +1 -0
  180. package/dist/tests/widget.test.js +424 -0
  181. package/dist/tests/widget.test.js.map +1 -0
  182. package/dist/tests/worker.test.js +306 -0
  183. package/dist/tests/worker.test.js.map +1 -0
  184. package/dist/tests/workflow.test.js +187 -0
  185. package/dist/tests/workflow.test.js.map +1 -0
  186. package/extensions/infinity-harness/index.ts +39 -12
  187. package/package.json +5 -2
  188. package/src/core/modelRouter.ts +23 -1
  189. package/src/daemon/index.ts +3 -3
  190. package/src/daemon/worker.ts +28 -5
  191. package/src/ui/config.ts +25 -1
  192. package/src/ui/wizard.ts +10 -5
@@ -0,0 +1,380 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { buildApiPayload, buildHtml, buildRemoteState, createRemoteServer, escapeHtml, } from "../src/remote.js";
6
+ import { defaultConfig } from "../src/core/config.js";
7
+ /**
8
+ * A keep-alive socket pooled by undici from a previous server on the same
9
+ * host:port can already be dead when the next server binds it; the first
10
+ * request over it fails with "other side closed". Retry rather than let
11
+ * connection pooling decide whether the suite passes.
12
+ */
13
+ async function get(url, init) {
14
+ let lastError;
15
+ for (let attempt = 0; attempt < 3; attempt++) {
16
+ try {
17
+ return await fetch(url, init);
18
+ }
19
+ catch (e) {
20
+ lastError = e;
21
+ await new Promise((r) => setTimeout(r, 25));
22
+ }
23
+ }
24
+ throw lastError;
25
+ }
26
+ function tmpProject() {
27
+ const d = mkdtempSync(join(tmpdir(), "pi-remote-"));
28
+ mkdirSync(join(d, "harness", "features"), { recursive: true });
29
+ return d;
30
+ }
31
+ function writeFeatureList(dir, fl) {
32
+ writeFileSync(join(dir, "harness", "features", "feature-list.json"), JSON.stringify(fl, null, 2), "utf-8");
33
+ }
34
+ function writeConfig(dir, mutate = () => { }) {
35
+ const c = defaultConfig();
36
+ mutate(c);
37
+ writeFileSync(join(dir, "harness", "config.json"), JSON.stringify(c, null, 2), "utf-8");
38
+ }
39
+ function baseFeatureList(overrides) {
40
+ return {
41
+ version: "2.0",
42
+ baseRevision: 7,
43
+ goals: [{ id: "goal-001", title: "infinity-harness v1.0 — superset visual harness", description: "test" }],
44
+ sprints: [{ id: "sprint-005", name: "F5 — Remote", goalId: "goal-001" }],
45
+ features: [
46
+ {
47
+ id: "feature-005",
48
+ name: "Remote Read-Only Web View → 1.0.0",
49
+ sprintId: "sprint-005",
50
+ goalId: "goal-001",
51
+ passes: false,
52
+ tasks: [
53
+ { id: "task-010", key: "remote-core", description: "Build src/remote.ts", status: "complete", dependsOn: [], subtasks: [] },
54
+ { id: "task-011", key: "remote-wire", description: "Wire enforcer & bump", status: "pending", dependsOn: ["remote-core"], subtasks: [] },
55
+ ],
56
+ },
57
+ ],
58
+ ...(overrides ?? {}),
59
+ };
60
+ }
61
+ // ── escapeHtml (re-exported for callers that build their own fragments) ─────
62
+ {
63
+ assert.equal(escapeHtml(`a & b`), "a &amp; b");
64
+ assert.equal(escapeHtml(`<x>"y"`), "&lt;x&gt;&quot;y&quot;");
65
+ assert.equal(escapeHtml(`it's`), "it&#39;s");
66
+ console.log("✓ escapeHtml");
67
+ }
68
+ // ── buildRemoteState: the snapshot the dashboard renders ───────────────────
69
+ {
70
+ const dir = tmpProject();
71
+ try {
72
+ writeFeatureList(dir, baseFeatureList());
73
+ writeConfig(dir, (c) => {
74
+ c.currentPhase = "build";
75
+ c.taskRetryCount = 3;
76
+ c.maxRetries = 9;
77
+ });
78
+ const st = buildRemoteState(dir);
79
+ assert.equal(st.baseRevision, 7, "baseRevision matches fixture");
80
+ assert.ok(Array.isArray(st.features) && st.features.length === 1);
81
+ assert.equal(st.features[0].id, "feature-005");
82
+ assert.ok(Array.isArray(st.goals) && st.goals.length === 1);
83
+ assert.equal(st.list.baseRevision, 7, "the whole plan comes along for the renderer");
84
+ assert.equal(st.list.features[0].tasks.length, 2);
85
+ // Progress is computed here so every consumer sees the same numbers.
86
+ assert.equal(st.progress.tasksDone, 1);
87
+ assert.equal(st.progress.tasksTotal, 2);
88
+ assert.equal(st.progress.percent, 50);
89
+ assert.equal(st.phase, "build", "the phase comes from the config, not the plan");
90
+ assert.equal(st.paused, false);
91
+ assert.deepEqual(st.enabledPhases, defaultConfig().phases.enabled);
92
+ assert.deepEqual(st.retries, { task: 3, max: 9 });
93
+ assert.equal(st.gate, null, "no gate has been recorded yet");
94
+ assert.equal(st.router, null, "no model-router.json in this project");
95
+ assert.equal(st.rework, null);
96
+ assert.ok(typeof st.timestamp === "string" && new Date(st.timestamp).toISOString() === st.timestamp);
97
+ console.log("✓ buildRemoteState shape: baseRevision, plan, progress, phase, retries, timestamp");
98
+ }
99
+ finally {
100
+ rmSync(dir, { recursive: true, force: true });
101
+ }
102
+ }
103
+ // ── buildRemoteState: the last recorded gate, never a fresh run ────────────
104
+ {
105
+ const dir = tmpProject();
106
+ try {
107
+ writeFeatureList(dir, baseFeatureList());
108
+ writeConfig(dir, (c) => {
109
+ c.currentPhase = "build";
110
+ c.commands.test = "exit 1"; // would fail if the gate were actually run
111
+ c.gateHistory = [
112
+ { phase: "define", result: "pass", timestamp: "2026-08-01T00:00:00.000Z" },
113
+ { phase: "build", result: "fail", timestamp: "2026-08-02T00:00:00.000Z", feature: "feature-005", task: "remote-wire" },
114
+ ];
115
+ });
116
+ const st = buildRemoteState(dir);
117
+ assert.ok(st.gate, "the most recent verdict is reported");
118
+ assert.equal(st.gate.phase, "build", "the last entry wins, not the first");
119
+ assert.equal(st.gate.overall, false);
120
+ assert.deepEqual(st.gate.failures, ["see terminal for detail"]);
121
+ assert.equal(st.gate.feature, "feature-005");
122
+ assert.equal(st.gate.task, "remote-wire");
123
+ assert.equal(st.gate.checks.length, 1);
124
+ assert.match(st.gate.checks[0].name, /last recorded gate \(2026-08-02T00:00:00.000Z\)/);
125
+ assert.match(st.gate.checks[0].detail, /failed on build/);
126
+ // Opening the page must not perturb the run: no gate history is appended.
127
+ assert.equal(buildRemoteState(dir).gate.checks.length, 1);
128
+ const cfg = JSON.parse(readFileSync(join(dir, "harness", "config.json"), "utf-8"));
129
+ assert.equal(cfg.gateHistory.length, 2, "reading the state records nothing");
130
+ console.log("✓ buildRemoteState reports the last recorded gate without running one");
131
+ }
132
+ finally {
133
+ rmSync(dir, { recursive: true, force: true });
134
+ }
135
+ }
136
+ // ── buildRemoteState: missing plan -> empty state ──────────────────────────
137
+ {
138
+ const dir = tmpProject();
139
+ try {
140
+ const st = buildRemoteState(dir);
141
+ assert.equal(st.baseRevision, 0);
142
+ assert.equal(st.features.length, 0);
143
+ assert.deepEqual(st.goals, []);
144
+ assert.equal(st.progress.tasksTotal, 0);
145
+ assert.equal(st.phase, null, "an uninitialised project has no phase");
146
+ assert.equal(st.paused, false);
147
+ assert.ok(buildHtml(st).startsWith("<!doctype html>"), "an empty project still renders a page");
148
+ console.log("✓ buildRemoteState missing file returns empty");
149
+ }
150
+ finally {
151
+ rmSync(dir, { recursive: true, force: true });
152
+ }
153
+ }
154
+ // ── router / rework side files are surfaced verbatim ───────────────────────
155
+ {
156
+ const dir = tmpProject();
157
+ try {
158
+ writeFeatureList(dir, baseFeatureList());
159
+ writeConfig(dir);
160
+ writeFileSync(join(dir, "harness", "model-router.json"), JSON.stringify({ enabled: true, default: "m-1" }), "utf-8");
161
+ writeFileSync(join(dir, "harness", "rework.json"), JSON.stringify({ active: true, impactedCount: 4 }), "utf-8");
162
+ const st = buildRemoteState(dir);
163
+ assert.deepEqual(st.router, { enabled: true, default: "m-1" });
164
+ assert.deepEqual(st.rework, { active: true, impactedCount: 4 });
165
+ const html = buildHtml(st);
166
+ assert.match(html, /router/, "the router badge is rendered");
167
+ assert.match(html, /4 impacted/, "the rework badge carries its one detail");
168
+ // A corrupt side file must not take the page down.
169
+ writeFileSync(join(dir, "harness", "rework.json"), "{ broken", "utf-8");
170
+ assert.equal(buildRemoteState(dir).rework, null);
171
+ console.log("✓ router/rework side files are surfaced and degrade safely");
172
+ }
173
+ finally {
174
+ rmSync(dir, { recursive: true, force: true });
175
+ }
176
+ }
177
+ // ── buildHtml: a self-contained page that escapes everything ───────────────
178
+ {
179
+ const dir = tmpProject();
180
+ try {
181
+ const dangerous = baseFeatureList({
182
+ features: [
183
+ {
184
+ id: "feature-005",
185
+ name: "XSS <test>",
186
+ sprintId: "sprint-005",
187
+ tasks: [
188
+ { id: "t1", key: "k1", description: `a & b <script>"x"</script>`, status: "pending", dependsOn: [], subtasks: [] },
189
+ ],
190
+ },
191
+ ],
192
+ });
193
+ writeFeatureList(dir, dangerous);
194
+ writeConfig(dir, (c) => {
195
+ c.currentPhase = "build";
196
+ });
197
+ const st = buildRemoteState(dir);
198
+ const html = buildHtml(st);
199
+ assert.ok(html.includes("infinity-harness"), "html identifies the harness");
200
+ assert.match(html, /rev 7/, "html shows the plan revision");
201
+ assert.ok(!html.includes(`<script>"x"</script>`), "html escaped script");
202
+ assert.ok(html.includes("&amp;") && html.includes("&lt;script&gt;"), "html escaped entities");
203
+ assert.ok(html.includes("XSS &lt;test&gt;"), "the feature name is escaped, not stripped");
204
+ assert.match(html, /fetch\(/, "the page polls itself");
205
+ assert.ok(!html.includes("http://") && !html.includes("https://"), "no external references");
206
+ console.log("✓ buildHtml escaping + self-contained page");
207
+ }
208
+ finally {
209
+ rmSync(dir, { recursive: true, force: true });
210
+ }
211
+ }
212
+ // ── buildApiPayload: compact, and no full plan ─────────────────────────────
213
+ {
214
+ const dir = tmpProject();
215
+ try {
216
+ writeFeatureList(dir, baseFeatureList());
217
+ writeConfig(dir, (c) => {
218
+ c.currentPhase = "build";
219
+ });
220
+ const payload = buildApiPayload(buildRemoteState(dir));
221
+ assert.equal(payload.baseRevision, 7);
222
+ assert.equal(payload.phase, "build");
223
+ assert.equal(payload.paused, false);
224
+ assert.ok(!("list" in payload), "the full plan is left out to keep the payload small");
225
+ const features = payload.features;
226
+ assert.equal(features.length, 1);
227
+ assert.equal(features[0].tasks.length, 2);
228
+ assert.equal(features[0].tasks[0].key, "remote-core");
229
+ assert.deepEqual(features[0].tasks[1].dependsOn, ["remote-core"]);
230
+ assert.equal(JSON.stringify(payload).length > 0, true);
231
+ console.log("✓ buildApiPayload");
232
+ }
233
+ finally {
234
+ rmSync(dir, { recursive: true, force: true });
235
+ }
236
+ }
237
+ // ── createRemoteServer: ephemeral port shape + HTML + health ────────────────
238
+ {
239
+ const dir = tmpProject();
240
+ try {
241
+ writeFeatureList(dir, baseFeatureList());
242
+ writeConfig(dir, (c) => {
243
+ c.currentPhase = "build";
244
+ });
245
+ const srv = await createRemoteServer({ projectDir: dir, host: "127.0.0.1", port: 0 });
246
+ try {
247
+ assert.ok(srv.url.startsWith("http://127.0.0.1:"), `url ${srv.url}`);
248
+ assert.ok(srv.port > 0 && srv.port < 65536);
249
+ // GET /api/harness shape
250
+ const r1 = await get(`${srv.url}/api/harness`);
251
+ assert.equal(r1.status, 200);
252
+ const j1 = await r1.json();
253
+ assert.equal(j1.baseRevision, 7);
254
+ assert.ok(Array.isArray(j1.features) && j1.features.length === 1);
255
+ assert.equal(j1.progress.tasksTotal, 2);
256
+ assert.equal(j1.phase, "build");
257
+ assert.ok(typeof j1.timestamp === "string");
258
+ const ct1 = r1.headers.get("content-type") ?? "";
259
+ assert.ok(ct1.includes("application/json"));
260
+ assert.equal(r1.headers.get("cache-control"), "no-store", "a dashboard must never be cached");
261
+ // GET / serves the dashboard
262
+ const r2 = await get(`${srv.url}/`);
263
+ assert.equal(r2.status, 200);
264
+ const html = await r2.text();
265
+ assert.ok(html.includes("infinity-harness"), "GET / html identifies the harness");
266
+ assert.ok(html.includes("Remote Read-Only Web View"), "GET / html shows the plan");
267
+ assert.ok(html.includes(`rev ${j1.baseRevision}`), "GET / html mentions the revision");
268
+ const ct2 = r2.headers.get("content-type") ?? "";
269
+ assert.ok(ct2.includes("text/html"));
270
+ // The page renders untrusted model output; the CSP is the second line of
271
+ // defence behind escaping.
272
+ const csp = r2.headers.get("content-security-policy") ?? "";
273
+ assert.match(csp, /default-src 'none'/);
274
+ assert.ok(!csp.includes("script-src 'self'"), "no remote script origin is allowed");
275
+ assert.equal(r2.headers.get("x-content-type-options"), "nosniff");
276
+ // GET /api/health
277
+ const r3 = await get(`${srv.url}/api/health`);
278
+ assert.equal(r3.status, 200);
279
+ const j3 = await r3.json();
280
+ assert.equal(j3.ok, true);
281
+ assert.ok(typeof j3.timestamp === "string");
282
+ // HEAD is allowed; the surface is read-only, not GET-only.
283
+ const rh = await get(`${srv.url}/api/health`, { method: "HEAD" });
284
+ assert.equal(rh.status, 200);
285
+ // 404 unknown
286
+ const r4 = await get(`${srv.url}/not-here`);
287
+ assert.equal(r4.status, 404);
288
+ // 405 POST rejected — nothing here writes.
289
+ const r5 = await get(`${srv.url}/api/harness`, { method: "POST" });
290
+ assert.equal(r5.status, 405);
291
+ assert.equal(r5.headers.get("allow"), "GET, HEAD");
292
+ const r6 = await get(`${srv.url}/`, { method: "DELETE" });
293
+ assert.equal(r6.status, 405);
294
+ console.log("✓ createRemoteServer ephemeral port shape + HTML + health + 404/405");
295
+ }
296
+ finally {
297
+ await srv.close();
298
+ await srv.close(); // second close no throw
299
+ console.log("✓ close frees port (idempotent)");
300
+ // verify port freed: can bind same port again quickly
301
+ const srv2 = await createRemoteServer({ projectDir: dir, host: "127.0.0.1", port: srv.port });
302
+ const r = await get(`${srv2.url}/api/harness`);
303
+ assert.equal(r.status, 200);
304
+ await srv2.close();
305
+ console.log("✓ close frees port -> re-bind same port works");
306
+ }
307
+ }
308
+ finally {
309
+ rmSync(dir, { recursive: true, force: true });
310
+ }
311
+ }
312
+ // ── a non-loopback bind has to be asked for explicitly ─────────────────────
313
+ {
314
+ const dir = tmpProject();
315
+ const prior = process.env.INFINITY_HARNESS_ALLOW_REMOTE;
316
+ try {
317
+ delete process.env.INFINITY_HARNESS_ALLOW_REMOTE;
318
+ await assert.rejects(() => createRemoteServer({ projectDir: dir, host: "0.0.0.0", port: 0 }), /refusing to bind the dashboard to 0.0.0.0/, "the page exposes source paths and task text");
319
+ console.log("✓ non-loopback binds are refused by default");
320
+ }
321
+ finally {
322
+ if (prior === undefined)
323
+ delete process.env.INFINITY_HARNESS_ALLOW_REMOTE;
324
+ else
325
+ process.env.INFINITY_HARNESS_ALLOW_REMOTE = prior;
326
+ rmSync(dir, { recursive: true, force: true });
327
+ }
328
+ }
329
+ // ── concurrent fetches x5 serialized ────────────────────────────────────────
330
+ {
331
+ const dir = tmpProject();
332
+ try {
333
+ writeFeatureList(dir, baseFeatureList());
334
+ const srv = await createRemoteServer({ projectDir: dir, host: "127.0.0.1", port: 0 });
335
+ try {
336
+ const results = await Promise.all(Array.from({ length: 5 }, () => get(`${srv.url}/api/harness`).then((r) => r.json())));
337
+ for (const j of results)
338
+ assert.equal(j.baseRevision, 7);
339
+ console.log("✓ concurrent fetches x5 serialized (all same baseRevision)");
340
+ }
341
+ finally {
342
+ await srv.close();
343
+ }
344
+ }
345
+ finally {
346
+ rmSync(dir, { recursive: true, force: true });
347
+ }
348
+ }
349
+ // ── read-only: repeated reads do not mutate baseRevision / file ─────────────
350
+ {
351
+ const dir = tmpProject();
352
+ try {
353
+ const fl = baseFeatureList();
354
+ writeFeatureList(dir, fl);
355
+ writeConfig(dir, (c) => {
356
+ c.currentPhase = "build";
357
+ });
358
+ const planBefore = readFileSync(join(dir, "harness", "features", "feature-list.json"), "utf-8");
359
+ const configBefore = readFileSync(join(dir, "harness", "config.json"), "utf-8");
360
+ const srv = await createRemoteServer({ projectDir: dir, host: "127.0.0.1", port: 0 });
361
+ try {
362
+ for (let i = 0; i < 3; i++) {
363
+ const j = await get(`${srv.url}/api/harness`).then((r) => r.json());
364
+ assert.equal(j.baseRevision, 7);
365
+ await get(`${srv.url}/`).then((r) => r.text());
366
+ }
367
+ assert.equal(readFileSync(join(dir, "harness", "features", "feature-list.json"), "utf-8"), planBefore, "the plan file is byte-identical after serving the dashboard");
368
+ assert.equal(readFileSync(join(dir, "harness", "config.json"), "utf-8"), configBefore, "the config file is byte-identical too — no gate history, no counters");
369
+ console.log("✓ read-only: GETs do not mutate any harness file");
370
+ }
371
+ finally {
372
+ await srv.close();
373
+ }
374
+ }
375
+ finally {
376
+ rmSync(dir, { recursive: true, force: true });
377
+ }
378
+ }
379
+ console.log("All remote tests PASS");
380
+ //# sourceMappingURL=remote.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.test.js","sourceRoot":"","sources":["../../tests/remote.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { amendPlan, loadReplanHistory, clearReplanHistory } from "../src/replan.js";
6
+ function tmpProject() {
7
+ const d = mkdtempSync(join(tmpdir(), "pi-replan-"));
8
+ mkdirSync(join(d, "harness", "features"), { recursive: true });
9
+ return d;
10
+ }
11
+ function writeFeatureList(dir, tasks, baseRevision = 0) {
12
+ const features = [{ id: "feature-006", name: "F", passes: false, tasks }];
13
+ writeFileSync(join(dir, "harness", "features", "feature-list.json"), JSON.stringify({ version: "0.1", baseRevision, features, goals: [], sprints: [] }, null, 2));
14
+ writeFileSync(join(dir, "harness", "config.json"), JSON.stringify({ replan: { maxReplans: 10 } }, null, 2));
15
+ }
16
+ // --- amendPlan adds task, bumps, guards cycle/missing, maxReplans ---
17
+ {
18
+ const proj = tmpProject();
19
+ try {
20
+ writeFeatureList(proj, [
21
+ { id: "task-012", key: "schema-rework", description: "A", status: "complete", dependsOn: [] },
22
+ ], 2);
23
+ // add task
24
+ let res = await amendPlan({ projectDir: proj, reason: "add feature", addFeatures: [{ id: "feature-007", name: "NewF", tasks: [{ id: "task-014", key: "router-core", description: "C", status: "pending", dependsOn: [] }] }] });
25
+ assert.equal(res.added.features, 1);
26
+ assert.equal(res.baseRevision, 3);
27
+ // add task to existing feature
28
+ res = await amendPlan({ projectDir: proj, reason: "add task", addTasks: [{ featureId: "feature-006", task: { id: "task-013", key: "rework-replan", description: "B", status: "pending", dependsOn: ["schema-rework"] } }] });
29
+ assert.equal(res.added.tasks, 1);
30
+ assert.equal(res.baseRevision, 4);
31
+ // cycle rejected - add tasks that create cycle
32
+ try {
33
+ await amendPlan({ projectDir: proj, addTasks: [
34
+ { featureId: "feature-006", task: { id: "task-015", key: "cyc-a", description: "Cyc", status: "pending", dependsOn: ["cyc-b"] } },
35
+ { featureId: "feature-006", task: { id: "task-016", key: "cyc-b", description: "CycB", status: "pending", dependsOn: ["cyc-a"] } },
36
+ ] });
37
+ assert.fail("expected cycle");
38
+ }
39
+ catch (e) {
40
+ assert.match(e.message, /cycle/);
41
+ }
42
+ // missing dep rejected
43
+ try {
44
+ await amendPlan({ projectDir: proj, addTasks: [{ featureId: "feature-006", task: { id: "task-017", key: "bad", description: "Bad", status: "pending", dependsOn: ["nonexistent"] } }] });
45
+ assert.fail("expected missing");
46
+ }
47
+ catch (e) {
48
+ assert.match(e.message, /missing|references/);
49
+ }
50
+ // maxReplans guard - separate project with max 2
51
+ {
52
+ const proj2 = tmpProject();
53
+ try {
54
+ writeFileSync(join(proj2, "harness", "config.json"), JSON.stringify({ replan: { maxReplans: 2 } }, null, 2));
55
+ writeFileSync(join(proj2, "harness", "features", "feature-list.json"), JSON.stringify({ version: "0.1", baseRevision: 0, features: [{ id: "feature-006", name: "F", passes: false, tasks: [{ id: "task-012", key: "schema-rework", description: "A", status: "complete", dependsOn: [] }] }], goals: [], sprints: [] }, null, 2));
56
+ await amendPlan({ projectDir: proj2, addTasks: [{ featureId: "feature-006", task: { id: "task-013", key: "rework-replan", description: "B2", status: "pending", dependsOn: ["schema-rework"] } }] });
57
+ await amendPlan({ projectDir: proj2, addTasks: [{ featureId: "feature-006", task: { id: "task-014", key: "router-core", description: "C", status: "pending" } }] });
58
+ try {
59
+ await amendPlan({ projectDir: proj2, addTasks: [{ featureId: "feature-006", task: { id: "task-018", key: "extra", description: "Extra", status: "pending" } }] });
60
+ assert.fail("expected maxReplans");
61
+ }
62
+ catch (e) {
63
+ assert.match(e.message, /maxReplans/);
64
+ }
65
+ }
66
+ finally {
67
+ rmSync(proj2, { recursive: true, force: true });
68
+ }
69
+ }
70
+ // history
71
+ const hist = loadReplanHistory(proj);
72
+ assert.equal(hist.length, 2);
73
+ await clearReplanHistory(proj);
74
+ assert.ok(!existsSync(join(proj, "harness/replan.json")));
75
+ assert.deepEqual(loadReplanHistory(proj), []);
76
+ console.log("✓ amendPlan add, bump, cycle/missing, maxReplans, history");
77
+ }
78
+ finally {
79
+ rmSync(proj, { recursive: true, force: true });
80
+ }
81
+ }
82
+ // --- a status alias is not an unresolved dependency ---
83
+ // This module used to parse feature-list.json raw, so a task stored as "done"
84
+ // never compared equal to "complete" and validateDeps rejected every amendment
85
+ // to a plan that used an alias — with the nonsense message that a task in
86
+ // flight had unresolved dependencies. Plan reads now go through the core
87
+ // loader, which normalises aliases on the way in.
88
+ {
89
+ const proj = tmpProject();
90
+ try {
91
+ writeFeatureList(proj, [
92
+ { id: "task-012", key: "schema-rework", description: "A", status: "done", dependsOn: [] },
93
+ { id: "task-013", key: "rework-replan", description: "B", status: "in_progress", dependsOn: ["schema-rework"] },
94
+ ], 1);
95
+ const res = await amendPlan({
96
+ projectDir: proj,
97
+ reason: "alias",
98
+ addTasks: [{ featureId: "feature-006", task: { id: "task-014", key: "router-core", description: "C", status: "pending" } }],
99
+ });
100
+ assert.equal(res.added.tasks, 1, "an aliased status must not read as an unresolved dependency");
101
+ assert.equal(res.baseRevision, 2);
102
+ const file = JSON.parse(readFileSync(join(proj, "harness", "features", "feature-list.json"), "utf-8"));
103
+ const statuses = new Map(file.features[0].tasks.map((t) => [t.key, t.status]));
104
+ assert.equal(statuses.get("schema-rework"), "complete", '"done" is normalised to the canonical status on write');
105
+ assert.equal(statuses.get("rework-replan"), "in_progress", "the dependent is left where it was");
106
+ console.log("✓ status aliases normalise instead of failing dependency validation");
107
+ }
108
+ finally {
109
+ rmSync(proj, { recursive: true, force: true });
110
+ }
111
+ }
112
+ console.log("All replan tests PASS");
113
+ //# sourceMappingURL=replan.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replan.test.js","sourceRoot":"","sources":["../../tests/replan.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { shouldBounceToRework } from "../src/review.js";
6
+ function tmpProject(cfg) {
7
+ const d = mkdtempSync(join(tmpdir(), "pi-review-"));
8
+ mkdirSync(join(d, "harness"), { recursive: true });
9
+ writeFileSync(join(d, "harness/config.json"), JSON.stringify(cfg, null, 2));
10
+ return d;
11
+ }
12
+ {
13
+ const proj = tmpProject({ review: { allowBackward: true, maxBounces: 2, bounceRequiresDelta: true } });
14
+ try {
15
+ let r = shouldBounceToRework({ projectDir: proj, fileDelta: true, bounceCount: 0 });
16
+ assert.equal(r.shouldBounce, true, "fileDelta true -> bounce");
17
+ r = shouldBounceToRework({ projectDir: proj, fileDelta: false, bounceCount: 0 });
18
+ assert.equal(r.shouldBounce, false, "fileDelta false ignored when bounceRequiresDelta");
19
+ assert.match(r.reason, /bounceRequiresDelta/);
20
+ console.log("✓ bounceRequiresDelta fileDelta");
21
+ }
22
+ finally {
23
+ rmSync(proj, { recursive: true, force: true });
24
+ }
25
+ }
26
+ {
27
+ const proj = tmpProject({ review: { allowBackward: true, maxBounces: 2, bounceRequiresDelta: false } });
28
+ try {
29
+ const r = shouldBounceToRework({ projectDir: proj, fileDelta: false, bounceCount: 0 });
30
+ assert.equal(r.shouldBounce, true, "bounceRequiresDelta false -> fileDelta false still bounces");
31
+ console.log("✓ bounceRequiresDelta false ignores fileDelta");
32
+ }
33
+ finally {
34
+ rmSync(proj, { recursive: true, force: true });
35
+ }
36
+ }
37
+ {
38
+ const proj = tmpProject({ review: { allowBackward: true, maxBounces: 2, bounceRequiresDelta: true } });
39
+ try {
40
+ const r = shouldBounceToRework({ projectDir: proj, fileDelta: true, bounceCount: 2 });
41
+ assert.equal(r.shouldBounce, false, "maxBounces guard");
42
+ assert.match(r.reason, /maxBounces/);
43
+ console.log("✓ maxBounces guard");
44
+ }
45
+ finally {
46
+ rmSync(proj, { recursive: true, force: true });
47
+ }
48
+ }
49
+ {
50
+ const proj = tmpProject({ review: { allowBackward: false, maxBounces: 2, bounceRequiresDelta: true } });
51
+ try {
52
+ const r = shouldBounceToRework({ projectDir: proj, fileDelta: true, bounceCount: 0 });
53
+ assert.equal(r.shouldBounce, false);
54
+ assert.match(r.reason, /allowBackward/);
55
+ console.log("✓ allowBackward false");
56
+ }
57
+ finally {
58
+ rmSync(proj, { recursive: true, force: true });
59
+ }
60
+ }
61
+ {
62
+ // fresh-read: toggle config between calls
63
+ const proj = tmpProject({ review: { allowBackward: true, maxBounces: 2, bounceRequiresDelta: true } });
64
+ try {
65
+ let r = shouldBounceToRework({ projectDir: proj, fileDelta: false, bounceCount: 0 });
66
+ assert.equal(r.shouldBounce, false);
67
+ writeFileSync(join(proj, "harness/config.json"), JSON.stringify({ review: { allowBackward: true, maxBounces: 2, bounceRequiresDelta: false } }, null, 2));
68
+ r = shouldBounceToRework({ projectDir: proj, fileDelta: false, bounceCount: 0 });
69
+ assert.equal(r.shouldBounce, true, "fresh-read toggle");
70
+ console.log("✓ fresh-read toggle");
71
+ }
72
+ finally {
73
+ rmSync(proj, { recursive: true, force: true });
74
+ }
75
+ }
76
+ console.log("All review tests PASS");
77
+ //# sourceMappingURL=review.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.test.js","sourceRoot":"","sources":["../../tests/review.test.ts"],"names":[],"mappings":"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"}