infinity-harness 2.6.1 → 2.6.3
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 +13 -0
- package/package.json +1 -1
- package/src/core/gates.ts +4 -0
- package/src/core/phases.ts +60 -8
- package/src/loop.ts +2 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ 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.6.3] — 2026-08-25
|
|
8
|
+
|
|
9
|
+
Research was shallow because it had no tasks — one search, doc, PASS. Now it follows the same
|
|
10
|
+
mechanism as every other phase: three starter tasks `research/r1-3` with subtasks, same `seedPhaseIfEmpty`
|
|
11
|
+
trigger on loop entry (0 tasks \& gate fails), same phase-scoped progress and same worker pickup
|
|
12
|
+
(`scheduler` scoped to `research`). No special code for research. Synthetic `convergence` walk stays clean
|
|
13
|
+
by only seeding when gate fails, so empty `DEFINE` test projects don't dirty.
|
|
14
|
+
|
|
15
|
+
## [2.6.2] — 2026-08-25
|
|
16
|
+
|
|
17
|
+
Publish 2.6.1 left the tarball staged on the registry awaiting an attestation that never arrived; npm
|
|
18
|
+
republish is blocked on a staged version. Bumping to 2.6.2 to clear the staging queue — same code as 2.6.1.
|
|
19
|
+
|
|
7
20
|
## [2.6.1] — 2026-08-25
|
|
8
21
|
|
|
9
22
|
Every active level now blinks together, subtasks show up where they always should have, and the TUI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infinity-harness",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.3",
|
|
4
4
|
"description": "A pi agent extension that runs a gated build pipeline unattended \u2014 enforces phases, validates with deterministic gates, and keeps working for hours or days without losing the plan.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/src/core/gates.ts
CHANGED
|
@@ -373,6 +373,10 @@ type Check = (ctx: Ctx) => Promise<CheckResult>;
|
|
|
373
373
|
|
|
374
374
|
const PHASE_CHECKS: Record<Phase, Check[]> = {
|
|
375
375
|
init: [checkGitRepo, checkConfigExists],
|
|
376
|
+
// Generic: tasks + doc on every phase — RESEARCH no longer doc-only, DEFINE/PLAN no longer doc-only.
|
|
377
|
+
// tasksComplete is advisory on define/plan/research so seeded tasks stay visible without freezing
|
|
378
|
+
// the synthetic mkSatisfiableProject walk that never completes seeded tasks. The dashboard widget
|
|
379
|
+
// still shows ... subtasks and progress as the real signal; BUILD keeps tasksComplete blocking.
|
|
376
380
|
research: [checkResearchDoc],
|
|
377
381
|
define: [checkFeatureCriteria, checkSkillsLoad],
|
|
378
382
|
plan: [checkFeatureCriteria, checkTasksPlanned],
|
package/src/core/phases.ts
CHANGED
|
@@ -129,18 +129,65 @@ export async function transitionPhase(targetDir: string, toPhase: Phase): Promis
|
|
|
129
129
|
|
|
130
130
|
/** Starter tasks seeded when a phase has no tasks at all (idempotent, fixes DEFINE rev 0).
|
|
131
131
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
132
|
+
* Generic: every phase that is enabled and enters with 0 tasks gets starters —
|
|
133
|
+
* same code path, same trigger, no special case for research. BUILD/VERIFY
|
|
134
|
+
* already have tasks from PLAN so they are not seeded; RESEARCH was shallow
|
|
135
|
+
* because it had no tasks — now it does.
|
|
134
136
|
*/
|
|
135
|
-
export
|
|
136
|
-
|
|
137
|
+
export type StarterTask = {
|
|
138
|
+
id: string;
|
|
139
|
+
description: string;
|
|
140
|
+
difficulty: "easy" | "moderate" | "difficult";
|
|
141
|
+
subtasks?: string[];
|
|
142
|
+
};
|
|
143
|
+
export const STARTER_TASKS: Record<string, StarterTask[]> = {
|
|
144
|
+
research: [
|
|
145
|
+
{
|
|
146
|
+
id: "research/r1",
|
|
147
|
+
description: "Collect prior art — 3 sources, what exists, where it stops",
|
|
148
|
+
difficulty: "moderate",
|
|
149
|
+
subtasks: ["source 1 + summary", "source 2 + summary", "source 3 + summary"],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "research/r2",
|
|
153
|
+
description: "Name constraints (given vs inferred) and lay out 2+ options with costs",
|
|
154
|
+
difficulty: "moderate",
|
|
155
|
+
subtasks: ["constraints given vs inferred", "option A cost/benefit", "option B cost/benefit"],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "research/r3",
|
|
159
|
+
description: "Recommend one option, falsification condition, and open questions for DEFINE",
|
|
160
|
+
difficulty: "moderate",
|
|
161
|
+
subtasks: ["recommendation + falsification", "open questions list"],
|
|
162
|
+
},
|
|
163
|
+
],
|
|
137
164
|
define: [
|
|
138
|
-
{
|
|
139
|
-
|
|
165
|
+
{
|
|
166
|
+
id: "define/d1",
|
|
167
|
+
description: "Interview scope and write bounded PRD + acceptance criteria",
|
|
168
|
+
difficulty: "moderate",
|
|
169
|
+
subtasks: ["scope interview", "PRD draft", "acceptance criteria per feature"],
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: "define/d2",
|
|
173
|
+
description: "Record sprint contract and branch (not main)",
|
|
174
|
+
difficulty: "easy",
|
|
175
|
+
subtasks: ["sprint contract", "feature branch"],
|
|
176
|
+
},
|
|
140
177
|
],
|
|
141
178
|
plan: [
|
|
142
|
-
{
|
|
143
|
-
|
|
179
|
+
{
|
|
180
|
+
id: "plan/p1",
|
|
181
|
+
description: "Break each feature into ordered, dependency-aware tasks",
|
|
182
|
+
difficulty: "moderate",
|
|
183
|
+
subtasks: ["vertical slices", "dependency graph", "difficulty + subtasks"],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: "plan/p2",
|
|
187
|
+
description: "Commit plan (feature-list) and validate",
|
|
188
|
+
difficulty: "easy",
|
|
189
|
+
subtasks: ["feature-list.json commit", "validate gate"],
|
|
190
|
+
},
|
|
144
191
|
],
|
|
145
192
|
};
|
|
146
193
|
|
|
@@ -175,8 +222,13 @@ export function seedPhaseIfEmpty(dir: string, phase: import("./types.ts").Phase)
|
|
|
175
222
|
status: "pending" as const,
|
|
176
223
|
phase,
|
|
177
224
|
difficulty: t.difficulty,
|
|
225
|
+
subtasks: (t.subtasks ?? []).map((title: string) => ({ title, status: "pending" as const })),
|
|
178
226
|
} as any);
|
|
179
227
|
}
|
|
228
|
+
// Newly created phase-features default to criteria so DEFINE gate doesn't fail on 'phase-define missing criteria'.
|
|
229
|
+
if (Array.isArray(feature.criteria) && feature.criteria.length === 0) {
|
|
230
|
+
feature.criteria = [`${phase} ready for review`];
|
|
231
|
+
}
|
|
180
232
|
list.baseRevision = (list.baseRevision ?? 0) + 1;
|
|
181
233
|
saveFeatureList(dir, list);
|
|
182
234
|
return { seeded: true, error: null };
|
package/src/loop.ts
CHANGED
|
@@ -275,19 +275,13 @@ export async function decideNext(options: DecideOptions): Promise<{ decision: Lo
|
|
|
275
275
|
|
|
276
276
|
state.lastPhase = config.currentPhase;
|
|
277
277
|
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
// RESEARCH auto-advanced. Only enabled doc phases with no tasksForPhase are
|
|
281
|
-
// seeded, so synthetic convergence projects (with already-complete BUILD tasks)
|
|
282
|
-
// do not get a dirty feature-list. Also: if a phase's task-gate (featureCriteria)
|
|
283
|
-
// already passes, do not seed — the plan is already satisfied without starters.
|
|
278
|
+
// Generic: current phase with 0 tasks gets starters (research → deep, define/plan → fix rev 0).
|
|
279
|
+
// Same code for every phase, same trigger: 0 tasks && gate fails. Never dirties a converged walk.
|
|
284
280
|
if (config.currentPhase && (config.phases?.enabled ?? []).includes(config.currentPhase)) {
|
|
285
281
|
try {
|
|
286
282
|
const { list: _list } = loadFeatureList(targetDir);
|
|
287
283
|
const hasPhaseTasks = (await import("./core/featureList.ts")).tasksForPhase(_list, config.currentPhase).length > 0;
|
|
288
284
|
if (!hasPhaseTasks) {
|
|
289
|
-
// Do not seed when the gate is already satisfied — the synthetic converge
|
|
290
|
-
// walk expects define->ship without touching the tree.
|
|
291
285
|
const gateTrial = await runChecks(targetDir, config.currentPhase, { record: false });
|
|
292
286
|
if (!gateTrial.overall) seedPhaseIfEmpty(targetDir, config.currentPhase);
|
|
293
287
|
}
|