ultimate-pi 0.10.1 → 0.12.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 (135) hide show
  1. package/.agents/skills/harness-debate-plan/SKILL.md +44 -0
  2. package/.agents/skills/harness-decisions/SKILL.md +3 -3
  3. package/.agents/skills/harness-orchestration/SKILL.md +59 -25
  4. package/.agents/skills/harness-plan/SKILL.md +16 -15
  5. package/.pi/agents/harness/adversary.md +0 -1
  6. package/.pi/agents/harness/evaluator.md +0 -1
  7. package/.pi/agents/harness/executor.md +1 -2
  8. package/.pi/agents/harness/incident-recorder.md +0 -1
  9. package/.pi/agents/harness/meta-optimizer.md +0 -1
  10. package/.pi/agents/harness/planning/decompose.md +83 -0
  11. package/.pi/agents/harness/planning/execution-plan-author.md +30 -0
  12. package/.pi/agents/harness/planning/hypothesis-validator.md +23 -0
  13. package/.pi/agents/harness/planning/hypothesis.md +89 -0
  14. package/.pi/agents/harness/planning/plan-adversary.md +18 -0
  15. package/.pi/agents/harness/planning/plan-evaluator.md +18 -0
  16. package/.pi/agents/harness/planning/review-integrator.md +23 -0
  17. package/.pi/agents/harness/planning/scout-graphify.md +54 -0
  18. package/.pi/agents/harness/planning/scout-semantic.md +47 -0
  19. package/.pi/agents/harness/planning/scout-structure.md +50 -0
  20. package/.pi/agents/harness/planning/sprint-contract-auditor.md +18 -0
  21. package/.pi/agents/harness/planning/stack-researcher.md +24 -0
  22. package/.pi/agents/harness/tie-breaker.md +0 -1
  23. package/.pi/agents/harness/trace-librarian.md +0 -1
  24. package/.pi/extensions/debate-orchestrator.ts +90 -53
  25. package/.pi/extensions/harness-ask-user.ts +5 -0
  26. package/.pi/extensions/harness-plan-approval.ts +137 -3
  27. package/.pi/extensions/harness-run-context.ts +146 -6
  28. package/.pi/extensions/harness-subagents.ts +10 -5
  29. package/.pi/extensions/harness-web-tools.ts +2 -0
  30. package/.pi/extensions/lib/extension-load-guard.ts +39 -0
  31. package/.pi/extensions/lib/harness-posthog.ts +6 -1
  32. package/.pi/extensions/lib/harness-spawn-budget.ts +75 -0
  33. package/.pi/extensions/lib/harness-subagent-auth.ts +123 -0
  34. package/.pi/extensions/lib/{harness-subagents/harness-subagent-policy.ts → harness-subagent-policy.ts} +34 -9
  35. package/.pi/extensions/lib/harness-subagent-precheck.ts +95 -0
  36. package/.pi/extensions/lib/harness-subagents-bridge.ts +176 -0
  37. package/.pi/extensions/lib/plan-approval/create-plan.ts +9 -7
  38. package/.pi/extensions/lib/plan-approval/plan-review.ts +393 -0
  39. package/.pi/extensions/lib/plan-approval/schema.ts +16 -1
  40. package/.pi/extensions/lib/plan-approval/types.ts +16 -0
  41. package/.pi/extensions/lib/plan-approval/validate.ts +2 -0
  42. package/.pi/extensions/lib/plan-debate-envelope.ts +84 -0
  43. package/.pi/extensions/lib/{harness-subagents/spawn-policy.ts → spawn-policy.ts} +2 -5
  44. package/.pi/extensions/policy-gate.ts +1 -1
  45. package/.pi/extensions/review-integrity.ts +48 -29
  46. package/.pi/extensions/ultimate-pi-vcc.ts +5 -0
  47. package/.pi/harness/agents.manifest.json +126 -82
  48. package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -6
  49. package/.pi/harness/docs/adrs/0033-parent-orchestrated-planning.md +34 -0
  50. package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +41 -0
  51. package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +27 -0
  52. package/.pi/harness/docs/adrs/README.md +2 -0
  53. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r1.yaml +25 -0
  54. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r4.yaml +26 -0
  55. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/sprint-audit-r4.yaml +5 -0
  56. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/plan-packet.yaml +196 -0
  57. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/plan-review.md +14 -0
  58. package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/research-brief.yaml +32 -0
  59. package/.pi/harness/evals/smoke/run-context.fixture.json +1 -1
  60. package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +88 -0
  61. package/.pi/harness/specs/README.md +1 -1
  62. package/.pi/harness/specs/harness-posthog-event.schema.json +6 -1
  63. package/.pi/harness/specs/harness-spawn-context.schema.json +2 -1
  64. package/.pi/harness/specs/plan-adversary-brief.schema.json +45 -0
  65. package/.pi/harness/specs/plan-decomposition-brief.schema.json +108 -0
  66. package/.pi/harness/specs/plan-execution-plan-brief.schema.json +13 -0
  67. package/.pi/harness/specs/plan-execution-plan.schema.json +255 -0
  68. package/.pi/harness/specs/plan-hypothesis-brief.schema.json +96 -0
  69. package/.pi/harness/specs/plan-hypothesis-eval.schema.json +61 -0
  70. package/.pi/harness/specs/plan-packet.schema.json +14 -5
  71. package/.pi/harness/specs/plan-review-round-draft.schema.json +68 -0
  72. package/.pi/harness/specs/plan-sprint-audit-turn.schema.json +29 -0
  73. package/.pi/harness/specs/plan-stack-brief.schema.json +65 -0
  74. package/.pi/harness/specs/plan-validation-turn.schema.json +42 -0
  75. package/.pi/harness/specs/round-result.schema.json +16 -9
  76. package/.pi/lib/debate-orchestrator-types.ts +38 -0
  77. package/.pi/lib/harness-agent-discovery.mjs +81 -0
  78. package/.pi/lib/harness-run-context.ts +76 -38
  79. package/.pi/lib/harness-yaml.mjs +73 -0
  80. package/.pi/lib/harness-yaml.ts +90 -0
  81. package/.pi/prompts/harness-auto.md +13 -11
  82. package/.pi/prompts/harness-critic.md +2 -2
  83. package/.pi/prompts/harness-eval.md +3 -3
  84. package/.pi/prompts/harness-incident.md +2 -2
  85. package/.pi/prompts/harness-plan.md +106 -37
  86. package/.pi/prompts/harness-review.md +2 -2
  87. package/.pi/prompts/harness-router-tune.md +1 -1
  88. package/.pi/prompts/harness-run.md +2 -2
  89. package/.pi/prompts/harness-setup.md +15 -6
  90. package/.pi/prompts/harness-trace.md +2 -2
  91. package/.pi/scripts/harness-agents-manifest.mjs +1 -1
  92. package/.pi/scripts/harness-resolve-up-pkg.mjs +13 -0
  93. package/.pi/scripts/harness-verify.mjs +28 -19
  94. package/.pi/scripts/validate-plan-dag.mjs +258 -0
  95. package/.pi/scripts/vendor-sync-pi-subagents.sh +19 -0
  96. package/CHANGELOG.md +24 -0
  97. package/THIRD_PARTY_NOTICES.md +8 -0
  98. package/biome.json +4 -1
  99. package/package.json +6 -4
  100. package/.pi/agents/harness/planner.md +0 -54
  101. package/.pi/extensions/lib/harness-subagents/agent-loader.ts +0 -126
  102. package/.pi/extensions/lib/harness-subagents/agent-manifest.ts +0 -119
  103. package/.pi/extensions/lib/harness-subagents/agent-parser.ts +0 -87
  104. package/.pi/extensions/lib/harness-subagents/blackboard-tool.ts +0 -118
  105. package/.pi/extensions/lib/harness-subagents/blackboard.ts +0 -175
  106. package/.pi/extensions/lib/harness-subagents/parent-ask-user-bridge.ts +0 -10
  107. package/.pi/extensions/lib/harness-subagents/parent-harness-ui-bridge.ts +0 -310
  108. package/.pi/extensions/lib/harness-subagents/parent-harness-ui-hooks.ts +0 -59
  109. package/.pi/extensions/lib/harness-subagents/types-blackboard.ts +0 -27
  110. package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +0 -558
  111. package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +0 -684
  112. package/.pi/extensions/lib/harness-subagents/vendored/agent-types.ts +0 -175
  113. package/.pi/extensions/lib/harness-subagents/vendored/context.ts +0 -59
  114. package/.pi/extensions/lib/harness-subagents/vendored/cross-extension-rpc.ts +0 -134
  115. package/.pi/extensions/lib/harness-subagents/vendored/custom-agents.ts +0 -5
  116. package/.pi/extensions/lib/harness-subagents/vendored/default-agents.ts +0 -123
  117. package/.pi/extensions/lib/harness-subagents/vendored/env.ts +0 -43
  118. package/.pi/extensions/lib/harness-subagents/vendored/group-join.ts +0 -144
  119. package/.pi/extensions/lib/harness-subagents/vendored/index.ts +0 -2494
  120. package/.pi/extensions/lib/harness-subagents/vendored/invocation-config.ts +0 -52
  121. package/.pi/extensions/lib/harness-subagents/vendored/memory.ts +0 -182
  122. package/.pi/extensions/lib/harness-subagents/vendored/model-resolver.ts +0 -92
  123. package/.pi/extensions/lib/harness-subagents/vendored/output-file.ts +0 -115
  124. package/.pi/extensions/lib/harness-subagents/vendored/prompts.ts +0 -103
  125. package/.pi/extensions/lib/harness-subagents/vendored/schedule-store.ts +0 -177
  126. package/.pi/extensions/lib/harness-subagents/vendored/schedule.ts +0 -416
  127. package/.pi/extensions/lib/harness-subagents/vendored/settings.ts +0 -210
  128. package/.pi/extensions/lib/harness-subagents/vendored/skill-loader.ts +0 -108
  129. package/.pi/extensions/lib/harness-subagents/vendored/types.ts +0 -187
  130. package/.pi/extensions/lib/harness-subagents/vendored/ui/agent-widget.ts +0 -639
  131. package/.pi/extensions/lib/harness-subagents/vendored/ui/conversation-viewer.ts +0 -324
  132. package/.pi/extensions/lib/harness-subagents/vendored/ui/schedule-menu.ts +0 -110
  133. package/.pi/extensions/lib/harness-subagents/vendored/usage.ts +0 -71
  134. package/.pi/extensions/lib/harness-subagents/vendored/worktree.ts +0 -195
  135. /package/.pi/extensions/{00-ultimate-pi-system-prompt.ts → custom-system-prompt.ts} +0 -0
@@ -1,71 +1,140 @@
1
1
  ---
2
- description: Build a strict read-only PlanPacket before any mutating work.
2
+ description: PM-grade harness plan scouts, ExecutionPlan, DAG validation, Review Gate debate, approval.
3
3
  argument-hint: "\"<task>\" [--risk low|med|high] [--budget <amount>] [--quick]"
4
4
  ---
5
5
 
6
6
  # harness-plan
7
7
 
8
- Orchestrator only spawn `harness/planner` once. The planner runs clarification (`ask_user`), approval (`approve_plan`), and persists the plan (`create_plan`). Do **not** write `plan-packet.json` in this parent session.
8
+ You are the **planning PM** for this harness run. Produce an execution baseline (`plan-packet.yaml` + `plan-review.md`), not strategy theater. Parent owns `ask_user`, `approve_plan`, `create_plan`, debate bus commands, and YAML writes under `.pi/harness/runs/<run_id>/`.
9
9
 
10
- ## Step 0Parse arguments
10
+ Never `write`/`edit` the final canonical packet except via **`write_harness_yaml`** for run artifacts and **`create_plan`** after approval. Do not paste JSON into `.yaml` files subagents emit JSON; you convert via `write_harness_yaml`.
11
11
 
12
- Read `$ARGUMENTS`:
12
+ ## Allowed subagents
13
13
 
14
- - task statement (required)
15
- - optional: `--risk low|med|high`, `--budget <amount>`, `--quick`
14
+ - `harness/planning/scout-graphify`
15
+ - `harness/planning/scout-structure`
16
+ - `harness/planning/scout-semantic` (skip when `--quick`)
17
+ - `harness/planning/decompose`
18
+ - `harness/planning/hypothesis`
19
+ - `harness/planning/stack-researcher`
20
+ - `harness/planning/execution-plan-author`
21
+ - `harness/planning/hypothesis-validator` (debate R1 only)
22
+ - `harness/planning/plan-evaluator`
23
+ - `harness/planning/plan-adversary`
24
+ - `harness/planning/sprint-contract-auditor`
25
+ - `harness/planning/review-integrator`
16
26
 
17
- If task is missing:
27
+ Read **harness-debate-plan** skill before Review Gate rounds.
18
28
 
19
- `Usage: /harness-plan "<task>" [--risk low|med|high] [--budget <amount>] [--quick]`
29
+ ## Performance rules
20
30
 
21
- `--quick` narrows planning breadth only it does **not** skip user approval.
31
+ 1. Use `subagent` with `agentScope: "both"` and parallel `tasks` where lanes are independent.
32
+ 2. Each `subagent` call blocks until subprocesses finish — batch parallel scouts in one `tasks` array.
33
+ 3. Cap: **12** harness subagent invocations per parent session (extension-enforced).
34
+ 4. Compact task text: embed `HarnessSpawnContext` JSON + lane-specific instructions only.
35
+
36
+ ## Step 0 — Parse `$ARGUMENTS`
37
+
38
+ - task (required)
39
+ - `--risk low|med|high`, `--budget`, `--quick`
40
+
41
+ `--quick` skips **scout-semantic** and post-run adversary only — **never** skip graphify, structure, decompose, hypothesis, stack research, execution plan, DAG validation, or **4-round plan debate**.
22
42
 
23
43
  ## Active plan context
24
44
 
25
- Use injected context only **do not** read `.pi/harness/specs/*.schema.json` or explore specs with bash.
45
+ Use `[HarnessActivePlan]` / `[HarnessRunContext]` only. On revise: preserve `plan_id` / `task_id`. Canonical paths: `plan-packet.yaml`, `research-brief.yaml`, `artifacts/*.yaml`.
46
+
47
+ ## Phase 1 — Parallel scouts
48
+
49
+ ```json
50
+ {
51
+ "agentScope": "both",
52
+ "tasks": [
53
+ { "agent": "harness/planning/scout-graphify", "task": "<HarnessSpawnContext + graphify lane>", "timeoutMs": 90000 },
54
+ { "agent": "harness/planning/scout-structure", "task": "<HarnessSpawnContext + structure lane>", "timeoutMs": 90000 }
55
+ ]
56
+ }
57
+ ```
58
+
59
+ Add `harness/planning/scout-semantic` to `tasks` unless `--quick`. Require graphify + structure success.
60
+
61
+ ## Phase 2 & 3 — Decompose + hypothesis (parallel)
62
+
63
+ One `subagent` call with `tasks` for `harness/planning/decompose` and `harness/planning/hypothesis`. Parse `PlanDecompositionBrief` and `PlanHypothesisBrief` from outputs. Persist with `write_harness_yaml` → `artifacts/decomposition.yaml` and `artifacts/hypothesis.yaml`.
64
+
65
+ ## Phase 4 — Draft shell + fork
66
+
67
+ Build draft `PlanPacket` (`contract_version: "1.1.0"`):
68
+
69
+ - `scope`, `assumptions`, `acceptance_checks`, `risk_level`, `rollback_plan`
70
+ - `execution_plan` placeholder until Phase 4b
71
+
72
+ `ask_user` when `dialectical_fork` is material.
26
73
 
27
- If `[HarnessActivePlan]` is present:
74
+ Initialize `research-brief.yaml` with decomposition + hypothesis (`write_harness_yaml`).
28
75
 
29
- - Treat task as **revise/amend** unless `/harness-new-run` was used.
30
- - Pass `mode: revise` using the `HarnessSpawnContext` JSON in `[HarnessRunContext]`.
76
+ ## Phase 4a Stack research
31
77
 
32
- Otherwise use `HarnessSpawnContext` from `[HarnessRunContext]` for greenfield `mode: create`.
78
+ ```
79
+ subagent({ agentScope: "both", agent: "harness/planning/stack-researcher", task: "<HarnessSpawnContext + stack research brief>" })
80
+ ```
33
81
 
34
- ## Orchestration (required)
82
+ `write_harness_yaml` `artifacts/stack.yaml`; merge into `research-brief.yaml` → `stack`.
35
83
 
36
- 1. Copy the `HarnessSpawnContext=…` JSON from `[HarnessRunContext]` into the spawn prompt (adjust `risk_level`, `quick`, `mode` from `$ARGUMENTS` if needed). Do **not** add “call ask_user for approval” in the `Agent` prompt the planner agent instructions already define `approve_plan` / `create_plan`.
37
- 2. Spawn **once** with **`inherit_context: false`**:
84
+ ## Phase 4bExecution plan author
38
85
 
39
86
  ```
40
- Agent({ subagent_type: "harness/planner", prompt: "<task + HarnessSpawnContext JSON + output schema>" })
87
+ subagent({ agentScope: "both", agent: "harness/planning/execution-plan-author", task: "<HarnessSpawnContext + execution plan brief>" })
41
88
  ```
42
89
 
43
- 3. `get_subagent_result` parse final JSON (`status`, `plan_packet`, `human_summary`, `clarification`) via fenced `json` block. Treat `plan_packet` in that JSON as **read-only summary context** — not input for another approval tool call.
44
- 4. If `status === "ready"` and `[HarnessRunContext]` shows `plan_ready: true` (planner called `create_plan`), confirm `plan_packet_path` exists — do **not** write the file yourself.
45
- 5. If `needs_clarification`, tell the user the planner is waiting do **not** re-spawn; user should answer in the subagent or re-run `/harness-plan`.
46
- 6. Do **not** call `ask_user`, `approve_plan`, or `create_plan` in this parent session.
90
+ Merge `execution_plan` into draft `plan-packet.yaml` (`write_harness_yaml`). Save `artifacts/execution-plan-draft.yaml` the same way.
91
+
92
+ ## Phase 4cDAG validation (hard gate)
93
+
94
+ ```bash
95
+ node .pi/scripts/validate-plan-dag.mjs --packet .pi/harness/runs/<run_id>/plan-packet.yaml --write
96
+ ```
97
+
98
+ Must **pass** before debate. On fail: fix via author or parent patches, re-run.
99
+
100
+ ## Phase 5 — Review Gate debate (4 rounds, even with `--quick`)
101
+
102
+ 1. `/harness-debate-open plan-<run_id>`
103
+ 2. For rounds 1–4 (`debate_round_focus`: spec, wbs, schedule, quality):
104
+
105
+ | Round | Extra spawns (before integrator) |
106
+ |-------|----------------------------------|
107
+ | 1 | `hypothesis-validator` (blind: task + hypothesis only) → `plan-evaluator` → `plan-adversary` |
108
+ | 2 | `plan-evaluator` → `plan-adversary` (optional `sprint-contract-auditor` if done_criteria thin) |
109
+ | 3 | `plan-evaluator` → `plan-adversary` |
110
+ | 4 | `plan-evaluator` → `plan-adversary` → **`sprint-contract-auditor` (required)** |
111
+
112
+ Then `review-integrator` → `write_harness_yaml` → `artifacts/review-round-r{N}.yaml` → build bus envelope → `/harness-debate-round '<json>'`.
113
+
114
+ 3. `/harness-debate-consensus` after round 4.
115
+
116
+ **R1 blind rule:** hypothesis-validator prompt must exclude decomposition, scouts, PlanPacket, prior debate.
117
+
118
+ If R1 `revision_recommended` or `relevance.passes === false`: one `hypothesis` re-spawn, update brief, continue.
47
119
 
48
- ## After subagent returns (no second approval)
120
+ **Blockers:** `policy_decision: block` do not `approve_plan`. `human_required` → `ask_user` before approval.
49
121
 
50
- User approval happens **once**, inside the planner subagent: `approve_plan` uses the parent TUI bridge. You are the orchestrator, **not** an approver.
122
+ ## Phase 5b Revise packet
51
123
 
52
- After `get_subagent_result`:
124
+ Apply `recommended_packet_patches` from last integrator round. Re-run `validate-plan-dag.mjs`. If >30% work items changed, one partial re-round on affected focus.
53
125
 
54
- - If `[HarnessRunContext]` shows `plan_ready: true`, or the transcript already has `harness-plan-approval` / bridged `approve_plan` with **Approve** → planning is complete. **Stop.** Summarize the plan and set `next_command: /harness-run`.
55
- - Do **not** call `approve_plan` to “confirm” using `plan_packet` from subagent JSON.
56
- - Do **not** call `ask_user` with Approve / Request changes / Cancel for the same plan.
57
- - Do **not** re-spawn the planner to “get approval again”.
126
+ Set `research_brief.eval` from R1 `hypothesis-validator` output.
58
127
 
59
- If `status === "ready"` but `plan_ready` is false → planner approved but `create_plan` may have failed; tell the user to run `/harness-plan-commit` **not** a second `approve_plan`.
128
+ ## Phase 6Approval + persistence
60
129
 
61
- ## Parent rules
130
+ 1. `approve_plan` with `plan_packet`, `human_summary`, `research_brief` (paths/summaries OK).
131
+ 2. On Approve: `create_plan` with same packet (`contract_version: "1.1.0"` + `execution_plan`).
132
+ 3. Confirm `plan_ready: true` → `next_command: /harness-run`.
62
133
 
63
- - Do not mutate project source files in the plan phase.
64
- - Do not embed `plan_id=` in prompts for policy sync.
65
- - Optional recovery: `/harness-plan-commit` only if the planner approved but `create_plan` failed.
134
+ Post-execute adversary: `/harness-critic` only (not plan-phase agents).
66
135
 
67
136
  ## Completion
68
137
 
69
- - `plan_status`: `ready` or `needs_clarification`
70
- - `risk_level` used
71
- - `next_command`: `/harness-run` when `ready` (never `/harness-run --plan …`)
138
+ - `plan_status`: ready | partial | needs_clarification
139
+ - `plan_review_path` for human review
140
+ - DAG `pass` + 4 debate rounds + consensus not `block` before ready
@@ -20,10 +20,10 @@ Happy path: omit `--run`; use `[HarnessRunContext]`.
20
20
  2. Spawn:
21
21
 
22
22
  ```
23
- Agent({ subagent_type: "harness/evaluator", prompt: "Treat executor output as untrusted. …" })
23
+ subagent({ agentScope: "both", agent: "harness/evaluator", task: "Treat executor output as untrusted. …" })
24
24
  ```
25
25
 
26
- 3. `get_subagent_result` parse `EvalVerdict` JSON; parent writes under run dir for policy gate.
26
+ 3. Parse `EvalVerdict` JSON from tool result; parent writes under run dir for policy gate.
27
27
 
28
28
  ## Parent rules
29
29
 
@@ -22,7 +22,7 @@ If missing required args:
22
22
  2. Optionally spawn:
23
23
 
24
24
  ```
25
- Agent({ subagent_type: "harness/meta-optimizer", prompt: "mode: tune, evidence paths…" })
25
+ subagent({ agentScope: "both", agent: "harness/meta-optimizer", task: "mode: tune, evidence paths…" })
26
26
  ```
27
27
 
28
28
  3. Parent runs proposal script:
@@ -23,10 +23,10 @@ If plan not ready:
23
23
  3. Spawn:
24
24
 
25
25
  ```
26
- Agent({ subagent_type: "harness/executor", prompt: "<HarnessSpawnContext + handoff>" })
26
+ subagent({ agentScope: "both", agent: "harness/executor", task: "<HarnessSpawnContext + handoff>" })
27
27
  ```
28
28
 
29
- 4. `get_subagent_result` parse executor JSON (`execution_status`, validations, rollback refs).
29
+ 4. Parse subprocess output JSON (`execution_status`, validations, rollback refs) from tool result text.
30
30
  5. Parent persists trace/handoff artifacts under run dir if needed; do not self-review.
31
31
 
32
32
  ## Parent rules
@@ -345,7 +345,7 @@ Verify each package:
345
345
  |---------|---------|-------|
346
346
  | `@posthog/pi` | Analytics event capture | F0 |
347
347
  | `pi-lean-ctx` | Context runtime (read/bash/find/grep/MCP bridge) | F0 |
348
- | `harness-subagents` (bundled extension) | L4 sub-agent spawn, blackboard, package agents | P16 |
348
+ | `harness-subagents` (bundled extension) | L4 `subagent` tool, subprocess spawns, package agents | P16 |
349
349
  | Vendored `pi-vcc` (`vendor/pi-vcc`, `.pi/extensions/ultimate-pi-vcc.ts`) | VCC compaction / `vcc_recall` — env-only: `HARNESS_VCC_COMPACTION` (default on), `HARNESS_VCC_DEBUG` | Shipped |
350
350
  | `pi-model-router` | Vendored (`vendor/`); activates after `.pi/model-router.json` exists | F0 |
351
351
 
@@ -383,11 +383,11 @@ Manual override: **`/router profile auto`** anytime after reload if they changed
383
383
 
384
384
  ## Step 3.6 — Harness agents (package-resolved)
385
385
 
386
- `harness-subagents` loads agents from the installed **`ultimate-pi`** package (`$UP_PKG/.pi/agents/**`) with namespaced ids (`harness/planner`, `pi-pi/agent-expert`). **Do not copy** agents into the project unless you want a deliberate override.
386
+ `harness-subagents` loads agents from the installed **`ultimate-pi`** package (`$UP_PKG/.pi/agents/**`) with namespaced ids (`harness/executor`, `harness/planning/scout-graphify`, `pi-pi/agent-expert`). **Do not copy** agents into the project unless you want a deliberate override.
387
387
 
388
388
  **Slash commands are orchestrators:** `/harness-plan`, `/harness-run`, etc. spawn `harness/*` agents via the `Agent` tool — bootstrap stays **script-first**; only optionally spawn `harness/sentrux-bootstrap` for Sentrux (see Step 4.2).
389
389
 
390
- Optional per-repo overrides: place `.md` files at the **same relative path** (e.g. `.pi/agents/harness/planner.md` overrides the package planner).
390
+ Optional per-repo overrides: place `.md` files at the **same relative path** (e.g. `.pi/agents/harness/planning/scout-graphify.md` overrides the package scout).
391
391
 
392
392
  Verify manifest drift after `pi update ultimate-pi`:
393
393
 
@@ -478,16 +478,25 @@ Template keys (placeholders — user fills secrets): `HARNESS_TELEMETRY_ENABLED`
478
478
 
479
479
  ### 4.1 — .gitignore Entries
480
480
 
481
- Ensure `.gitignore` contains:
481
+ Ensure `.gitignore` contains harness runtime entries (see repo root `.gitignore` — **do not** ignore `.pi/harness/specs/`; JSON schemas are shared contracts):
482
+
482
483
  ```
483
484
  .env
484
485
  .web/
485
486
  .searxng/
486
487
  .raw/
487
488
  .vault-meta/
488
- .pi/harness/critics/
489
+ .pi/harness/active-run.json
490
+ .pi/harness/release-readiness-report.md
489
491
  .pi/harness/plans/
490
- .pi/harness/specs/
492
+ .pi/harness/critics/
493
+ .pi/harness/runs/**
494
+ !.pi/harness/runs/README.md
495
+ .pi/harness/incidents/*
496
+ !.pi/harness/incidents/README.md
497
+ .pi/harness/debates/*
498
+ !.pi/harness/debates/README.md
499
+ .pi/harness/router/proposals/*
491
500
 
492
501
  # Model router config (user-specific — generated from env)
493
502
  .pi/model-router.json
@@ -20,10 +20,10 @@ Happy path: omit `--run`.
20
20
  2. Spawn:
21
21
 
22
22
  ```
23
- Agent({ subagent_type: "harness/trace-librarian", prompt: "…" })
23
+ subagent({ agentScope: "both", agent: "harness/trace-librarian", task: "…" })
24
24
  ```
25
25
 
26
- 3. `get_subagent_result` — present timeline and artifact index to user.
26
+ 3. Present timeline and artifact index from tool result to user.
27
27
 
28
28
  ## Completion
29
29
 
@@ -14,7 +14,7 @@ import {
14
14
  isSafeAgentId,
15
15
  sha256Content,
16
16
  walkAgentsDir,
17
- } from "../../test/harness-subagents-loader.core.mjs";
17
+ } from "../lib/harness-agent-discovery.mjs";
18
18
 
19
19
  const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
20
20
  const MANIFEST_PATH = join(ROOT, ".pi", "harness", "agents.manifest.json");
@@ -30,7 +30,20 @@ function hasHarnessScripts(root) {
30
30
  return existsSync(join(root, ".pi", "scripts", "harness-cli-verify.sh"));
31
31
  }
32
32
 
33
+ function isSourceCheckout(root) {
34
+ try {
35
+ const pkg = requireFromCwd.resolve("./package.json");
36
+ return dirname(pkg) === root;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
33
42
  function tryResolveUltimatePi() {
43
+ if (hasHarnessScripts(process.cwd()) && isSourceCheckout(process.cwd())) {
44
+ return process.cwd();
45
+ }
46
+
34
47
  if (process.env.ULTIMATE_PI_PKG) {
35
48
  const envRoot = process.env.ULTIMATE_PI_PKG;
36
49
  if (hasHarnessScripts(envRoot)) return envRoot;
@@ -202,32 +202,41 @@ async function main() {
202
202
  if (!(await fileExists(runCtxLib))) fail("missing lib/harness-run-context.ts");
203
203
  ok("lib/harness-run-context.ts");
204
204
 
205
- const vendoredIndex = join(
205
+ const subagentsVendor = join(
206
+ ROOT,
207
+ "vendor",
208
+ "pi-subagents",
209
+ "src",
210
+ "subagents.ts",
211
+ );
212
+ if (!(await fileExists(subagentsVendor))) {
213
+ fail("missing vendor/pi-subagents/src/subagents.ts");
214
+ }
215
+ const bridgePath = join(
206
216
  ROOT,
207
217
  ".pi",
208
218
  "extensions",
209
219
  "lib",
210
- "harness-subagents",
211
- "vendored",
212
- "index.ts",
220
+ "harness-subagents-bridge.ts",
213
221
  );
214
- const vendoredSrc = await readFile(vendoredIndex, "utf-8");
215
- const runCtxImport = vendoredSrc.match(
216
- /from ["']([^"']*harness-run-context\.js)["']/,
217
- );
218
- if (!runCtxImport) {
219
- fail("vendored/index.ts must import harness-run-context.js");
222
+ if (!(await fileExists(bridgePath))) {
223
+ fail("missing harness-subagents-bridge.ts");
220
224
  }
221
- const runCtxImportPath = resolve(
222
- dirname(vendoredIndex),
223
- runCtxImport[1].replace(/\.js$/, ".ts"),
224
- );
225
- if (runCtxImportPath !== runCtxLib) {
226
- fail(
227
- `vendored/index.ts harness-run-context import resolves to ${runCtxImportPath}, expected ${runCtxLib}`,
228
- );
225
+ const bridgeSrc = await readFile(bridgePath, "utf-8");
226
+ if (!bridgeSrc.includes("precheckHarnessSubagentSpawn")) {
227
+ fail("harness-subagents-bridge must run precheckHarnessSubagentSpawn");
228
+ }
229
+ if (!bridgeSrc.includes("packageRoot")) {
230
+ fail("harness-subagents-bridge must pass packageRoot for agent discovery");
231
+ }
232
+ const subagentsSrc = await readFile(subagentsVendor, "utf-8");
233
+ if (!subagentsSrc.includes("discoverAgents")) {
234
+ fail("vendor subagents.ts must implement discoverAgents");
235
+ }
236
+ if (!subagentsSrc.includes("packageRoot")) {
237
+ fail("vendor subagents.ts must pass packageRoot into discovery");
229
238
  }
230
- ok("vendored/index.ts harness-run-context import path");
239
+ ok("vendor pi-subagents + harness bridge");
231
240
 
232
241
  const policyGateSrc = await readFile(
233
242
  join(ROOT, ".pi", "extensions", "policy-gate.ts"),
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * validate-plan-dag — deterministic ExecutionPlan DAG checks (YAML packet in).
4
+ */
5
+
6
+ import { access } from "node:fs/promises";
7
+ import { constants } from "node:fs";
8
+ import { dirname, join, resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import { readYamlFile, writeYamlFile } from "../lib/harness-yaml.mjs";
11
+
12
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
13
+
14
+ const MINIMUMS = {
15
+ low: { phases: 2, work_items: 5, acceptance_checks: 3, risks: 0 },
16
+ med: { phases: 3, work_items: 8, acceptance_checks: 5, risks: 3 },
17
+ high: { phases: 4, work_items: 12, acceptance_checks: 8, risks: 3 },
18
+ };
19
+
20
+ function fail(msg) {
21
+ console.error(`validate-plan-dag: FAIL: ${msg}`);
22
+ process.exit(1);
23
+ }
24
+
25
+ function ok(msg) {
26
+ console.log(` ✓ ${msg}`);
27
+ }
28
+
29
+ function topoSort(workItems) {
30
+ const ids = new Set(workItems.map((w) => w.work_item_id));
31
+ const adj = new Map();
32
+ for (const w of workItems) {
33
+ adj.set(w.work_item_id, (w.depends_on ?? []).filter((d) => ids.has(d)));
34
+ }
35
+ const visited = new Set();
36
+ const stack = new Set();
37
+ const order = [];
38
+ const cycles = [];
39
+
40
+ function dfs(n, path) {
41
+ if (stack.has(n)) {
42
+ cycles.push([...path, n]);
43
+ return;
44
+ }
45
+ if (visited.has(n)) return;
46
+ visited.add(n);
47
+ stack.add(n);
48
+ for (const d of adj.get(n) ?? []) dfs(d, [...path, n]);
49
+ stack.delete(n);
50
+ order.push(n);
51
+ }
52
+
53
+ for (const id of ids) dfs(id, []);
54
+ order.reverse();
55
+ return { order, cycles };
56
+ }
57
+
58
+ function computeCriticalPath(workItems) {
59
+ const ids = new Set(workItems.map((w) => w.work_item_id));
60
+ const len = new Map();
61
+ for (const w of workItems) len.set(w.work_item_id, 0);
62
+ let changed = true;
63
+ while (changed) {
64
+ changed = false;
65
+ for (const w of workItems) {
66
+ const deps = (w.depends_on ?? []).filter((d) => ids.has(d));
67
+ const base = deps.length === 0 ? 0 : Math.max(...deps.map((d) => len.get(d) ?? 0)) + 1;
68
+ if (base > (len.get(w.work_item_id) ?? 0)) {
69
+ len.set(w.work_item_id, base);
70
+ changed = true;
71
+ }
72
+ }
73
+ }
74
+ const maxLen = Math.max(0, ...len.values());
75
+ const end = workItems.filter((w) => len.get(w.work_item_id) === maxLen).map((w) => w.work_item_id);
76
+ // Backtrack one longest path
77
+ const path = [];
78
+ let cur = end[0];
79
+ if (!cur) return [];
80
+ const byId = new Map(workItems.map((w) => [w.work_item_id, w]));
81
+ while (cur) {
82
+ path.unshift(cur);
83
+ const w = byId.get(cur);
84
+ const deps = (w?.depends_on ?? []).filter((d) => ids.has(d));
85
+ if (deps.length === 0) break;
86
+ cur = deps.reduce((a, b) => ((len.get(a) ?? 0) >= (len.get(b) ?? 0) ? a : b));
87
+ }
88
+ return path;
89
+ }
90
+
91
+ export function validateExecutionPlan(packet, projectRoot = ROOT) {
92
+ const errors = [];
93
+ const ep = packet.execution_plan;
94
+ if (!ep) {
95
+ errors.push("execution_plan required");
96
+ return { status: "fail", errors, report: null };
97
+ }
98
+
99
+ const risk = packet.risk_level ?? "med";
100
+ const min = MINIMUMS[risk] ?? MINIMUMS.med;
101
+ const phases = ep.phases ?? [];
102
+ const workItems = ep.work_items ?? [];
103
+ const conflicts = [];
104
+
105
+ if (phases.length < min.phases) {
106
+ errors.push(`need >= ${min.phases} phases for risk ${risk}`);
107
+ }
108
+ if (workItems.length < min.work_items) {
109
+ errors.push(`need >= ${min.work_items} work_items for risk ${risk}`);
110
+ }
111
+ const ac = packet.acceptance_checks ?? [];
112
+ if (ac.length < min.acceptance_checks) {
113
+ errors.push(`need >= ${min.acceptance_checks} acceptance_checks`);
114
+ }
115
+ if ((ep.risk_register ?? []).length < min.risks) {
116
+ errors.push(`need >= ${min.risks} risks for risk ${risk}`);
117
+ }
118
+
119
+ const phaseIds = new Set(phases.map((p) => p.phase_id));
120
+ const phaseIndex = new Map(phases.map((p, i) => [p.phase_id, i]));
121
+ const wiIds = new Set(workItems.map((w) => w.work_item_id));
122
+
123
+ for (const p of phases) {
124
+ if (!p.exit_criteria?.length) errors.push(`phase ${p.phase_id} missing exit_criteria`);
125
+ if (!p.work_item_ids?.length) errors.push(`phase ${p.phase_id} has no work items`);
126
+ }
127
+
128
+ const wiInPhase = new Set();
129
+ for (const w of workItems) {
130
+ if (!phaseIds.has(w.phase_id)) {
131
+ errors.push(`work_item ${w.work_item_id} unknown phase_id`);
132
+ }
133
+ wiInPhase.add(w.work_item_id);
134
+ for (const d of w.depends_on ?? []) {
135
+ if (!wiIds.has(d)) errors.push(`work_item ${w.work_item_id} depends_on missing ${d}`);
136
+ }
137
+ if (!w.non_code && (!w.files || w.files.length === 0)) {
138
+ errors.push(`work_item ${w.work_item_id} needs files[] or non_code: true`);
139
+ }
140
+ }
141
+
142
+ for (const p of phases) {
143
+ for (const wid of p.work_item_ids ?? []) {
144
+ if (!wiIds.has(wid)) errors.push(`phase ${p.phase_id} references missing ${wid}`);
145
+ }
146
+ }
147
+
148
+ const { order, cycles } = topoSort(workItems);
149
+ if (cycles.length) errors.push(`cycle detected: ${JSON.stringify(cycles[0])}`);
150
+
151
+ // File conflicts
152
+ for (let i = 0; i < workItems.length; i++) {
153
+ for (let j = i + 1; j < workItems.length; j++) {
154
+ const a = workItems[i];
155
+ const b = workItems[j];
156
+ const filesA = new Set(a.files ?? []);
157
+ const overlap = (b.files ?? []).filter((f) => filesA.has(f));
158
+ if (overlap.length === 0) continue;
159
+ const reachable = (from, to, seen = new Set()) => {
160
+ if (from === to) return true;
161
+ if (seen.has(from)) return false;
162
+ seen.add(from);
163
+ const w = workItems.find((x) => x.work_item_id === from);
164
+ for (const d of w?.depends_on ?? []) {
165
+ if (reachable(d, to, seen)) return true;
166
+ }
167
+ return false;
168
+ };
169
+ if (!reachable(a.work_item_id, b.work_item_id) && !reachable(b.work_item_id, a.work_item_id)) {
170
+ if ((phaseIndex.get(a.phase_id) ?? 0) === (phaseIndex.get(b.phase_id) ?? 0)) {
171
+ conflicts.push(
172
+ `file overlap ${overlap.join(",")} between ${a.work_item_id} and ${b.work_item_id} without dependency`,
173
+ );
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ const computedCp = computeCriticalPath(workItems);
180
+ const authorCp = ep.schedule_metadata?.critical_path_work_item_ids ?? [];
181
+ if (computedCp.length >= 3 && authorCp.length) {
182
+ const same =
183
+ authorCp.length === computedCp.length &&
184
+ authorCp.every((id, i) => id === computedCp[i]);
185
+ if (!same) {
186
+ errors.push(
187
+ `critical_path mismatch author=${authorCp.join("→")} computed=${computedCp.join("→")}`,
188
+ );
189
+ }
190
+ }
191
+
192
+ const acIds = new Set(
193
+ ac.map((c) => (typeof c === "string" ? c : c.id)).filter(Boolean),
194
+ );
195
+ for (const w of workItems) {
196
+ for (const acid of w.acceptance_check_ids ?? []) {
197
+ if (!acIds.has(acid)) errors.push(`${w.work_item_id} references orphan ${acid}`);
198
+ }
199
+ }
200
+ for (const acid of acIds) {
201
+ const used = workItems.some((w) => (w.acceptance_check_ids ?? []).includes(acid));
202
+ if (!used) errors.push(`orphan acceptance check ${acid}`);
203
+ }
204
+
205
+ const status = errors.length === 0 && conflicts.length === 0 ? "pass" : "fail";
206
+ const report = {
207
+ status,
208
+ topological_order: order,
209
+ cycles,
210
+ conflicts: [...conflicts, ...errors],
211
+ };
212
+ return { status, errors: [...errors, ...conflicts], report };
213
+ }
214
+
215
+ async function main() {
216
+ const args = process.argv.slice(2);
217
+ let packetPath = null;
218
+ let writeBack = false;
219
+ for (let i = 0; i < args.length; i++) {
220
+ if (args[i] === "--packet" && args[i + 1]) packetPath = args[++i];
221
+ else if (args[i] === "--write") writeBack = true;
222
+ }
223
+
224
+ if (!packetPath) {
225
+ console.error("Usage: validate-plan-dag.mjs --packet <plan-packet.yaml> [--write]");
226
+ process.exit(2);
227
+ }
228
+
229
+ const abs = resolve(packetPath);
230
+ try {
231
+ await access(abs, constants.R_OK);
232
+ } catch {
233
+ fail(`cannot read ${abs}`);
234
+ }
235
+
236
+ const packet = await readYamlFile(abs);
237
+ const { status, errors, report } = validateExecutionPlan(packet, dirname(abs));
238
+
239
+ if (writeBack && report && packet.execution_plan) {
240
+ packet.execution_plan.dag_validation = {
241
+ status: report.status,
242
+ topological_order: report.topological_order,
243
+ cycles: report.cycles,
244
+ conflicts: report.conflicts,
245
+ };
246
+ await writeYamlFile(abs, packet);
247
+ }
248
+
249
+ if (status !== "pass") {
250
+ for (const e of errors) console.error(` - ${e}`);
251
+ fail("validation failed");
252
+ }
253
+ ok(`DAG validation pass (${report.topological_order.length} work items)`);
254
+ }
255
+
256
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
257
+ main();
258
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Re-fetch upstream pi-subagents from narumiruna/pi-extensions.
3
+ set -euo pipefail
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
+ VEND="$ROOT/vendor/pi-subagents"
6
+ BASE="https://raw.githubusercontent.com/narumiruna/pi-extensions/main/extensions/pi-subagents"
7
+
8
+ mkdir -p "$VEND/src"
9
+ curl -fsSL "$BASE/LICENSE" -o "$VEND/LICENSE"
10
+ curl -fsSL "$BASE/src/subagents.ts" -o "$VEND/src/subagents.upstream.ts"
11
+
12
+ # Preserve ultimate-pi harness extensions (agents.ts, harness patches applied to subagents.ts manually or via merge).
13
+ if [[ ! -f "$VEND/src/agents.ts" ]]; then
14
+ curl -fsSL "$BASE/src/agents.ts" -o "$VEND/src/agents.ts"
15
+ fi
16
+
17
+ sed -i 's/from "typebox"/from "@sinclair\/typebox"/g' "$VEND/src/subagents.upstream.ts" 2>/dev/null || true
18
+
19
+ echo "Fetched upstream into $VEND/src/subagents.upstream.ts — merge harness changes into subagents.ts before commit."