ultimate-pi 0.18.0 → 0.19.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 (314) hide show
  1. package/.agents/skills/harness-debate-plan/SKILL.md +1 -1
  2. package/.agents/skills/harness-decisions/SKILL.md +2 -3
  3. package/.agents/skills/harness-governor/SKILL.md +6 -5
  4. package/.agents/skills/harness-orchestration/SKILL.md +4 -4
  5. package/.agents/skills/harness-review/SKILL.md +7 -7
  6. package/.agents/skills/harness-sentrux-setup/SKILL.md +4 -3
  7. package/.agents/skills/harness-steer/SKILL.md +1 -1
  8. package/.agents/skills/sentrux/SKILL.md +9 -9
  9. package/.pi/PACKAGING.md +4 -4
  10. package/.pi/SYSTEM.md +54 -120
  11. package/.pi/agents/harness/incident-recorder.md +0 -1
  12. package/.pi/agents/harness/planning/decompose.md +1 -3
  13. package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
  14. package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
  15. package/.pi/agents/harness/planning/hypothesis.md +0 -2
  16. package/.pi/agents/harness/planning/implementation-researcher.md +0 -2
  17. package/.pi/agents/harness/planning/plan-adversary.md +0 -2
  18. package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
  19. package/.pi/agents/harness/planning/planning-context.md +0 -2
  20. package/.pi/agents/harness/planning/review-integrator.md +0 -2
  21. package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
  22. package/.pi/agents/harness/planning/stack-researcher.md +0 -2
  23. package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +0 -2
  24. package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +0 -2
  25. package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -2
  26. package/.pi/agents/harness/{executor.md → running/executor.md} +0 -2
  27. package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
  28. package/.pi/agents/harness/sentrux-steward.md +0 -2
  29. package/.pi/agents/harness/trace-librarian.md +0 -1
  30. package/.pi/extensions/00-harness-project-control.ts +133 -0
  31. package/.pi/extensions/00-posthog-network-bootstrap.ts +1 -1
  32. package/.pi/extensions/agt-kill-switch.ts +57 -0
  33. package/.pi/extensions/agt-prompt-guard.ts +32 -0
  34. package/.pi/extensions/budget-guard.ts +2 -0
  35. package/.pi/extensions/custom-footer.ts +46 -145
  36. package/.pi/extensions/custom-header.ts +1 -1
  37. package/.pi/extensions/custom-system-prompt.ts +1 -1
  38. package/.pi/extensions/debate-orchestrator.ts +7 -5
  39. package/.pi/extensions/harness-ask-user.ts +8 -8
  40. package/.pi/extensions/harness-debate-tools.ts +27 -43
  41. package/.pi/extensions/harness-lens.ts +94 -0
  42. package/.pi/extensions/harness-live-widget.ts +33 -2
  43. package/.pi/extensions/harness-plan-approval.ts +12 -12
  44. package/.pi/extensions/harness-run-context.ts +1214 -852
  45. package/.pi/extensions/harness-subagent-governance.ts +8 -0
  46. package/.pi/extensions/harness-subagent-submit.ts +36 -164
  47. package/.pi/extensions/harness-subagents.ts +4 -4
  48. package/.pi/extensions/harness-telemetry.ts +3 -1
  49. package/.pi/extensions/harness-web-tools.ts +3 -3
  50. package/.pi/extensions/observation-bus.ts +2 -0
  51. package/.pi/extensions/policy-gate.ts +27 -5
  52. package/.pi/extensions/review-integrity.ts +91 -10
  53. package/.pi/extensions/sentrux-rules-sync.ts +3 -1
  54. package/.pi/extensions/subagent-governance.ts +92 -0
  55. package/.pi/extensions/test-diff-integrity.ts +1 -0
  56. package/.pi/extensions/trace-recorder.ts +3 -1
  57. package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
  58. package/.pi/harness/README.md +6 -2
  59. package/.pi/harness/agents.manifest.json +38 -49
  60. package/.pi/harness/agents.policy.yaml +275 -0
  61. package/.pi/harness/corpus/graphify-kb-updater.config.json +55 -0
  62. package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +2 -1
  63. package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
  64. package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
  65. package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +3 -2
  66. package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
  67. package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
  68. package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
  69. package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
  70. package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
  71. package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
  72. package/.pi/harness/docs/adrs/README.md +6 -0
  73. package/.pi/harness/docs/graphify-kb-updater-runbook.md +11 -5
  74. package/.pi/harness/docs/practice-map.md +2 -2
  75. package/.pi/harness/evolution/README.md +1 -2
  76. package/.pi/harness/examples/agents.policy.project.yaml +19 -0
  77. package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
  78. package/.pi/harness/policies/bash-denylists.yaml +5 -0
  79. package/.pi/harness/policies/defaults.yaml +51 -0
  80. package/.pi/harness/policies/orchestrator.yaml +18 -0
  81. package/.pi/harness/policies/phases.yaml +10 -0
  82. package/.pi/harness/policies/roles.yaml +5 -0
  83. package/.pi/harness/policies/web-guard.yaml +5 -0
  84. package/.pi/harness/policies/workflow-sequences.yaml +9 -0
  85. package/.pi/harness/sentrux/architecture.manifest.json +26 -4
  86. package/.pi/harness/specs/harness-spawn-context.schema.json +1 -1
  87. package/.pi/harness/specs/observation.schema.json +2 -1
  88. package/.pi/lib/agents-policy.d.mts +70 -0
  89. package/.pi/lib/agents-policy.mjs +325 -0
  90. package/.pi/lib/agents-policy.ts +19 -0
  91. package/.pi/lib/agt/audit-run-sink.ts +52 -0
  92. package/.pi/lib/agt/build-evaluation-context.ts +285 -0
  93. package/.pi/lib/agt/config.ts +28 -0
  94. package/.pi/lib/agt/delegation.ts +69 -0
  95. package/.pi/lib/agt/evaluate-policy.ts +56 -0
  96. package/.pi/lib/agt/identity-registry.ts +41 -0
  97. package/.pi/lib/agt/index.ts +55 -0
  98. package/.pi/lib/agt/kill-switch-state.ts +11 -0
  99. package/.pi/lib/agt/legacy-evaluate.ts +101 -0
  100. package/.pi/lib/agt/policy-engine.ts +154 -0
  101. package/.pi/lib/agt/rings.ts +21 -0
  102. package/.pi/lib/agt/sre-hooks.ts +45 -0
  103. package/.pi/lib/agt/trust-run-store.ts +26 -0
  104. package/.pi/lib/agt/workflow-history.ts +29 -0
  105. package/.pi/lib/agt-governance-active.ts +14 -0
  106. package/.pi/lib/agt-tool-guard.ts +78 -0
  107. package/.pi/lib/ask-user/dialog.ts +314 -0
  108. package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
  109. package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
  110. package/.pi/{extensions/lib → lib}/extension-load-guard.ts +21 -0
  111. package/.pi/lib/harness-agt-tool-guard.ts +5 -0
  112. package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +6 -16
  113. package/.pi/lib/harness-debate-core-deps.ts +14 -0
  114. package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
  115. package/.pi/lib/harness-lens/.gitattributes +1 -0
  116. package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
  117. package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
  118. package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
  119. package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
  120. package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
  121. package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
  122. package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
  123. package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
  124. package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
  125. package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
  126. package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
  127. package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
  128. package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
  129. package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
  130. package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
  131. package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
  132. package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
  133. package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
  134. package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
  135. package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
  136. package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
  137. package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
  138. package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
  139. package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
  140. package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
  141. package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
  142. package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
  143. package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
  144. package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
  145. package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
  146. package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
  147. package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
  148. package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
  149. package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
  150. package/.pi/lib/harness-lens/clients/types.ts +59 -0
  151. package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
  152. package/.pi/lib/harness-lens/index.ts +532 -0
  153. package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
  154. package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
  155. package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
  156. package/.pi/lib/harness-project-config.ts +91 -0
  157. package/.pi/lib/harness-run-context-responses.ts +9 -0
  158. package/.pi/lib/harness-run-context.ts +1 -3
  159. package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +4 -3
  160. package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +5 -28
  161. package/.pi/lib/harness-subagent-auth.ts +51 -0
  162. package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +13 -10
  163. package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
  164. package/.pi/lib/harness-subagent-submit-register.ts +163 -0
  165. package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -55
  166. package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
  167. package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
  168. package/.pi/lib/harness-ui-state.ts +27 -12
  169. package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
  170. package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
  171. package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
  172. package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
  173. package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
  174. package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
  175. package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
  176. package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +3 -52
  177. package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
  178. package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
  179. package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
  180. package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
  181. package/.pi/prompts/harness-auto.md +2 -2
  182. package/.pi/prompts/harness-plan.md +4 -6
  183. package/.pi/prompts/harness-review.md +9 -9
  184. package/.pi/prompts/harness-run.md +7 -7
  185. package/.pi/prompts/harness-setup.md +42 -68
  186. package/.pi/prompts/harness-steer.md +2 -2
  187. package/.pi/scripts/README.md +3 -5
  188. package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
  189. package/.pi/scripts/graphify-kb-updater.mjs +48 -8
  190. package/.pi/scripts/harness-agents-manifest.mjs +61 -4
  191. package/.pi/scripts/harness-agt-doctor.ts +36 -0
  192. package/.pi/scripts/harness-cli-verify.sh +9 -2
  193. package/.pi/scripts/harness-project-toggle.mjs +129 -0
  194. package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
  195. package/.pi/scripts/harness-verify.mjs +113 -39
  196. package/.pi/scripts/harness-web-policy-guard.mjs +2 -2
  197. package/.pi/scripts/validate-plan-dag.mjs +65 -74
  198. package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
  199. package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
  200. package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
  201. package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
  202. package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
  203. package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
  204. package/.pi/skills/architecture/layered/SKILL.md +68 -0
  205. package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
  206. package/.pi/skills/architecture/microservices/SKILL.md +64 -0
  207. package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
  208. package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
  209. package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
  210. package/.pi/skills/architecture/service-based/SKILL.md +64 -0
  211. package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
  212. package/.pi/skills/architecture/space-based/SKILL.md +60 -0
  213. package/.pi/skills/ast-grep/SKILL.md +40 -321
  214. package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
  215. package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
  216. package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
  217. package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
  218. package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
  219. package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
  220. package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
  221. package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
  222. package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
  223. package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
  224. package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
  225. package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
  226. package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
  227. package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
  228. package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
  229. package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
  230. package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
  231. package/.pi/skills/lsp-navigation/SKILL.md +89 -0
  232. package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
  233. package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
  234. package/.pi/skills/quality/security-review/SKILL.md +34 -0
  235. package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
  236. package/.pi/skills/quality/testability-design/SKILL.md +33 -0
  237. package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
  238. package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
  239. package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
  240. package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
  241. package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
  242. package/.sentrux/rules.toml +20 -4
  243. package/AGENTS.md +5 -0
  244. package/CHANGELOG.md +26 -0
  245. package/README.md +85 -58
  246. package/THIRD_PARTY_NOTICES.md +12 -21
  247. package/package.json +15 -7
  248. package/vendor/pi-subagents/src/agents.ts +45 -1
  249. package/vendor/pi-subagents/src/subagents.ts +866 -811
  250. package/vendor/pi-vcc/src/core/brief.ts +68 -99
  251. package/vendor/pi-vcc/src/core/settings.ts +2 -2
  252. package/.agents/skills/caveman/SKILL.md +0 -67
  253. package/.pi/agents/harness/meta-optimizer.md +0 -36
  254. package/.pi/agents/harness/planning/scout-graphify.md +0 -39
  255. package/.pi/agents/harness/planning/scout-semantic.md +0 -41
  256. package/.pi/agents/harness/planning/scout-structure.md +0 -37
  257. package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
  258. package/.pi/extensions/lib/harness-subagent-auth.ts +0 -209
  259. package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
  260. package/.pi/extensions/pi-model-router-harness.ts +0 -42
  261. package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
  262. package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
  263. package/.pi/model-router.example.json +0 -36
  264. package/.pi/prompts/harness-critic.md +0 -10
  265. package/.pi/prompts/harness-eval.md +0 -10
  266. package/.pi/prompts/harness-router-tune.md +0 -52
  267. package/.pi/scripts/harness-generate-model-router.mjs +0 -327
  268. package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
  269. package/.pi/scripts/harness-sync-model-router.mjs +0 -97
  270. package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
  271. package/vendor/pi-model-router/.prettierignore +0 -4
  272. package/vendor/pi-model-router/.prettierrc +0 -5
  273. package/vendor/pi-model-router/AGENTS.md +0 -39
  274. package/vendor/pi-model-router/LICENSE +0 -21
  275. package/vendor/pi-model-router/README.md +0 -99
  276. package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
  277. package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
  278. package/vendor/pi-model-router/extensions/commands.ts +0 -720
  279. package/vendor/pi-model-router/extensions/config.ts +0 -348
  280. package/vendor/pi-model-router/extensions/constants.ts +0 -1
  281. package/vendor/pi-model-router/extensions/index.ts +0 -478
  282. package/vendor/pi-model-router/extensions/provider.ts +0 -580
  283. package/vendor/pi-model-router/extensions/routing.ts +0 -564
  284. package/vendor/pi-model-router/extensions/state.ts +0 -52
  285. package/vendor/pi-model-router/extensions/types.ts +0 -95
  286. package/vendor/pi-model-router/extensions/ui.ts +0 -144
  287. package/vendor/pi-model-router/model-router.example.json +0 -48
  288. package/vendor/pi-model-router/package.json +0 -48
  289. package/vendor/pi-model-router/tsconfig.json +0 -16
  290. /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
  291. /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
  292. /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
  293. /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
  294. /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
  295. /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
  296. /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
  297. /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
  298. /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
  299. /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
  300. /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
  301. /package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +0 -0
  302. /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
  303. /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
  304. /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
  305. /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
  306. /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
  307. /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
  308. /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
  309. /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
  310. /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
  311. /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
  312. /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
  313. /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
  314. /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
@@ -7,21 +7,26 @@ import type {
7
7
  ExtensionAPI,
8
8
  ExtensionContext,
9
9
  } from "@earendil-works/pi-coding-agent";
10
- import type { AgentConfig } from "../../../vendor/pi-subagents/src/agents.js";
10
+ import type { AgentConfig } from "../../vendor/pi-subagents/src/agents.js";
11
11
  import {
12
12
  createSubagentsExtension,
13
13
  type HarnessSubagentsOptions,
14
14
  type SpawnAuthForward,
15
- } from "../../../vendor/pi-subagents/src/subagents.js";
15
+ } from "../../vendor/pi-subagents/src/subagents.js";
16
+ import { subagentGovernanceExtensionPath } from "../extensions/subagent-governance.js";
17
+ import { getAgentKind } from "./agents-policy.mjs";
18
+ import {
19
+ delegationEnvFromBundle,
20
+ mintSubagentDelegation,
21
+ } from "./agt/delegation.js";
22
+ import { spawnCircuitOpen } from "./agt/sre-hooks.js";
23
+ import { refreshHarnessCocoindexIndex } from "./harness-cocoindex-refresh.js";
24
+ import { captureHarnessEvent } from "./harness-posthog.js";
16
25
  import {
17
26
  getLatestRunContext,
18
27
  getRunIdFromSession,
19
28
  type HarnessPhase,
20
- } from "../../lib/harness-run-context.js";
21
- import { parseSpawnContextFromTask } from "../../lib/harness-spawn-parse.js";
22
- import { harnessSubagentSubmitExtensionPath } from "../harness-subagent-submit.js";
23
- import { refreshHarnessCocoindexIndex } from "./harness-cocoindex-refresh.js";
24
- import { captureHarnessEvent } from "./harness-posthog.js";
29
+ } from "./harness-run-context.js";
25
30
  import {
26
31
  checkHarnessSpawnBudget,
27
32
  countHarnessAgentsInRequest,
@@ -29,6 +34,7 @@ import {
29
34
  recordSpawnEnd,
30
35
  recordSpawnStart,
31
36
  } from "./harness-spawn-budget.js";
37
+ import { parseSpawnContextFromTask } from "./harness-spawn-parse.js";
32
38
  import {
33
39
  isUsableApiKey,
34
40
  resolveConcreteSubagentModel,
@@ -111,19 +117,52 @@ async function resolveHarnessSpawnAuth(
111
117
  export function createHarnessSubagentsExtension(
112
118
  packageRoot: string,
113
119
  ): (pi: ExtensionAPI) => void {
114
- const submitExtPath = harnessSubagentSubmitExtensionPath(packageRoot);
120
+ const governanceExtPath = subagentGovernanceExtensionPath(packageRoot);
115
121
  const options: HarnessSubagentsOptions = {
116
122
  packageRoot,
117
- harnessSubprocessExtensionPath: submitExtPath,
123
+ subprocessGovernanceExtensionPath: governanceExtPath,
124
+ harnessSubprocessExtensionPath: governanceExtPath,
118
125
  resolveSubprocessEnv: (task, agent) => {
119
- if (!agent.name.startsWith("harness/")) return undefined;
126
+ const projectRoot = process.cwd();
127
+ const base: Record<string, string> = {
128
+ PI_HARNESS_SUBPROCESS: "1",
129
+ HARNESS_AGENT_ID: agent.name,
130
+ HARNESS_PKG_ROOT: packageRoot,
131
+ HARNESS_PROJECT_ROOT: projectRoot,
132
+ };
120
133
  const ctx = parseSpawnContextFromTask(task);
121
- if (!ctx?.run_id) return undefined;
134
+ if (!ctx?.run_id) return base;
135
+ if (spawnCircuitOpen(ctx.run_id)) {
136
+ return undefined;
137
+ }
138
+ const runDir =
139
+ ctx.run_dir ?? join(packageRoot, ".pi", "harness", "runs", ctx.run_id);
140
+ const kind = getAgentKind(packageRoot, projectRoot, agent.name);
141
+ let delegationEnv: Record<string, string> = {};
142
+ try {
143
+ const bundle = mintSubagentDelegation({
144
+ runId: ctx.run_id,
145
+ runDir,
146
+ agentId: agent.name,
147
+ agentKind: kind,
148
+ });
149
+ delegationEnv = delegationEnvFromBundle(bundle);
150
+ } catch {
151
+ /* identity mint best-effort */
152
+ }
122
153
  return {
154
+ ...base,
123
155
  HARNESS_RUN_ID: ctx.run_id,
124
- HARNESS_RUN_DIR:
125
- ctx.run_dir ??
126
- join(packageRoot, ".pi", "harness", "runs", ctx.run_id),
156
+ HARNESS_RUN_DIR: runDir,
157
+ HARNESS_SUBAGENT_PHASE_HINT:
158
+ kind === "executor"
159
+ ? "execute"
160
+ : kind === "evaluator"
161
+ ? "evaluate"
162
+ : kind === "adversary"
163
+ ? "adversary"
164
+ : "plan",
165
+ ...delegationEnv,
127
166
  };
128
167
  },
129
168
  defaultAgentScope: "both",
@@ -13,7 +13,7 @@ import {
13
13
  loadRunContextForSubprocess,
14
14
  nowIso,
15
15
  policyBootstrapFromRunContext,
16
- } from "../../lib/harness-run-context.js";
16
+ } from "./harness-run-context.js";
17
17
 
18
18
  type PolicyState = {
19
19
  phase: "plan" | "execute" | "evaluate" | "adversary" | "merge";
@@ -375,8 +375,6 @@ export const HARNESS_PHASE_ORDER: readonly HarnessPhase[] = [
375
375
  "plan",
376
376
  "execute",
377
377
  "evaluate",
378
- "adversary",
379
- "merge",
380
378
  ] as const;
381
379
 
382
380
  export function formatHarnessPhaseLabel(phase: HarnessPhase): string {
@@ -384,13 +382,11 @@ export function formatHarnessPhaseLabel(phase: HarnessPhase): string {
384
382
  case "plan":
385
383
  return "plan";
386
384
  case "execute":
387
- return "build";
385
+ return "run";
388
386
  case "evaluate":
389
- return "eval";
390
387
  case "adversary":
391
- return "review";
392
388
  case "merge":
393
- return "merge";
389
+ return "review";
394
390
  }
395
391
  }
396
392
 
@@ -400,6 +396,25 @@ export function nextHarnessPhase(phase: HarnessPhase): HarnessPhase | null {
400
396
  return HARNESS_PHASE_ORDER[index + 1] ?? null;
401
397
  }
402
398
 
399
+ function mainPhaseCommandForStatus(state: HarnessUiState): string | null {
400
+ const command = state.nextRecommendedCommand;
401
+ if (!command) return null;
402
+ const normalized = command.toLowerCase();
403
+
404
+ if (normalized.includes("/harness-plan")) {
405
+ return normalized.includes("revise")
406
+ ? "/harness-plan (mode: revise)"
407
+ : "/harness-plan";
408
+ }
409
+ if (normalized.includes("/harness-review")) return "/harness-review";
410
+ if (normalized.includes("/harness-run-status")) {
411
+ return state.phase === "execute" ? "/harness-review" : null;
412
+ }
413
+ if (normalized.includes("/harness-run")) return "/harness-run";
414
+ if (normalized.includes("/harness-steer")) return "/harness-run";
415
+ return null;
416
+ }
417
+
403
418
  function truncateStatusCommand(command: string, maxLen = 40): string {
404
419
  if (command.length <= maxLen) return command;
405
420
  return `${command.slice(0, maxLen - 3)}...`;
@@ -430,9 +445,10 @@ export function deriveHarnessStatusHint(state: HarnessUiState): {
430
445
  ) {
431
446
  return { text: "Waiting for your input", severity: "warning" };
432
447
  }
433
- if (state.nextRecommendedCommand) {
448
+ const mainPhaseCommand = mainPhaseCommandForStatus(state);
449
+ if (mainPhaseCommand) {
434
450
  return {
435
- text: `Next: ${truncateStatusCommand(state.nextRecommendedCommand)}`,
451
+ text: `Next: ${truncateStatusCommand(mainPhaseCommand)}`,
436
452
  severity: "accent",
437
453
  };
438
454
  }
@@ -450,13 +466,12 @@ export function deriveHarnessStatusHint(state: HarnessUiState): {
450
466
  }
451
467
  switch (state.phase) {
452
468
  case "execute":
453
- return { text: "Implementing changes", severity: "accent" };
469
+ return { text: "Running changes", severity: "accent" };
454
470
  case "evaluate":
455
- return { text: "Running checks", severity: "accent" };
456
471
  case "adversary":
457
- return { text: "Review gate", severity: "accent" };
472
+ return { text: "Reviewing changes", severity: "accent" };
458
473
  case "merge":
459
- return { text: "Ready to finish", severity: "accent" };
474
+ return { text: "Review complete", severity: "accent" };
460
475
  default:
461
476
  return { text: "Planning", severity: "muted" };
462
477
  }
@@ -8,8 +8,8 @@ import {
8
8
  saveProjectActiveRun,
9
9
  saveRunContextToDisk,
10
10
  validatePlanPacket,
11
- } from "../../../lib/harness-run-context.js";
12
- import { writeYamlFile } from "../../../lib/harness-yaml.js";
11
+ } from "../harness-run-context.js";
12
+ import { writeYamlFile } from "../harness-yaml.js";
13
13
  import { writePlanReviewMarkdown } from "./plan-review.js";
14
14
 
15
15
  export const CREATE_PLAN_SNIPPET = "create_plan()";
@@ -1,5 +1,5 @@
1
- import type { PlanPacketLike } from "../../../lib/harness-run-context.js";
2
- import { stringifyYaml } from "../../../lib/harness-yaml.js";
1
+ import type { PlanPacketLike } from "../harness-run-context.js";
2
+ import { stringifyYaml } from "../harness-yaml.js";
3
3
 
4
4
  /** Canonical YAML for plan_packet (same shape as plan-packet.yaml on disk). */
5
5
  export function formatPlanPacketYaml(packet: PlanPacketLike): string {
@@ -5,14 +5,14 @@ import {
5
5
  canonicalPlanReviewPath,
6
6
  type HarnessRunContext,
7
7
  type PlanPacketLike,
8
- } from "../../../lib/harness-run-context.js";
8
+ } from "../harness-run-context.js";
9
9
  import { formatPlanPacketYaml } from "./format-plan.js";
10
10
  import type { PlanResearchBrief } from "./types.js";
11
11
 
12
12
  export {
13
13
  canonicalPlanReviewPath,
14
14
  PLAN_REVIEW_BASENAME,
15
- } from "../../../lib/harness-run-context.js";
15
+ } from "../harness-run-context.js";
16
16
 
17
17
  export type PlanReviewStatus = "draft" | "approved" | "committed";
18
18
 
@@ -33,223 +33,184 @@ function strList(value: unknown): string[] {
33
33
  .filter((item): item is string => Boolean(item));
34
34
  }
35
35
 
36
- /** Render Darwin research sections for plan-review.md. */
37
- export function formatResearchBriefMarkdown(
38
- research: PlanResearchBrief | null | undefined,
39
- ): string {
40
- if (!research) return "";
41
- const lines: string[] = [];
42
- const decomp = asRecord(research.decomposition);
43
- const hyp = asRecord(research.hypothesis);
44
- const evalBrief = asRecord(research.eval);
36
+ function pushListSection(
37
+ lines: string[],
38
+ title: string,
39
+ items: string[],
40
+ ): void {
41
+ if (!items.length) return;
42
+ lines.push(`**${title}:**`);
43
+ for (const item of items) lines.push(`- ${item}`);
44
+ lines.push("");
45
+ }
45
46
 
46
- if (decomp) {
47
- lines.push("## Phase 1 — Problem decomposition");
47
+ function appendDecompositionMarkdown(
48
+ lines: string[],
49
+ decomp: Record<string, unknown>,
50
+ ): void {
51
+ lines.push("## Phase 1 — Problem decomposition", "");
52
+ const restate = str(decomp.problem_restatement);
53
+ if (restate) lines.push("**What is being asked?**", "", restate, "");
54
+ const types = strList(decomp.problem_types);
55
+ if (types.length) lines.push(`**Problem type(s):** ${types.join(", ")}`, "");
56
+ const scope = asRecord(decomp.scope);
57
+ if (scope) {
58
+ const focus = str(scope.narrowed_focus);
59
+ if (focus) lines.push("**Scope:**", "", focus, "");
60
+ pushListSection(lines, "Excluded", strList(scope.excluded));
61
+ }
62
+ for (const [label, key] of [
63
+ ["Hard constraints", "hard_constraints"],
64
+ ["Soft constraints", "soft_constraints"],
65
+ ["Success metrics", "success_metrics"],
66
+ ] as const) {
67
+ pushListSection(lines, label, strList(decomp[key]));
68
+ }
69
+ const prior = asRecord(decomp.prior_art);
70
+ if (prior) {
71
+ lines.push("**Prior art:**", "");
72
+ const best = str(prior.best_approach);
73
+ const gap = str(prior.gap);
74
+ if (best) lines.push(`- Best approach: ${best}`);
75
+ if (gap) lines.push(`- Gap: ${gap}`);
76
+ for (const dead of strList(prior.dead_ends))
77
+ lines.push(`- Dead end: ${dead}`);
48
78
  lines.push("");
49
- const restate = str(decomp.problem_restatement);
50
- if (restate) {
51
- lines.push("**What is being asked?**");
52
- lines.push("");
53
- lines.push(restate);
54
- lines.push("");
55
- }
56
- const types = strList(decomp.problem_types);
57
- if (types.length) {
58
- lines.push(`**Problem type(s):** ${types.join(", ")}`);
59
- lines.push("");
60
- }
61
- const scope = asRecord(decomp.scope);
62
- if (scope) {
63
- const focus = str(scope.narrowed_focus);
64
- if (focus) {
65
- lines.push("**Scope:**");
66
- lines.push("");
67
- lines.push(focus);
68
- lines.push("");
69
- }
70
- const excluded = strList(scope.excluded);
71
- if (excluded.length) {
72
- lines.push("**Excluded:**");
73
- for (const item of excluded) lines.push(`- ${item}`);
74
- lines.push("");
75
- }
76
- }
79
+ }
80
+ const core = str(decomp.core_tension);
81
+ if (core) lines.push("**Core tension:**", "", core, "");
82
+ }
83
+
84
+ function appendHypothesisMarkdown(
85
+ lines: string[],
86
+ hyp: Record<string, unknown>,
87
+ ): void {
88
+ lines.push("## Phase 2 — DARWIN hypothesis", "");
89
+ const primary = asRecord(hyp.primary);
90
+ if (primary) {
77
91
  for (const [label, key] of [
78
- ["Hard constraints", "hard_constraints"],
79
- ["Soft constraints", "soft_constraints"],
80
- ["Success metrics", "success_metrics"],
92
+ ["Claim", "claim"],
93
+ ["Mechanism", "mechanism"],
94
+ ["Prediction", "prediction"],
95
+ ["Experiment", "experiment"],
96
+ ["Resolves tension", "tension_resolution"],
81
97
  ] as const) {
82
- const items = strList(decomp[key]);
83
- if (items.length) {
84
- lines.push(`**${label}:**`);
85
- for (const item of items) lines.push(`- ${item}`);
86
- lines.push("");
87
- }
88
- }
89
- const prior = asRecord(decomp.prior_art);
90
- if (prior) {
91
- lines.push("**Prior art:**");
92
- lines.push("");
93
- const best = str(prior.best_approach);
94
- const gap = str(prior.gap);
95
- if (best) lines.push(`- Best approach: ${best}`);
96
- if (gap) lines.push(`- Gap: ${gap}`);
97
- for (const dead of strList(prior.dead_ends)) {
98
- lines.push(`- Dead end: ${dead}`);
99
- }
100
- lines.push("");
101
- }
102
- const core = str(decomp.core_tension);
103
- if (core) {
104
- lines.push("**Core tension:**");
105
- lines.push("");
106
- lines.push(core);
107
- lines.push("");
98
+ const text = str(primary[key]);
99
+ if (text) lines.push(`**${label}:** ${text}`, "");
108
100
  }
109
101
  }
110
-
111
- if (hyp) {
112
- lines.push("## Phase 2 — DARWIN hypothesis");
102
+ const fork = asRecord(hyp.dialectical_fork);
103
+ if (fork) {
104
+ const forkText = str(fork.fork);
105
+ if (forkText) lines.push(`**Dialectical fork:** ${forkText}`, "");
106
+ const pathA = str(fork.path_a);
107
+ const pathB = str(fork.path_b);
108
+ if (pathA) lines.push(`- **Path A:** ${pathA}`);
109
+ if (pathB) lines.push(`- **Path B:** ${pathB}`);
113
110
  lines.push("");
114
- const primary = asRecord(hyp.primary);
115
- if (primary) {
116
- for (const [label, key] of [
117
- ["Claim", "claim"],
118
- ["Mechanism", "mechanism"],
119
- ["Prediction", "prediction"],
120
- ["Experiment", "experiment"],
121
- ["Resolves tension", "tension_resolution"],
122
- ] as const) {
123
- const text = str(primary[key]);
124
- if (text) {
125
- lines.push(`**${label}:** ${text}`);
126
- lines.push("");
127
- }
128
- }
129
- }
130
- const fork = asRecord(hyp.dialectical_fork);
131
- if (fork) {
132
- const forkText = str(fork.fork);
133
- if (forkText) {
134
- lines.push(`**Dialectical fork:** ${forkText}`);
135
- lines.push("");
136
- }
137
- const pathA = str(fork.path_a);
138
- const pathB = str(fork.path_b);
139
- if (pathA) lines.push(`- **Path A:** ${pathA}`);
140
- if (pathB) lines.push(`- **Path B:** ${pathB}`);
141
- lines.push("");
142
- }
143
- const alts = Array.isArray(hyp.alternatives) ? hyp.alternatives : [];
144
- if (alts.length) {
145
- lines.push("**Alternatives:**");
146
- for (const alt of alts) {
147
- const rec = asRecord(alt);
148
- if (!rec) continue;
149
- const claim = str(rec.claim);
150
- const bet = str(rec.key_bet);
151
- if (claim) lines.push(`- ${claim}${bet ? ` (bet: ${bet})` : ""}`);
152
- }
153
- lines.push("");
154
- }
155
- const steps = strList(hyp.recommended_next_steps);
156
- if (steps.length) {
157
- lines.push("**Recommended next steps:**");
158
- for (const step of steps) lines.push(`1. ${step}`);
159
- lines.push("");
111
+ }
112
+ const alts = Array.isArray(hyp.alternatives) ? hyp.alternatives : [];
113
+ if (alts.length) {
114
+ lines.push("**Alternatives:**");
115
+ for (const alt of alts) {
116
+ const rec = asRecord(alt);
117
+ const claim = rec ? str(rec.claim) : null;
118
+ const bet = rec ? str(rec.key_bet) : null;
119
+ if (claim) lines.push(`- ${claim}${bet ? ` (bet: ${bet})` : ""}`);
160
120
  }
121
+ lines.push("");
161
122
  }
162
-
163
- const impl = asRecord(research.implementation);
164
- if (impl) {
165
- lines.push("## Phase 3.5 — Implementation research");
123
+ const steps = strList(hyp.recommended_next_steps);
124
+ if (steps.length) {
125
+ lines.push("**Recommended next steps:**");
126
+ for (const step of steps) lines.push(`1. ${step}`);
166
127
  lines.push("");
167
- const framing = str(impl.problem_framing);
168
- if (framing) {
169
- lines.push("**Problem framing:**");
170
- lines.push("");
171
- lines.push(framing);
172
- lines.push("");
173
- }
174
- const rec = asRecord(impl.recommended_approach);
175
- if (rec) {
176
- const summary = str(rec.summary);
177
- const conf = str(rec.recommended_approach_confidence);
178
- if (summary) {
179
- lines.push(
180
- `**Recommended approach**${conf ? ` (${conf} confidence)` : ""}:`,
181
- );
182
- lines.push("");
183
- lines.push(summary);
184
- lines.push("");
185
- }
186
- const rationale = str(rec.confidence_rationale);
187
- if (rationale) {
188
- lines.push(`*Rationale:* ${rationale}`);
189
- lines.push("");
190
- }
191
- }
192
- const patterns = Array.isArray(impl.solution_patterns)
193
- ? impl.solution_patterns
194
- : [];
195
- if (patterns.length) {
196
- lines.push("**Solution patterns:**");
197
- for (const p of patterns) {
198
- const pat = asRecord(p);
199
- const name = pat ? str(pat.name) : null;
200
- const fit = pat ? str(pat.fit) : null;
201
- if (name) lines.push(`- **${name}**${fit ? `: ${fit}` : ""}`);
202
- }
203
- lines.push("");
204
- }
205
- const openQs = strList(impl.open_questions);
206
- if (openQs.length) {
207
- lines.push("**Open questions:**");
208
- for (const q of openQs) lines.push(`- ${q}`);
209
- lines.push("");
210
- }
211
- const anti = strList(impl.anti_patterns);
212
- if (anti.length) {
213
- lines.push("**Anti-patterns:**");
214
- for (const a of anti) lines.push(`- ${a}`);
215
- lines.push("");
216
- }
217
128
  }
129
+ }
218
130
 
219
- if (evalBrief) {
220
- lines.push("## Self-evaluation");
221
- lines.push("");
222
- lines.push("| Dimension | Score | Rationale |");
223
- lines.push("|-----------|-------|-----------|");
224
- const dims = asRecord(evalBrief.dimensions);
225
- if (dims) {
226
- for (const name of [
227
- "novelty",
228
- "coherence",
229
- "testability",
230
- "impact",
231
- ] as const) {
232
- const dim = asRecord(dims[name]);
233
- if (!dim) continue;
234
- const score = typeof dim.score === "number" ? String(dim.score) : "?";
235
- const rationale = str(dim.rationale) ?? "";
236
- lines.push(`| ${name} | ${score}/100 | ${rationale} |`);
237
- }
131
+ function appendImplementationMarkdown(
132
+ lines: string[],
133
+ impl: Record<string, unknown>,
134
+ ): void {
135
+ lines.push("## Phase 3.5 — Implementation research", "");
136
+ const framing = str(impl.problem_framing);
137
+ if (framing) lines.push("**Problem framing:**", "", framing, "");
138
+ const rec = asRecord(impl.recommended_approach);
139
+ if (rec) {
140
+ const summary = str(rec.summary);
141
+ const conf = str(rec.recommended_approach_confidence);
142
+ if (summary) {
143
+ lines.push(
144
+ `**Recommended approach**${conf ? ` (${conf} confidence)` : ""}:`,
145
+ );
146
+ lines.push("", summary, "");
238
147
  }
239
- const rel = asRecord(evalBrief.relevance);
240
- if (rel) {
241
- const passes = rel.passes === true ? "✓" : "✗";
242
- const rationale = str(rel.rationale) ?? "";
243
- lines.push(`| Relevance | ${passes} | ${rationale} |`);
148
+ const rationale = str(rec.confidence_rationale);
149
+ if (rationale) lines.push(`*Rationale:* ${rationale}`, "");
150
+ }
151
+ const patterns = Array.isArray(impl.solution_patterns)
152
+ ? impl.solution_patterns
153
+ : [];
154
+ if (patterns.length) {
155
+ lines.push("**Solution patterns:**");
156
+ for (const p of patterns) {
157
+ const pat = asRecord(p);
158
+ const name = pat ? str(pat.name) : null;
159
+ const fit = pat ? str(pat.fit) : null;
160
+ if (name) lines.push(`- **${name}**${fit ? `: ${fit}` : ""}`);
244
161
  }
245
162
  lines.push("");
246
- const summary = str(evalBrief.human_summary);
247
- if (summary) {
248
- lines.push(summary);
249
- lines.push("");
163
+ }
164
+ pushListSection(lines, "Open questions", strList(impl.open_questions));
165
+ pushListSection(lines, "Anti-patterns", strList(impl.anti_patterns));
166
+ }
167
+
168
+ function appendEvaluationMarkdown(
169
+ lines: string[],
170
+ evalBrief: Record<string, unknown>,
171
+ ): void {
172
+ lines.push("## Self-evaluation", "");
173
+ lines.push("| Dimension | Score | Rationale |");
174
+ lines.push("|-----------|-------|-----------|");
175
+ const dims = asRecord(evalBrief.dimensions);
176
+ if (dims) {
177
+ for (const name of [
178
+ "novelty",
179
+ "coherence",
180
+ "testability",
181
+ "impact",
182
+ ] as const) {
183
+ const dim = asRecord(dims[name]);
184
+ if (!dim) continue;
185
+ const score = typeof dim.score === "number" ? String(dim.score) : "?";
186
+ lines.push(`| ${name} | ${score}/100 | ${str(dim.rationale) ?? ""} |`);
250
187
  }
251
188
  }
189
+ const rel = asRecord(evalBrief.relevance);
190
+ if (rel) {
191
+ const passes = rel.passes === true ? "✓" : "✗";
192
+ lines.push(`| Relevance | ${passes} | ${str(rel.rationale) ?? ""} |`);
193
+ }
194
+ lines.push("");
195
+ const summary = str(evalBrief.human_summary);
196
+ if (summary) lines.push(summary, "");
197
+ }
252
198
 
199
+ /** Render Darwin research sections for plan-review.md. */
200
+ export function formatResearchBriefMarkdown(
201
+ research: PlanResearchBrief | null | undefined,
202
+ ): string {
203
+ if (!research) return "";
204
+ const lines: string[] = [];
205
+ const sections = [
206
+ [asRecord(research.decomposition), appendDecompositionMarkdown],
207
+ [asRecord(research.hypothesis), appendHypothesisMarkdown],
208
+ [asRecord(research.implementation), appendImplementationMarkdown],
209
+ [asRecord(research.eval), appendEvaluationMarkdown],
210
+ ] as const;
211
+ for (const [section, append] of sections) {
212
+ if (section) append(lines, section);
213
+ }
253
214
  return lines.length ? `${lines.join("\n")}\n` : "";
254
215
  }
255
216
 
@@ -4,7 +4,7 @@ import type {
4
4
  ToolRenderResultOptions,
5
5
  } from "@earendil-works/pi-coding-agent";
6
6
  import { Text, truncateToWidth } from "@earendil-works/pi-tui";
7
- import type { PlanPacketLike } from "../../../lib/harness-run-context.js";
7
+ import type { PlanPacketLike } from "../harness-run-context.js";
8
8
  import { formatPlanPacketLines } from "./format-plan.js";
9
9
  import type { ApprovePlanToolDetails } from "./types.js";
10
10
 
@@ -7,8 +7,8 @@ import {
7
7
  RESEARCH_BRIEF_BASENAME,
8
8
  readPlanPacketFromPath,
9
9
  validatePlanPacket,
10
- } from "../../../lib/harness-run-context.js";
11
- import { readYamlFile } from "../../../lib/harness-yaml.js";
10
+ } from "../harness-run-context.js";
11
+ import { readYamlFile } from "../harness-yaml.js";
12
12
  import type { ApprovePlanParams, PlanResearchBrief } from "./types.js";
13
13
 
14
14
  function isNonEmptyPacket(
@@ -1,5 +1,5 @@
1
- import type { PlanPacketLike } from "../../../lib/harness-run-context.js";
2
1
  import type { AskResponse, DialogResult } from "../ask-user/types.js";
2
+ import type { PlanPacketLike } from "../harness-run-context.js";
3
3
 
4
4
  export const DEFAULT_PLAN_APPROVAL_OPTIONS = [
5
5
  "Approve",
@@ -1,9 +1,9 @@
1
+ import type { AskResponse } from "../ask-user/types.js";
2
+ import { formatResultText } from "../ask-user/validate.js";
1
3
  import {
2
4
  type PlanPacketLike,
3
5
  validatePlanPacket,
4
- } from "../../../lib/harness-run-context.js";
5
- import type { AskResponse } from "../ask-user/types.js";
6
- import { formatResultText } from "../ask-user/validate.js";
6
+ } from "../harness-run-context.js";
7
7
  import type {
8
8
  ApprovePlanParams,
9
9
  ApprovePlanToolDetails,