ultimate-pi 0.22.0 → 0.22.2

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 (78) hide show
  1. package/.agents/skills/harness-context/SKILL.md +3 -3
  2. package/.agents/skills/harness-debate-plan/SKILL.md +2 -2
  3. package/.agents/skills/harness-decisions/SKILL.md +2 -2
  4. package/.agents/skills/harness-eval/SKILL.md +1 -1
  5. package/.agents/skills/harness-git-commit/SKILL.md +1 -1
  6. package/.agents/skills/harness-governor/SKILL.md +5 -5
  7. package/.agents/skills/harness-ls-lint-setup/SKILL.md +2 -2
  8. package/.agents/skills/harness-orchestration/SKILL.md +4 -4
  9. package/.agents/skills/harness-plan/SKILL.md +2 -2
  10. package/.agents/skills/harness-review/SKILL.md +2 -2
  11. package/.agents/skills/harness-sentrux-repair/SKILL.md +1 -1
  12. package/.agents/skills/harness-sentrux-setup/SKILL.md +2 -2
  13. package/.agents/skills/harness-spec/SKILL.md +1 -1
  14. package/.agents/skills/harness-steer/SKILL.md +2 -2
  15. package/.agents/skills/posthog-analyst/SKILL.md +1 -1
  16. package/.agents/skills/sentrux/SKILL.md +4 -4
  17. package/.agents/skills/web-retrieval/SKILL.md +1 -1
  18. package/.pi/agents/harness/ls-lint-steward.md +3 -3
  19. package/.pi/agents/harness/planning/decompose.md +1 -1
  20. package/.pi/agents/harness/planning/execution-plan-author.md +1 -1
  21. package/.pi/agents/harness/planning/hypothesis-validator.md +1 -1
  22. package/.pi/agents/harness/planning/hypothesis.md +1 -1
  23. package/.pi/agents/harness/planning/plan-adversary.md +1 -1
  24. package/.pi/agents/harness/planning/plan-evaluator.md +2 -2
  25. package/.pi/agents/harness/planning/plan-synthesizer.md +2 -2
  26. package/.pi/agents/harness/planning/review-integrator.md +1 -1
  27. package/.pi/agents/harness/planning/sprint-contract-auditor.md +5 -5
  28. package/.pi/agents/harness/running/executor.md +1 -1
  29. package/.pi/agents/harness/sentrux-repair-advisor.md +1 -1
  30. package/.pi/agents/harness/sentrux-steward.md +2 -2
  31. package/.pi/extensions/agt-kill-switch.ts +7 -1
  32. package/.pi/extensions/harness-plan-approval.ts +9 -1
  33. package/.pi/extensions/harness-run-context.ts +529 -84
  34. package/.pi/extensions/policy-gate.ts +15 -2
  35. package/.pi/harness/agents.manifest.json +16 -16
  36. package/.pi/harness/agents.policy.yaml +82 -3
  37. package/.pi/harness/specs/plan-task-clarification.schema.json +10 -1
  38. package/.pi/lib/agents-policy.mjs +42 -1
  39. package/.pi/lib/agt/build-evaluation-context.ts +3 -1
  40. package/.pi/lib/agt/kill-switch-state.ts +14 -0
  41. package/.pi/lib/agt/legacy-evaluate.ts +3 -1
  42. package/.pi/lib/ask-user/index.ts +2 -0
  43. package/.pi/lib/ask-user/merge-task-clarification.ts +5 -0
  44. package/.pi/lib/ask-user/policy.ts +23 -0
  45. package/.pi/lib/ask-user/presenters/glimpse.ts +8 -1
  46. package/.pi/lib/ask-user/presenters/headless.ts +15 -0
  47. package/.pi/lib/ask-user/presenters/select.ts +11 -2
  48. package/.pi/lib/ask-user/validate-core.mjs +16 -0
  49. package/.pi/lib/harness-artifact-gate.ts +75 -5
  50. package/.pi/lib/harness-repair-brief.ts +30 -4
  51. package/.pi/lib/harness-run-context.ts +804 -17
  52. package/.pi/lib/harness-schema-validate.ts +147 -38
  53. package/.pi/lib/harness-spawn-policy.ts +9 -0
  54. package/.pi/lib/harness-spawn-topology.ts +109 -7
  55. package/.pi/lib/harness-subagent-precheck.ts +21 -0
  56. package/.pi/lib/harness-subagent-submit-pipeline.ts +95 -21
  57. package/.pi/lib/harness-subagent-submit-register.ts +6 -1
  58. package/.pi/lib/harness-subagents-bridge.ts +3 -0
  59. package/.pi/lib/harness-yaml.ts +11 -3
  60. package/.pi/lib/plan-approval/create-plan.ts +2 -6
  61. package/.pi/lib/plan-debate-gate.ts +87 -0
  62. package/.pi/lib/plan-debate-lane.ts +8 -2
  63. package/.pi/lib/plan-human-gates.ts +322 -0
  64. package/.pi/prompts/harness-clear.md +25 -0
  65. package/.pi/prompts/harness-plan.md +11 -7
  66. package/.pi/prompts/harness-review.md +5 -5
  67. package/.pi/prompts/harness-run.md +2 -2
  68. package/.pi/prompts/harness-sentrux-steward.md +2 -2
  69. package/.pi/prompts/harness-setup.md +3 -3
  70. package/.pi/prompts/harness-steer.md +5 -5
  71. package/.pi/scripts/generate-agents-policy-yaml.mjs +73 -7
  72. package/.pi/scripts/harness-reconcile-run-context.mjs +62 -0
  73. package/.pi/scripts/harness-schema-compile-verify.mjs +29 -0
  74. package/.pi/scripts/harness-verify.mjs +100 -0
  75. package/AGENTS.md +1 -0
  76. package/CHANGELOG.md +13 -0
  77. package/README.md +4 -0
  78. package/package.json +9 -6
@@ -19,6 +19,7 @@ import {
19
19
  laneArtifactPathsForParallelProbesRound,
20
20
  laneArtifactPathsForRound,
21
21
  } from "./plan-debate-lanes.js";
22
+ import { getPlanDebateRoundStatus } from "./plan-debate-round-status.js";
22
23
  import {
23
24
  getMessengerRoundState,
24
25
  loadMessengerState,
@@ -331,3 +332,89 @@ export function isReviewRoundArtifactPath(relPath: string): boolean {
331
332
  norm === CONSOLIDATED_REVIEW_ARTIFACT
332
333
  );
333
334
  }
335
+
336
+ function roundIndexForFocus(
337
+ focus: PlanDebateFocus,
338
+ required: readonly PlanDebateFocus[],
339
+ ): number {
340
+ const idx = required.indexOf(focus);
341
+ return idx >= 0 ? idx + 1 : 1;
342
+ }
343
+
344
+ /** Actionable recovery steps when approve_plan is blocked by the debate gate. */
345
+ export async function buildPlanDebateGateRecovery(
346
+ projectRoot: string,
347
+ runId: string,
348
+ gate: PlanDebateGateResult,
349
+ ): Promise<string> {
350
+ const runDir = join(projectRoot, ".pi", "harness", "runs", runId);
351
+ const messenger = await loadMessengerState(runDir);
352
+ const required: readonly PlanDebateFocus[] =
353
+ messenger?.required_focuses && messenger.required_focuses.length > 0
354
+ ? messenger.required_focuses
355
+ : (["spec", "wbs", "schedule", "quality"] as const);
356
+ const profile =
357
+ messenger?.debate_profile ?? gate.debate_profile ?? "standard";
358
+ const mode = messenger?.review_gate_mode ?? "threaded";
359
+ const coverage = gate.focus_coverage;
360
+
361
+ const lines: string[] = [
362
+ "Review Gate must finish before approve_plan.",
363
+ "",
364
+ "Blocking checks:",
365
+ ...gate.errors.map((e) => `- ${e}`),
366
+ "",
367
+ `Debate profile: ${profile}, mode: ${mode}, required focuses: ${required.join(", ")}`,
368
+ "",
369
+ ];
370
+
371
+ const needsConsensus = gate.errors.some(
372
+ (e) => e.includes("consensus") || e.includes(".consensus.json"),
373
+ );
374
+ const needsRounds = gate.errors.some(
375
+ (e) =>
376
+ e.includes("review_gate_ready") ||
377
+ e.includes("focus not covered") ||
378
+ e.includes("missing artifacts/") ||
379
+ e.includes("round events"),
380
+ );
381
+
382
+ if (needsRounds) {
383
+ const nextFocus: PlanDebateFocus =
384
+ (coverage?.missing[0] as PlanDebateFocus | undefined) ??
385
+ required[0] ??
386
+ "spec";
387
+ const roundIndex =
388
+ mode === "consolidated" ? 1 : roundIndexForFocus(nextFocus, required);
389
+ const status = await getPlanDebateRoundStatus(runDir, roundIndex, runId, {
390
+ debate_round_focus: mode === "consolidated" ? "all" : nextFocus,
391
+ });
392
+ lines.push(
393
+ `Next round: ${roundIndex} (focus: ${mode === "consolidated" ? "all" : nextFocus})`,
394
+ );
395
+ if (status.missing.length > 0) {
396
+ lines.push("Missing lane artifacts:");
397
+ for (const m of status.missing) {
398
+ lines.push(`- ${m}`);
399
+ }
400
+ }
401
+ if (status.next_tool) {
402
+ lines.push(`Next tool: ${status.next_tool}`);
403
+ }
404
+ lines.push(
405
+ "Workflow: complete lane subagents (one per batch) → review-integrator → harness_debate_submit_round → harness_debate_focus_coverage.",
406
+ );
407
+ }
408
+
409
+ if (needsConsensus) {
410
+ lines.push(
411
+ "When all required focuses are covered and the last round has review_gate_ready: true, call harness_debate_consensus, then approve_plan again.",
412
+ );
413
+ }
414
+
415
+ if (gate.warnings.length > 0) {
416
+ lines.push("", "Warnings:", ...gate.warnings.map((w) => `- ${w}`));
417
+ }
418
+
419
+ return lines.join("\n");
420
+ }
@@ -48,12 +48,14 @@ export async function applyDebateLaneFromDoc(opts: {
48
48
  lane: DebateLaneKind;
49
49
  doc: Record<string, unknown>;
50
50
  roundIndex?: number;
51
+ skipArtifactWrite?: boolean;
51
52
  }): Promise<ApplyDebateLaneResult> {
52
53
  return applyDebateLane({
53
54
  runDir: opts.runDir,
54
55
  lane: opts.lane,
55
56
  content: JSON.stringify(opts.doc),
56
57
  roundIndex: opts.roundIndex,
58
+ skipArtifactWrite: opts.skipArtifactWrite,
57
59
  });
58
60
  }
59
61
 
@@ -95,6 +97,8 @@ export async function applyDebateLane(opts: {
95
97
  lane: DebateLaneKind;
96
98
  content: string;
97
99
  roundIndex?: number;
100
+ /** When true, artifact YAML was already written (e.g. submit pipeline); only messenger side effects run. */
101
+ skipArtifactWrite?: boolean;
98
102
  }): Promise<ApplyDebateLaneResult> {
99
103
  const errors: string[] = [];
100
104
  let doc: Record<string, unknown>;
@@ -121,8 +125,10 @@ export async function applyDebateLane(opts: {
121
125
  : (opts.roundIndex ?? 1);
122
126
  const relPath = laneArtifactPath(opts.lane, roundIndex);
123
127
  const absPath = join(opts.runDir, relPath);
124
- await mkdir(dirname(absPath), { recursive: true });
125
- await writeYamlFile(absPath, doc);
128
+ if (!opts.skipArtifactWrite) {
129
+ await mkdir(dirname(absPath), { recursive: true });
130
+ await writeYamlFile(absPath, doc);
131
+ }
126
132
 
127
133
  let messengerPosted = false;
128
134
  let nextStep: string | undefined;
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Human-in-the-loop gates for /harness-plan — Phase 0 ask_user and Phase 6 approve_plan.
3
+ */
4
+
5
+ import { constants } from "node:fs";
6
+ import { access } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import {
9
+ isHarnessNonInteractive,
10
+ isPlanApprovalAskUser,
11
+ } from "./ask-user/policy.js";
12
+ import {
13
+ hasPlanUserApproval,
14
+ indexOfLastPlanCommand,
15
+ } from "./harness-run-context.js";
16
+ import { validatePlanApprovalReadiness } from "./plan-approval-readiness.js";
17
+ import {
18
+ buildPlanDebateGateRecovery,
19
+ validatePlanDebateGate,
20
+ } from "./plan-debate-gate.js";
21
+ import {
22
+ isTaskClarificationReady,
23
+ readTaskClarificationDoc,
24
+ type TaskClarificationReadiness,
25
+ validateTaskClarificationDoc,
26
+ } from "./plan-task-clarification.js";
27
+
28
+ const EXPLICIT_ACCEPTANCE_RE =
29
+ /\b(acceptance|success criteria|definition of done|done when|must (pass|satisfy)|out of scope|in scope)\b/i;
30
+
31
+ type SessionEntryLike = {
32
+ type?: string;
33
+ customType?: string;
34
+ data?: unknown;
35
+ message?: {
36
+ role?: string;
37
+ toolName?: string;
38
+ details?: unknown;
39
+ content?: string | unknown[];
40
+ };
41
+ };
42
+
43
+ function isNonInteractivePlan(): boolean {
44
+ return (
45
+ process.env.HARNESS_PLAN_NONINTERACTIVE === "1" || isHarnessNonInteractive()
46
+ );
47
+ }
48
+
49
+ function askUserCallWasTaskClarification(details: unknown): boolean {
50
+ if (!details || typeof details !== "object") return false;
51
+ const d = details as { cancelled?: boolean; input?: unknown };
52
+ if (d.cancelled) return false;
53
+ const input = d.input as
54
+ | { question?: string; options?: unknown[]; questions?: unknown[] }
55
+ | undefined;
56
+ if (!input) return true;
57
+ return !isPlanApprovalAskUser(input);
58
+ }
59
+
60
+ export function hasTaskClarificationAskUserSincePlanCommand(
61
+ entries: unknown[],
62
+ ): boolean {
63
+ if (isNonInteractivePlan()) return true;
64
+ const since = Math.max(0, indexOfLastPlanCommand(entries));
65
+ for (let i = since; i < entries.length; i++) {
66
+ const entry = entries[i] as SessionEntryLike;
67
+ if (
68
+ entry.type === "custom" &&
69
+ entry.customType === "harness-task-clarification-engagement"
70
+ ) {
71
+ return true;
72
+ }
73
+ if (entry.type !== "message" || entry.message?.role !== "toolResult") {
74
+ continue;
75
+ }
76
+ if (entry.message.toolName !== "ask_user") continue;
77
+ if (askUserCallWasTaskClarification(entry.message.details)) {
78
+ return true;
79
+ }
80
+ }
81
+ return false;
82
+ }
83
+
84
+ export function hasClarificationFollowUpUserMessage(
85
+ entries: unknown[],
86
+ ): boolean {
87
+ const since = Math.max(0, indexOfLastPlanCommand(entries));
88
+ for (let i = since; i < entries.length; i++) {
89
+ const entry = entries[i] as SessionEntryLike;
90
+ if (entry.type !== "message" || entry.message?.role !== "user") continue;
91
+ const content = entry.message.content;
92
+ const text =
93
+ typeof content === "string"
94
+ ? content.trim()
95
+ : Array.isArray(content)
96
+ ? content
97
+ .filter(
98
+ (c): c is { type: string; text?: string } =>
99
+ typeof c === "object" && c !== null && "type" in c,
100
+ )
101
+ .map((c) => c.text ?? "")
102
+ .join("")
103
+ .trim()
104
+ : "";
105
+ if (!text || text.startsWith("/")) continue;
106
+ return true;
107
+ }
108
+ return false;
109
+ }
110
+
111
+ export function isExplicitTaskAcceptance(taskSummary: string): boolean {
112
+ const t = taskSummary.trim();
113
+ if (t.length < 24) return false;
114
+ return EXPLICIT_ACCEPTANCE_RE.test(t);
115
+ }
116
+
117
+ export interface TaskClarificationHumanGateResult {
118
+ ok: boolean;
119
+ errors: string[];
120
+ }
121
+
122
+ export function validateTaskClarificationHumanGate(
123
+ entries: unknown[],
124
+ doc: Record<string, unknown> | null,
125
+ opts?: {
126
+ quick?: boolean;
127
+ taskSummary?: string;
128
+ allowFollowUpMessage?: boolean;
129
+ },
130
+ ): TaskClarificationHumanGateResult {
131
+ const errors: string[] = [];
132
+ const status = String(doc?.status ?? "").toLowerCase();
133
+ if (status !== "ready") {
134
+ return { ok: true, errors };
135
+ }
136
+
137
+ const engagement = doc?.user_engagement as { source?: string } | undefined;
138
+ if (engagement?.source === "ask_user") {
139
+ return { ok: true, errors };
140
+ }
141
+
142
+ if (hasTaskClarificationAskUserSincePlanCommand(entries)) {
143
+ return { ok: true, errors };
144
+ }
145
+
146
+ if (
147
+ opts?.allowFollowUpMessage &&
148
+ hasClarificationFollowUpUserMessage(entries)
149
+ ) {
150
+ return { ok: true, errors };
151
+ }
152
+
153
+ if (opts?.quick && isExplicitTaskAcceptance(opts.taskSummary ?? "")) {
154
+ return { ok: true, errors };
155
+ }
156
+
157
+ errors.push(
158
+ "Phase 0 requires ask_user before task-clarification status: ready. Call ask_user (harness-decisions skill), merge answers, then harness_artifact_ready.",
159
+ );
160
+ return { ok: false, errors };
161
+ }
162
+
163
+ async function fileExists(path: string): Promise<boolean> {
164
+ try {
165
+ await access(path, constants.R_OK);
166
+ return true;
167
+ } catch {
168
+ return false;
169
+ }
170
+ }
171
+
172
+ export interface PlanHumanGateStatus {
173
+ phase0Ready: boolean;
174
+ phase0NeedsAskUser: boolean;
175
+ debateComplete: boolean;
176
+ debateRequired: boolean;
177
+ approvalRequired: boolean;
178
+ approvalRecorded: boolean;
179
+ nextRequiredAction: string | null;
180
+ /** Actionable Review Gate recovery when debateRequired. */
181
+ debateRecoveryHint: string | null;
182
+ }
183
+
184
+ export async function resolvePlanHumanGateStatus(
185
+ projectRoot: string,
186
+ runId: string,
187
+ entries: unknown[],
188
+ opts?: { quick?: boolean; taskSummary?: string; lastOutcome?: string | null },
189
+ ): Promise<PlanHumanGateStatus> {
190
+ const runDir = join(projectRoot, ".pi", "harness", "runs", runId);
191
+ const clar = await isTaskClarificationReady(runDir);
192
+ const clarDoc = clar.ok ? await readTaskClarificationDoc(runDir) : null;
193
+ const humanGate = validateTaskClarificationHumanGate(entries, clarDoc, {
194
+ quick: opts?.quick,
195
+ taskSummary: opts?.taskSummary,
196
+ allowFollowUpMessage: opts?.lastOutcome === "needs_clarification",
197
+ });
198
+ const phase0Ready = clar.ok && humanGate.ok;
199
+ const phase0NeedsAskUser = clar.ok && !humanGate.ok;
200
+ const approvalRecorded = hasPlanUserApproval(entries, {
201
+ sincePlanCommand: true,
202
+ });
203
+ const dagPath = join(runDir, "plan-packet.yaml");
204
+ const hasPacket = await fileExists(dagPath);
205
+ const messengerPath = join(runDir, "debate-messenger", "state.json");
206
+ const debateOpened = await fileExists(messengerPath);
207
+
208
+ let debateComplete = true;
209
+ let debateGate = null;
210
+ let readinessOk = false;
211
+ let approvalRequired = false;
212
+
213
+ if (phase0Ready && !approvalRecorded) {
214
+ const readiness = await validatePlanApprovalReadiness(projectRoot, runId, {
215
+ risk_level: String(clarDoc?.risk_level ?? "med"),
216
+ quick: opts?.quick,
217
+ });
218
+ readinessOk = readiness.ok;
219
+ debateGate = await validatePlanDebateGate(projectRoot, runId);
220
+ debateComplete = debateGate.ok;
221
+ approvalRequired = readiness.ok && debateComplete && hasPacket;
222
+ }
223
+
224
+ const debateRequired =
225
+ phase0Ready &&
226
+ !debateComplete &&
227
+ !approvalRecorded &&
228
+ (debateOpened || hasPacket);
229
+
230
+ let debateRecoveryHint: string | null = null;
231
+ let nextRequiredAction: string | null = null;
232
+ if (!phase0Ready) {
233
+ nextRequiredAction = phase0NeedsAskUser
234
+ ? "ask_user (Phase 0 task contract)"
235
+ : "complete artifacts/task-clarification.yaml (Phase 0)";
236
+ } else if (debateRequired && debateGate) {
237
+ debateRecoveryHint = await buildPlanDebateGateRecovery(
238
+ projectRoot,
239
+ runId,
240
+ debateGate,
241
+ );
242
+ nextRequiredAction =
243
+ "Complete Review Gate (debate rounds + harness_debate_consensus) before approve_plan";
244
+ } else if (approvalRequired && !approvalRecorded) {
245
+ nextRequiredAction = "approve_plan then create_plan (Phase 6)";
246
+ }
247
+
248
+ return {
249
+ phase0Ready,
250
+ phase0NeedsAskUser,
251
+ debateComplete,
252
+ debateRequired,
253
+ approvalRequired,
254
+ approvalRecorded,
255
+ nextRequiredAction,
256
+ debateRecoveryHint,
257
+ };
258
+ }
259
+
260
+ export function formatPlanHumanGateBlock(status: PlanHumanGateStatus): string {
261
+ if (!status.nextRequiredAction) return "";
262
+ const lines = [
263
+ "[HarnessPlanGate]",
264
+ `next_required_action=${status.nextRequiredAction}`,
265
+ `phase0_ready=${status.phase0Ready}`,
266
+ `review_gate_complete=${status.debateComplete}`,
267
+ `review_gate_required=${status.debateRequired}`,
268
+ `plan_approval_required=${status.approvalRequired}`,
269
+ `plan_approval_recorded=${status.approvalRecorded}`,
270
+ ];
271
+ if (status.debateRequired) {
272
+ lines.push(
273
+ "Do not end this turn with prose only — call harness_debate_round_status / harness_debate_focus_coverage and spawn the next debate lane subagent (one per batch).",
274
+ );
275
+ } else {
276
+ lines.push(
277
+ "Do not spawn planning subagents or end this turn until the required human step completes.",
278
+ );
279
+ }
280
+ if (status.debateRecoveryHint) {
281
+ lines.push("", status.debateRecoveryHint);
282
+ }
283
+ return lines.join("\n");
284
+ }
285
+
286
+ export async function shouldBlockSubagentForMissingPlanApproval(
287
+ projectRoot: string,
288
+ runId: string,
289
+ entries: unknown[],
290
+ phase: string,
291
+ ): Promise<{ block: boolean; reason?: string }> {
292
+ if (phase !== "plan" || isNonInteractivePlan()) return { block: false };
293
+ if (hasPlanUserApproval(entries, { sincePlanCommand: true })) {
294
+ return { block: false };
295
+ }
296
+ const status = await resolvePlanHumanGateStatus(projectRoot, runId, entries);
297
+ if (!status.approvalRequired) return { block: false };
298
+ return {
299
+ block: true,
300
+ reason:
301
+ "Plan Review Gate is complete but user approval is missing. Call approve_plan (then create_plan) before further subagent work.",
302
+ };
303
+ }
304
+
305
+ export async function validateTaskClarificationReadyWithHumanGate(
306
+ runDir: string,
307
+ entries: unknown[],
308
+ opts?: { quick?: boolean; taskSummary?: string; lastOutcome?: string | null },
309
+ ): Promise<TaskClarificationReadiness & { humanErrors: string[] }> {
310
+ const doc = await readTaskClarificationDoc(runDir);
311
+ const base = validateTaskClarificationDoc(doc, { requireReady: true });
312
+ const human = validateTaskClarificationHumanGate(entries, doc, {
313
+ quick: opts?.quick,
314
+ taskSummary: opts?.taskSummary,
315
+ allowFollowUpMessage: opts?.lastOutcome === "needs_clarification",
316
+ });
317
+ return {
318
+ ok: base.ok && human.ok,
319
+ errors: [...base.errors, ...human.errors],
320
+ humanErrors: human.errors,
321
+ };
322
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: Safely delete historical harness run directories while preserving the active run.
3
+ ---
4
+
5
+ # harness-clear
6
+
7
+ Delete only historical run directories under `.pi/harness/runs/`.
8
+
9
+ ## What this does
10
+
11
+ - enumerates delete candidates strictly from `.pi/harness/runs/<run_id>/`
12
+ - always preserves active run ids discovered from session context and active-run pointer
13
+ - asks for one confirmation before any filesystem mutation
14
+ - fails closed: cancel/decline/timeout/error/unavailable confirmation paths delete nothing
15
+ - reports deleted vs protected/skipped counts
16
+
17
+ ## Usage
18
+
19
+ `/harness-clear`
20
+
21
+ ## Safety boundaries
22
+
23
+ - in scope: historical run directories only
24
+ - out of scope: full `.pi/harness/` reset, non-run harness assets, active-run deletion overrides
25
+ - confirmation is mandatory; non-affirmative outcomes are no-op
@@ -5,13 +5,17 @@ argument-hint: "\"<task>\" [--risk low|med|high] [--quick]"
5
5
 
6
6
  # harness-plan
7
7
 
8
- You are the **planning orchestrator** (agent-native; ADR 0042). Produce an execution baseline (`plan-packet.yaml` + `plan-review.md`) with **lake-sized** outcomes and path-first tools. Parent owns gates: `ask_user`, `approve_plan({ human_summary? })`, `create_plan()`, plan-verify, and scoped writes under `.pi/harness/runs/<run_id>/`.
8
+ You are the **planning orchestrator**. Produce an execution baseline (`plan-packet.yaml` + `plan-review.md`) with **lake-sized** outcomes and path-first tools. Parent owns gates: `ask_user`, `approve_plan({ human_summary? })`, `create_plan()`, plan-verify, and scoped writes under `.pi/harness/runs/<run_id>/`.
9
9
 
10
- **Practice map:** `.pi/harness/docs/practice-map.md` phase proven practice agent spawn topology.
10
+ Use the phase order and spawn topology defined in this prompt directly.
11
11
 
12
12
  Subagents persist artifacts via scoped **`submit_*`** tools (deterministic YAML under the run dir). Parent uses **`harness_artifact_ready`** to gate phases (no JSON parsing). Parent merges still use **`write_harness_yaml`** for `research-brief.yaml`, `plan-packet.yaml`, `planning-context.yaml`, and integrator patches.
13
13
 
14
- **Phase 0 is mandatory** before reconnaissance or any planning subagent. `write_harness_yaml` and spawn topology enforce `artifacts/task-clarification.yaml` with `status: ready` (ADR 0053).
14
+ ### Subagent submit gate (required)
15
+
16
+ After a subprocess **`submit_*`** succeeds (or the artifact path is on disk and schema-valid), call **`harness_artifact_ready({ paths: ["<that-artifact>"] })` once** before the next phase or spawn. If spawn topology returns **Duplicate spawn blocked**, do **not** re-spawn that agent — call `harness_artifact_ready` on the existing artifact and advance. Never call the same `submit_*` twice with identical content (idempotent noop — end the subprocess turn instead).
17
+
18
+ **Phase 0 is mandatory** before reconnaissance or any planning subagent. `write_harness_yaml` and spawn topology enforce `artifacts/task-clarification.yaml` with `status: ready`.
15
19
 
16
20
  ## Allowed subagents
17
21
 
@@ -34,7 +38,7 @@ Read **harness-debate-plan** skill before Review Gate rounds.
34
38
 
35
39
  1. Parallel `tasks` only for **independent** merges (implementation ∥ stack research; plan-evaluator ∥ plan-adversary for `parallel_probes`). **Never** parallelize decompose ∥ hypothesis.
36
40
  2. Max **2** research lanes, **1** debate agent, **1** optional `planning-context` subagent per `subagent` call.
37
- 3. Downstream agents **read** upstream artifacts — do not re-derive (see practice-map anti-patterns).
41
+ 3. Downstream agents **read** upstream artifacts — do not re-derive upstream work.
38
42
 
39
43
  ## Performance rules
40
44
 
@@ -57,7 +61,7 @@ Use `[HarnessActivePlan]` / `[HarnessRunContext]` only. On revise: preserve `pla
57
61
 
58
62
  ## Phase 0 — Task clarification (mandatory; parent-led)
59
63
 
60
- **Practice:** Collect requirements / pool of shared meaning before WBS (PMBOK; Crucial Conversations). **ADR 0053.**
64
+ **Practice:** Collect requirements and shared meaning before WBS (PMBOK; Crucial Conversations).
61
65
 
62
66
  **Goal:** `artifacts/task-clarification.yaml` with `status: ready`, `unresolved_questions: []`, and a canonical `clarified_task`. No full planning until gated.
63
67
 
@@ -121,7 +125,7 @@ Decompose treats **`task-clarification.yaml` as authoritative** for scope; §1.1
121
125
 
122
126
  ## Phase 2b — Hypothesis-driven approach (sequential)
123
127
 
124
- **Practice:** Lean exploration — falsifiable claim before plan detail (DARWIN / ADR 0034).
128
+ **Practice:** Lean exploration — require a falsifiable claim before plan detail.
125
129
 
126
130
  **Requires** `artifacts/decomposition.yaml`. Do **not** spawn in parallel with decompose.
127
131
 
@@ -262,7 +266,7 @@ Med/low non-fork plans with clear stack and no implementation `open_questions` d
262
266
 
263
267
  ## Phase 5 — Structured inspection / Review Gate (Fagan-style)
264
268
 
265
- **Practice:** Code Complete collaborative construction; Fagan inspection with rubrics in `.pi/harness/docs/planning-rubrics.md`. Parent is **chair**; one debate agent per `subagent` batch.
269
+ **Practice:** Code Complete collaborative construction with Fagan-style inspection criteria. Parent is **chair**; one debate agent per `subagent` batch.
266
270
 
267
271
  **Forbidden:** parallel `subagent` calls for any debate lane agent in one batch.
268
272
 
@@ -7,7 +7,7 @@ argument-hint: "[--run <run-id>] [--quick] [--readonly] [--trace <trace-ref>]"
7
7
 
8
8
  You are the **post-run verification PM** (PMBOK Monitoring and Controlling). Run measure → judge → red team in one command. Parent owns `ask_user`, deterministic scripts, `harness_artifact_ready`, and run ownership (`--claim` on resume). Subagents persist via **`submit_*`** only (no parent `write` to verdict artifacts).
9
9
 
10
- **Practice map:** `.pi/harness/docs/practice-map.md`
10
+ Follow the review sequence in this prompt directly: deterministic checks → benchmark evaluator → verdict evaluator → adversary → optional tie-breaker.
11
11
 
12
12
  Read **harness-orchestration** and **harness-review** skills before spawning.
13
13
 
@@ -91,7 +91,7 @@ notes: "…"
91
91
 
92
92
  ## Phase 1b — Sentrux repair advisor (subagent)
93
93
 
94
- **Practice:** Close the loop from fitness-function observation bounded repair directives (ADR 0052). Skip when `artifacts/sentrux-repair-plan.yaml` already exists and `HARNESS_SENTRUX_RESCAN` is unset.
94
+ **Practice:** Close the loop from fitness-function observation to bounded repair directives. Skip when `artifacts/sentrux-repair-plan.yaml` already exists and `HARNESS_SENTRUX_RESCAN` is unset.
95
95
 
96
96
  Spawn when **any**:
97
97
 
@@ -131,7 +131,7 @@ Gate:
131
131
  harness_artifact_ready({ paths: ["artifacts/eval-verdict.yaml"] })
132
132
  ```
133
133
 
134
- **Do not stop** after benchmark fail — continue to verdict (and adversary per tier) so `review-outcome.yaml` can route steer vs replan (ADR 0044).
134
+ **Do not stop** after benchmark fail — continue to verdict (and adversary per tier) so `review-outcome.yaml` can route steer vs replan.
135
135
 
136
136
  ## Phase 3 — Policy / quality audit (verdict evaluator)
137
137
 
@@ -153,7 +153,7 @@ Gate again with `harness_artifact_ready`.
153
153
 
154
154
  ## Phase 4 — Independent red team (adversary)
155
155
 
156
- **Practice:** Generator–evaluator separation; adversary distinct from measurer (ADR 0032).
156
+ **Practice:** Generator–evaluator separation; adversary stays distinct from the measurer.
157
157
 
158
158
  Skip when `--quick`. **Tiered steer:** full adversary on initial run + steer attempt 1; lite review (no adversary) on steer attempts 2+ unless prior `block_merge`.
159
159
 
@@ -185,7 +185,7 @@ subagent({ agentScope: "both", agent: "harness/reviewing/tie-breaker", task: "
185
185
 
186
186
  - **Never** parse subprocess JSON to write `eval-verdict.yaml` or `adversary-report.yaml` — use `submit_*` + `harness_artifact_ready` only.
187
187
  - Do not edit `plan-packet.yaml`.
188
- - Do not run inline review checks in this session (subagent isolation per ADR 0032).
188
+ - Do not run inline review checks in this session (keep review work isolated to subagents).
189
189
  - Same Pi session as `/harness-run` is preferred; `--claim` makes cross-session resume work.
190
190
 
191
191
  ## Phase 6 — Review outcome + repair brief (parent)
@@ -4,7 +4,7 @@ description: Execute only against an approved PlanPacket with strict phase gates
4
4
 
5
5
  # harness-run
6
6
 
7
- **Practice map:** `.pi/harness/docs/practice-map.md`
7
+ Follow this prompt's execution flow directly: baseline gate → executor spawn → structural observation → review handoff.
8
8
 
9
9
  You orchestrate the **Executing Process Group** — spawn `harness/running/executor` only. Do **not** implement inline.
10
10
 
@@ -106,7 +106,7 @@ phase: execute
106
106
 
107
107
  ## Parent rules
108
108
 
109
- - On `scope_drift`, finish handoff and recommend **`/harness-review`** (review classifies `plan_gap` vs `implementation_gap` ADR 0044).
109
+ - On `scope_drift`, finish handoff and recommend **`/harness-review`** (review classifies whether the gap is planning or implementation).
110
110
  - Do not call `ask_user` for plan-level ambiguity — return to plan command.
111
111
 
112
112
  ## Completion
@@ -39,14 +39,14 @@ Gate: `harness_artifact_ready({ paths: ["artifacts/sentrux-manifest-proposal.yam
39
39
  Read `artifacts/sentrux-manifest-proposal.yaml`.
40
40
 
41
41
  - `change_class: none` → report no manifest change; stop.
42
- - Otherwise → `ask_user` with summary, evidence bullets, and `adr_draft` if `adr_required`.
42
+ - Otherwise → `ask_user` with summary, evidence bullets, and any draft decision text when a formal decision record is required.
43
43
 
44
44
  On approval:
45
45
 
46
46
  1. Apply `manifest_patch` to `.pi/harness/sentrux/architecture.manifest.json` (parent `write` or manual edit).
47
47
  2. `node "$UP_PKG/.pi/scripts/harness-sentrux-bootstrap.mjs" --force`
48
48
  3. Append session custom entry `harness-architecture-changed` (triggers rules sync extension).
49
- 4. If `adr_required`, file harness ADR snippet or `docs/adr/` entry per team convention.
49
+ 4. If a formal decision record is required, file it in the target project's standard decision-log location.
50
50
 
51
51
  On reject: keep manifest unchanged; document decision in run notes.
52
52
 
@@ -289,7 +289,7 @@ Quick smoke test:
289
289
  sg -p 'function $NAME($$$ARGS) { $$$BODY }' --json 2>/dev/null | head -5 && echo "✓ ast-grep pattern matching works" || echo "! ast-grep smoke test — may need language-specific config"
290
290
  ```
291
291
 
292
- ### 2.7 — gh CLI (GitHub Issues Spec Storage — ADR-025)
292
+ ### 2.7 — gh CLI (GitHub Issues Spec Storage)
293
293
 
294
294
  ```bash
295
295
  if ! command -v gh &>/dev/null || [ "$FORCE" = "true" ]; then
@@ -335,7 +335,7 @@ Installed and smoke-tested by `harness-cli-verify.sh` (`npm install -g @ls-lint/
335
335
 
336
336
  ## Step 3 — Pi Extension Packages
337
337
 
338
- Bundled extensions load from the installed `ultimate-pi` package. The harness lens wrapper at `.pi/extensions/harness-lens.ts` loads `.pi/extensions/lib/harness-lens/` for edit autopatch, secrets blocking, deferred format, and LSP tools. Structural search uses shell `sg` (installed globally by setup); architecture gates use Sentrux. See [ADR 0045](.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md).
338
+ Bundled extensions load from the installed `ultimate-pi` package. The harness lens wrapper at `.pi/extensions/harness-lens.ts` loads `.pi/extensions/lib/harness-lens/` for edit autopatch, secrets blocking, deferred format, and LSP tools. Structural search uses shell `sg` (installed globally by setup); architecture gates use Sentrux.
339
339
 
340
340
  Harness lens findings are **complementary** to Sentrux:
341
341
 
@@ -471,7 +471,7 @@ Harness `ask_user` supports terminal (TUI), headless (CI), and Glimpse WebView (
471
471
  | **Desktop Linux / macOS / WSLg** | `auto` or `glimpse` for richer questionnaires |
472
472
  | **CI / `--non-interactive`** | Prompts skipped; do not expect WebView |
473
473
 
474
- Append `HARNESS_ASK_USER_UI=tui` to `.env` when WebView is unavailable. The first real `ask_user` reports `ui_backend` and `ui_degraded` in tool details. See ADR 0054.
474
+ Append `HARNESS_ASK_USER_UI=tui` to `.env` when WebView is unavailable. The first real `ask_user` reports `ui_backend` and `ui_degraded` in tool details.
475
475
 
476
476
  Template keys (placeholders — user fills secrets): `HARNESS_TELEMETRY_ENABLED`, `HARNESS_WEB_*`, `HARNESS_VCC_COMPACTION`, `HARNESS_VCC_DEBUG`, plus commented optional PostHog / Graphify vars.
477
477
 
@@ -5,7 +5,7 @@ argument-hint: "[--attempt N]"
5
5
 
6
6
  # harness-steer
7
7
 
8
- Thin orchestrator for the **steer loop** (ADR 0044). Run only after `/harness-review` produced `artifacts/review-outcome.yaml` and `artifacts/repair-brief.yaml` with `remediation_class: implementation_gap`.
8
+ Thin orchestrator for the **steer loop**. Run only after `/harness-review` produced `artifacts/review-outcome.yaml` and `artifacts/repair-brief.yaml` with `remediation_class: implementation_gap`.
9
9
 
10
10
  ## Preconditions
11
11
 
@@ -19,10 +19,10 @@ Thin orchestrator for the **steer loop** (ADR 0044). Run only after `/harness-re
19
19
  2. Update `artifacts/steer-state.yaml` (`attempt`, `max_attempts`, `active: true`).
20
20
  3. Set policy phase to **execute** before spawning executor (required for mutating tools).
21
21
  4. One `ask_user` steer gate unless `run-context.steer_approved` is already true.
22
- 5. Spawn **`harness/running/executor`** with `HarnessSpawnContext.mode: repair` and `repair_brief_path: artifacts/repair-brief.yaml`. Repair uses the same hash-anchored `read`/`edit`, batching, and pre-handoff verification rules as `/harness-run` (ADR 0051).
23
- 6. Optional: `node "$UP_PKG/.pi/scripts/harness-sentrux-cli.mjs" gate --save` after repair to refresh baseline (ADR 0044).
24
- 7. Optional: `node "$UP_PKG/.pi/scripts/harness-ls-lint-cli.mjs"` after repair to confirm filename conventions (ADR 0052).
25
- 7. `next_command`: **`/harness-review`** (always re-verify; tiered adversary on attempts 2+ per practice-map).
22
+ 5. Spawn **`harness/running/executor`** with `HarnessSpawnContext.mode: repair` and `repair_brief_path: artifacts/repair-brief.yaml`. Repair uses the same hash-anchored `read`/`edit`, batching, and pre-handoff verification rules as `/harness-run`.
23
+ 6. Optional: `node "$UP_PKG/.pi/scripts/harness-sentrux-cli.mjs" gate --save` after repair to refresh the structural baseline.
24
+ 7. Optional: `node "$UP_PKG/.pi/scripts/harness-ls-lint-cli.mjs"` after repair to confirm filename conventions.
25
+ 7. `next_command`: **`/harness-review`** (always re-verify; use tiered adversary on attempts 2+).
26
26
 
27
27
  ## Forbidden
28
28