@tea-agent/loop-agent 0.1.0-fe-test.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 (1050) hide show
  1. package/AGENTS.md +114 -0
  2. package/CHANGELOG.md +2283 -0
  3. package/README.md +177 -0
  4. package/bin/agent-worker.js +22 -0
  5. package/bin/loop-agent.js +57 -0
  6. package/dist/adapters/aimax.js +91 -0
  7. package/dist/adapters/context.js +32 -0
  8. package/dist/adapters/index.js +29 -0
  9. package/dist/adapters/loop-agent.js +192 -0
  10. package/dist/adapters/types.js +1 -0
  11. package/dist/application/context-usage/skill-resolution-stats.js +263 -0
  12. package/dist/application/dag/args.js +486 -0
  13. package/dist/application/dag/generate-task-dag.js +408 -0
  14. package/dist/application/dag/report-dag.js +14 -0
  15. package/dist/application/dag/run-dag.js +109 -0
  16. package/dist/application/dag/validate-dag.js +166 -0
  17. package/dist/application/evaluation/alias.js +184 -0
  18. package/dist/application/evaluation/budget.js +192 -0
  19. package/dist/application/evaluation/campaign-hash.js +47 -0
  20. package/dist/application/evaluation/campaign-matrix.js +372 -0
  21. package/dist/application/evaluation/campaign-scorecard.js +135 -0
  22. package/dist/application/evaluation/campaign.js +370 -0
  23. package/dist/application/evaluation/candidate-hash.js +75 -0
  24. package/dist/application/evaluation/candidate.js +69 -0
  25. package/dist/application/evaluation/corpus-hash.js +38 -0
  26. package/dist/application/evaluation/corpus.js +56 -0
  27. package/dist/application/evaluation/experiment.js +294 -0
  28. package/dist/application/evaluation/ignition.js +198 -0
  29. package/dist/application/evaluation/integrity-audit.js +162 -0
  30. package/dist/application/evaluation/outer-loop.js +132 -0
  31. package/dist/application/evaluation/pi-cell-executor.js +39 -0
  32. package/dist/application/evaluation/private-verifier.js +46 -0
  33. package/dist/application/evaluation/promotion-policy.js +151 -0
  34. package/dist/application/evaluation/proposer.js +98 -0
  35. package/dist/application/evaluation/replay.js +289 -0
  36. package/dist/application/evaluation/types.js +652 -0
  37. package/dist/application/loop/run-action.js +19 -0
  38. package/dist/application/task-lifecycle/advance.js +1204 -0
  39. package/dist/application/task-lifecycle/gates.js +130 -0
  40. package/dist/application/task-lifecycle/index.js +7 -0
  41. package/dist/application/task-lifecycle/observe.js +424 -0
  42. package/dist/application/task-lifecycle/plan-transitions.js +251 -0
  43. package/dist/application/task-lifecycle/recommendations.js +180 -0
  44. package/dist/application/task-lifecycle/record.js +73 -0
  45. package/dist/application/task-lifecycle/types.js +1 -0
  46. package/dist/build-stamp.json +6 -0
  47. package/dist/cli/catalog.js +24 -0
  48. package/dist/cli/command-definitions.js +659 -0
  49. package/dist/cli/help.js +56 -0
  50. package/dist/cli/index.js +5 -0
  51. package/dist/cli/program.js +647 -0
  52. package/dist/cli/router.js +13 -0
  53. package/dist/cli/update/init-surface-notifier.js +167 -0
  54. package/dist/cli/update/notifier.js +117 -0
  55. package/dist/cli/update/npm-client.js +151 -0
  56. package/dist/cli/update/policy.js +92 -0
  57. package/dist/cli/update/runtime-activity.js +29 -0
  58. package/dist/cli/update/state.js +68 -0
  59. package/dist/cli-governance/active-residue-check.js +38 -0
  60. package/dist/cli.js +57 -0
  61. package/dist/commands/client-recovery.js +1373 -0
  62. package/dist/commands/closeout.js +13 -0
  63. package/dist/commands/coverage-audit.js +14 -0
  64. package/dist/commands/coverage-report.js +50 -0
  65. package/dist/commands/cursor-prompt.js +182 -0
  66. package/dist/commands/dag-approve.js +142 -0
  67. package/dist/commands/dag-final-verification.js +76 -0
  68. package/dist/commands/dag-init-hybrid.js +57 -0
  69. package/dist/commands/dag-reconcile-run.js +5 -0
  70. package/dist/commands/dag-reconcile-tasks.js +51 -0
  71. package/dist/commands/dag-reject.js +91 -0
  72. package/dist/commands/dag-report.js +76 -0
  73. package/dist/commands/dag-request-interrupt.js +20 -0
  74. package/dist/commands/dag-rerun-task.js +19 -0
  75. package/dist/commands/dag-rerun.js +111 -0
  76. package/dist/commands/dag-resume.js +35 -0
  77. package/dist/commands/dag-run-task.js +12 -0
  78. package/dist/commands/dag-validate.js +20 -0
  79. package/dist/commands/dag-workflow-compile.js +91 -0
  80. package/dist/commands/dag-workflow-plan.js +130 -0
  81. package/dist/commands/dag-workflow-validate.js +66 -0
  82. package/dist/commands/delegate.js +130 -0
  83. package/dist/commands/docs-archive.js +5 -0
  84. package/dist/commands/docs-audit.js +6 -0
  85. package/dist/commands/doctor.js +283 -0
  86. package/dist/commands/eval.js +1398 -0
  87. package/dist/commands/examples.js +90 -0
  88. package/dist/commands/goal.js +92 -0
  89. package/dist/commands/handoff-check.js +5 -0
  90. package/dist/commands/harvest.js +44 -0
  91. package/dist/commands/import-prd.js +81 -0
  92. package/dist/commands/init-upgrade.js +3085 -0
  93. package/dist/commands/init.js +3630 -0
  94. package/dist/commands/inspect.js +11 -0
  95. package/dist/commands/instructions.js +223 -0
  96. package/dist/commands/knowledge.js +162 -0
  97. package/dist/commands/loop-benchmark.js +72 -0
  98. package/dist/commands/loop.js +251 -0
  99. package/dist/commands/new-task.js +5 -0
  100. package/dist/commands/operator.js +44 -0
  101. package/dist/commands/pi-prompt.js +174 -0
  102. package/dist/commands/pi-reuse-benchmark.js +153 -0
  103. package/dist/commands/plan-list.js +5 -0
  104. package/dist/commands/plan.js +50 -0
  105. package/dist/commands/promote-run.js +29 -0
  106. package/dist/commands/reference-index.js +16 -0
  107. package/dist/commands/run-dag-progress.js +123 -0
  108. package/dist/commands/run-dag.js +22 -0
  109. package/dist/commands/spine.js +38 -0
  110. package/dist/commands/stats.js +113 -0
  111. package/dist/commands/status.js +57 -0
  112. package/dist/commands/study-init.js +192 -0
  113. package/dist/commands/task-advance.js +366 -0
  114. package/dist/commands/task-contract.js +269 -0
  115. package/dist/commands/task-source-prepare.js +479 -0
  116. package/dist/commands/task-status.js +133 -0
  117. package/dist/commands/workflow.js +259 -0
  118. package/dist/commands/worktree-create.js +31 -0
  119. package/dist/commands/worktree-list.js +5 -0
  120. package/dist/commands/worktree-remove.js +26 -0
  121. package/dist/executors/config-core.js +5 -0
  122. package/dist/executors/config.js +2 -0
  123. package/dist/executors/dag-pi-executor.js +1277 -0
  124. package/dist/executors/dag-static-executor.js +42 -0
  125. package/dist/executors/dag.js +3 -0
  126. package/dist/executors/index.js +6 -0
  127. package/dist/executors/model-routing.js +71 -0
  128. package/dist/executors/pi-defaults.js +9 -0
  129. package/dist/executors/pi-event-serializer.js +65 -0
  130. package/dist/executors/pi-executor.js +981 -0
  131. package/dist/executors/pi-playwright-cli-tool.js +1006 -0
  132. package/dist/executors/pi-prompt-transport.js +198 -0
  133. package/dist/executors/pi-reuse-benchmark.js +316 -0
  134. package/dist/executors/pi-runtime-reuse.js +29 -0
  135. package/dist/executors/pi-sdk-executor.js +722 -0
  136. package/dist/executors/pi-sdk.js +1 -0
  137. package/dist/executors/pi-writer-tool-policy.js +266 -0
  138. package/dist/executors/pi.js +3 -0
  139. package/dist/executors/playwright-cli-launcher.js +63 -0
  140. package/dist/executors/process-tree.js +33 -0
  141. package/dist/executors/shell-executor.js +3055 -0
  142. package/dist/executors/shell-presets.js +99 -0
  143. package/dist/executors/shell-verification.js +262 -0
  144. package/dist/executors/shell-write-guard.js +543 -0
  145. package/dist/executors/shell.js +3 -0
  146. package/dist/executors/static.js +1 -0
  147. package/dist/governance/checks.js +437 -0
  148. package/dist/governance/document-index-closure.js +164 -0
  149. package/dist/governance/exec-plans.js +549 -0
  150. package/dist/governance/harness.js +9 -0
  151. package/dist/governance/index.js +3 -0
  152. package/dist/governance/manifest-types.js +254 -0
  153. package/dist/governance/manifest.js +2 -0
  154. package/dist/governance/path-guard.js +69 -0
  155. package/dist/governance/path-guards.js +2 -0
  156. package/dist/governance/profiles.js +3 -0
  157. package/dist/governance/requirement-coverage.js +425 -0
  158. package/dist/governance/skill-safety.js +135 -0
  159. package/dist/governance/spine-audit.js +155 -0
  160. package/dist/infrastructure/evaluation/alias-store.js +199 -0
  161. package/dist/infrastructure/evaluation/campaign-store.js +154 -0
  162. package/dist/infrastructure/evaluation/candidate-store.js +439 -0
  163. package/dist/infrastructure/evaluation/corpus-store.js +181 -0
  164. package/dist/infrastructure/evaluation/experiment-store.js +124 -0
  165. package/dist/infrastructure/evaluation/ignition-store.js +82 -0
  166. package/dist/infrastructure/evaluation/private-verifier-store.js +145 -0
  167. package/dist/infrastructure/evaluation/proposer-store.js +78 -0
  168. package/dist/infrastructure/evaluation/store.js +40 -0
  169. package/dist/infrastructure/harness/active-residue-policy.js +73 -0
  170. package/dist/infrastructure/harness/artifact-store.js +72 -0
  171. package/dist/infrastructure/harness/atomic-write.js +50 -0
  172. package/dist/infrastructure/harness/completed-facts-guard.js +40 -0
  173. package/dist/infrastructure/harness/loop-action-store.js +20 -0
  174. package/dist/infrastructure/harness/loop-store.js +41 -0
  175. package/dist/infrastructure/harness/one-shot-run-store.js +94 -0
  176. package/dist/infrastructure/harness/task-store.js +108 -0
  177. package/dist/records/closeout.js +2 -0
  178. package/dist/records/harvest.js +215 -0
  179. package/dist/records/index.js +3 -0
  180. package/dist/records/one-shot-runs.js +386 -0
  181. package/dist/records/promotion.js +200 -0
  182. package/dist/shared/artifacts-core.js +107 -0
  183. package/dist/shared/artifacts.js +2 -0
  184. package/dist/shared/context-files.js +32 -0
  185. package/dist/shared/context.js +2 -0
  186. package/dist/shared/copy-dir.js +17 -0
  187. package/dist/shared/git-progress.js +172 -0
  188. package/dist/shared/index.js +5 -0
  189. package/dist/shared/logger.js +17 -0
  190. package/dist/shared/one-shot-prompt-args.js +98 -0
  191. package/dist/shared/openspec-spec.js +125 -0
  192. package/dist/shared/operator/capabilities.js +2659 -0
  193. package/dist/shared/operator/command-lifecycle.js +94 -0
  194. package/dist/shared/operator/envelope.js +59 -0
  195. package/dist/shared/operator/index.js +5 -0
  196. package/dist/shared/operator/registry.js +38 -0
  197. package/dist/shared/operator/types.js +5 -0
  198. package/dist/shared/output-truncation.js +37 -0
  199. package/dist/shared/package-metadata.js +530 -0
  200. package/dist/shared/path-refs.js +31 -0
  201. package/dist/shared/pi-retry-settings.js +23 -0
  202. package/dist/shared/playwright-cli-command-policy.js +41 -0
  203. package/dist/shared/preview.js +39 -0
  204. package/dist/shared/prompts.js +26 -0
  205. package/dist/shared/reference-context.js +264 -0
  206. package/dist/shared/resilient-git.js +133 -0
  207. package/dist/shared/runtime-activity.js +6 -0
  208. package/dist/shared/timeout-policy.js +24 -0
  209. package/dist/shared/timeout.js +1 -0
  210. package/dist/shared/types.js +5 -0
  211. package/dist/sidecars/cursor-prompt/executor.js +428 -0
  212. package/dist/sidecars/cursor-prompt/index.js +3 -0
  213. package/dist/sidecars/cursor-prompt/stream.js +121 -0
  214. package/dist/task/config-types.js +235 -0
  215. package/dist/task/config.js +2 -0
  216. package/dist/task/contract/adopt.js +166 -0
  217. package/dist/task/contract/apply.js +326 -0
  218. package/dist/task/contract/canonicalize.js +60 -0
  219. package/dist/task/contract/constants.js +30 -0
  220. package/dist/task/contract/diff.js +177 -0
  221. package/dist/task/contract/hash.js +42 -0
  222. package/dist/task/contract/import-revision.js +96 -0
  223. package/dist/task/contract/index.js +17 -0
  224. package/dist/task/contract/journal.js +155 -0
  225. package/dist/task/contract/lock.js +153 -0
  226. package/dist/task/contract/observe.js +296 -0
  227. package/dist/task/contract/paths.js +19 -0
  228. package/dist/task/contract/project.js +185 -0
  229. package/dist/task/contract/recover.js +312 -0
  230. package/dist/task/contract/request-ledger.js +37 -0
  231. package/dist/task/contract/schema.js +153 -0
  232. package/dist/task/contract/transaction.js +160 -0
  233. package/dist/task/contract/types.js +1 -0
  234. package/dist/task/contract/validate-draft.js +106 -0
  235. package/dist/task/dag-source-paths.js +50 -0
  236. package/dist/task/delegate.js +208 -0
  237. package/dist/task/frontend-preflight.js +131 -0
  238. package/dist/task/frontend-project-capability.js +570 -0
  239. package/dist/task/goal-audit.js +51 -0
  240. package/dist/task/goal-policy.js +8 -0
  241. package/dist/task/goal.js +3 -0
  242. package/dist/task/ids.js +1 -0
  243. package/dist/task/index.js +12 -0
  244. package/dist/task/lifecycle.js +1 -0
  245. package/dist/task/operator/capabilities.js +6 -0
  246. package/dist/task/operator/envelope.js +2 -0
  247. package/dist/task/operator/index.js +5 -0
  248. package/dist/task/operator/registry.js +2 -0
  249. package/dist/task/operator/types.js +1 -0
  250. package/dist/task/paths.js +1 -0
  251. package/dist/task/read-model.js +140 -0
  252. package/dist/task/runtime.js +708 -0
  253. package/dist/task/source-prepare/artifact-meta.js +137 -0
  254. package/dist/task/source-prepare/build-draft.js +232 -0
  255. package/dist/task/source-prepare/completeness.js +248 -0
  256. package/dist/task/source-prepare/index.js +10 -0
  257. package/dist/task/source-prepare/parse-intent.js +496 -0
  258. package/dist/task/source-prepare/path-policy.js +197 -0
  259. package/dist/task/source-prepare/placeholder-paths.js +77 -0
  260. package/dist/task/source-prepare/prepare.js +920 -0
  261. package/dist/task/source-prepare/reference-integrity.js +290 -0
  262. package/dist/task/source-prepare/semantic-intake.js +717 -0
  263. package/dist/task/source-prepare/types.js +7 -0
  264. package/dist/task/source-references.js +228 -0
  265. package/dist/task/source-state.js +1 -0
  266. package/dist/task/state.js +41 -0
  267. package/dist/task/subagent-guidance.js +1 -0
  268. package/dist/task/task-demand-routing.js +443 -0
  269. package/dist/task/workflow-state-types.js +92 -0
  270. package/dist/task/worktree-cleanup.js +140 -0
  271. package/dist/task/worktree.js +388 -0
  272. package/dist/verification/maven/cache.js +142 -0
  273. package/dist/verification/maven/index.js +120 -0
  274. package/dist/verification/maven/plan-commands.js +421 -0
  275. package/dist/verification/maven/pom-static.js +136 -0
  276. package/dist/verification/maven/scope-resolve.js +153 -0
  277. package/dist/verification/maven/stale.js +130 -0
  278. package/dist/verification/maven/types.js +23 -0
  279. package/dist/verification/maven/workspace-graph.js +322 -0
  280. package/dist/worker/cli.js +957 -0
  281. package/dist/worker/closeout/apply.js +73 -0
  282. package/dist/worker/closeout/preview.js +30 -0
  283. package/dist/worker/console/app-data.js +316 -0
  284. package/dist/worker/console/chat/artifact-card.js +30 -0
  285. package/dist/worker/console/chat/assistant-content.js +121 -0
  286. package/dist/worker/console/chat/chat-event-store.js +981 -0
  287. package/dist/worker/console/chat/chat-ui-policy.js +25 -0
  288. package/dist/worker/console/chat/compaction-errors.js +64 -0
  289. package/dist/worker/console/chat/composer-draft-store.js +45 -0
  290. package/dist/worker/console/chat/context-panel.js +54 -0
  291. package/dist/worker/console/chat/contract-apply-receipt-store.js +174 -0
  292. package/dist/worker/console/chat/explore-tools.js +299 -0
  293. package/dist/worker/console/chat/human-gate-card.js +45 -0
  294. package/dist/worker/console/chat/interview-adapter.js +149 -0
  295. package/dist/worker/console/chat/mcp-inventory.js +221 -0
  296. package/dist/worker/console/chat/model-resolver.js +215 -0
  297. package/dist/worker/console/chat/operation-card.js +92 -0
  298. package/dist/worker/console/chat/pi-console-config.js +376 -0
  299. package/dist/worker/console/chat/pi-runtime.js +2446 -0
  300. package/dist/worker/console/chat/repo-browser.js +140 -0
  301. package/dist/worker/console/chat/repo-walk.js +125 -0
  302. package/dist/worker/console/chat/resource-loader.js +59 -0
  303. package/dist/worker/console/chat/resource-preferences-store.js +152 -0
  304. package/dist/worker/console/chat/routes.js +3137 -0
  305. package/dist/worker/console/chat/runtime-context.js +102 -0
  306. package/dist/worker/console/chat/runtime-selection.js +96 -0
  307. package/dist/worker/console/chat/semantic-activity.js +489 -0
  308. package/dist/worker/console/chat/session-store.js +484 -0
  309. package/dist/worker/console/chat/shortcuts.js +217 -0
  310. package/dist/worker/console/chat/tool-adapter.js +131 -0
  311. package/dist/worker/console/chat/tool-preview.js +247 -0
  312. package/dist/worker/console/chat/tools.js +212 -0
  313. package/dist/worker/console/chat/turn-execution-registry.js +82 -0
  314. package/dist/worker/console/chat/turn-process.js +346 -0
  315. package/dist/worker/console/chat/usage.js +165 -0
  316. package/dist/worker/console/chat/workspace-landing.js +103 -0
  317. package/dist/worker/console/dag-confirmation.js +347 -0
  318. package/dist/worker/console/dag-execution-receipt.js +393 -0
  319. package/dist/worker/console/doctor.js +230 -0
  320. package/dist/worker/console/draft-store.js +158 -0
  321. package/dist/worker/console/harness-pi-model-matrix.js +137 -0
  322. package/dist/worker/console/human-gate-token.js +130 -0
  323. package/dist/worker/console/index.js +17 -0
  324. package/dist/worker/console/inspect-split.js +82 -0
  325. package/dist/worker/console/interview/assessment.js +67 -0
  326. package/dist/worker/console/interview/grill-me.js +277 -0
  327. package/dist/worker/console/interview/session.js +100 -0
  328. package/dist/worker/console/interview/tools.js +109 -0
  329. package/dist/worker/console/loopback.js +16 -0
  330. package/dist/worker/console/mutation-gate-receipt-store.js +184 -0
  331. package/dist/worker/console/night-aux-ticker.js +146 -0
  332. package/dist/worker/console/observe-health-match.js +179 -0
  333. package/dist/worker/console/observe-link.js +42 -0
  334. package/dist/worker/console/open-browser.js +134 -0
  335. package/dist/worker/console/operation-run-facts.js +190 -0
  336. package/dist/worker/console/operation-runner.js +353 -0
  337. package/dist/worker/console/operation-sse.js +184 -0
  338. package/dist/worker/console/operation-store.js +169 -0
  339. package/dist/worker/console/operation-wait.js +355 -0
  340. package/dist/worker/console/operator-actions.js +3768 -0
  341. package/dist/worker/console/operator-selection.js +105 -0
  342. package/dist/worker/console/operator-surface-health.js +24 -0
  343. package/dist/worker/console/operator-user-error.js +45 -0
  344. package/dist/worker/console/pi-readiness.js +288 -0
  345. package/dist/worker/console/prd-identity.js +102 -0
  346. package/dist/worker/console/prd-intake-bridge.js +441 -0
  347. package/dist/worker/console/prd-reference-discovery.js +124 -0
  348. package/dist/worker/console/recovery-cta.js +320 -0
  349. package/dist/worker/console/recovery-error-copy.js +198 -0
  350. package/dist/worker/console/recovery-selection.js +107 -0
  351. package/dist/worker/console/repo-fingerprint.js +35 -0
  352. package/dist/worker/console/resolve-dag-run-for-task.js +115 -0
  353. package/dist/worker/console/resource-loader.js +95 -0
  354. package/dist/worker/console/routes.js +475 -0
  355. package/dist/worker/console/security.js +170 -0
  356. package/dist/worker/console/server.js +357 -0
  357. package/dist/worker/console/sibling-controller.js +33 -0
  358. package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-BmOEx0gA.js +1 -0
  359. package/dist/worker/console/static/assets/arc-BGiVVe8b.js +1 -0
  360. package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-nDdu4rgJ.js +36 -0
  361. package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-Bb5u0BWf.js +132 -0
  362. package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-BlhC0j94.js +10 -0
  363. package/dist/worker/console/static/assets/channel-Tl-FkUqr.js +1 -0
  364. package/dist/worker/console/static/assets/chunk-2GRJ4B5K-Ai8_qQXI.js +1 -0
  365. package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B9gyCjqX.js +1 -0
  366. package/dist/worker/console/static/assets/chunk-5RXB4S5H-Bzbj3zGH.js +231 -0
  367. package/dist/worker/console/static/assets/chunk-5VM5RSS4-BT415PvT.js +15 -0
  368. package/dist/worker/console/static/assets/chunk-6Q2QTUOP-D_A5_5QL.js +88 -0
  369. package/dist/worker/console/static/assets/chunk-GF5L2VYU-srJkaP3f.js +206 -0
  370. package/dist/worker/console/static/assets/chunk-JWPE2WC7-UYGvEP4l.js +1 -0
  371. package/dist/worker/console/static/assets/chunk-KBJHAD2P-B8luBrrz.js +1 -0
  372. package/dist/worker/console/static/assets/chunk-RYQCIY6F-DN7wXZSq.js +1 -0
  373. package/dist/worker/console/static/assets/chunk-XXDRQBXY-DXKyMuKS.js +1 -0
  374. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BaehtRk4.js +1 -0
  375. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BaehtRk4.js +1 -0
  376. package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-dqvoynXj.js +1 -0
  377. package/dist/worker/console/static/assets/cynefin-VYW2F7L2--PcyL34A.js +166 -0
  378. package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DvWs_hTY.js +62 -0
  379. package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +321 -0
  380. package/dist/worker/console/static/assets/dagre-VZM6K2ZE-DxeIxbyO.js +4 -0
  381. package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  382. package/dist/worker/console/static/assets/diagram-7IWD3JNH-DPCG61Of.js +30 -0
  383. package/dist/worker/console/static/assets/diagram-B4RE2ZJO-CmuU50-U.js +3 -0
  384. package/dist/worker/console/static/assets/diagram-LBJQPF4R-B61XLD_v.js +24 -0
  385. package/dist/worker/console/static/assets/diagram-Q27KOJAE-BB7HTRRY.js +24 -0
  386. package/dist/worker/console/static/assets/diagram-UB23O5K3-DvkboAIp.js +41 -0
  387. package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-BdJ_y1fL.js +1 -0
  388. package/dist/worker/console/static/assets/erDiagram-JOGREHBK-9bUBZTRi.js +85 -0
  389. package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-Dw4L4RBa.js +156 -0
  390. package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-Jr82N_QP.js +292 -0
  391. package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-DAz9wFLr.js +106 -0
  392. package/dist/worker/console/static/assets/graph-DOmOIIwC.js +1 -0
  393. package/dist/worker/console/static/assets/index-B7Y-0g3J.css +1 -0
  394. package/dist/worker/console/static/assets/index-DkCrqaYg.js +325 -0
  395. package/dist/worker/console/static/assets/infoDiagram-6WML65LV-CBB5Np7y.js +2 -0
  396. package/dist/worker/console/static/assets/init-Gi6I4Gst.js +1 -0
  397. package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-D8IOp94y.js +70 -0
  398. package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-Dgd3AHGG.js +139 -0
  399. package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BmYOxLE9.js +89 -0
  400. package/dist/worker/console/static/assets/layout-D-LzfAck.js +1 -0
  401. package/dist/worker/console/static/assets/linear-DfYTEF6J.js +1 -0
  402. package/dist/worker/console/static/assets/map-DxJ2ADlA.js +1 -0
  403. package/dist/worker/console/static/assets/mermaid.core-BjuFPRqa.js +308 -0
  404. package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS-CLyUViGd.js +96 -0
  405. package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +1 -0
  406. package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-BvX2Di4-.js +1 -0
  407. package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-BFj0_OLv.js +39 -0
  408. package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-BIUrD_hx.js +7 -0
  409. package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-CHBB48fj.js +1 -0
  410. package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-sJrYw2Sf.js +84 -0
  411. package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-BzHH6fPR.js +40 -0
  412. package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-7QCnelCt.js +162 -0
  413. package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-sDYSxn72.js +1 -0
  414. package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-XpiUdwzm.js +1 -0
  415. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-eEhq2A8Y.js +1 -0
  416. package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-DvqgNPzD.js +2 -0
  417. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-la8AC7fo.js +8 -0
  418. package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-2TyOYNx0.js +120 -0
  419. package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-B-nM6wM2.js +34 -0
  420. package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-B86wPdbr.js +78 -0
  421. package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-Dr1C4AQz.js +7 -0
  422. package/dist/worker/console/static/favicon.svg +37 -0
  423. package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  424. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  425. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  426. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  427. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  428. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  429. package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  430. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  431. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  432. package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  433. package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  434. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  435. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  436. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  437. package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  438. package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  439. package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  440. package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  441. package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  442. package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  443. package/dist/worker/console/static/index.html +15 -0
  444. package/dist/worker/console/static-src/app/console-types.js +184 -0
  445. package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
  446. package/dist/worker/console/static-src/app/useOperatorActions.js +324 -0
  447. package/dist/worker/console/static-src/app/usePrdImport.js +172 -0
  448. package/dist/worker/console/static-src/app/useRecoveryActions.js +289 -0
  449. package/dist/worker/console/static-src/app/useRecoveryConsole.js +425 -0
  450. package/dist/worker/console/static-src/app/useTaskWizard.js +296 -0
  451. package/dist/worker/console/static-src/chat-markdown-security.js +38 -0
  452. package/dist/worker/console/static-src/chat-view-types.js +2 -0
  453. package/dist/worker/console/static-src/night/night-types.js +24 -0
  454. package/dist/worker/console/static-src/night/useNightBoard.js +94 -0
  455. package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
  456. package/dist/worker/console/static-src/night-prepare-result.js +118 -0
  457. package/dist/worker/console/static-src/operator-chat/activity-journey.js +125 -0
  458. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +73 -0
  459. package/dist/worker/console/static-src/operator-chat/activity-references.js +20 -0
  460. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +420 -0
  461. package/dist/worker/console/static-src/operator-chat/format.js +71 -0
  462. package/dist/worker/console/static-src/operator-chat/landing-density.js +23 -0
  463. package/dist/worker/console/static-src/operator-chat/mutation-gate.js +45 -0
  464. package/dist/worker/console/static-src/operator-chat/refs.js +86 -0
  465. package/dist/worker/console/static-src/operator-chat/resource-auto-invocation.js +91 -0
  466. package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
  467. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +257 -0
  468. package/dist/worker/console/static-src/operator-chat/session-title-watcher.js +128 -0
  469. package/dist/worker/console/static-src/operator-chat/sidebar-split.js +90 -0
  470. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +24 -0
  471. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +141 -0
  472. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +38 -0
  473. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
  474. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +690 -0
  475. package/dist/worker/console/static-src/operator-chat/turn-submission.js +158 -0
  476. package/dist/worker/console/static-src/operator-chat/types.js +1 -0
  477. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +59 -0
  478. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +495 -0
  479. package/dist/worker/console/static-src/operator-chat/useChatStream.js +780 -0
  480. package/dist/worker/console/static-src/operator-chat/useChatThread.js +280 -0
  481. package/dist/worker/console/static-src/operator-chat/useComposer.js +257 -0
  482. package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
  483. package/dist/worker/console/static-src/operator-chat/useOverlayFocus.js +84 -0
  484. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +148 -0
  485. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +358 -0
  486. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +196 -0
  487. package/dist/worker/console/static-src/operator-chat/useWorkspaceLayout.js +58 -0
  488. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +35 -0
  489. package/dist/worker/console/static-src/prd-file-import.js +218 -0
  490. package/dist/worker/console/vite.config.js +27 -0
  491. package/dist/worker/console/workflow-kinds.js +46 -0
  492. package/dist/worker/delivery/final-verification.js +302 -0
  493. package/dist/worker/delivery/git-transaction.js +497 -0
  494. package/dist/worker/delivery/package.js +537 -0
  495. package/dist/worker/delivery/verification-bundle.js +544 -0
  496. package/dist/worker/feature/acceptance-policy.js +227 -0
  497. package/dist/worker/feature/advance.js +301 -0
  498. package/dist/worker/feature/decision-loader.js +99 -0
  499. package/dist/worker/feature/discover.js +14 -0
  500. package/dist/worker/feature/doctor.js +223 -0
  501. package/dist/worker/feature/fullstack-validate.js +337 -0
  502. package/dist/worker/feature/next-action.js +140 -0
  503. package/dist/worker/feature/profile-schema.js +47 -0
  504. package/dist/worker/feature/ready-plan-projection.js +82 -0
  505. package/dist/worker/feature/reducer.js +137 -0
  506. package/dist/worker/feature/review.js +678 -0
  507. package/dist/worker/feature/run.js +390 -0
  508. package/dist/worker/feature/scaffold.js +812 -0
  509. package/dist/worker/feature/types.js +1 -0
  510. package/dist/worker/follow-up/approve.js +273 -0
  511. package/dist/worker/follow-up/factory.js +234 -0
  512. package/dist/worker/follow-up/paths.js +25 -0
  513. package/dist/worker/follow-up/policy.js +26 -0
  514. package/dist/worker/follow-up/schema.js +93 -0
  515. package/dist/worker/follow-up/store.js +96 -0
  516. package/dist/worker/loop-agent/command-result.js +1 -0
  517. package/dist/worker/loop-agent/controller-protocol.js +143 -0
  518. package/dist/worker/loop-agent/loop-agent-client.js +489 -0
  519. package/dist/worker/loop-agent/parse-json.js +14 -0
  520. package/dist/worker/materialize/harness-task-lifecycle-probe.js +126 -0
  521. package/dist/worker/materialize/harness-task-lineage.js +220 -0
  522. package/dist/worker/materialize/harness-task-materializer.js +648 -0
  523. package/dist/worker/metrics/projector.js +139 -0
  524. package/dist/worker/observability/dag-execution-trajectory.js +591 -0
  525. package/dist/worker/observability/event-history.js +216 -0
  526. package/dist/worker/observability/event-store.js +83 -0
  527. package/dist/worker/observability/events.js +79 -0
  528. package/dist/worker/observability/interrupt-eligibility.js +264 -0
  529. package/dist/worker/observability/progress-composite.js +34 -0
  530. package/dist/worker/observability/read-model.js +2499 -0
  531. package/dist/worker/observability/snapshot-store.js +43 -0
  532. package/dist/worker/observability/types.js +1 -0
  533. package/dist/worker/observe/dag-node-execution-output.js +180 -0
  534. package/dist/worker/observe/dag-run-artifacts.js +90 -0
  535. package/dist/worker/observe/health.js +58 -0
  536. package/dist/worker/observe/night-jobs.js +104 -0
  537. package/dist/worker/observe/node-input.js +441 -0
  538. package/dist/worker/observe/paths.js +174 -0
  539. package/dist/worker/observe/routes.js +1077 -0
  540. package/dist/worker/observe/server.js +117 -0
  541. package/dist/worker/observe/spec-evidence.js +398 -0
  542. package/dist/worker/observe/static/api.js +117 -0
  543. package/dist/worker/observe/static/app.js +169 -0
  544. package/dist/worker/observe/static/constants.js +182 -0
  545. package/dist/worker/observe/static/copy.js +67 -0
  546. package/dist/worker/observe/static/custom-select.js +567 -0
  547. package/dist/worker/observe/static/dag-edge-routing.js +368 -0
  548. package/dist/worker/observe/static/dag-helpers.js +161 -0
  549. package/dist/worker/observe/static/dag-history-labels.js +95 -0
  550. package/dist/worker/observe/static/dag-layout.d.ts +36 -0
  551. package/dist/worker/observe/static/dag-layout.js +163 -0
  552. package/dist/worker/observe/static/dag-model.js +145 -0
  553. package/dist/worker/observe/static/dom.js +220 -0
  554. package/dist/worker/observe/static/favicon.svg +37 -0
  555. package/dist/worker/observe/static/format-pool.d.ts +71 -0
  556. package/dist/worker/observe/static/format-pool.js +153 -0
  557. package/dist/worker/observe/static/format.js +347 -0
  558. package/dist/worker/observe/static/index.html +409 -0
  559. package/dist/worker/observe/static/kpi.js +78 -0
  560. package/dist/worker/observe/static/markdown-render.js +124 -0
  561. package/dist/worker/observe/static/operator-chrome.css +517 -0
  562. package/dist/worker/observe/static/operator-chrome.d.ts +83 -0
  563. package/dist/worker/observe/static/operator-chrome.js +623 -0
  564. package/dist/worker/observe/static/relations.js +133 -0
  565. package/dist/worker/observe/static/router.js +156 -0
  566. package/dist/worker/observe/static/run-processing.js +148 -0
  567. package/dist/worker/observe/static/shell-chrome.js +65 -0
  568. package/dist/worker/observe/static/state.js +496 -0
  569. package/dist/worker/observe/static/styles.css +3567 -0
  570. package/dist/worker/observe/static/views/batch.js +227 -0
  571. package/dist/worker/observe/static/views/dag-graph.js +508 -0
  572. package/dist/worker/observe/static/views/dag-inspector.js +1722 -0
  573. package/dist/worker/observe/static/views/dag-trajectory.js +313 -0
  574. package/dist/worker/observe/static/views/dag.js +483 -0
  575. package/dist/worker/observe/static/views/dags.js +877 -0
  576. package/dist/worker/observe/static/views/dashboard.js +747 -0
  577. package/dist/worker/observe/static/views/failures.js +143 -0
  578. package/dist/worker/observe/static/views/feature.js +492 -0
  579. package/dist/worker/observe/static/views/night.js +201 -0
  580. package/dist/worker/observe/static/views/pool.js +708 -0
  581. package/dist/worker/observe/static/views/run.js +453 -0
  582. package/dist/worker/observe/static/views/session-timeline.js +771 -0
  583. package/dist/worker/observe/static/views/shell.js +7 -0
  584. package/dist/worker/observe/static/views/task.js +315 -0
  585. package/dist/worker/observe/static/views/timeline.js +163 -0
  586. package/dist/worker/outcomes/adapters.js +153 -0
  587. package/dist/worker/outcomes/declared-artifacts.js +103 -0
  588. package/dist/worker/outcomes/evidence-tokens.js +29 -0
  589. package/dist/worker/outcomes/gate.js +40 -0
  590. package/dist/worker/outcomes/projector.js +227 -0
  591. package/dist/worker/outcomes/registry.js +1 -0
  592. package/dist/worker/outcomes/store.js +131 -0
  593. package/dist/worker/outcomes/types.js +79 -0
  594. package/dist/worker/pool/attempt-identity.js +41 -0
  595. package/dist/worker/pool/attempt-lease.js +184 -0
  596. package/dist/worker/pool/attempt-transition.js +210 -0
  597. package/dist/worker/pool/begin-attempt-with-lease.js +26 -0
  598. package/dist/worker/pool/begin-attempt.js +35 -0
  599. package/dist/worker/pool/doctor.js +165 -0
  600. package/dist/worker/pool/failure-routing.js +182 -0
  601. package/dist/worker/pool/migrate-state.js +303 -0
  602. package/dist/worker/pool/reconcile.js +285 -0
  603. package/dist/worker/pool/recovery-decision.js +163 -0
  604. package/dist/worker/pool/run-owner-store.js +126 -0
  605. package/dist/worker/pool/run-store.js +360 -0
  606. package/dist/worker/pool/runtime-reconcile-inventory.js +127 -0
  607. package/dist/worker/pool/state-projection.js +57 -0
  608. package/dist/worker/pool/types.js +17 -0
  609. package/dist/worker/pool/validation.js +144 -0
  610. package/dist/worker/preflight.js +157 -0
  611. package/dist/worker/profile-mapping.js +76 -0
  612. package/dist/worker/progress-reporter.js +63 -0
  613. package/dist/worker/report/morning-report.js +155 -0
  614. package/dist/worker/repos/repo-resolver.js +23 -0
  615. package/dist/worker/run-task/execute-prepared-task.js +178 -0
  616. package/dist/worker/run-task/run-task.js +912 -0
  617. package/dist/worker/runner/run-ready.js +561 -0
  618. package/dist/worker/runner/single-task-attempt.js +176 -0
  619. package/dist/worker/scheduler/admission.js +547 -0
  620. package/dist/worker/scheduler/auto-followup.js +99 -0
  621. package/dist/worker/scheduler/cli.js +644 -0
  622. package/dist/worker/scheduler/clock-install/darwin-launchd.js +238 -0
  623. package/dist/worker/scheduler/clock-install/linux-systemd-user.js +224 -0
  624. package/dist/worker/scheduler/clock-install/types.js +1 -0
  625. package/dist/worker/scheduler/clock-install/win32-schtasks.js +227 -0
  626. package/dist/worker/scheduler/clock-install.js +284 -0
  627. package/dist/worker/scheduler/clock.js +420 -0
  628. package/dist/worker/scheduler/dispatcher.js +503 -0
  629. package/dist/worker/scheduler/doctor.js +431 -0
  630. package/dist/worker/scheduler/evidence.js +170 -0
  631. package/dist/worker/scheduler/git-base.js +90 -0
  632. package/dist/worker/scheduler/index.js +25 -0
  633. package/dist/worker/scheduler/lease.js +114 -0
  634. package/dist/worker/scheduler/lifecycle.js +348 -0
  635. package/dist/worker/scheduler/lock.js +80 -0
  636. package/dist/worker/scheduler/morning-window.js +191 -0
  637. package/dist/worker/scheduler/night-git-finalizer.js +114 -0
  638. package/dist/worker/scheduler/night-harvest.js +412 -0
  639. package/dist/worker/scheduler/paths.js +92 -0
  640. package/dist/worker/scheduler/prepared-attempt-recovery.js +471 -0
  641. package/dist/worker/scheduler/recovery.js +277 -0
  642. package/dist/worker/scheduler/reservation.js +146 -0
  643. package/dist/worker/scheduler/retry.js +199 -0
  644. package/dist/worker/scheduler/scheduler-loop.js +272 -0
  645. package/dist/worker/scheduler/store.js +275 -0
  646. package/dist/worker/scheduler/traceability.js +54 -0
  647. package/dist/worker/scheduler/trigger.js +258 -0
  648. package/dist/worker/scheduler/types.js +369 -0
  649. package/dist/worker/scheduler/workspace-adapter.js +128 -0
  650. package/dist/worker/task-graph/acceptance-schema.js +40 -0
  651. package/dist/worker/task-graph/ready-planner.js +267 -0
  652. package/dist/worker/task-graph/ready-queue.js +23 -0
  653. package/dist/worker/task-graph/task-graph-schema.js +59 -0
  654. package/dist/worker/task-graph/types.js +1 -0
  655. package/dist/worker/task-graph/validate.js +293 -0
  656. package/dist/worker/task-spec/complexity-mapping.js +8 -0
  657. package/dist/worker/task-spec/schema.js +125 -0
  658. package/dist/worker/task-spec/types.js +1 -0
  659. package/dist/worker/task-spec/validate.js +391 -0
  660. package/dist/worker/task-spec/workflow-routing.js +149 -0
  661. package/dist/workflows/dag/authoring.js +8 -0
  662. package/dist/workflows/dag/authority-surface.js +138 -0
  663. package/dist/workflows/dag/backend-test-analysis-contract.js +507 -0
  664. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +1985 -0
  665. package/dist/workflows/dag/backend-test-case-manifest.js +638 -0
  666. package/dist/workflows/dag/backend-test-classification-contract.js +38 -0
  667. package/dist/workflows/dag/backend-test-contract-envelope.js +205 -0
  668. package/dist/workflows/dag/backend-test-coverage-contract.js +202 -0
  669. package/dist/workflows/dag/backend-test-execution-contract.js +715 -0
  670. package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
  671. package/dist/workflows/dag/backend-test-intake-context.js +178 -0
  672. package/dist/workflows/dag/backend-test-layout.js +133 -0
  673. package/dist/workflows/dag/backend-test-markdown-workflow.js +1984 -0
  674. package/dist/workflows/dag/backend-test-module-stem.js +26 -0
  675. package/dist/workflows/dag/backend-test-pytest-collection.js +859 -0
  676. package/dist/workflows/dag/backend-test-result-contract.js +1030 -0
  677. package/dist/workflows/dag/backend-test-scenario-param.js +1670 -0
  678. package/dist/workflows/dag/backend-test-scenario-partitions.js +249 -0
  679. package/dist/workflows/dag/backend-test-semantic-review-contract.js +36 -0
  680. package/dist/workflows/dag/backend-test-stability-contract.js +57 -0
  681. package/dist/workflows/dag/backend-test-writer-completeness.js +1058 -0
  682. package/dist/workflows/dag/budget-enforcement.js +67 -0
  683. package/dist/workflows/dag/canvas-observer.js +474 -0
  684. package/dist/workflows/dag/context-policy.js +137 -0
  685. package/dist/workflows/dag/contract-output-registry.js +14 -0
  686. package/dist/workflows/dag/contract-validator-registrations.js +10 -0
  687. package/dist/workflows/dag/controller-identity.js +104 -0
  688. package/dist/workflows/dag/convergence/controller.js +596 -0
  689. package/dist/workflows/dag/decision-envelope.js +557 -0
  690. package/dist/workflows/dag/decision-evidence.js +153 -0
  691. package/dist/workflows/dag/decision-gates.js +1 -0
  692. package/dist/workflows/dag/dynamic-runtime/condition.js +48 -0
  693. package/dist/workflows/dag/dynamic-runtime/loop-until.js +161 -0
  694. package/dist/workflows/dag/dynamic-runtime/map.js +435 -0
  695. package/dist/workflows/dag/dynamic-runtime/reduction.js +72 -0
  696. package/dist/workflows/dag/dynamic-runtime/shared.js +203 -0
  697. package/dist/workflows/dag/event-observer.js +132 -0
  698. package/dist/workflows/dag/executor-registry.js +23 -0
  699. package/dist/workflows/dag/facts.js +4 -0
  700. package/dist/workflows/dag/failure-category.js +118 -0
  701. package/dist/workflows/dag/failure-routing.js +105 -0
  702. package/dist/workflows/dag/final-verification.js +180 -0
  703. package/dist/workflows/dag/frontend-implementation-contract.js +1911 -0
  704. package/dist/workflows/dag/frontend-lint-baseline.js +474 -0
  705. package/dist/workflows/dag/frontend-plan-render.js +90 -0
  706. package/dist/workflows/dag/frontend-prewrite-gate.js +1011 -0
  707. package/dist/workflows/dag/frontend-project-capability.js +1 -0
  708. package/dist/workflows/dag/frontend-recovery-plan.js +73 -0
  709. package/dist/workflows/dag/frontend-recovery-root-manifest.js +123 -0
  710. package/dist/workflows/dag/frontend-recovery-run.js +539 -0
  711. package/dist/workflows/dag/frontend-repair.js +541 -0
  712. package/dist/workflows/dag/frontend-review-context.js +116 -0
  713. package/dist/workflows/dag/frontend-risk.js +161 -0
  714. package/dist/workflows/dag/frontend-test-case-checklist.js +343 -0
  715. package/dist/workflows/dag/frontend-test-case-manifest.js +104 -0
  716. package/dist/workflows/dag/frontend-test-case-quality.js +105 -0
  717. package/dist/workflows/dag/frontend-test-html-report.js +174 -0
  718. package/dist/workflows/dag/frontend-test-l5-report.js +177 -0
  719. package/dist/workflows/dag/frontend-test-result-contract.js +487 -0
  720. package/dist/workflows/dag/frontend-verification-trace.js +275 -0
  721. package/dist/workflows/dag/frontend-worktree-diff.js +201 -0
  722. package/dist/workflows/dag/frontend-writer-recovery.js +106 -0
  723. package/dist/workflows/dag/frontend-writer-rollback.js +821 -0
  724. package/dist/workflows/dag/governance-constants.js +5 -0
  725. package/dist/workflows/dag/governance-profile.js +413 -0
  726. package/dist/workflows/dag/index.js +6 -0
  727. package/dist/workflows/dag/init-hybrid.js +7345 -0
  728. package/dist/workflows/dag/interrupt-request.js +559 -0
  729. package/dist/workflows/dag/knowledge-curator.js +165 -0
  730. package/dist/workflows/dag/l5-report-metrics.js +33 -0
  731. package/dist/workflows/dag/lifecycle.js +855 -0
  732. package/dist/workflows/dag/liveness-policy.js +251 -0
  733. package/dist/workflows/dag/node-execution.js +1081 -0
  734. package/dist/workflows/dag/observer-compose.js +52 -0
  735. package/dist/workflows/dag/output-protocol.js +356 -0
  736. package/dist/workflows/dag/project-governance-context.js +514 -0
  737. package/dist/workflows/dag/prompt-source.js +88 -0
  738. package/dist/workflows/dag/prompt.js +348 -0
  739. package/dist/workflows/dag/reconcile-run.js +145 -0
  740. package/dist/workflows/dag/reconcile-tasks.js +404 -0
  741. package/dist/workflows/dag/recovery-recommendation.js +329 -0
  742. package/dist/workflows/dag/repair-artifact.js +363 -0
  743. package/dist/workflows/dag/report.js +1229 -0
  744. package/dist/workflows/dag/rerun-plan.js +614 -0
  745. package/dist/workflows/dag/rerun-run.js +556 -0
  746. package/dist/workflows/dag/rerun-task.js +370 -0
  747. package/dist/workflows/dag/retry-policy.js +263 -0
  748. package/dist/workflows/dag/run-store.js +41 -0
  749. package/dist/workflows/dag/runner.js +1069 -0
  750. package/dist/workflows/dag/runtime-contract.js +87 -0
  751. package/dist/workflows/dag/runtime.js +5 -0
  752. package/dist/workflows/dag/scheduler.js +497 -0
  753. package/dist/workflows/dag/sdd-embedded.js +128 -0
  754. package/dist/workflows/dag/skill-instructions.js +478 -0
  755. package/dist/workflows/dag/skill-snapshot.js +553 -0
  756. package/dist/workflows/dag/skills.js +41 -0
  757. package/dist/workflows/dag/spec.js +3 -0
  758. package/dist/workflows/dag/task-contract-binding.js +138 -0
  759. package/dist/workflows/dag/task-demand-routing.js +1 -0
  760. package/dist/workflows/dag/topo.js +30 -0
  761. package/dist/workflows/dag/types.js +1145 -0
  762. package/dist/workflows/dag/upstream-artifacts.js +98 -0
  763. package/dist/workflows/dag/validate.js +998 -0
  764. package/dist/workflows/dag/workspace-checkpoint.js +186 -0
  765. package/dist/workflows/dynamic/artifacts.js +65 -0
  766. package/dist/workflows/dynamic/compile.js +371 -0
  767. package/dist/workflows/dynamic/compileTypes.js +1 -0
  768. package/dist/workflows/dynamic/errors.js +5 -0
  769. package/dist/workflows/dynamic/index.js +7 -0
  770. package/dist/workflows/dynamic/profiles.js +156 -0
  771. package/dist/workflows/dynamic/spec.js +115 -0
  772. package/dist/workflows/dynamic/validate.js +275 -0
  773. package/dist/workflows/loop/actions/dag-action.js +129 -0
  774. package/dist/workflows/loop/actions/pi-review.js +267 -0
  775. package/dist/workflows/loop/actions/shared.js +157 -0
  776. package/dist/workflows/loop/actions/shell-verify.js +82 -0
  777. package/dist/workflows/loop/actions/types.js +1 -0
  778. package/dist/workflows/loop/actions/workflow-action.js +255 -0
  779. package/dist/workflows/loop/actions.js +160 -0
  780. package/dist/workflows/loop/benchmark.js +510 -0
  781. package/dist/workflows/loop/closeout.js +135 -0
  782. package/dist/workflows/loop/context.js +47 -0
  783. package/dist/workflows/loop/events.js +26 -0
  784. package/dist/workflows/loop/hash.js +32 -0
  785. package/dist/workflows/loop/index.js +8 -0
  786. package/dist/workflows/loop/paths.js +17 -0
  787. package/dist/workflows/loop/policy/auto-policy.js +112 -0
  788. package/dist/workflows/loop/policy/path-patterns.js +13 -0
  789. package/dist/workflows/loop/rounds.js +81 -0
  790. package/dist/workflows/loop/signals.js +52 -0
  791. package/dist/workflows/loop/state.js +116 -0
  792. package/dist/workflows/loop/templates.js +54 -0
  793. package/dist/workflows/loop/types.js +28 -0
  794. package/docs/README.md +82 -0
  795. package/docs/architecture/README.md +35 -0
  796. package/docs/architecture/dag-execution.md +165 -0
  797. package/docs/architecture/evolution.md +108 -0
  798. package/docs/architecture/facts-and-state.md +74 -0
  799. package/docs/architecture/runtime-boundaries.md +226 -0
  800. package/docs/architecture/system-overview.md +100 -0
  801. package/docs/architecture/worker-and-feature.md +122 -0
  802. package/docs/governance/README.md +18 -0
  803. package/docs/governance/harness-methodology-debugging.md +177 -0
  804. package/docs/governance/harness-methodology-tdd.md +130 -0
  805. package/docs/governance/harness-methodology-verification.md +27 -0
  806. package/docs/init-surface.manifest.json +358 -0
  807. package/docs/operations/README.md +15 -0
  808. package/docs/operations/local-development-environment.md +77 -0
  809. package/docs/skills/README.md +7 -0
  810. package/docs/skills/vetted-skill-registry.md +49 -0
  811. package/docs/templates/README.md +67 -0
  812. package/docs/templates/adr.md +60 -0
  813. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -0
  814. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -0
  815. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -0
  816. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -0
  817. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -0
  818. package/docs/templates/agent-dag-report.schema.json +473 -0
  819. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -0
  820. package/docs/templates/agent-dag.base.json +190 -0
  821. package/docs/templates/agent-dag.final-verification.json +185 -0
  822. package/docs/templates/agent-dag.schema.json +590 -0
  823. package/docs/templates/agent-dag.supervised-implementation.json +640 -0
  824. package/docs/templates/agent-worker-production-readiness-checklist.md +47 -0
  825. package/docs/templates/backend-test-analysis.schema.json +37 -0
  826. package/docs/templates/backend-test-case-manifest.schema.json +223 -0
  827. package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
  828. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +41 -0
  829. package/docs/templates/backend-test-dag.json +671 -0
  830. package/docs/templates/backend-test-dag.retrospect.prompt.md +165 -0
  831. package/docs/templates/backend-test-dag.review-cases.prompt.md +36 -0
  832. package/docs/templates/backend-test-execution.schema.json +138 -0
  833. package/docs/templates/backend-test-result.schema.json +99 -0
  834. package/docs/templates/branch-merge-report.md +115 -0
  835. package/docs/templates/evaluation/agents-map-slim-v1.candidate.json +9 -0
  836. package/docs/templates/evaluation/agents-map-slim-v1.md +87 -0
  837. package/docs/templates/evaluation/agents-map-verbose-v0.candidate.json +9 -0
  838. package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -0
  839. package/docs/templates/evaluation/campaign-budget-v1.json +12 -0
  840. package/docs/templates/evaluation/campaign-dogfood-v0.json +24 -0
  841. package/docs/templates/evaluation/campaign-evidence-v1.json +44 -0
  842. package/docs/templates/evaluation/context-policy-baseline-v1.json +17 -0
  843. package/docs/templates/evaluation/context-policy-role-specialized-v1.json +28 -0
  844. package/docs/templates/evaluation/corpus-dogfood-v0.manifest.json +118 -0
  845. package/docs/templates/evaluation/matrix-dag-dry-run-v1.json +21 -0
  846. package/docs/templates/evaluation/matrix-fixture-v1.json +10 -0
  847. package/docs/templates/evaluation/private-verifier-dogfood-v0.json +16 -0
  848. package/docs/templates/exec-plan.md +66 -0
  849. package/docs/templates/feature-spec.md +53 -0
  850. package/docs/templates/frontend-design-contract.md +68 -0
  851. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
  852. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
  853. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
  854. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
  855. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
  856. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
  857. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
  858. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
  859. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
  860. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
  861. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
  862. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
  863. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
  864. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
  865. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
  866. package/docs/templates/frontend-eval/metrics.md +138 -0
  867. package/docs/templates/frontend-eval/smoke-targets.md +53 -0
  868. package/docs/templates/frontend-implementation-contract.schema.json +35 -0
  869. package/docs/templates/frontend-task-constraints.md +62 -0
  870. package/docs/templates/frontend-task-requirement.md +104 -0
  871. package/docs/templates/frontend-test-case-checklist.md +44 -0
  872. package/docs/templates/frontend-test-case.example.md +143 -0
  873. package/docs/templates/frontend-test-dag.generate-cases.prompt.md +63 -0
  874. package/docs/templates/frontend-test-dag.json +409 -0
  875. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +21 -0
  876. package/docs/templates/frontend-test-dag.retrospect.prompt.md +5 -0
  877. package/docs/templates/frontend-test-dag.review-cases.prompt.md +5 -0
  878. package/docs/templates/frontend-test-dag.review-execution.prompt.md +3 -0
  879. package/docs/templates/frontend-test-standard-scenarios.v1.json +159 -0
  880. package/docs/templates/harness.schema.json +351 -0
  881. package/docs/templates/hybrid-dag.json +188 -0
  882. package/docs/templates/init-evolution-review.md +35 -0
  883. package/docs/templates/init-managed-agents.md +157 -0
  884. package/docs/templates/interactive-ui-round2-experiment.md +66 -0
  885. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -0
  886. package/docs/templates/knowledge-sync-dag.json +178 -0
  887. package/docs/templates/knowledge-sync-draft.schema.json +71 -0
  888. package/docs/templates/product-line/AGENTS.md +9 -0
  889. package/docs/templates/product-line/README.md +42 -0
  890. package/docs/templates/product-line/acceptance.yaml +23 -0
  891. package/docs/templates/product-line/closeout.yaml +9 -0
  892. package/docs/templates/product-line/design.md +13 -0
  893. package/docs/templates/product-line/feature-scaffold-batch.example.yaml +31 -0
  894. package/docs/templates/product-line/feature.yaml +11 -0
  895. package/docs/templates/product-line/links.md +10 -0
  896. package/docs/templates/product-line/requirement.md +17 -0
  897. package/docs/templates/product-line/scaffold-samples/README.md +36 -0
  898. package/docs/templates/product-line/scaffold-samples/backend-only/acceptance.yaml +16 -0
  899. package/docs/templates/product-line/scaffold-samples/backend-only/design.md +14 -0
  900. package/docs/templates/product-line/scaffold-samples/backend-only/feature.yaml +14 -0
  901. package/docs/templates/product-line/scaffold-samples/backend-only/requirement.md +6 -0
  902. package/docs/templates/product-line/scaffold-samples/backend-only/tasks/BE-IMPL-001.yaml +83 -0
  903. package/docs/templates/product-line/scaffold-samples/backend-only/tasks/task-graph.yaml +14 -0
  904. package/docs/templates/product-line/scaffold-samples/fe-with-api/acceptance.yaml +18 -0
  905. package/docs/templates/product-line/scaffold-samples/fe-with-api/design.md +18 -0
  906. package/docs/templates/product-line/scaffold-samples/fe-with-api/feature.yaml +14 -0
  907. package/docs/templates/product-line/scaffold-samples/fe-with-api/requirement.md +6 -0
  908. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/BE-IMPL-001.yaml +84 -0
  909. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/CONTRACT-001.yaml +84 -0
  910. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/FE-IMPL-001.yaml +86 -0
  911. package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/task-graph.yaml +40 -0
  912. package/docs/templates/product-line/scaffold-samples/frontend-only/acceptance.yaml +16 -0
  913. package/docs/templates/product-line/scaffold-samples/frontend-only/design.md +14 -0
  914. package/docs/templates/product-line/scaffold-samples/frontend-only/feature.yaml +14 -0
  915. package/docs/templates/product-line/scaffold-samples/frontend-only/requirement.md +6 -0
  916. package/docs/templates/product-line/scaffold-samples/frontend-only/tasks/FE-IMPL-001.yaml +85 -0
  917. package/docs/templates/product-line/scaffold-samples/frontend-only/tasks/task-graph.yaml +14 -0
  918. package/docs/templates/product-line/task-graph.yaml +23 -0
  919. package/docs/templates/product-line/task.yaml +68 -0
  920. package/docs/templates/product-line/test-plan.md +7 -0
  921. package/docs/templates/production-readiness-checklist.md +57 -0
  922. package/docs/templates/progress-log.md +27 -0
  923. package/docs/templates/project-start-checklist.md +9 -0
  924. package/docs/templates/qa-report.md +48 -0
  925. package/docs/templates/sprint-contract.md +29 -0
  926. package/docs/templates/worker-dogfood-evidence.md +80 -0
  927. package/docs/templates/worker-dogfood-setup.md +68 -0
  928. package/examples/decision-gate-agent-dag.json +177 -0
  929. package/examples/example-dag.json +46 -0
  930. package/examples/hybrid-loop-agent-dag.json +189 -0
  931. package/examples/l5-report-coms-process-definition.html +322 -0
  932. package/examples/l5-report-demo.html +79 -0
  933. package/harness.json +90 -0
  934. package/package.json +98 -0
  935. package/scripts/kb-bootstrap-init-skeleton.sh +241 -0
  936. package/scripts/kb-graph-incremental-prepare.mjs +386 -0
  937. package/scripts/kb-graph-materialize.mjs +105 -0
  938. package/scripts/kb-graph-promote.mjs +164 -0
  939. package/scripts/kb-query.mjs +554 -0
  940. package/skills/agent-worker/SKILL.md +48 -0
  941. package/skills/agent-worker/references/agent-worker-operator.md +159 -0
  942. package/skills/ai-engineering-context/SKILL.md +48 -0
  943. package/skills/analyze-product-dependencies/SKILL.md +108 -0
  944. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +35 -0
  945. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +38 -0
  946. package/skills/analyze-product-dependencies/references/example.md +76 -0
  947. package/skills/analyze-product-dependencies/references/forward-test-cases.md +110 -0
  948. package/skills/analyze-product-dependencies/references/input-contract.md +34 -0
  949. package/skills/analyze-product-dependencies/references/kb-integration.md +64 -0
  950. package/skills/analyze-product-dependencies/references/scouting-rules.md +76 -0
  951. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +460 -0
  952. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +194 -0
  953. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +200 -0
  954. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +90 -0
  955. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +138 -0
  956. package/skills/analyze-product-requirements/SKILL.md +145 -0
  957. package/skills/analyze-product-requirements/references/acceptance-criteria.md +87 -0
  958. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +70 -0
  959. package/skills/analyze-product-requirements/references/example.md +104 -0
  960. package/skills/analyze-product-requirements/references/forward-test-cases.md +144 -0
  961. package/skills/analyze-product-requirements/references/kb-integration.md +56 -0
  962. package/skills/analyze-product-requirements/references/product-analysis-schema.md +41 -0
  963. package/skills/analyze-product-requirements/references/product-requirement-schema.md +42 -0
  964. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +66 -0
  965. package/skills/analyze-product-requirements/scripts/compute-source-identity.mjs +35 -0
  966. package/skills/analyze-product-requirements/scripts/test-validators.mjs +501 -0
  967. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +100 -0
  968. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +109 -0
  969. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +109 -0
  970. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +175 -0
  971. package/skills/browser-tools/SKILL.md +196 -0
  972. package/skills/browser-tools/browser-content.js +103 -0
  973. package/skills/browser-tools/browser-cookies.js +35 -0
  974. package/skills/browser-tools/browser-eval.js +53 -0
  975. package/skills/browser-tools/browser-hn-scraper.js +108 -0
  976. package/skills/browser-tools/browser-nav.js +44 -0
  977. package/skills/browser-tools/browser-pick.js +162 -0
  978. package/skills/browser-tools/browser-screenshot.js +34 -0
  979. package/skills/browser-tools/browser-start.js +86 -0
  980. package/skills/browser-tools/package-lock.json +2556 -0
  981. package/skills/browser-tools/package.json +19 -0
  982. package/skills/code-review-core/SKILL.md +20 -0
  983. package/skills/codebase-scout/SKILL.md +19 -0
  984. package/skills/frontend-bounded-implement/SKILL.md +53 -0
  985. package/skills/frontend-design-review/SKILL.md +79 -0
  986. package/skills/frontend-design-review/references/review-checklist.md +43 -0
  987. package/skills/frontend-implementation/SKILL.md +52 -0
  988. package/skills/frontend-implementation/references/code-standards.md +33 -0
  989. package/skills/frontend-implementation/references/design-spec.md +56 -0
  990. package/skills/frontend-implementation/references/node-contracts.md +31 -0
  991. package/skills/frontend-review/SKILL.md +55 -0
  992. package/skills/frontend-review/references/review-findings.md +50 -0
  993. package/skills/frontend-verification/SKILL.md +61 -0
  994. package/skills/frontend-verification/references/verification-checklist.md +48 -0
  995. package/skills/grill-me/SKILL.md +10 -0
  996. package/skills/grill-with-docs/SKILL.md +80 -0
  997. package/skills/grill-with-docs/adr-format.md +58 -0
  998. package/skills/grill-with-docs/context-format.md +52 -0
  999. package/skills/init-capability-evolution/SKILL.md +70 -0
  1000. package/skills/local-jacoco-coverage/SKILL.md +281 -0
  1001. package/skills/local-jacoco-coverage/references/requirement-to-source-mapping.md +85 -0
  1002. package/skills/local-jacoco-coverage/references/runtime-alignment.md +106 -0
  1003. package/skills/local-jacoco-coverage/scripts/run-coverage-analysis.sh +148 -0
  1004. package/skills/local-jacoco-coverage/scripts/start-jacoco-agent.sh +110 -0
  1005. package/skills/loop-agent/SKILL.md +68 -0
  1006. package/skills/loop-agent/references/README.md +67 -0
  1007. package/skills/loop-agent/references/command-reference.md +645 -0
  1008. package/skills/loop-agent/references/docs-converge.md +126 -0
  1009. package/skills/loop-agent/references/harness-policy.md +289 -0
  1010. package/skills/loop-agent/references/hybrid-dag.md +245 -0
  1011. package/skills/loop-agent/references/learned/README.md +21 -0
  1012. package/skills/loop-agent/references/long-running-loop.md +57 -0
  1013. package/skills/loop-agent/references/model-routing.md +45 -0
  1014. package/skills/loop-agent/references/multi-worktree.md +54 -0
  1015. package/skills/loop-agent/references/one-shot-runs.md +84 -0
  1016. package/skills/loop-agent/references/orchestrator-and-interventions.md +175 -0
  1017. package/skills/loop-agent/references/pi-prompt.md +23 -0
  1018. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -0
  1019. package/skills/loop-agent/references/post-implementation-and-patterns.md +54 -0
  1020. package/skills/loop-agent/references/source-and-plan-practice.md +169 -0
  1021. package/skills/loop-agent/references/task-workflow.md +104 -0
  1022. package/skills/loop-agent/references/verification-and-failure-handling.md +148 -0
  1023. package/skills/playwright-cli/SKILL.md +93 -0
  1024. package/skills/playwright-cli/references/element-attributes.md +23 -0
  1025. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  1026. package/skills/playwright-cli/references/request-mocking.md +87 -0
  1027. package/skills/playwright-cli/references/running-code.md +241 -0
  1028. package/skills/playwright-cli/references/session-management.md +225 -0
  1029. package/skills/playwright-cli/references/storage-state.md +275 -0
  1030. package/skills/playwright-cli/references/test-generation.md +433 -0
  1031. package/skills/playwright-cli/references/tracing.md +5 -0
  1032. package/skills/playwright-cli/references/video-recording.md +5 -0
  1033. package/skills/playwright-cli-case-generator/SKILL.md +108 -0
  1034. package/skills/requesting-code-review/SKILL.md +101 -0
  1035. package/skills/requesting-code-review/code-reviewer.md +168 -0
  1036. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  1037. package/skills/systematic-debugging/SKILL.md +312 -0
  1038. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  1039. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  1040. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  1041. package/skills/systematic-debugging/find-polluter.sh +63 -0
  1042. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  1043. package/skills/systematic-debugging/test-academic.md +14 -0
  1044. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  1045. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  1046. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  1047. package/skills/test-driven-development/SKILL.md +27 -0
  1048. package/skills/using-git-worktrees/SKILL.md +215 -0
  1049. package/skills/verification-before-completion/SKILL.md +154 -0
  1050. package/skills/webapp-testing/SKILL.md +19 -0
@@ -0,0 +1,3630 @@
1
+ import { createHash } from "node:crypto";
2
+ import { access, copyFile, mkdir, readdir, readFile, rename, rm, rmdir, stat, writeFile, } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { isDeepStrictEqual } from "node:util";
6
+ import { fileURLToPath } from "node:url";
7
+ import { isInitRuntimeActive, } from "../shared/runtime-activity.js";
8
+ import { loadHarnessManifest } from "../governance/harness.js";
9
+ import { OPENCODE_CONTEXT_OVERFLOW_COMPACT_PLUGIN_PATH, OPENCODE_TRANSIENT_RETRY_PLUGIN_PATH, PI_CONTEXT_OVERFLOW_EXTENSION_PATH, PI_PROJECT_SETTINGS_PATH, applyPiRetryMerge, applyProjectPiSettingsMerge, buildOpenCodeContextOverflowCompactPluginSource, buildProjectPiSettingsSource, buildOpenCodeTransientRetryPluginSource, buildPiContextOverflowExtensionSource, inspectProjectPiSettings, parseClientRecoveryMode, runClientRecovery, } from "./client-recovery.js";
10
+ const MANAGED_BLOCK_START = "<!-- LOOP_AGENT_INIT_START -->";
11
+ const MANAGED_BLOCK_END = "<!-- LOOP_AGENT_INIT_END -->";
12
+ const GITIGNORE_BLOCK_START = "# LOOP_AGENT_INIT_START";
13
+ const GITIGNORE_BLOCK_END = "# LOOP_AGENT_INIT_END";
14
+ const INIT_SURFACE_STATE_PATH = ".harness/init-surface.json";
15
+ const DEFAULT_GOVERNANCE_ROOT = "ai_workspace/loop-agent";
16
+ const CORE_DOC_FILES = [
17
+ { source: "README.md", target: "README.md" },
18
+ {
19
+ source: "architecture/runtime-boundaries.md",
20
+ target: "architecture/runtime-boundaries.md",
21
+ },
22
+ {
23
+ source: "governance/development-principles.md",
24
+ target: "development-principles.md",
25
+ },
26
+ { source: "governance/feature-workflow.md", target: "feature-workflow.md" },
27
+ {
28
+ source: "governance/verification-matrix.md",
29
+ target: "verification-matrix.md",
30
+ },
31
+ { source: "runtime/loop-agent-harness.md", target: "loop-agent-harness.md" },
32
+ {
33
+ source: "governance/harness-methodology-tdd.md",
34
+ target: "harness-methodology-tdd.md",
35
+ },
36
+ {
37
+ source: "governance/harness-methodology-verification.md",
38
+ target: "harness-methodology-verification.md",
39
+ },
40
+ {
41
+ source: "governance/harness-methodology-debugging.md",
42
+ target: "harness-methodology-debugging.md",
43
+ },
44
+ { source: "templates/README.md", target: "templates/README.md" },
45
+ ];
46
+ function coreDocSourceForTarget(target) {
47
+ return (CORE_DOC_FILES.find((entry) => entry.target === target)?.source ?? target);
48
+ }
49
+ const GOVERNANCE_README_DIRS = [
50
+ "decisions",
51
+ "design",
52
+ "exec-plans",
53
+ "exec-plans/active",
54
+ "exec-plans/completed",
55
+ "progress",
56
+ "reports",
57
+ ];
58
+ const DAG_HARD_GATE_TRIGGER = "For changes that affect the project's public contract, execution surface, delivery pipeline, automation/governance, data model, security or permission model, cross-module behavior, or user-visible workflows, this is a hard pre-edit gate: create the task, write both source files, generate the DAG, and review the DAG/writeSet before editing implementation files.";
59
+ const COMPAT_PROMPTS = {
60
+ "analyze.md": [
61
+ "# Role: Task Analyst",
62
+ "",
63
+ "Read the repository entrypoints, task source materials, and relevant docs.",
64
+ "Return an analysis report covering objective, current state, affected files, constraints, risks, and open questions.",
65
+ "Do not modify files.",
66
+ "",
67
+ ].join("\n"),
68
+ "spec.md": [
69
+ "# Role: Specification Writer",
70
+ "",
71
+ "Turn the task source and analysis into a bounded implementation contract.",
72
+ "State scope, non-goals, acceptance criteria, allowed paths, forbidden paths, and verification commands.",
73
+ "Do not modify files outside task-owned artifacts.",
74
+ "",
75
+ ].join("\n"),
76
+ "plan.md": [
77
+ "# Role: Implementation Planner",
78
+ "",
79
+ "Create a minimal, ordered, verifiable implementation plan from the task source and analysis.",
80
+ "Keep the plan aligned with allowed paths, forbidden paths, hard constraints, rollback points, and verification.",
81
+ "Do not modify implementation files.",
82
+ "",
83
+ ].join("\n"),
84
+ "implement.md": [
85
+ "# Role: Bounded Implementer",
86
+ "",
87
+ "Implement the approved plan with the smallest focused change.",
88
+ "Stay within allowed paths and writeSet. Preserve unrelated user changes. Do not use placeholder implementations.",
89
+ "",
90
+ ].join("\n"),
91
+ "verify.md": [
92
+ "# Role: Verifier",
93
+ "",
94
+ "Run or specify deterministic verification for the completed change.",
95
+ "Report exact commands, pass/fail results, residual risk, and whether acceptance criteria are covered.",
96
+ "",
97
+ ].join("\n"),
98
+ "retrospective.md": [
99
+ "# Role: Retrospective Writer",
100
+ "",
101
+ "Summarize what changed, why, verification evidence, risks, and follow-up work.",
102
+ "Promote durable lessons to docs, reports, progress, tests, scripts, or templates when appropriate.",
103
+ "",
104
+ ].join("\n"),
105
+ "feature-study-analyze.md": [
106
+ "# Role: Feature Study Analyst",
107
+ "",
108
+ "Analyze configured reference repos/docs read-only. Identify architecture, state, data flow, and transferable patterns.",
109
+ "Do not edit reference repositories.",
110
+ "",
111
+ ].join("\n"),
112
+ "feature-study-plan.md": [
113
+ "# Role: Feature Study Planner",
114
+ "",
115
+ "Produce a lightweight implementation plan from the feature-study analysis.",
116
+ "Prefer simple recoverable files and verifiable increments over heavyweight storage or broad rewrites.",
117
+ "",
118
+ ].join("\n"),
119
+ };
120
+ const GOVERNANCE_ROOT_TOKEN = "__LOOP_AGENT_GOVERNANCE_ROOT__";
121
+ const PROJECT_NAME_TOKEN = "__LOOP_AGENT_PROJECT_NAME__";
122
+ /** Package-shipped render source for AGENTS.md managed block; not projected to target repos. */
123
+ const MANAGED_AGENTS_TEMPLATE_PATH = "docs/templates/init-managed-agents.md";
124
+ /** Files under package assets that init must not copy into target projects. */
125
+ const PACKAGE_ONLY_SURFACE_FILES = new Set([MANAGED_AGENTS_TEMPLATE_PATH]);
126
+ let managedAgentsTemplateCache;
127
+ const INIT_CHECK_ENGINEERING_STRUCTURE_SH = `#!/usr/bin/env bash
128
+ set -euo pipefail
129
+
130
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
131
+ cd "\${ROOT_DIR}"
132
+
133
+ required_paths=(
134
+ "README.md"
135
+ "harness.json"
136
+ "AGENTS.md"
137
+ "${GOVERNANCE_ROOT_TOKEN}/README.md"
138
+ "${GOVERNANCE_ROOT_TOKEN}/development-principles.md"
139
+ "${GOVERNANCE_ROOT_TOKEN}/feature-workflow.md"
140
+ "${GOVERNANCE_ROOT_TOKEN}/verification-matrix.md"
141
+ "${GOVERNANCE_ROOT_TOKEN}/architecture/runtime-boundaries.md"
142
+ "${GOVERNANCE_ROOT_TOKEN}/templates"
143
+ ".agents/skills/loop-agent/SKILL.md"
144
+ ".harness/prompts/analyze.md"
145
+ ".harness/prompts/plan.md"
146
+ ".harness/tasks"
147
+ ".harness/dag-runs/active"
148
+ ".harness/dag-runs/completed"
149
+ ".harness/dag-runs/paused"
150
+ ".harness/runs/active"
151
+ ".harness/runs/completed"
152
+ ".harness/runs/failed"
153
+ "scripts/check-engineering-structure.sh"
154
+ "scripts/check-doc-index.sh"
155
+ "scripts/check-doc-links.sh"
156
+ "scripts/check-active-plan-status.sh"
157
+ "scripts/check-exec-plan-index-sync.sh"
158
+ "scripts/check-harness-runtime-clean.sh"
159
+ "scripts/check-architecture-boundaries.sh"
160
+ "scripts/check-skill-entry.sh"
161
+ "scripts/check-repo.sh"
162
+ "scripts/ci-governance.sh"
163
+ "scripts/ci-tests.sh"
164
+ "scripts/ci.sh"
165
+ )
166
+
167
+ missing=()
168
+ for item in "\${required_paths[@]}"; do
169
+ if [[ ! -e "\${item}" ]]; then
170
+ missing+=("\${item}")
171
+ fi
172
+ done
173
+
174
+ if (( \${#missing[@]} > 0 )); then
175
+ echo "loop-agent 初始化检查失败,缺少必要路径:"
176
+ for item in "\${missing[@]}"; do
177
+ echo " - \${item}"
178
+ done
179
+ exit 1
180
+ fi
181
+
182
+ echo "loop-agent 工程结构检查通过"
183
+ `;
184
+ const INIT_CHECK_DOC_INDEX_SH = `#!/usr/bin/env bash
185
+ set -euo pipefail
186
+
187
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
188
+ cd "\${ROOT_DIR}"
189
+
190
+ required_docs=(
191
+ "${GOVERNANCE_ROOT_TOKEN}/README.md"
192
+ "${GOVERNANCE_ROOT_TOKEN}/development-principles.md"
193
+ "${GOVERNANCE_ROOT_TOKEN}/feature-workflow.md"
194
+ "${GOVERNANCE_ROOT_TOKEN}/verification-matrix.md"
195
+ "${GOVERNANCE_ROOT_TOKEN}/architecture/runtime-boundaries.md"
196
+ "${GOVERNANCE_ROOT_TOKEN}/loop-agent-harness.md"
197
+ "${GOVERNANCE_ROOT_TOKEN}/harness-methodology-tdd.md"
198
+ "${GOVERNANCE_ROOT_TOKEN}/harness-methodology-verification.md"
199
+ "${GOVERNANCE_ROOT_TOKEN}/harness-methodology-debugging.md"
200
+ "${GOVERNANCE_ROOT_TOKEN}/templates"
201
+ "${GOVERNANCE_ROOT_TOKEN}/exec-plans/active/README.md"
202
+ "${GOVERNANCE_ROOT_TOKEN}/exec-plans/completed/README.md"
203
+ "${GOVERNANCE_ROOT_TOKEN}/progress/README.md"
204
+ "${GOVERNANCE_ROOT_TOKEN}/reports/README.md"
205
+ "${GOVERNANCE_ROOT_TOKEN}/decisions/README.md"
206
+ )
207
+
208
+ missing=()
209
+ for item in "\${required_docs[@]}"; do
210
+ if [[ ! -e "\${item}" ]]; then
211
+ missing+=("\${item}")
212
+ fi
213
+ done
214
+
215
+ if (( \${#missing[@]} > 0 )); then
216
+ echo "docs 索引检查失败,缺少必要文档或目录:" >&2
217
+ for item in "\${missing[@]}"; do
218
+ echo " - \${item}" >&2
219
+ done
220
+ exit 1
221
+ fi
222
+
223
+ echo "docs 索引检查通过"
224
+ `;
225
+ const INIT_CHECK_DOC_LINKS_SH = `#!/usr/bin/env bash
226
+ set -euo pipefail
227
+
228
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
229
+ cd "\${ROOT_DIR}"
230
+
231
+ if [[ ! -d "${GOVERNANCE_ROOT_TOKEN}" ]]; then
232
+ echo "docs 链接检查失败:缺少 ${GOVERNANCE_ROOT_TOKEN}/" >&2
233
+ exit 1
234
+ fi
235
+
236
+ missing=()
237
+ while IFS= read -r -d '' file; do
238
+ while IFS= read -r link; do
239
+ [[ -z "\${link}" ]] && continue
240
+ [[ "\${link}" == http://* || "\${link}" == https://* || "\${link}" == mailto:* || "\${link}" == "#"* ]] && continue
241
+ [[ "\${link}" == *"://"* ]] && continue
242
+ target="\${link%%#*}"
243
+ [[ -z "\${target}" ]] && continue
244
+ [[ "\${target}" == /* ]] && continue
245
+ base="$(dirname "\${file}")"
246
+ if [[ ! -e "\${base}/\${target}" ]]; then
247
+ missing+=("\${file}: \${link}")
248
+ fi
249
+ done < <(grep -Eo '\\[[^]]+\\]\\([^)]+\\)' "\${file}" | sed -E 's/^.*\\(([^)]+)\\)$/\\1/' || true)
250
+ done < <(find "${GOVERNANCE_ROOT_TOKEN}" -type f -name '*.md' -print0)
251
+
252
+ if (( \${#missing[@]} > 0 )); then
253
+ echo "docs 链接检查失败:" >&2
254
+ for item in "\${missing[@]}"; do
255
+ echo " - \${item}" >&2
256
+ done
257
+ exit 1
258
+ fi
259
+
260
+ echo "docs 链接检查通过"
261
+ `;
262
+ const INIT_CHECK_ACTIVE_PLAN_STATUS_SH = `#!/usr/bin/env bash
263
+ set -euo pipefail
264
+
265
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
266
+ cd "\${ROOT_DIR}"
267
+
268
+ active_dir="${GOVERNANCE_ROOT_TOKEN}/exec-plans/active"
269
+ [[ -d "\${active_dir}" ]] || { echo "active plan 检查失败:缺少 \${active_dir}" >&2; exit 1; }
270
+
271
+ bad=()
272
+ while IFS= read -r -d '' file; do
273
+ name="$(basename "\${file}")"
274
+ [[ "\${name}" == "README.md" ]] && continue
275
+ if grep -Eiq 'status:[[:space:]]*(completed|done|closed)|状态[::][[:space:]]*(已完成|完成|关闭)' "\${file}"; then
276
+ bad+=("\${file}")
277
+ fi
278
+ done < <(find "\${active_dir}" -maxdepth 1 -type f -name '*.md' -print0)
279
+
280
+ if (( \${#bad[@]} > 0 )); then
281
+ echo "active plan 状态检查失败:已完成计划不应留在 active 目录:" >&2
282
+ for item in "\${bad[@]}"; do
283
+ echo " - \${item}" >&2
284
+ done
285
+ exit 1
286
+ fi
287
+
288
+ echo "active plan 状态检查通过"
289
+ `;
290
+ const INIT_CHECK_EXEC_PLAN_INDEX_SYNC_SH = `#!/usr/bin/env bash
291
+ set -euo pipefail
292
+
293
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
294
+ cd "\${ROOT_DIR}"
295
+
296
+ required=(
297
+ "${GOVERNANCE_ROOT_TOKEN}/exec-plans/README.md"
298
+ "${GOVERNANCE_ROOT_TOKEN}/exec-plans/active/README.md"
299
+ "${GOVERNANCE_ROOT_TOKEN}/exec-plans/completed/README.md"
300
+ )
301
+
302
+ for item in "\${required[@]}"; do
303
+ if [[ ! -f "\${item}" ]]; then
304
+ echo "exec-plan 索引同步检查失败:缺少 \${item}" >&2
305
+ exit 1
306
+ fi
307
+ done
308
+
309
+ echo "exec-plan 索引同步检查通过"
310
+ `;
311
+ const INIT_CHECK_HARNESS_RUNTIME_CLEAN_SH = `#!/usr/bin/env bash
312
+ set -euo pipefail
313
+
314
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
315
+ cd "\${ROOT_DIR}"
316
+
317
+ failures=()
318
+
319
+ count_entries() {
320
+ local dir="$1"
321
+ if [[ ! -d "\${dir}" ]]; then
322
+ echo 0
323
+ return
324
+ fi
325
+ find "\${dir}" -mindepth 1 -maxdepth 1 ! -name .gitkeep | wc -l | tr -d ' '
326
+ }
327
+
328
+ if [[ "\${HARNESS_ALLOW_ACTIVE_DAG_RUNS:-}" != "1" ]]; then
329
+ active_dag_count="$(count_entries ".harness/dag-runs/active")"
330
+ [[ "\${active_dag_count}" == "0" ]] || failures+=(".harness/dag-runs/active contains \${active_dag_count} entries")
331
+ fi
332
+
333
+ if [[ "\${HARNESS_ALLOW_ACTIVE_TOOL_RUNS:-\${HARNESS_ALLOW_ACTIVE_ONE_SHOT_RUNS:-}}" != "1" ]]; then
334
+ active_run_count="$(count_entries ".harness/runs/active")"
335
+ [[ "\${active_run_count}" == "0" ]] || failures+=(".harness/runs/active contains \${active_run_count} entries")
336
+ fi
337
+
338
+ if [[ -d artifacts ]]; then
339
+ artifact_count="$(find artifacts -mindepth 1 -maxdepth 1 ! -name .gitkeep | wc -l | tr -d ' ')"
340
+ [[ "\${artifact_count}" == "0" ]] || failures+=("root artifacts/ contains \${artifact_count} entries; use ${GOVERNANCE_ROOT_TOKEN}/reports or .harness run artifacts")
341
+ fi
342
+
343
+ if (( \${#failures[@]} > 0 )); then
344
+ echo "[HARNESS RUNTIME DIRTY]" >&2
345
+ for item in "\${failures[@]}"; do
346
+ echo " - \${item}" >&2
347
+ done
348
+ exit 1
349
+ fi
350
+
351
+ echo "harness runtime clean check passed"
352
+ `;
353
+ const INIT_CHECK_ARCHITECTURE_BOUNDARIES_SH = `#!/usr/bin/env bash
354
+ set -euo pipefail
355
+
356
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
357
+ cd "\${ROOT_DIR}"
358
+
359
+ boundary_doc="${GOVERNANCE_ROOT_TOKEN}/architecture/runtime-boundaries.md"
360
+ if [[ ! -f "\${boundary_doc}" ]]; then
361
+ echo "architecture boundary 检查失败:缺少 \${boundary_doc}" >&2
362
+ exit 1
363
+ fi
364
+
365
+ violations=()
366
+
367
+ check_ts_import_boundary() {
368
+ local from_dir="$1"
369
+ local forbidden_dir="$2"
370
+ local label="$3"
371
+ [[ -d "\${from_dir}" && -d "\${forbidden_dir}" ]] || return 0
372
+ while IFS= read -r -d '' file; do
373
+ if grep -Eq "from ['\\"][^'\\"]*\${forbidden_dir}/|import\\(['\\"][^'\\"]*\${forbidden_dir}/|require\\(['\\"][^'\\"]*\${forbidden_dir}/" "\${file}"; then
374
+ violations+=("\${label}: \${file} imports \${forbidden_dir}")
375
+ fi
376
+ done < <(find "\${from_dir}" -type f \\( -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' -o -name '*.mjs' -o -name '*.cjs' \\) -print0)
377
+ }
378
+
379
+ # These checks are intentionally optional and stack-agnostic. They activate only
380
+ # when a target repository has recognizable source directories.
381
+ check_ts_import_boundary "src/workflows" "src/commands" "workflow-runtime"
382
+ check_ts_import_boundary "src/executors" "src/commands" "executors"
383
+ check_ts_import_boundary "src/domain" "src/infrastructure" "domain"
384
+
385
+ if (( \${#violations[@]} > 0 )); then
386
+ echo "architecture boundary 检查失败:" >&2
387
+ for item in "\${violations[@]}"; do
388
+ echo " - \${item}" >&2
389
+ done
390
+ echo "请更新 \${boundary_doc} 中的层边界,或修正反向依赖。" >&2
391
+ exit 1
392
+ fi
393
+
394
+ echo "architecture boundary 检查通过"
395
+ `;
396
+ const INIT_CHECK_SKILL_ENTRY_SH = `#!/usr/bin/env bash
397
+ set -euo pipefail
398
+
399
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
400
+ cd "\${ROOT_DIR}"
401
+
402
+ check_skill() {
403
+ local skill_name="$1"
404
+ shift
405
+ local skill_dir=".agents/skills/\${skill_name}"
406
+ local skill_md="\${skill_dir}/SKILL.md"
407
+ local missing=()
408
+
409
+ if [[ ! -f "\${skill_md}" ]]; then
410
+ echo "skill entry 检查失败:缺少 \${skill_md}" >&2
411
+ exit 1
412
+ fi
413
+ grep -Eq "^name:[[:space:]]*\${skill_name}[[:space:]]*$" "\${skill_md}" || missing+=("frontmatter name")
414
+ grep -Eq '^description:[[:space:]]*[^[:space:]]' "\${skill_md}" || missing+=("frontmatter description")
415
+
416
+ local ref
417
+ for ref in "$@"; do
418
+ [[ -f "\${skill_dir}/\${ref}" ]] || missing+=("\${ref}")
419
+ grep -Fq "\${ref}" "\${skill_md}" || missing+=("SKILL.md -> \${ref}")
420
+ done
421
+ if (( \${#missing[@]} > 0 )); then
422
+ echo "skill entry 检查失败(\${skill_name}):" >&2
423
+ printf ' - %s\\n' "\${missing[@]}" >&2
424
+ exit 1
425
+ fi
426
+
427
+ local line_count
428
+ line_count="$(wc -l < "\${skill_md}" | tr -d ' ')"
429
+ if [[ "\${line_count}" =~ ^[0-9]+$ && "\${line_count}" -gt 180 ]]; then
430
+ echo "skill entry 检查失败:\${skill_md} 行数为 \${line_count},入口应保持精简" >&2
431
+ exit 1
432
+ fi
433
+ echo "skill entry 检查通过:\${skill_name} references ok, lines=\${line_count}"
434
+ }
435
+
436
+ check_skill "loop-agent" \\
437
+ "references/harness-policy.md" \\
438
+ "references/hybrid-dag.md" \\
439
+ "references/verification-and-failure-handling.md" \\
440
+ "references/command-reference.md" \\
441
+ "references/source-and-plan-practice.md"
442
+ check_skill "agent-worker" "references/agent-worker-operator.md"
443
+
444
+ # loop-agent description is a YAML folded block (>-); grep ^description: only
445
+ # captures the indicator line. Validate the general-demand trigger terms
446
+ # against the whole skill file so host auto-discovery phrasing cannot drift.
447
+ loop_agent_skill=".agents/skills/loop-agent/SKILL.md"
448
+ loop_agent_text="$(tr '[:upper:]' '[:lower:]' < "\${loop_agent_skill}")"
449
+ for term in "loop-agent 帮我完成" "帮我实现" "帮我修复" "帮我开发" "agent dag" "task advance" "task status" "dag validate" "dag execute"; do
450
+ if [[ "\${loop_agent_text}" != *"\${term}"* ]]; then
451
+ echo "skill entry 检查失败(loop-agent):description 缺少触发/路由词 \${term}" >&2
452
+ exit 1
453
+ fi
454
+ done
455
+
456
+ description_line="$(grep -E '^description:' .agents/skills/agent-worker/SKILL.md | head -n 1 | tr '[:upper:]' '[:lower:]')"
457
+ for term in "agent-worker" "feature packet" "taskspec" "task pool" "self-host" "candidate" "loop-agent"; do
458
+ if [[ "\${description_line}" != *"\${term}"* ]]; then
459
+ echo "skill entry 检查失败(agent-worker):description 缺少触发/路由词 \${term}" >&2
460
+ exit 1
461
+ fi
462
+ done
463
+ `;
464
+ const INIT_CHECK_REPO_SH = `#!/usr/bin/env bash
465
+ set -euo pipefail
466
+
467
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
468
+ cd "\${ROOT_DIR}"
469
+
470
+ checks=(
471
+ "scripts/check-engineering-structure.sh"
472
+ "scripts/check-doc-index.sh"
473
+ "scripts/check-doc-links.sh"
474
+ "scripts/check-active-plan-status.sh"
475
+ "scripts/check-exec-plan-index-sync.sh"
476
+ "scripts/check-harness-runtime-clean.sh"
477
+ "scripts/check-architecture-boundaries.sh"
478
+ "scripts/check-skill-entry.sh"
479
+ "scripts/check-product-line-docs.sh"
480
+ )
481
+
482
+ for check in "\${checks[@]}"; do
483
+ echo "==> bash \${check}"
484
+ bash "\${check}"
485
+ echo
486
+ done
487
+
488
+ echo "仓库治理检查全部通过"
489
+ `;
490
+ const INIT_CHECK_PRODUCT_LINE_DOCS_SH = `#!/usr/bin/env bash
491
+ set -euo pipefail
492
+
493
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
494
+ cd "\${ROOT_DIR}"
495
+
496
+ if ! command -v agent-worker >/dev/null 2>&1; then
497
+ echo "product-line docs check skipped: agent-worker is not installed"
498
+ exit 0
499
+ fi
500
+
501
+ feature_dirs=()
502
+ for root in features product/features dogfood/features; do
503
+ [[ -d "\${root}" ]] || continue
504
+ while IFS= read -r feature_dir; do
505
+ if [[ ! -f "\${feature_dir}/acceptance.yaml" ]]; then
506
+ echo "product-line docs check failed: incomplete feature packet missing acceptance.yaml: \${feature_dir}" >&2
507
+ exit 1
508
+ fi
509
+ feature_dirs+=("\${feature_dir}")
510
+ done < <(find "\${root}" -mindepth 1 -maxdepth 1 -type d | sort)
511
+ done
512
+
513
+ if [[ \${#feature_dirs[@]} -eq 0 ]]; then
514
+ echo "product-line docs check skipped: no feature packets found"
515
+ exit 0
516
+ fi
517
+
518
+ for feature_dir in "\${feature_dirs[@]}"; do
519
+ echo "==> validate product-line feature: \${feature_dir}"
520
+ agent-worker task validate-feature "\${feature_dir}"
521
+ done
522
+
523
+ echo "product-line docs checks passed: \${#feature_dirs[@]} feature packet(s)"
524
+ `;
525
+ const INIT_CI_GOVERNANCE_SH = `#!/usr/bin/env bash
526
+ set -euo pipefail
527
+
528
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
529
+ cd "\${ROOT_DIR}"
530
+
531
+ bash scripts/check-repo.sh
532
+ `;
533
+ const INIT_CI_TESTS_SH = `#!/usr/bin/env bash
534
+ set -euo pipefail
535
+
536
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
537
+ cd "\${ROOT_DIR}"
538
+
539
+ ran=0
540
+ notes=()
541
+
542
+ run_cmd() {
543
+ echo "==> $*"
544
+ "$@"
545
+ ran=1
546
+ }
547
+
548
+ run_bash() {
549
+ echo "==> $*"
550
+ bash -lc "$*"
551
+ ran=1
552
+ }
553
+
554
+ has_make_target() {
555
+ local target="$1"
556
+ [[ -f Makefile || -f makefile ]] || return 1
557
+ grep -Eq "^[[:alnum:]_.-]*\${target}[[:alnum:]_.-]*:" Makefile makefile 2>/dev/null
558
+ }
559
+
560
+ has_npm_script() {
561
+ local script="$1"
562
+ [[ -f package.json ]] || return 1
563
+ command -v node >/dev/null 2>&1 || return 1
564
+ node -e "const fs=require('fs'); const p=JSON.parse(fs.readFileSync('package.json','utf8')); process.exit(p.scripts && p.scripts[process.argv[1]] ? 0 : 1)" "\${script}" >/dev/null 2>&1
565
+ }
566
+
567
+ dag_lint_assessment_allows_skip() {
568
+ [[ -n "\${HARNESS_DAG_RUN_DIR:-}" ]] || return 1
569
+ command -v node >/dev/null 2>&1 || return 1
570
+ node - "\${HARNESS_DAG_RUN_DIR}" <<'NODE'
571
+ const fs = require("fs");
572
+ const path = require("path");
573
+ const crypto = require("crypto");
574
+ const runDir = path.resolve(process.argv[2]);
575
+ const assessmentPath = path.join(runDir, "contracts", "frontend-lint-assessment.json");
576
+ const fail = () => process.exit(1);
577
+ const sha256 = (value) => crypto.createHash("sha256").update(value).digest("hex");
578
+ const readContained = (relative) => {
579
+ if (typeof relative !== "string" || path.isAbsolute(relative)) fail();
580
+ const absolute = path.resolve(runDir, relative);
581
+ if (absolute !== runDir && !absolute.startsWith(runDir + path.sep)) fail();
582
+ return fs.readFileSync(absolute);
583
+ };
584
+ let assessment;
585
+ try {
586
+ assessment = JSON.parse(fs.readFileSync(assessmentPath, "utf8"));
587
+ } catch {
588
+ fail();
589
+ }
590
+ if (
591
+ assessment.schemaVersion !== 1 ||
592
+ assessment.schemaId !== "frontend-lint-assessment-v1" ||
593
+ !["passed", "baseline-debt"].includes(assessment.status) ||
594
+ !assessment.commandIdentity ||
595
+ !Array.isArray(assessment.commandIdentity.commands) ||
596
+ assessment.commandIdentity.commands.length === 0 ||
597
+ !Array.isArray(assessment.writerChangedFiles) ||
598
+ !Array.isArray(assessment.blockingDiagnostics) ||
599
+ assessment.blockingDiagnostics.length !== 0 ||
600
+ !Array.isArray(assessment.blockingReasons) ||
601
+ assessment.blockingReasons.length !== 0
602
+ ) fail();
603
+ const commandHash = sha256(JSON.stringify(assessment.commandIdentity.commands));
604
+ if (commandHash !== assessment.commandIdentity.sha256) fail();
605
+ if (!assessment.commandIdentity.commands.every((command) =>
606
+ /(?:^|[\\s'"])npm(?:['"])?\\s+(?:['"])?run(?:['"])?\\s+(?:['"])?lint(?:['"])?(?:\\s|$)/.test(command)
607
+ )) fail();
608
+ if (
609
+ (assessment.status === "passed" && assessment.currentExitCode !== 0) ||
610
+ (assessment.status === "baseline-debt" && assessment.currentExitCode === 0)
611
+ ) fail();
612
+ if (!assessment.baselineRef || assessment.baselineRef.nodeId !== "frontend-lint-baseline-shell") fail();
613
+ const baselineRaw = readContained(assessment.baselineRef.path);
614
+ if (sha256(baselineRaw) !== assessment.baselineRef.sha256) fail();
615
+ let baseline;
616
+ try {
617
+ baseline = JSON.parse(baselineRaw);
618
+ } catch {
619
+ fail();
620
+ }
621
+ if (
622
+ baseline.schemaVersion !== 1 ||
623
+ baseline.schemaId !== "frontend-lint-baseline-v1" ||
624
+ baseline.status !== "available" ||
625
+ baseline.commandIdentity?.sha256 !== assessment.commandIdentity.sha256
626
+ ) fail();
627
+ if (assessment.status === "baseline-debt") {
628
+ if (
629
+ !Array.isArray(assessment.currentDiagnostics) ||
630
+ assessment.currentDiagnostics.length === 0 ||
631
+ assessment.currentDiagnostics.length !== assessment.toleratedDiagnosticCount ||
632
+ !Array.isArray(baseline.diagnostics)
633
+ ) fail();
634
+ const key = (item) => JSON.stringify([
635
+ item.file, item.line, item.column, item.severity, item.message, item.ruleId ?? null,
636
+ ]);
637
+ const baselineCounts = new Map();
638
+ for (const item of baseline.diagnostics) {
639
+ const value = key(item);
640
+ baselineCounts.set(value, (baselineCounts.get(value) || 0) + 1);
641
+ }
642
+ const changed = new Set(assessment.writerChangedFiles);
643
+ for (const item of assessment.currentDiagnostics) {
644
+ if (changed.has(item.file)) fail();
645
+ const value = key(item);
646
+ const count = baselineCounts.get(value) || 0;
647
+ if (count === 0) fail();
648
+ baselineCounts.set(value, count - 1);
649
+ }
650
+ }
651
+ if (!Array.isArray(assessment.rawEvidenceRefs) || assessment.rawEvidenceRefs.length === 0) fail();
652
+ for (const ref of assessment.rawEvidenceRefs) {
653
+ if (sha256(readContained(ref.path)) !== ref.sha256) fail();
654
+ }
655
+ process.exit(0);
656
+ NODE
657
+ }
658
+
659
+ if [[ -f package.json ]]; then
660
+ if command -v npm >/dev/null 2>&1; then
661
+ for script in lint typecheck test build; do
662
+ if has_npm_script "\${script}"; then
663
+ if [[ "\${script}" == "lint" ]] && dag_lint_assessment_allows_skip; then
664
+ echo "==> lint handled by frontend DAG assessment (status: passed or baseline-debt)"
665
+ ran=1
666
+ continue
667
+ fi
668
+ run_cmd npm run "\${script}"
669
+ fi
670
+ done
671
+ else
672
+ notes+=("package.json exists but npm is not available")
673
+ fi
674
+ fi
675
+
676
+ if has_make_target verify; then
677
+ run_cmd make verify
678
+ elif has_make_target test; then
679
+ run_cmd make test
680
+ fi
681
+
682
+ if [[ -f go.mod ]]; then
683
+ if command -v go >/dev/null 2>&1; then
684
+ run_cmd go test ./...
685
+ else
686
+ notes+=("go.mod exists but go is not available")
687
+ fi
688
+ fi
689
+
690
+ if [[ -f Cargo.toml ]]; then
691
+ if command -v cargo >/dev/null 2>&1; then
692
+ run_cmd cargo test
693
+ else
694
+ notes+=("Cargo.toml exists but cargo is not available")
695
+ fi
696
+ fi
697
+
698
+ if [[ -f pyproject.toml || -f pytest.ini || -f tox.ini ]]; then
699
+ if command -v pytest >/dev/null 2>&1; then
700
+ run_cmd pytest
701
+ elif command -v tox >/dev/null 2>&1 && [[ -f tox.ini ]]; then
702
+ run_cmd tox
703
+ else
704
+ notes+=("Python project metadata found but pytest/tox is not available")
705
+ fi
706
+ fi
707
+
708
+ if [[ -f pom.xml ]]; then
709
+ if command -v mvn >/dev/null 2>&1; then
710
+ run_cmd mvn test
711
+ else
712
+ notes+=("pom.xml exists but mvn is not available")
713
+ fi
714
+ fi
715
+
716
+ if [[ -f build.gradle || -f build.gradle.kts || -f settings.gradle || -f settings.gradle.kts ]]; then
717
+ if [[ -x ./gradlew ]]; then
718
+ run_cmd ./gradlew test
719
+ elif command -v gradle >/dev/null 2>&1; then
720
+ run_cmd gradle test
721
+ else
722
+ notes+=("Gradle project metadata found but gradle/gradlew is not available")
723
+ fi
724
+ fi
725
+
726
+ if compgen -G "*.sln" >/dev/null || compgen -G "*.csproj" >/dev/null; then
727
+ if command -v dotnet >/dev/null 2>&1; then
728
+ run_cmd dotnet test
729
+ else
730
+ notes+=(".NET project metadata found but dotnet is not available")
731
+ fi
732
+ fi
733
+
734
+ if (( ran == 0 )); then
735
+ echo "未检测到可安全自动运行的项目测试入口。"
736
+ echo "loop-agent 治理检查仍可运行;请让初始化模型根据目标项目技术栈补充 scripts/ci-tests.sh 与 ${GOVERNANCE_ROOT_TOKEN}/verification-matrix.md。"
737
+ fi
738
+
739
+ if (( \${#notes[@]} > 0 )); then
740
+ echo "项目验证探测备注:"
741
+ for note in "\${notes[@]}"; do
742
+ echo " - \${note}"
743
+ done
744
+ fi
745
+ `;
746
+ const INIT_CI_SH = `#!/usr/bin/env bash
747
+ set -euo pipefail
748
+
749
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
750
+ cd "\${ROOT_DIR}"
751
+
752
+ bash scripts/ci-governance.sh
753
+ bash scripts/ci-tests.sh
754
+ `;
755
+ const INIT_SCRIPT_FILES = {
756
+ "scripts/check-engineering-structure.sh": INIT_CHECK_ENGINEERING_STRUCTURE_SH,
757
+ "scripts/check-doc-index.sh": INIT_CHECK_DOC_INDEX_SH,
758
+ "scripts/check-doc-links.sh": INIT_CHECK_DOC_LINKS_SH,
759
+ "scripts/check-active-plan-status.sh": INIT_CHECK_ACTIVE_PLAN_STATUS_SH,
760
+ "scripts/check-exec-plan-index-sync.sh": INIT_CHECK_EXEC_PLAN_INDEX_SYNC_SH,
761
+ "scripts/check-harness-runtime-clean.sh": INIT_CHECK_HARNESS_RUNTIME_CLEAN_SH,
762
+ "scripts/check-architecture-boundaries.sh": INIT_CHECK_ARCHITECTURE_BOUNDARIES_SH,
763
+ "scripts/check-skill-entry.sh": INIT_CHECK_SKILL_ENTRY_SH,
764
+ "scripts/check-product-line-docs.sh": INIT_CHECK_PRODUCT_LINE_DOCS_SH,
765
+ "scripts/check-repo.sh": INIT_CHECK_REPO_SH,
766
+ "scripts/ci-governance.sh": INIT_CI_GOVERNANCE_SH,
767
+ "scripts/ci-tests.sh": INIT_CI_TESTS_SH,
768
+ "scripts/ci.sh": INIT_CI_SH,
769
+ };
770
+ function buildInitScriptFiles(governanceRoot) {
771
+ return Object.fromEntries(Object.entries(INIT_SCRIPT_FILES).map(([relativePath, content]) => [
772
+ relativePath,
773
+ content.replaceAll(GOVERNANCE_ROOT_TOKEN, governanceRoot),
774
+ ]));
775
+ }
776
+ function repoRelative(targetRoot, filePath) {
777
+ return path.relative(targetRoot, filePath).split(path.sep).join("/");
778
+ }
779
+ async function exists(filePath) {
780
+ return Boolean(await stat(filePath).catch(() => null));
781
+ }
782
+ async function findPackageRoot() {
783
+ let current = path.dirname(fileURLToPath(import.meta.url));
784
+ while (true) {
785
+ if (await exists(path.join(current, "package.json")))
786
+ return current;
787
+ const parent = path.dirname(current);
788
+ if (parent === current)
789
+ return path.resolve(".");
790
+ current = parent;
791
+ }
792
+ }
793
+ async function readJsonIfExists(filePath) {
794
+ if (!(await exists(filePath)))
795
+ return {};
796
+ return JSON.parse(await readFile(filePath, "utf-8"));
797
+ }
798
+ function mergeRecord(base, patch) {
799
+ return {
800
+ ...(typeof base === "object" && base !== null && !Array.isArray(base)
801
+ ? base
802
+ : {}),
803
+ ...patch,
804
+ };
805
+ }
806
+ async function loadManagedAgentsTemplate(assetRoot) {
807
+ if (managedAgentsTemplateCache !== undefined)
808
+ return managedAgentsTemplateCache;
809
+ const templatePath = path.join(assetRoot, MANAGED_AGENTS_TEMPLATE_PATH);
810
+ const raw = await readFile(templatePath, "utf-8");
811
+ // Prefer a START marker on its own line so header notes mentioning the token are ignored.
812
+ const lines = raw.split(/\r?\n/);
813
+ const startLine = lines.findIndex((line) => line.trim() === MANAGED_BLOCK_START);
814
+ const endLine = startLine >= 0
815
+ ? lines.findIndex((line, index) => index > startLine && line.trim() === MANAGED_BLOCK_END)
816
+ : -1;
817
+ if (startLine < 0 || endLine < 0) {
818
+ throw new Error(`managed agents template missing LOOP_AGENT_INIT markers: ${MANAGED_AGENTS_TEMPLATE_PATH}`);
819
+ }
820
+ managedAgentsTemplateCache = lines.slice(startLine, endLine + 1).join("\n");
821
+ return managedAgentsTemplateCache;
822
+ }
823
+ async function buildManagedAgentsBlock(input) {
824
+ const template = await loadManagedAgentsTemplate(input.assetRoot);
825
+ return template
826
+ .replaceAll(PROJECT_NAME_TOKEN, input.projectName)
827
+ .replaceAll(GOVERNANCE_ROOT_TOKEN, input.governanceRoot);
828
+ }
829
+ function mergeManagedBlock(existing, block) {
830
+ const start = existing.indexOf(MANAGED_BLOCK_START);
831
+ const end = existing.indexOf(MANAGED_BLOCK_END);
832
+ if (start >= 0 && end > start) {
833
+ return (`${existing.slice(0, start).trimEnd()}\n\n${block}\n${existing.slice(end + MANAGED_BLOCK_END.length).trimStart()}`.trimEnd() +
834
+ "\n");
835
+ }
836
+ return `${existing.trimEnd()}\n\n${block}\n`;
837
+ }
838
+ function mergeGitignoreManagedBlock(existing, block) {
839
+ const start = existing.indexOf(GITIGNORE_BLOCK_START);
840
+ const end = existing.indexOf(GITIGNORE_BLOCK_END);
841
+ if (start >= 0 && end > start) {
842
+ return (`${existing.slice(0, start).trimEnd()}\n\n${block}\n${existing.slice(end + GITIGNORE_BLOCK_END.length).trimStart()}`.trimEnd() +
843
+ "\n");
844
+ }
845
+ if (!existing.trim())
846
+ return `${block}\n`;
847
+ return `${existing.trimEnd()}\n\n${block}\n`;
848
+ }
849
+ /**
850
+ * Shared ignore rules for loop-agent runtime facts. The four directories are
851
+ * local, rebuildable runtime surface: every developer runs loop-agent init
852
+ * after cloning, so nothing inside them is team-shareable by default.
853
+ * `scripts/` and `ai_workspace/loop-agent/` stay on the shared commit surface.
854
+ */
855
+ export function buildManagedGitignoreBlock() {
856
+ return [
857
+ GITIGNORE_BLOCK_START,
858
+ "# loop-agent runtime: local, rebuildable facts; do not ignore scripts/ or ai_workspace/loop-agent/",
859
+ ".harness/",
860
+ ".agents/",
861
+ ".task-pool/",
862
+ ".worktrees/",
863
+ GITIGNORE_BLOCK_END,
864
+ ].join("\n");
865
+ }
866
+ function extractGitignoreManagedBlock(content) {
867
+ const start = content.indexOf(GITIGNORE_BLOCK_START);
868
+ const end = content.indexOf(GITIGNORE_BLOCK_END);
869
+ if (start < 0 || end <= start)
870
+ return undefined;
871
+ return content.slice(start, end + GITIGNORE_BLOCK_END.length);
872
+ }
873
+ function buildHarness(input) {
874
+ const templateExecutors = isRecord(input.template.executors)
875
+ ? { ...input.template.executors }
876
+ : {};
877
+ const existingExecutors = isRecord(input.existing.executors)
878
+ ? input.existing.executors
879
+ : {};
880
+ // Cursor executor must never be projected; Pi-only governed runtime.
881
+ delete templateExecutors.cursor;
882
+ delete existingExecutors.cursor;
883
+ const mergedExecutors = mergeRecord(templateExecutors, existingExecutors);
884
+ // requiresApiKey is Cursor-facing only; never project it on pi.
885
+ if (isRecord(mergedExecutors.pi) && "requiresApiKey" in mergedExecutors.pi) {
886
+ const pi = { ...mergedExecutors.pi };
887
+ delete pi.requiresApiKey;
888
+ mergedExecutors.pi = pi;
889
+ }
890
+ // Init always writes a Pi tier matrix. `defaultModel` remains runtime
891
+ // compatibility for old projects only and is never fresh-init output.
892
+ if (input.model) {
893
+ const piExisting = isRecord(mergedExecutors.pi) ? mergedExecutors.pi : {};
894
+ mergedExecutors.pi = {
895
+ ...piExisting,
896
+ LOW: input.model,
897
+ MED: input.model,
898
+ HIGH: input.model,
899
+ };
900
+ }
901
+ if (isRecord(mergedExecutors.pi)) {
902
+ const pi = { ...mergedExecutors.pi };
903
+ if (hasCompletePiTierMatrix(pi))
904
+ delete pi.defaultModel;
905
+ mergedExecutors.pi = pi;
906
+ }
907
+ const harness = {
908
+ ...input.existing,
909
+ $schema: expectedHarnessSchemaRef(input.governanceRoot),
910
+ version: 1,
911
+ project: input.projectName,
912
+ adapter: "loop-agent",
913
+ governanceRoot: input.governanceRoot,
914
+ features: mergeRecord(input.template.features, input.existing.features),
915
+ workflowPolicy: mergeRecord(input.template.workflowPolicy, input.existing.workflowPolicy),
916
+ entrypoints: mergeRecord(input.existing.entrypoints, {
917
+ readme: "README.md",
918
+ agents: "AGENTS.md",
919
+ governanceIndex: `${input.governanceRoot}/README.md`,
920
+ principles: `${input.governanceRoot}/development-principles.md`,
921
+ workflow: `${input.governanceRoot}/feature-workflow.md`,
922
+ verificationMatrix: `${input.governanceRoot}/verification-matrix.md`,
923
+ loopAgentHarness: `${input.governanceRoot}/loop-agent-harness.md`,
924
+ }),
925
+ artifacts: mergeRecord(input.existing.artifacts, {
926
+ templatesDir: `${input.governanceRoot}/templates`,
927
+ execPlansDir: `${input.governanceRoot}/exec-plans`,
928
+ progressDir: `${input.governanceRoot}/progress`,
929
+ reportsDir: `${input.governanceRoot}/reports`,
930
+ decisionsDir: `${input.governanceRoot}/decisions`,
931
+ taskPoolDir: ".harness/task-pool",
932
+ }),
933
+ scripts: mergeRecord(input.existing.scripts, {
934
+ checkEngineeringStructure: "scripts/check-engineering-structure.sh",
935
+ checkDocIndex: "scripts/check-doc-index.sh",
936
+ checkDocLinks: "scripts/check-doc-links.sh",
937
+ checkActivePlanStatus: "scripts/check-active-plan-status.sh",
938
+ checkExecPlanIndexSync: "scripts/check-exec-plan-index-sync.sh",
939
+ checkHarnessRuntimeClean: "scripts/check-harness-runtime-clean.sh",
940
+ checkArchitectureBoundaries: "scripts/check-architecture-boundaries.sh",
941
+ checkSkillEntry: "scripts/check-skill-entry.sh",
942
+ checkRepo: "scripts/check-repo.sh",
943
+ ciGovernance: "scripts/ci-governance.sh",
944
+ ciTests: "scripts/ci-tests.sh",
945
+ ci: "scripts/ci.sh",
946
+ quickVerify: "bash scripts/check-repo.sh",
947
+ standardVerify: "bash scripts/ci-governance.sh",
948
+ fullVerify: "bash scripts/ci.sh",
949
+ }),
950
+ executors: mergedExecutors,
951
+ };
952
+ // Fresh init owns only the current DAG-facing surface. Legacy step routing is
953
+ // diagnosed by check-update rather than copied into a new harness projection.
954
+ for (const legacyField of [
955
+ "model",
956
+ "models",
957
+ "modelProfiles",
958
+ "modelRouting",
959
+ "verify",
960
+ "sequentialWorkflowRole",
961
+ ]) {
962
+ delete harness[legacyField];
963
+ }
964
+ return harness;
965
+ }
966
+ async function writeTextIfMissing(input) {
967
+ const target = path.join(input.repoRoot, input.relativePath);
968
+ await mkdir(path.dirname(target), { recursive: true });
969
+ if (await exists(target)) {
970
+ input.skipped.push(input.relativePath);
971
+ return;
972
+ }
973
+ await writeFile(target, input.content, "utf-8");
974
+ input.written.push(input.relativePath);
975
+ }
976
+ async function writeText(input) {
977
+ const target = path.join(input.repoRoot, input.relativePath);
978
+ await mkdir(path.dirname(target), { recursive: true });
979
+ if (!input.merge && (await exists(target))) {
980
+ input.skipped.push(input.relativePath);
981
+ return;
982
+ }
983
+ await writeFile(target, input.content, "utf-8");
984
+ input.written.push(input.relativePath);
985
+ }
986
+ async function copyFileIfMissing(input) {
987
+ const source = path.join(input.assetRoot, input.sourceRelativePath);
988
+ const target = path.join(input.repoRoot, input.targetRelativePath);
989
+ await mkdir(path.dirname(target), { recursive: true });
990
+ if ((await exists(target)) && !input.merge) {
991
+ input.skipped.push(input.targetRelativePath);
992
+ return;
993
+ }
994
+ if (await exists(target)) {
995
+ input.skipped.push(input.targetRelativePath);
996
+ return;
997
+ }
998
+ await copyFile(source, target);
999
+ input.written.push(input.targetRelativePath);
1000
+ }
1001
+ async function copyDirMerge(input) {
1002
+ const source = path.join(input.assetRoot, input.sourceRelativePath);
1003
+ const target = path.join(input.repoRoot, input.targetRelativePath);
1004
+ const sourcePrefix = `${input.sourceRelativePath.replaceAll(path.sep, "/").replace(/\/$/, "")}/`;
1005
+ await copyDirSkippingPackageOnly({
1006
+ source,
1007
+ target,
1008
+ packageRelativePrefix: sourcePrefix,
1009
+ });
1010
+ input.written.push(input.targetRelativePath.endsWith("/")
1011
+ ? input.targetRelativePath
1012
+ : `${input.targetRelativePath}/`);
1013
+ }
1014
+ /** Like copyDir, but skips package-only assets that must not land in target projects. */
1015
+ async function copyDirSkippingPackageOnly(input) {
1016
+ await mkdir(input.target, { recursive: true });
1017
+ const entries = await readdir(input.source, { withFileTypes: true });
1018
+ for (const entry of entries) {
1019
+ const srcPath = path.join(input.source, entry.name);
1020
+ const destPath = path.join(input.target, entry.name);
1021
+ const packageRelative = `${input.packageRelativePrefix}${entry.name}`;
1022
+ if (entry.isDirectory()) {
1023
+ await copyDirSkippingPackageOnly({
1024
+ source: srcPath,
1025
+ target: destPath,
1026
+ packageRelativePrefix: `${packageRelative}/`,
1027
+ });
1028
+ continue;
1029
+ }
1030
+ if (PACKAGE_ONLY_SURFACE_FILES.has(packageRelative))
1031
+ continue;
1032
+ if (entry.isFile() || entry.isSymbolicLink()) {
1033
+ await copyFile(srcPath, destPath);
1034
+ }
1035
+ }
1036
+ }
1037
+ async function ensureHarnessDirs(repoRoot, written) {
1038
+ const dirs = [
1039
+ ".harness/prompts",
1040
+ ".harness/tasks",
1041
+ ".harness/dag-runs/active",
1042
+ ".harness/dag-runs/completed",
1043
+ ".harness/dag-runs/paused",
1044
+ ".harness/runs/active",
1045
+ ".harness/runs/completed",
1046
+ ".harness/runs/failed",
1047
+ ".harness/cache",
1048
+ ".harness/live",
1049
+ ];
1050
+ for (const dir of dirs) {
1051
+ await mkdir(path.join(repoRoot, dir), { recursive: true });
1052
+ written.push(dir);
1053
+ }
1054
+ // No .gitkeep placeholders: .harness/ is fully ignored by the managed
1055
+ // gitignore block, and runtime dirs are recreated on demand by init/doctor.
1056
+ }
1057
+ async function writeCompatPrompts(input) {
1058
+ for (const [name, content] of Object.entries(COMPAT_PROMPTS)) {
1059
+ await writeText({
1060
+ repoRoot: input.repoRoot,
1061
+ relativePath: path.join(".harness", "prompts", name),
1062
+ content,
1063
+ merge: input.merge,
1064
+ written: input.written,
1065
+ skipped: input.skipped,
1066
+ });
1067
+ }
1068
+ }
1069
+ function sha256Text(value) {
1070
+ return createHash("sha256").update(value).digest("hex");
1071
+ }
1072
+ function sha256Buffer(value) {
1073
+ return createHash("sha256").update(value).digest("hex");
1074
+ }
1075
+ function isRecord(value) {
1076
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1077
+ }
1078
+ /** A complete matrix has three effective values, not just three present keys. */
1079
+ function hasCompletePiTierMatrix(pi) {
1080
+ return ["LOW", "MED", "HIGH"].every((tier) => {
1081
+ const value = pi[tier];
1082
+ if (typeof value === "string")
1083
+ return value.length > 0 && value !== "default";
1084
+ if (!isRecord(value))
1085
+ return false;
1086
+ const keys = Object.keys(value);
1087
+ if (!keys.every((key) => key === "model" || key === "thinking"))
1088
+ return false;
1089
+ if (typeof value.model !== "string" || value.model.length === 0)
1090
+ return false;
1091
+ if (value.model === "default")
1092
+ return false;
1093
+ return value.thinking === undefined || typeof value.thinking === "string";
1094
+ });
1095
+ }
1096
+ /** Resolve init CLI input to one unambiguous Pi `provider/model` reference. */
1097
+ export function normalizeInitModelReference(input) {
1098
+ const provider = input.provider?.trim();
1099
+ const model = input.model?.trim();
1100
+ if (input.provider !== undefined && !provider) {
1101
+ throw new Error("init --provider must be a non-empty provider id");
1102
+ }
1103
+ if (input.model !== undefined && !model) {
1104
+ throw new Error("init --model must be a non-empty model reference");
1105
+ }
1106
+ if (provider && !model) {
1107
+ throw new Error("init --provider requires --model");
1108
+ }
1109
+ if (!model)
1110
+ return undefined;
1111
+ const slash = model.indexOf("/");
1112
+ if (slash < 0) {
1113
+ if (!provider) {
1114
+ throw new Error("init --model must be a provider/model reference or be paired with --provider");
1115
+ }
1116
+ if (/\s|\//.test(provider) || /\s/.test(model)) {
1117
+ throw new Error("init provider/model values must not contain whitespace");
1118
+ }
1119
+ return `${provider}/${model}`;
1120
+ }
1121
+ const modelProvider = model.slice(0, slash);
1122
+ const modelId = model.slice(slash + 1);
1123
+ if (!modelProvider || !modelId || /\s/.test(modelProvider) || /\s/.test(modelId)) {
1124
+ throw new Error("init --model must use a non-empty provider/model reference");
1125
+ }
1126
+ if (provider && provider !== modelProvider) {
1127
+ throw new Error("init --provider conflicts with the provider qualified in --model");
1128
+ }
1129
+ return model;
1130
+ }
1131
+ function normalizeRelativePath(relativePath) {
1132
+ return relativePath.split(path.sep).join("/");
1133
+ }
1134
+ function expectedHarnessSchemaRef(governanceRoot) {
1135
+ return `./${governanceRoot}/templates/harness.schema.json`;
1136
+ }
1137
+ function manifestPathToTargetPath(relativePath, governanceRoot) {
1138
+ if (governanceRoot !== "docs" && relativePath.startsWith("docs/")) {
1139
+ return `${governanceRoot}/${relativePath.slice("docs/".length)}`;
1140
+ }
1141
+ if (relativePath.startsWith("skills/")) {
1142
+ return `.agents/${relativePath}`;
1143
+ }
1144
+ return relativePath;
1145
+ }
1146
+ function targetPathToManifestPath(relativePath, governanceRoot) {
1147
+ if (governanceRoot !== "docs" &&
1148
+ relativePath.startsWith(`${governanceRoot}/`)) {
1149
+ return `docs/${relativePath.slice(`${governanceRoot}/`.length)}`;
1150
+ }
1151
+ if (relativePath.startsWith(".agents/skills/")) {
1152
+ return relativePath.slice(".agents/".length);
1153
+ }
1154
+ return relativePath;
1155
+ }
1156
+ async function readPackageVersion(assetRoot) {
1157
+ const packageJson = JSON.parse(await readFile(path.join(assetRoot, "package.json"), "utf-8"));
1158
+ return typeof packageJson.version === "string"
1159
+ ? packageJson.version
1160
+ : "0.0.0";
1161
+ }
1162
+ async function readInitSurfaceManifest(assetRoot) {
1163
+ const manifestPath = path.join(assetRoot, "docs", "init-surface.manifest.json");
1164
+ const raw = await readFile(manifestPath, "utf-8");
1165
+ const manifest = JSON.parse(raw);
1166
+ const initFullRequired = manifest.initFullRequired ?? [];
1167
+ const initSurface = manifest.initSurface ?? {};
1168
+ const seenPaths = new Set();
1169
+ const entries = [];
1170
+ for (const relativePath of initFullRequired) {
1171
+ if (seenPaths.has(relativePath))
1172
+ continue;
1173
+ seenPaths.add(relativePath);
1174
+ entries.push({
1175
+ path: relativePath,
1176
+ mode: initSurface[relativePath] ?? inferInitSurfaceMode(relativePath),
1177
+ });
1178
+ }
1179
+ // Fresh `init --profile full` copies the entire `docs/templates/` and
1180
+ // `skills/` directories via copyDirMerge. Every file actually projected to a
1181
+ // target project must be represented in the desired init surface so that
1182
+ // check-update can detect missing or stale copies during controller upgrades.
1183
+ // The static manifest intentionally only pins explicit modes for the subset
1184
+ // that needs them; the rest are auto-discovered here as "copied" entries.
1185
+ for (const discovered of await discoverCopiedSurfaceFiles(assetRoot)) {
1186
+ if (seenPaths.has(discovered))
1187
+ continue;
1188
+ seenPaths.add(discovered);
1189
+ entries.push({
1190
+ path: discovered,
1191
+ mode: initSurface[discovered] ?? inferInitSurfaceMode(discovered),
1192
+ });
1193
+ }
1194
+ if (!entries.some((entry) => entry.path === INIT_SURFACE_STATE_PATH)) {
1195
+ entries.push({ path: INIT_SURFACE_STATE_PATH, mode: "state" });
1196
+ }
1197
+ return { manifest, raw, sha256: sha256Text(raw), entries };
1198
+ }
1199
+ /**
1200
+ * Returns manifest-path entries for every file that fresh `init --profile full`
1201
+ * copies from the bundled package into a target project: all of `docs/templates/`
1202
+ * and all of `skills/` (the latter is mirrored to `.agents/skills/` at the
1203
+ * target). Paths use the package source convention so manifestPathToTargetPath
1204
+ * can remap them per governance root.
1205
+ */
1206
+ async function discoverCopiedSurfaceFiles(assetRoot) {
1207
+ const discovered = [];
1208
+ const roots = [
1209
+ { source: "docs/templates", prefix: "docs/templates/" },
1210
+ { source: "skills", prefix: "skills/" },
1211
+ ];
1212
+ for (const { source, prefix } of roots) {
1213
+ const rootPath = path.join(assetRoot, source);
1214
+ const rootInfo = await stat(rootPath).catch(() => null);
1215
+ if (!rootInfo?.isDirectory())
1216
+ continue;
1217
+ const files = await listRelativeFiles(rootPath);
1218
+ for (const relativePath of files) {
1219
+ const manifestPath = `${prefix}${relativePath}`;
1220
+ if (PACKAGE_ONLY_SURFACE_FILES.has(manifestPath))
1221
+ continue;
1222
+ discovered.push(manifestPath);
1223
+ }
1224
+ }
1225
+ return discovered.sort();
1226
+ }
1227
+ function inferInitSurfaceMode(relativePath) {
1228
+ if (relativePath === INIT_SURFACE_STATE_PATH)
1229
+ return "state";
1230
+ if (relativePath === ".harness/tasks" ||
1231
+ relativePath === ".harness/dag-runs/active" ||
1232
+ relativePath.endsWith("/")) {
1233
+ return "directory";
1234
+ }
1235
+ if (relativePath === "README.md" ||
1236
+ relativePath === "AGENTS.md" ||
1237
+ relativePath === ".gitignore") {
1238
+ return "managed-block";
1239
+ }
1240
+ if (relativePath === "harness.json" ||
1241
+ relativePath === OPENCODE_TRANSIENT_RETRY_PLUGIN_PATH ||
1242
+ relativePath === OPENCODE_CONTEXT_OVERFLOW_COMPACT_PLUGIN_PATH ||
1243
+ relativePath === PI_CONTEXT_OVERFLOW_EXTENSION_PATH ||
1244
+ relativePath === PI_PROJECT_SETTINGS_PATH ||
1245
+ relativePath.startsWith("scripts/") ||
1246
+ relativePath.startsWith(".harness/prompts/") ||
1247
+ relativePath.startsWith("docs/README.md") ||
1248
+ relativePath.startsWith("docs/development-principles.md") ||
1249
+ relativePath.startsWith("docs/feature-workflow.md") ||
1250
+ relativePath.startsWith("docs/verification-matrix.md") ||
1251
+ relativePath.startsWith("docs/loop-agent-harness.md") ||
1252
+ relativePath.startsWith("docs/architecture/runtime-boundaries.md")) {
1253
+ return "generated";
1254
+ }
1255
+ return "copied";
1256
+ }
1257
+ async function buildDesiredSurfaceContent(input) {
1258
+ const manifestPath = targetPathToManifestPath(input.entry.path, input.governanceRoot);
1259
+ if (input.entry.mode === "directory" || input.entry.mode === "state")
1260
+ return {};
1261
+ if (manifestPath === "README.md") {
1262
+ return {
1263
+ content: buildManagedReadmeBlock({
1264
+ projectName: input.projectName,
1265
+ governanceRoot: input.governanceRoot,
1266
+ }),
1267
+ };
1268
+ }
1269
+ if (manifestPath === "AGENTS.md") {
1270
+ return {
1271
+ content: await buildManagedAgentsBlock({
1272
+ assetRoot: input.assetRoot,
1273
+ projectName: input.projectName,
1274
+ governanceRoot: input.governanceRoot,
1275
+ }),
1276
+ };
1277
+ }
1278
+ if (manifestPath === ".gitignore") {
1279
+ return { content: buildManagedGitignoreBlock() };
1280
+ }
1281
+ if (manifestPath === "harness.json") {
1282
+ const template = await readJsonIfExists(path.join(input.assetRoot, "harness.json"));
1283
+ return {
1284
+ content: `${JSON.stringify(buildHarness({
1285
+ existing: {},
1286
+ template,
1287
+ projectName: input.projectName,
1288
+ governanceRoot: input.governanceRoot,
1289
+ }), null, 2)}\n`,
1290
+ };
1291
+ }
1292
+ if (manifestPath === OPENCODE_TRANSIENT_RETRY_PLUGIN_PATH) {
1293
+ return { content: buildOpenCodeTransientRetryPluginSource() };
1294
+ }
1295
+ if (manifestPath === OPENCODE_CONTEXT_OVERFLOW_COMPACT_PLUGIN_PATH) {
1296
+ return { content: buildOpenCodeContextOverflowCompactPluginSource() };
1297
+ }
1298
+ if (manifestPath === PI_CONTEXT_OVERFLOW_EXTENSION_PATH) {
1299
+ return { content: buildPiContextOverflowExtensionSource() };
1300
+ }
1301
+ if (manifestPath === PI_PROJECT_SETTINGS_PATH) {
1302
+ return { content: buildProjectPiSettingsSource() };
1303
+ }
1304
+ if (manifestPath.startsWith("scripts/")) {
1305
+ const scripts = buildInitScriptFiles(input.governanceRoot);
1306
+ return { content: scripts[manifestPath] };
1307
+ }
1308
+ if (manifestPath.startsWith(".harness/prompts/")) {
1309
+ const name = path.basename(manifestPath);
1310
+ return { content: COMPAT_PROMPTS[name] };
1311
+ }
1312
+ // `.agents/skills/...` is a target-only mirror of the bundled `skills/...`
1313
+ // directory; the package ships `skills/`, never `.agents/skills/`.
1314
+ if (manifestPath.startsWith(".agents/skills/")) {
1315
+ const sourceManifestPath = manifestPath.slice(".agents/".length);
1316
+ const mirrorSourcePath = path.join(input.assetRoot, sourceManifestPath);
1317
+ if (await exists(mirrorSourcePath)) {
1318
+ return {
1319
+ content: await readFile(mirrorSourcePath, "utf-8"),
1320
+ sourcePath: mirrorSourcePath,
1321
+ };
1322
+ }
1323
+ return {};
1324
+ }
1325
+ if (manifestPath.startsWith("docs/")) {
1326
+ const doc = manifestPath.slice("docs/".length);
1327
+ const generated = buildGeneratedCoreDoc({
1328
+ doc,
1329
+ projectName: input.projectName,
1330
+ governanceRoot: input.governanceRoot,
1331
+ });
1332
+ if (generated !== undefined)
1333
+ return { content: generated };
1334
+ const mappedSourcePath = path.join(input.assetRoot, "docs", coreDocSourceForTarget(doc));
1335
+ if (await exists(mappedSourcePath)) {
1336
+ return {
1337
+ content: await readFile(mappedSourcePath, "utf-8"),
1338
+ sourcePath: mappedSourcePath,
1339
+ };
1340
+ }
1341
+ }
1342
+ const sourcePath = path.join(input.assetRoot, manifestPath);
1343
+ if (await exists(sourcePath))
1344
+ return { content: await readFile(sourcePath, "utf-8"), sourcePath };
1345
+ return {};
1346
+ }
1347
+ function extractManagedBlock(content) {
1348
+ const start = content.indexOf(MANAGED_BLOCK_START);
1349
+ const end = content.indexOf(MANAGED_BLOCK_END);
1350
+ if (start < 0 || end <= start)
1351
+ return undefined;
1352
+ return content.slice(start, end + MANAGED_BLOCK_END.length);
1353
+ }
1354
+ async function readExistingSurfaceState(repoRoot) {
1355
+ const filePath = path.join(repoRoot, INIT_SURFACE_STATE_PATH);
1356
+ if (!(await exists(filePath)))
1357
+ return undefined;
1358
+ const parsed = JSON.parse(await readFile(filePath, "utf-8"));
1359
+ if (!isRecord(parsed) || parsed.schemaVersion !== 1)
1360
+ return undefined;
1361
+ return parsed;
1362
+ }
1363
+ /** The IDE schema reference remains a structural update signal. */
1364
+ const HARNESS_STRUCTURAL_VALUE_KEYS = new Set(["$schema"]);
1365
+ const PI_MODEL_ROUTING_KEYS = new Set(["LOW", "MED", "HIGH", "defaultModel"]);
1366
+ function isPiModelRoutingPath(path) {
1367
+ return path.length === 2 && path[0] === "executors" && path[1] === "pi";
1368
+ }
1369
+ /**
1370
+ * Attribute-set iteration comparison for harness.json: object key shapes must
1371
+ * agree at every level, except that the Pi model-routing fields are opaque
1372
+ * project-owned configuration. Scalars and array contents may otherwise differ
1373
+ * freely unless the key is structurally sensitive.
1374
+ */
1375
+ function harnessJsonShapeMatches(current, desired, path = []) {
1376
+ if (isRecord(current) || isRecord(desired)) {
1377
+ if (!isRecord(current) || !isRecord(desired))
1378
+ return false;
1379
+ const filterKeys = (value) => Object.keys(value)
1380
+ .filter((key) => !isPiModelRoutingPath(path) || !PI_MODEL_ROUTING_KEYS.has(key))
1381
+ .sort();
1382
+ const currentKeys = filterKeys(current);
1383
+ const desiredKeys = filterKeys(desired);
1384
+ if (currentKeys.length !== desiredKeys.length)
1385
+ return false;
1386
+ for (let index = 0; index < currentKeys.length; index += 1) {
1387
+ const key = currentKeys[index];
1388
+ if (key !== desiredKeys[index])
1389
+ return false;
1390
+ if (HARNESS_STRUCTURAL_VALUE_KEYS.has(key)) {
1391
+ if (!isDeepStrictEqual(current[key], desired[key]))
1392
+ return false;
1393
+ continue;
1394
+ }
1395
+ if (!harnessJsonShapeMatches(current[key], desired[key], [...path, key])) {
1396
+ return false;
1397
+ }
1398
+ }
1399
+ return true;
1400
+ }
1401
+ return true;
1402
+ }
1403
+ /**
1404
+ * Stable package/source anchor for generated desired content: sha256 of the
1405
+ * desired content re-rendered through buildDesiredSurfaceContent with fixed
1406
+ * sentinel identity values (PROJECT_NAME_TOKEN / GOVERNANCE_ROOT_TOKEN). The
1407
+ * anchor is therefore deterministic and invariant to the real project name and
1408
+ * governance root: a project rename or governance-root change never invalidates
1409
+ * a previously accepted semantic merge, while package/template content changes
1410
+ * still do. Rendering with sentinels (instead of reverse split/join replacement
1411
+ * on already-rendered bytes) avoids false drift when a real project name is a
1412
+ * common substring such as `init` or `docs`.
1413
+ */
1414
+ async function stableDesiredSourceAnchor(input) {
1415
+ const manifestPath = targetPathToManifestPath(input.entry.path, input.governanceRoot);
1416
+ const sentinel = await buildDesiredSurfaceContent({
1417
+ assetRoot: input.assetRoot,
1418
+ repoRoot: input.repoRoot,
1419
+ projectName: PROJECT_NAME_TOKEN,
1420
+ governanceRoot: GOVERNANCE_ROOT_TOKEN,
1421
+ entry: { ...input.entry, path: manifestPath },
1422
+ });
1423
+ return sentinel.content === undefined
1424
+ ? undefined
1425
+ : sha256Text(sentinel.content);
1426
+ }
1427
+ /**
1428
+ * Lightweight read-only preflight for the post-upgrade init surface notifier.
1429
+ *
1430
+ * Returns the recorded controller version and state kind only when a valid
1431
+ * `.harness/init-surface.json` exists. Missing or corrupt state, the source
1432
+ * repo, and uninitialized directories all resolve to `undefined`, so the
1433
+ * notifier can cheaply skip without running a full surface diff.
1434
+ */
1435
+ export async function readRecordedSurfaceControllerVersion(repoRoot) {
1436
+ let state;
1437
+ try {
1438
+ state = await readExistingSurfaceState(repoRoot);
1439
+ }
1440
+ catch {
1441
+ return undefined;
1442
+ }
1443
+ if (!state || typeof state.controllerVersion !== "string")
1444
+ return undefined;
1445
+ return {
1446
+ controllerVersion: state.controllerVersion,
1447
+ stateKind: state.stateKind,
1448
+ };
1449
+ }
1450
+ async function buildCurrentSurfaceState(input) {
1451
+ const assetRoot = await findPackageRoot();
1452
+ const controllerVersion = await readPackageVersion(assetRoot);
1453
+ const manifest = await readInitSurfaceManifest(assetRoot);
1454
+ const files = {};
1455
+ for (const manifestEntry of manifest.entries) {
1456
+ const targetRelativePath = normalizeRelativePath(manifestPathToTargetPath(manifestEntry.path, input.governanceRoot));
1457
+ const entry = {
1458
+ ...manifestEntry,
1459
+ path: targetRelativePath,
1460
+ };
1461
+ const targetPath = path.join(input.repoRoot, targetRelativePath);
1462
+ const targetStat = await stat(targetPath).catch(() => null);
1463
+ const desired = await buildDesiredSurfaceContent({
1464
+ assetRoot,
1465
+ repoRoot: input.repoRoot,
1466
+ projectName: input.projectName,
1467
+ governanceRoot: input.governanceRoot,
1468
+ entry: manifestEntry,
1469
+ });
1470
+ const sourceSha256 = desired.content === undefined ? undefined : sha256Text(desired.content);
1471
+ const sourceAnchorSha256 = entry.mode !== "generated" || desired.content === undefined
1472
+ ? undefined
1473
+ : await stableDesiredSourceAnchor({
1474
+ assetRoot,
1475
+ repoRoot: input.repoRoot,
1476
+ projectName: input.projectName,
1477
+ governanceRoot: input.governanceRoot,
1478
+ entry,
1479
+ });
1480
+ const base = {
1481
+ status: targetStat ? "present" : "missing",
1482
+ relationship: "missing-from-target",
1483
+ sourceSha256,
1484
+ sourceAnchorSha256,
1485
+ sourcePath: desired.sourcePath
1486
+ ? repoRelative(assetRoot, desired.sourcePath)
1487
+ : undefined,
1488
+ mode: entry.mode,
1489
+ };
1490
+ if (entry.mode === "state") {
1491
+ files[targetRelativePath] = {
1492
+ ...base,
1493
+ status: "present",
1494
+ relationship: "state-file",
1495
+ };
1496
+ continue;
1497
+ }
1498
+ if (!targetStat) {
1499
+ files[targetRelativePath] = base;
1500
+ continue;
1501
+ }
1502
+ if (entry.mode === "directory") {
1503
+ files[targetRelativePath] = {
1504
+ ...base,
1505
+ relationship: targetStat.isDirectory()
1506
+ ? "directory-present"
1507
+ : "local-existing-unknown",
1508
+ };
1509
+ continue;
1510
+ }
1511
+ const current = await readFile(targetPath);
1512
+ const currentSha256 = sha256Buffer(current);
1513
+ if (entry.mode === "managed-block") {
1514
+ const text = current.toString("utf-8");
1515
+ const currentBlock = entry.path === ".gitignore" || targetRelativePath === ".gitignore"
1516
+ ? extractGitignoreManagedBlock(text)
1517
+ : extractManagedBlock(text);
1518
+ files[targetRelativePath] = {
1519
+ ...base,
1520
+ currentSha256,
1521
+ relationship: currentBlock &&
1522
+ sourceSha256 &&
1523
+ sha256Text(currentBlock) === sourceSha256
1524
+ ? "managed-block-current"
1525
+ : "managed-block-present",
1526
+ };
1527
+ continue;
1528
+ }
1529
+ let semanticallyMatchesGeneratedJson = false;
1530
+ if (targetRelativePath === "harness.json" &&
1531
+ desired.content !== undefined) {
1532
+ try {
1533
+ semanticallyMatchesGeneratedJson = harnessJsonShapeMatches(JSON.parse(current.toString("utf-8")), JSON.parse(desired.content));
1534
+ }
1535
+ catch {
1536
+ // Invalid JSON remains local-existing-unknown for model merge / doctor.
1537
+ }
1538
+ }
1539
+ files[targetRelativePath] = {
1540
+ ...base,
1541
+ currentSha256,
1542
+ relationship: semanticallyMatchesGeneratedJson ||
1543
+ (sourceSha256 !== undefined && currentSha256 === sourceSha256)
1544
+ ? entry.mode === "generated"
1545
+ ? "matches-current-generated"
1546
+ : "matches-current-package"
1547
+ : "local-existing-unknown",
1548
+ };
1549
+ }
1550
+ return {
1551
+ schemaVersion: 1,
1552
+ controllerVersion,
1553
+ stateKind: input.stateKind,
1554
+ generatedAt: input.generatedAt ?? new Date().toISOString(),
1555
+ manifestSha256: manifest.sha256,
1556
+ files,
1557
+ };
1558
+ }
1559
+ async function writeInitSurfaceState(input) {
1560
+ const state = await buildCurrentSurfaceState(input);
1561
+ if (input.stateKind === "recorded" && input.preserveOwnershipFrom) {
1562
+ for (const [pathName, file] of Object.entries(state.files)) {
1563
+ if (file.relationship !== "local-existing-unknown")
1564
+ continue;
1565
+ const priorFile = input.preserveOwnershipFrom.files[pathName];
1566
+ const priorHash = priorFile?.currentSha256;
1567
+ if (priorHash !== undefined) {
1568
+ file.currentSha256 = priorHash;
1569
+ }
1570
+ else {
1571
+ delete file.currentSha256;
1572
+ }
1573
+ if (priorFile?.acceptedMerge) {
1574
+ file.acceptedMerge = { ...priorFile.acceptedMerge };
1575
+ }
1576
+ }
1577
+ }
1578
+ if (input.acceptCurrentHarnessAsMerge) {
1579
+ const harness = state.files["harness.json"];
1580
+ if (harness?.relationship === "local-existing-unknown" &&
1581
+ harness.currentSha256 !== undefined &&
1582
+ harness.sourceSha256 !== undefined) {
1583
+ harness.acceptedMerge = {
1584
+ currentSha256: harness.currentSha256,
1585
+ desiredSha256: harness.sourceSha256,
1586
+ ...(harness.sourceAnchorSha256 !== undefined
1587
+ ? { sourceAnchorSha256: harness.sourceAnchorSha256 }
1588
+ : {}),
1589
+ };
1590
+ }
1591
+ }
1592
+ const target = path.join(input.repoRoot, INIT_SURFACE_STATE_PATH);
1593
+ await mkdir(path.dirname(target), { recursive: true });
1594
+ await writeFile(target, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
1595
+ return state;
1596
+ }
1597
+ function isCurrentInitRelationship(relationship) {
1598
+ return (relationship === "matches-current-package" ||
1599
+ relationship === "matches-current-generated" ||
1600
+ relationship === "managed-block-current" ||
1601
+ relationship === "directory-present" ||
1602
+ relationship === "state-file");
1603
+ }
1604
+ async function fileSha256IfExists(filePath) {
1605
+ const info = await stat(filePath).catch(() => null);
1606
+ if (!info || !info.isFile())
1607
+ return undefined;
1608
+ return sha256Buffer(await readFile(filePath));
1609
+ }
1610
+ async function isDirectoryEmpty(dirPath) {
1611
+ const info = await stat(dirPath).catch(() => null);
1612
+ if (!info || !info.isDirectory())
1613
+ return false;
1614
+ return (await readdir(dirPath)).length === 0;
1615
+ }
1616
+ async function listRelativeFiles(rootPath) {
1617
+ const rootInfo = await stat(rootPath).catch(() => null);
1618
+ if (!rootInfo?.isDirectory())
1619
+ return [];
1620
+ const files = [];
1621
+ async function walk(currentPath, relativeDir) {
1622
+ for (const entry of await readdir(currentPath, { withFileTypes: true })) {
1623
+ const relativePath = relativeDir
1624
+ ? `${relativeDir}/${entry.name}`
1625
+ : entry.name;
1626
+ const absolutePath = path.join(currentPath, entry.name);
1627
+ if (entry.isDirectory())
1628
+ await walk(absolutePath, relativePath);
1629
+ else if (entry.isFile())
1630
+ files.push(relativePath);
1631
+ }
1632
+ }
1633
+ await walk(rootPath, "");
1634
+ return files.sort();
1635
+ }
1636
+ async function expectedLegacyInitFileSha(input) {
1637
+ if (input.legacyPath.startsWith("docs/")) {
1638
+ const doc = input.legacyPath.slice("docs/".length);
1639
+ const generated = buildGeneratedCoreDoc({
1640
+ doc,
1641
+ projectName: input.projectName,
1642
+ governanceRoot: "docs",
1643
+ });
1644
+ if (generated !== undefined)
1645
+ return sha256Text(generated);
1646
+ const generatedDir = GOVERNANCE_README_DIRS.find((dir) => input.legacyPath === `docs/${dir}/README.md`);
1647
+ if (generatedDir) {
1648
+ return sha256Text(buildGovernanceDirectoryReadme({
1649
+ dir: generatedDir,
1650
+ projectName: input.projectName,
1651
+ }));
1652
+ }
1653
+ }
1654
+ if (!input.legacyPath.startsWith("docs/") &&
1655
+ !input.legacyPath.startsWith("skills/")) {
1656
+ return undefined;
1657
+ }
1658
+ if (input.legacyPath.startsWith("docs/")) {
1659
+ const doc = input.legacyPath.slice("docs/".length);
1660
+ return fileSha256IfExists(path.join(input.assetRoot, "docs", coreDocSourceForTarget(doc)));
1661
+ }
1662
+ return fileSha256IfExists(path.join(input.assetRoot, input.legacyPath));
1663
+ }
1664
+ function collectSafeRetiredDirectories(paths) {
1665
+ const dirs = new Set();
1666
+ for (const retiredPath of paths) {
1667
+ let dir = path.posix.dirname(retiredPath);
1668
+ while (dir !== "." && dir !== "/") {
1669
+ if (dir === "skills" ||
1670
+ dir.startsWith("skills/") ||
1671
+ dir === "docs" ||
1672
+ dir.startsWith("docs/")) {
1673
+ dirs.add(dir);
1674
+ }
1675
+ dir = path.posix.dirname(dir);
1676
+ }
1677
+ if (retiredPath.startsWith("skills/"))
1678
+ dirs.add("skills");
1679
+ }
1680
+ return [...dirs].sort((left, right) => right.split("/").length - left.split("/").length);
1681
+ }
1682
+ const PI_MODEL_TIERS = ["LOW", "MED", "HIGH"];
1683
+ const LEGACY_MODEL_FIELDS = [
1684
+ "model",
1685
+ "models",
1686
+ "modelProfiles",
1687
+ "modelRouting",
1688
+ "provider",
1689
+ "verify",
1690
+ "sequentialWorkflowRole",
1691
+ ];
1692
+ function isQualifiedModelReference(value) {
1693
+ if (typeof value !== "string" || value.trim() !== value)
1694
+ return false;
1695
+ const slash = value.indexOf("/");
1696
+ return (slash > 0 &&
1697
+ slash < value.length - 1 &&
1698
+ !/\s/.test(value.slice(0, slash)) &&
1699
+ !/\s/.test(value.slice(slash + 1)));
1700
+ }
1701
+ /**
1702
+ * Only a single, qualified old default can be expanded automatically. All
1703
+ * routing/profile/tier ambiguity remains in the target for a bounded merge.
1704
+ */
1705
+ function assessHarnessModelMigration(harness) {
1706
+ const hasLegacyFields = LEGACY_MODEL_FIELDS.some((field) => field in harness);
1707
+ const executors = harness.executors;
1708
+ if (executors !== undefined && !isRecord(executors)) {
1709
+ return hasLegacyFields
1710
+ ? { kind: "ambiguous", reason: "executors is not an object" }
1711
+ : { kind: "none" };
1712
+ }
1713
+ const executorRecord = executors ?? {};
1714
+ if ("cursor" in executorRecord) {
1715
+ return {
1716
+ kind: "ambiguous",
1717
+ reason: "executors.cursor has user-owned runtime semantics",
1718
+ };
1719
+ }
1720
+ if ("pi" in executorRecord && !isRecord(executorRecord.pi)) {
1721
+ return {
1722
+ kind: "ambiguous",
1723
+ reason: "executors.pi is not an object",
1724
+ };
1725
+ }
1726
+ const pi = isRecord(executorRecord.pi) ? executorRecord.pi : {};
1727
+ const hasDefaultModel = "defaultModel" in pi;
1728
+ const hasTier = PI_MODEL_TIERS.some((tier) => tier in pi);
1729
+ if (["requiresApiKey", "provider", "model"].some((field) => field in pi) ||
1730
+ Object.keys(pi).some((field) => !["description", "enabled", "defaultModel", ...PI_MODEL_TIERS].includes(field))) {
1731
+ return {
1732
+ kind: "ambiguous",
1733
+ reason: "executors.pi contains unknown or deprecated model semantics",
1734
+ };
1735
+ }
1736
+ if (hasLegacyFields) {
1737
+ return {
1738
+ kind: "ambiguous",
1739
+ reason: "legacy top-level model or workflow fields cannot be proven equivalent",
1740
+ };
1741
+ }
1742
+ if (!hasDefaultModel)
1743
+ return { kind: "none" };
1744
+ if (hasTier) {
1745
+ return {
1746
+ kind: "ambiguous",
1747
+ reason: "existing LOW/MED/HIGH tier configuration cannot be replaced by defaultModel automatically",
1748
+ };
1749
+ }
1750
+ if (pi.defaultModel === "default")
1751
+ return { kind: "none" };
1752
+ if (!isQualifiedModelReference(pi.defaultModel)) {
1753
+ return {
1754
+ kind: "ambiguous",
1755
+ reason: "defaultModel is not a complete provider/model reference",
1756
+ };
1757
+ }
1758
+ return { kind: "safe", model: pi.defaultModel, removeDefaultModel: true };
1759
+ }
1760
+ function needsHarnessModelMigration(harness) {
1761
+ return assessHarnessModelMigration(harness).kind === "safe";
1762
+ }
1763
+ function migrateHarnessModelFields(harness) {
1764
+ const assessment = assessHarnessModelMigration(harness);
1765
+ if (assessment.kind !== "safe") {
1766
+ throw new Error("ambiguous harness model migration must be merged manually");
1767
+ }
1768
+ const next = { ...harness };
1769
+ for (const field of LEGACY_MODEL_FIELDS)
1770
+ delete next[field];
1771
+ const executors = isRecord(next.executors) ? { ...next.executors } : {};
1772
+ const pi = isRecord(executors.pi) ? { ...executors.pi } : {};
1773
+ if (assessment.model) {
1774
+ for (const tier of PI_MODEL_TIERS)
1775
+ pi[tier] = assessment.model;
1776
+ }
1777
+ if (assessment.removeDefaultModel)
1778
+ delete pi.defaultModel;
1779
+ executors.pi = pi;
1780
+ next.executors = executors;
1781
+ return next;
1782
+ }
1783
+ function hasLegacyHarnessGovernancePaths(harness) {
1784
+ for (const sectionName of ["entrypoints", "artifacts"]) {
1785
+ const section = harness[sectionName];
1786
+ if (isRecord(section) &&
1787
+ Object.values(section).some((value) => typeof value === "string" && value.startsWith("docs/"))) {
1788
+ return true;
1789
+ }
1790
+ }
1791
+ return false;
1792
+ }
1793
+ async function collectRetiredLayoutActions(input) {
1794
+ const blockedTargetPaths = new Set();
1795
+ if (!input.recordedState)
1796
+ return { actions: [], blockedTargetPaths };
1797
+ const actions = [];
1798
+ const retiredOwnedPaths = [];
1799
+ const currentPaths = new Set(Object.keys(input.currentState.files));
1800
+ const recordedPaths = new Set(Object.keys(input.recordedState.files));
1801
+ for (const [oldPath, oldState] of Object.entries(input.recordedState.files)) {
1802
+ const targetPath = normalizeRelativePath(manifestPathToTargetPath(oldPath, input.governanceRoot));
1803
+ if (targetPath === oldPath || !currentPaths.has(targetPath))
1804
+ continue;
1805
+ if (oldState.mode === "directory" || oldState.mode === "state")
1806
+ continue;
1807
+ const oldAbsolute = path.join(input.repoRoot, oldPath);
1808
+ const oldHash = await fileSha256IfExists(oldAbsolute);
1809
+ if (!oldHash)
1810
+ continue;
1811
+ if (!oldState.currentSha256 || oldHash !== oldState.currentSha256) {
1812
+ input.humanDecisions.push({
1813
+ path: oldPath,
1814
+ reason: `legacy init file may have local edits; review before migrating to ${targetPath}`,
1815
+ });
1816
+ // The user owns a modified copy at the legacy path; do not silently
1817
+ // install the package version at the canonical target path.
1818
+ blockedTargetPaths.add(targetPath);
1819
+ continue;
1820
+ }
1821
+ const targetAbsolute = path.join(input.repoRoot, targetPath);
1822
+ const targetHash = await fileSha256IfExists(targetAbsolute);
1823
+ const targetState = input.currentState.files[targetPath];
1824
+ if (!targetHash) {
1825
+ if (targetState?.sourceSha256 !== undefined &&
1826
+ targetState.sourceSha256 !== oldHash) {
1827
+ actions.push({
1828
+ type: "remove-owned-file",
1829
+ path: oldPath,
1830
+ reason: `remove unchanged legacy init file so the current ${targetPath} can be installed`,
1831
+ });
1832
+ retiredOwnedPaths.push(oldPath);
1833
+ continue;
1834
+ }
1835
+ actions.push({
1836
+ type: "migrate-owned-file",
1837
+ path: oldPath,
1838
+ targetPath,
1839
+ reason: `move loop-agent-owned legacy init file to ${targetPath}`,
1840
+ });
1841
+ retiredOwnedPaths.push(oldPath);
1842
+ continue;
1843
+ }
1844
+ const recordedTargetState = input.recordedState.files[targetPath];
1845
+ if (targetHash === oldHash ||
1846
+ (input.recordedState.stateKind === "recorded" &&
1847
+ recordedTargetState?.currentSha256 !== undefined &&
1848
+ recordedTargetState.currentSha256 === targetHash) ||
1849
+ isCurrentInitRelationship(targetState?.relationship)) {
1850
+ actions.push({
1851
+ type: "remove-owned-file",
1852
+ path: oldPath,
1853
+ reason: `remove duplicate loop-agent-owned legacy init file; current path is ${targetPath}`,
1854
+ });
1855
+ retiredOwnedPaths.push(oldPath);
1856
+ continue;
1857
+ }
1858
+ input.humanDecisions.push({
1859
+ path: oldPath,
1860
+ reason: `legacy init file is owned, but target path ${targetPath} already exists with different content`,
1861
+ });
1862
+ blockedTargetPaths.add(targetPath);
1863
+ }
1864
+ for (const legacyRoot of ["docs", "skills"]) {
1865
+ for (const relativePath of await listRelativeFiles(path.join(input.repoRoot, legacyRoot))) {
1866
+ const oldPath = `${legacyRoot}/${relativePath}`;
1867
+ if (recordedPaths.has(oldPath))
1868
+ continue;
1869
+ const targetPath = normalizeRelativePath(manifestPathToTargetPath(oldPath, input.governanceRoot));
1870
+ if (targetPath === oldPath)
1871
+ continue;
1872
+ const expectedSha = await expectedLegacyInitFileSha({
1873
+ assetRoot: input.assetRoot,
1874
+ projectName: input.projectName,
1875
+ legacyPath: oldPath,
1876
+ });
1877
+ if (!expectedSha)
1878
+ continue;
1879
+ const oldHash = await fileSha256IfExists(path.join(input.repoRoot, oldPath));
1880
+ if (!oldHash)
1881
+ continue;
1882
+ const targetHash = await fileSha256IfExists(path.join(input.repoRoot, targetPath));
1883
+ if (targetHash === oldHash) {
1884
+ actions.push({
1885
+ type: "remove-owned-file",
1886
+ path: oldPath,
1887
+ reason: `remove duplicate legacy init file; current path is ${targetPath}`,
1888
+ });
1889
+ retiredOwnedPaths.push(oldPath);
1890
+ continue;
1891
+ }
1892
+ if (oldHash !== expectedSha) {
1893
+ input.humanDecisions.push({
1894
+ path: oldPath,
1895
+ reason: `legacy init-shaped file is not recorded and differs from the current bundled/generated asset; review before migrating to ${targetPath}`,
1896
+ });
1897
+ blockedTargetPaths.add(targetPath);
1898
+ continue;
1899
+ }
1900
+ if (!targetHash) {
1901
+ actions.push({
1902
+ type: "migrate-owned-file",
1903
+ path: oldPath,
1904
+ targetPath,
1905
+ reason: `move package-matching legacy init file to ${targetPath}`,
1906
+ });
1907
+ retiredOwnedPaths.push(oldPath);
1908
+ continue;
1909
+ }
1910
+ input.humanDecisions.push({
1911
+ path: oldPath,
1912
+ reason: `legacy init-shaped file matches the package, but target path ${targetPath} already exists with different content`,
1913
+ });
1914
+ blockedTargetPaths.add(targetPath);
1915
+ }
1916
+ }
1917
+ for (const dir of collectSafeRetiredDirectories(retiredOwnedPaths)) {
1918
+ actions.push({
1919
+ type: "remove-empty-directory",
1920
+ path: dir,
1921
+ reason: "remove directory if it is empty after legacy init files are migrated",
1922
+ });
1923
+ }
1924
+ return { actions, blockedTargetPaths };
1925
+ }
1926
+ async function resolveInitProjectContext(input) {
1927
+ const harness = await readJsonIfExists(path.join(input.repoRoot, "harness.json"));
1928
+ const recordedGovernanceRoot = typeof harness.governanceRoot === "string"
1929
+ ? harness.governanceRoot
1930
+ : undefined;
1931
+ return {
1932
+ projectName: input.projectName ??
1933
+ (typeof harness.project === "string" ? harness.project : undefined) ??
1934
+ path.basename(input.repoRoot),
1935
+ governanceRoot: input.governanceRoot ??
1936
+ (recordedGovernanceRoot === "docs"
1937
+ ? DEFAULT_GOVERNANCE_ROOT
1938
+ : recordedGovernanceRoot) ??
1939
+ DEFAULT_GOVERNANCE_ROOT,
1940
+ };
1941
+ }
1942
+ function buildManagedReadmeBlock(input) {
1943
+ return [
1944
+ MANAGED_BLOCK_START,
1945
+ "## loop-agent 治理",
1946
+ "",
1947
+ `本仓库已初始化为 \`${input.projectName}\` 的 loop-agent harness 项目。loop-agent 负责 Agent DAG 生成、校验、执行与收口,并保留运行态事实。下面的内容是 deterministic CLI 生成的保守入口;目标项目的具体语义(技术栈、模块、运行命令、验证命令)应由初始化模型根据实际文件补全。`,
1948
+ "",
1949
+ "### 项目入口",
1950
+ "",
1951
+ "- `README.md`(本文件):人类首次进入项目和 agent 开工的首入口,应同时覆盖项目概览与开发/验证入口。",
1952
+ "- `harness.json`:loop-agent entrypoints、验证命令与 adapter 设置。",
1953
+ "- `AGENTS.md`:agent 在本仓库的工作协议。",
1954
+ `- \`${input.governanceRoot}/README.md\` - 治理文档索引(含原则、工作流、验证矩阵与方法论)`,
1955
+ "",
1956
+ "### 开发与验证入口",
1957
+ "",
1958
+ "开发、测试和门禁命令(按目标项目实际技术栈补全;下面是 loop-agent 治理脚本):",
1959
+ "",
1960
+ "```bash",
1961
+ "bash scripts/check-repo.sh",
1962
+ "bash scripts/ci-governance.sh",
1963
+ "bash scripts/ci-tests.sh",
1964
+ "bash scripts/ci.sh",
1965
+ "loop-agent inspect",
1966
+ "loop-agent doctor",
1967
+ "loop-agent docs audit",
1968
+ "```",
1969
+ "",
1970
+ "`scripts/ci-tests.sh` 必须反映目标项目真实语言和工具链。初始化生成版本会保守探测常见入口;当已知项目专属命令时,应按目标项目实际情况适配,并在 verification-matrix.md 中同步登记。",
1971
+ "",
1972
+ "### loop-agent 工作流入口",
1973
+ "",
1974
+ "默认使用 Agent DAG 作为实现工作流:",
1975
+ "",
1976
+ "```bash",
1977
+ 'loop-agent task advance <task-id> "任务标题" \\',
1978
+ " --prd <prd.md> \\",
1979
+ ' --allowed-path "<glob>" \\',
1980
+ ' --verify "<label>:<command>" \\',
1981
+ " --json",
1982
+ "# 审查 writeSet gate digest 后:",
1983
+ 'loop-agent task advance <task-id> --approve-gate "write-set-review:<digest>" --json',
1984
+ "loop-agent task status <task-id> --json",
1985
+ "```",
1986
+ "",
1987
+ `详细工作流见 \`${input.governanceRoot}/feature-workflow.md\`;验证矩阵见 \`${input.governanceRoot}/verification-matrix.md\`。任务 source 仍必需(\`source/需求.md\` / \`source/执行约束.md\`),默认由 \`task advance --prd\` 从详细 PRD 确定性派生,而不是主会话手写或 LLM 写 md。高级任意 DagSpec 才用 \`dag validate|execute|report\`,不进入标准 happy path。`,
1988
+ "",
1989
+ "执行后使用 `loop-agent dag report --run-id <run-id> --markdown` 和 `loop-agent dag doctor --run-id <run-id> --markdown` 读取事实与诊断失败。失败 run 不做成功式 closeout;使用 `loop-agent dag closeout-draft --run-id <run-id>` 生成 failure handoff。",
1990
+ "",
1991
+ "### 文档导航",
1992
+ "",
1993
+ `- \`${input.governanceRoot}/README.md\` - 治理文档索引(含原则、工作流、验证矩阵与方法论)`,
1994
+ `- \`${input.governanceRoot}/development-principles.md\` - 仓库开发原则`,
1995
+ `- \`${input.governanceRoot}/architecture/runtime-boundaries.md\` - runtime 层边界与依赖方向`,
1996
+ `- \`${input.governanceRoot}/feature-workflow.md\` - 有边界的功能工作流`,
1997
+ `- \`${input.governanceRoot}/verification-matrix.md\` - 治理与项目专属验证命令`,
1998
+ `- \`${input.governanceRoot}/loop-agent-harness.md\` - 目标项目如何使用 loop-agent`,
1999
+ "- `AGENTS.md` - agent 工作协议",
2000
+ "- `harness.json` - loop-agent 入口与验证命令配置",
2001
+ "",
2002
+ "Windows 上运行 `scripts/*.sh` 时使用 Git Bash 或兼容 Bash。实际文件操作使用平台原生路径;`/` 仅用于稳定仓库引用、Markdown/JSON 证据引用和 glob 约定。",
2003
+ MANAGED_BLOCK_END,
2004
+ ].join("\n");
2005
+ }
2006
+ function buildTargetReadme(input) {
2007
+ return [
2008
+ `# ${input.projectName}`,
2009
+ "",
2010
+ "> 本文件是项目入口。上面一行保留项目名称;下方 loop-agent managed block 由 deterministic CLI 生成。本文件正文人类可维护,用于描述项目概览、技术栈、目录结构、运行/验证命令等。初始化模型应根据目标项目实际文件补全本节正文。",
2011
+ "",
2012
+ "## 项目概览",
2013
+ "",
2014
+ "<!-- 初始化模型补充:项目是什么、解决什么问题、当前阶段。 -->",
2015
+ "",
2016
+ "## 技术栈与目录结构",
2017
+ "",
2018
+ "<!-- 初始化模型补充:语言、框架、构建工具、关键依赖与目录约定。 -->",
2019
+ "",
2020
+ "## 开发与验证",
2021
+ "",
2022
+ "<!-- 初始化模型补充:本地开发、测试、构建、门禁命令。 -->",
2023
+ "",
2024
+ buildManagedReadmeBlock(input),
2025
+ "",
2026
+ ].join("\n");
2027
+ }
2028
+ function buildTargetDocsReadme(input) {
2029
+ return [
2030
+ "# 文档索引 / Documentation Index",
2031
+ "",
2032
+ `本目录是 \`${input.projectName}\` 的 loop-agent 治理根目录,用于沉淀目标项目的持久工程上下文:决策、契约、计划、验证、报告与交接资料,与具体语言、框架或业务领域无关。`,
2033
+ "",
2034
+ `This directory is the loop-agent governance root for \`${input.projectName}\`. It records durable engineering context for the target project, independent of language, framework, or business domain.`,
2035
+ "",
2036
+ "## 核心文档 / Core Documents",
2037
+ "",
2038
+ "- `development-principles.md` - 仓库开发原则 / repository development principles",
2039
+ "- `architecture/runtime-boundaries.md` - runtime 层边界与依赖方向 / runtime layer boundaries and dependency direction",
2040
+ "- `feature-workflow.md` - 有边界的功能工作流 / bounded feature workflow",
2041
+ "- `verification-matrix.md` - 治理与项目专属验证命令 / governance and project-specific verification commands",
2042
+ "- `loop-agent-harness.md` - 目标项目如何使用 loop-agent / how this target project uses loop-agent",
2043
+ "",
2044
+ "## 方法论 / Methodology",
2045
+ "",
2046
+ "- `harness-methodology-tdd.md` - 行为变更与缺陷修复的 TDD 纪律 / TDD discipline for behavior changes and bug fixes",
2047
+ "- `harness-methodology-verification.md` - 完成声明前的验证纪律 / verification discipline before completion claims",
2048
+ "- `harness-methodology-debugging.md` - 修复前的系统性调试工作流 / systematic debugging workflow before fixes",
2049
+ "",
2050
+ "## 制品目录 / Artifacts",
2051
+ "",
2052
+ "- `design/README.md` - 设计笔记与实现契约 / design notes and implementation contracts",
2053
+ "- `exec-plans/active/README.md` - 活跃执行计划 / active execution plans",
2054
+ "- `exec-plans/completed/README.md` - 已完成执行计划 / completed execution plans",
2055
+ "- `progress/README.md` - 进度交接日志 / progress handoff logs",
2056
+ "- `reports/README.md` - 验证与审计报告 / verification and audit reports",
2057
+ "- `decisions/README.md` - 架构决策 / architecture decisions",
2058
+ "- `templates/README.md` - 模板入口与类别说明 / template entrypoint and category guide",
2059
+ "- `templates/` - 可复用的计划、报告与 DAG 模板 / reusable planning, reporting, and DAG templates",
2060
+ "- `templates/production-readiness-checklist.md` - 低/中风险单仓库 DAG 任务的 production readiness 检查清单 / production readiness checklist for low/medium-risk single-repo DAG work",
2061
+ "- `templates/worker-dogfood-setup.md` - 发布控制器下的真实 Worker sample setup / real Worker sample setup with a published controller",
2062
+ "- `templates/worker-dogfood-evidence.md` - Worker sample、Observe、morning report 与 QA coverage evidence / Worker evidence template",
2063
+ "",
2064
+ "## 验证 / Verification",
2065
+ "",
2066
+ "```bash",
2067
+ "bash scripts/check-repo.sh",
2068
+ "bash scripts/ci-tests.sh",
2069
+ "bash scripts/ci.sh",
2070
+ "```",
2071
+ "",
2072
+ "`ci-tests.sh` 刻意保持语言中立:它会探测常见的项目验证入口,初始化模型应在已知目标项目专属命令时按实际情况适配本文件。 / `ci-tests.sh` is intentionally language-neutral. It detects common project verification entrypoints and should be adapted by the initialization model when the target project has custom commands.",
2073
+ "",
2074
+ ].join("\n");
2075
+ }
2076
+ function buildTargetRuntimeBoundaries(input) {
2077
+ return [
2078
+ "# Runtime Boundaries",
2079
+ "",
2080
+ `本文定义 \`${input.projectName}\` 的 runtime 层边界、允许的依赖方向和治理检查入口。初始化版本是语言中立(language-neutral)模板;初始化模型应根据目标项目真实目录和技术栈补充具体层名、模块边界和例外。`,
2081
+ "",
2082
+ "## 默认分层",
2083
+ "",
2084
+ "```text",
2085
+ "Interface / Entry layer",
2086
+ " └─ CLI、HTTP API、UI 页面、job 入口或其他用户/系统入口",
2087
+ "",
2088
+ "Application / Use-case layer",
2089
+ " └─ 一次用户意图或业务动作的编排接口",
2090
+ "",
2091
+ "Domain / Workflow layer",
2092
+ " └─ 核心业务规则、状态机、工作流或领域模型",
2093
+ "",
2094
+ "Executors / Integrations layer",
2095
+ " └─ 外部工具、SDK、数据库、消息队列、浏览器、模型或 shell 适配",
2096
+ "",
2097
+ "Worker adapter layer (optional)",
2098
+ " └─ 产品线 TaskSpec / Task Pool / local Observe 适配;通过已发布 loop-agent CLI 执行,不在进程内耦合 target command 或 application 层",
2099
+ "",
2100
+ "Infrastructure / Store layer",
2101
+ " └─ 文件系统、数据库、缓存、运行事实、原子写入和生命周期副作用",
2102
+ "",
2103
+ "Governance layer",
2104
+ " └─ scripts/check-*.sh、CI、文档审计、边界检查和验证矩阵",
2105
+ "```",
2106
+ "",
2107
+ "## 依赖方向",
2108
+ "",
2109
+ "- Entry layer 可以依赖 Application / Use-case layer。",
2110
+ "- Application / Use-case layer 可以依赖 Domain / Workflow、Infrastructure 和 Integrations。",
2111
+ "- Domain / Workflow layer 不应依赖 Entry layer 的格式化、argv、HTTP/UI 细节。",
2112
+ "- Executors / Integrations 不应依赖 Entry layer 的输出格式。",
2113
+ "- Worker adapter 应把 `.harness/task-pool/` 作为独立运行事实区;它通过 CLI/subprocess contract 调用 loop-agent,不能复制或直接耦合 target command/application 实现。",
2114
+ "- Infrastructure / Store 应集中副作用,不把 raw path mutation 或持久化细节扩散给上层。",
2115
+ "",
2116
+ "## 目标项目适配",
2117
+ "",
2118
+ "初始化后请根据真实项目结构补充:",
2119
+ "",
2120
+ "- 入口目录和入口文件。",
2121
+ "- 核心业务/domain/workflow 模块。",
2122
+ "- infrastructure/store/integration 模块。",
2123
+ "- 允许的例外、迁移计划和对应验证命令。",
2124
+ "",
2125
+ "## Governance hooks",
2126
+ "",
2127
+ "```bash",
2128
+ "bash scripts/check-architecture-boundaries.sh",
2129
+ "bash scripts/check-repo.sh",
2130
+ "```",
2131
+ "",
2132
+ "`scripts/check-architecture-boundaries.sh` 是保守模板:只有当目标项目存在可识别目录时才启用对应 import 检查。不要为了通过脚本删除真实边界问题;应更新本文或修正依赖方向。",
2133
+ "",
2134
+ ].join("\n");
2135
+ }
2136
+ function buildTargetDevelopmentPrinciples(input) {
2137
+ return [
2138
+ "# Development Principles",
2139
+ "",
2140
+ `\`${input.projectName}\` uses loop-agent governed development. These principles apply regardless of language, framework, or deployment model.`,
2141
+ "",
2142
+ "## Operating Stance",
2143
+ "",
2144
+ "- The repository is the record system. Decisions, contracts, plans, tests, reports, and handoffs belong in tracked files.",
2145
+ "- Work advances in small, reversible, verifiable increments.",
2146
+ "- Baseline verification comes before new work when the current state is uncertain.",
2147
+ "- Completion is defined by fresh evidence, not by intent or confidence.",
2148
+ "- Preserve unrelated user changes.",
2149
+ "",
2150
+ "## Principles",
2151
+ "",
2152
+ "1. One task advances one bounded work block.",
2153
+ "2. Search existing code, docs, scripts, and tests before designing new behavior.",
2154
+ "3. Shell verification is the completion authority.",
2155
+ `4. Runtime state belongs in \`.harness/\`; durable decisions belong in \`${input.governanceRoot}/\`.`,
2156
+ "5. Model writer nodes must be bounded by explicit allowed and forbidden paths.",
2157
+ "6. Advisory model output must be followed by deterministic verification.",
2158
+ "7. Repeated constraints should become docs, tests, scripts, checks, or templates.",
2159
+ "8. Do not keep hidden process state only in chat.",
2160
+ "9. Do not add placeholders as completed implementation.",
2161
+ "10. Prefer existing local project patterns before adding new abstractions.",
2162
+ "",
2163
+ "## Task Slicing: Vertical Tracer Bullets First",
2164
+ "",
2165
+ "Principle 1 covers **granularity** (one bounded block). This section covers **shape**: each slice should cross the real integration layers the work needs and leave an independently verifiable narrow loop.",
2166
+ "",
2167
+ "- Every slice needs its own acceptance criteria, verification commands, and failure conditions.",
2168
+ '- Prefer vertical tracer bullets over horizontal layering. Paths like "schema → API → UI → tests" are *possible* examples only; do not assume every project has those layers.',
2169
+ "- Horizontal anti-patterns: finish all of one layer before the next; or write every test first, then implement everything.",
2170
+ "- For behavior changes, use one failing test → minimal implementation → green → next behavior. Do not batch all RED then all GREEN.",
2171
+ "- Split large features into multiple independently runnable tasks/DAGs instead of one oversized writer across every layer.",
2172
+ "",
2173
+ "### Autonomy vs Governance (independent layers)",
2174
+ "",
2175
+ "| Dimension | Meaning | How to decide |",
2176
+ "|---|---|---|",
2177
+ "| Autonomy | Whether the slice needs synchronous human judgment, external access, or non-automatable decisions | Declare AFK/HITL in Contract, open questions, or human gate signals |",
2178
+ "| Governance | How strong review, repair, write-set, and verification gates must be | Default `--profile auto`; route to `minimal` / `standard` / `reviewed` / `supervised` by risk and delivery signals |",
2179
+ "",
2180
+ "- AFK does not mean `minimal` is required; ordinary automatable work may land on `standard` or `reviewed`.",
2181
+ "- HITL does not mean choosing `supervised` alone yields a correct human decision; require a concrete pause reason / decision gate.",
2182
+ "- Use `minimal` only for a single narrow writer, deterministic post shell verification, and no escalation signals.",
2183
+ "- High-risk, public-contract, init/runtime/CI/governance surface, or real human judgment gates should escalate via `auto`, or explicitly choose `reviewed` / `supervised`.",
2184
+ "",
2185
+ "## Target Project Adaptation",
2186
+ "",
2187
+ "The initialized scripts provide language-neutral governance. The initialization model should adapt project-specific verification commands after reading the target project's actual files and toolchain.",
2188
+ "",
2189
+ "Repo-local skills live under `.agents/skills/` (mirrored from the package `skills/` fallback).",
2190
+ "",
2191
+ ].join("\n");
2192
+ }
2193
+ function buildTargetFeatureWorkflow(input) {
2194
+ return [
2195
+ "# Feature Workflow",
2196
+ "",
2197
+ "This document describes how work should move through this target repository using loop-agent.",
2198
+ "",
2199
+ "## Session Protocol",
2200
+ "",
2201
+ "1. Orient: read `README.md`, `harness.json`, `AGENTS.md`, and this docs index.",
2202
+ "2. Select: choose one bounded work block.",
2203
+ "3. Contract: state deliverables, non-goals, completion criteria, verification commands, and failure conditions.",
2204
+ "4. Implement: make the smallest coherent change and update required docs, scripts, and tests.",
2205
+ "5. Verify: run governance checks plus target project verification.",
2206
+ `6. Handoff: record evidence in ${input.governanceRoot}/progress, ${input.governanceRoot}/reports, an exec plan, or an ADR when useful.`,
2207
+ "",
2208
+ "## Agent DAG Path",
2209
+ "",
2210
+ "```bash",
2211
+ 'loop-agent task advance <task-id> "Task title" \\',
2212
+ " --prd <prd.md> \\",
2213
+ ' --allowed-path "<glob>" \\',
2214
+ ' --verify "<label>:<command>" \\',
2215
+ " --json",
2216
+ "# Review writeSet gate digest, then:",
2217
+ 'loop-agent task advance <task-id> --approve-gate "write-set-review:<digest>" --json',
2218
+ "loop-agent task status <task-id> --json",
2219
+ "```",
2220
+ "",
2221
+ DAG_HARD_GATE_TRIGGER,
2222
+ "",
2223
+ "Before executing a DAG, review profile routing, governance profile, writer writeSet, allowed paths, forbidden paths, shell verification, and decision gate mode.",
2224
+ "Generated DAGs bind authoritative task-source paths, SHA-256 hashes, and explicit REQ/BR/AC identifiers. Frontend plans with explicit identifiers pass a deterministic coverage gate before final design review and implementation.",
2225
+ "After an interrupted run, repair the task source and regenerate the complete DAG. Do not construct an impl-only recovery DAG from an upstream summary; strict governance rejects a v3 orphan writer without sourceBinding or a read-only planner ancestor.",
2226
+ "",
2227
+ "## Specialized Task Kinds",
2228
+ "",
2229
+ "- standard tasks first use the structured task type in `source/需求.md`, then combine `allowedPaths` with strong React/Next/Vue project evidence for deterministic routing.",
2230
+ "- A frontend project defaults eligible implementation work to the frontend DAG without requirement keyword matching. Explicit backend, mixed, frontend-negated, and documentation/test-only scopes keep the template selected by the normal governance profile.",
2231
+ "- `frontend-mock-assess-pi` reads Mock/API/schema evidence and selects `native|browser-intercept|request-adapter|not-needed|blocked` before frontend planning; its deterministic gate rejects blocked or malformed output, while the existing frontend implementer remains the only writer.",
2232
+ "- Mock-backed verification never proves real API integration. When the backend was not exercised, closeout must retain the gap and name `<task-id>-real-api-integration-verify`; that follow-up is explicitly created/run after backend readiness, never automatic.",
2233
+ "- Automatic frontend classification selects the frontend implementation workflow and persists taskKind; explicit profiles, workflowPolicy, and supervised quality gates record governance strength without switching the business workflow back to a generic DAG.",
2234
+ "- A backend implementation does not select the backend test DAG. `backend-test` remains an explicit test-engineering workflow.",
2235
+ "- Explicit specialized `taskKind` values remain compatible and take precedence over task-source classification.",
2236
+ "",
2237
+ "Set an explicit specialized `taskKind` in `.harness/tasks/<task-id>/task.json` only when the dedicated workflow itself is part of the task contract:",
2238
+ "",
2239
+ '- `taskKind: "frontend-implementation"` explicitly selects the frontend DAG for compatibility or intentional override. Optional `frontendMock` config sets `policy: auto|required|disabled`, an existing `serviceRoot`, and generation-time-frozen `verifyCommands`; an unsafe or incomplete explicit required contract produces an assessment-only DAG with no writer, while a complete required contract adds Mock-specific verification only when trusted commands exist. Auto API tasks without a native service may use an existing browser interception harness or reversible request adapter, then continue through static and behavior verification.',
2240
+ '- `taskKind: "backend-test"` selects the dedicated backend test DAG. Its Pi nodes analyze requirements, generate and review backend cases, generate pytest, and retrospect on results; shell gate/execution nodes enforce the review verdict and run the target project\'s pytest. The backend test templates (`backend-test-dag.json` and the `backend-test-dag.*.prompt.md` files) ship inside the loop-agent package as static references and are projected to target projects under the governance `templates/` directory.',
2241
+ '- `taskKind: "knowledge-sync"` selects the Feature-scoped test-knowledge write-back DAG (collect → draft → validate → apply → pointer). Bind `featureId` in `task.json` (or hardConstraints / requirement text). It writes only under `features/<featureId>/…` after final verification evidence exists.',
2242
+ '- `taskKind: "knowledge-graph-bootstrap"` selects the business knowledge-graph bootstrap DAG (preflight → inventory → propose → validate → review → gate → promote → materialize). AI writes only `knowledge/bootstrap/staging/**`; promote is merge-new-only.',
2243
+ '- `taskKind: "frontend-test"` selects the FE-test RAG DAG. It writes a traceable frontend RAG package and Markdown case manifest, then executes manifest cases serially with `playwright-cli` in isolated test environments and retains per-case evidence. It never generates pytest or Playwright source code. `frontendTest.maxCasesPerBatch` defaults to 50 (maximum 100); optional `maxTokensPerCase` and `maxTotalTokens` stop only later cases after a completed case\'s token usage is recorded, marking them `blocked: token-budget-exhausted`. Need-login cases open LOGIN_URL then goto TARGET_URL; otherwise open or goto TARGET_URL. The generic playwright-cli skill is unchanged.',
2244
+ "- Only eligible read-only Pi nodes (planner, scout, reviewer, verifier, closeout with no write-capable tool profile) receive the conservative automatic retry policy. Supervisor, implementer, writer, docs-only, dynamic, shell, static, and decision-gate nodes are not retried automatically. Eligible nodes cannot write repository files; the controller only records immutable attempt evidence under `.harness/dag-runs/<state>/<run-id>/<node-id>/attempt-<n>.json`.",
2245
+ "",
2246
+ "Use the package-backed public knowledge CLI for graph operations. Do not require target projects to run package-only kb runtime scripts:",
2247
+ "",
2248
+ "```bash",
2249
+ "loop-agent knowledge graph-init --product-name <name>",
2250
+ "# edit knowledge/bootstrap/scope.yaml, then:",
2251
+ "loop-agent task advance <task-id> --task-kind knowledge-graph-bootstrap --json",
2252
+ "loop-agent knowledge query --mode by_feature --feature F-2026-004 --json",
2253
+ "loop-agent knowledge query --mode by_id --id SVC-order --json",
2254
+ 'loop-agent knowledge query --mode search --text "keyword" --json',
2255
+ "loop-agent knowledge graph-incremental-prepare --feature F-2026-004 --service <service>",
2256
+ "# review the prepared scope/staging; for a manual reviewed promotion:",
2257
+ "loop-agent knowledge graph-promote",
2258
+ "loop-agent knowledge graph-materialize",
2259
+ "```",
2260
+ "",
2261
+ 'Daily Feature test-knowledge write-back still uses `taskKind: "knowledge-sync"` with a bound `featureId`, separate from graph bootstrap/incremental entry points.',
2262
+ "",
2263
+ "## Verification",
2264
+ "",
2265
+ `Use \`${input.governanceRoot}/verification-matrix.md\` to choose the narrowest command that proves the claim.`,
2266
+ "",
2267
+ ].join("\n");
2268
+ }
2269
+ function buildTargetVerificationMatrix() {
2270
+ return [
2271
+ "# Verification Matrix",
2272
+ "",
2273
+ "Use the narrowest command that proves the claim. This matrix is language-neutral: governance checks are generated by loop-agent, while project-specific checks should reflect the target project's actual toolchain.",
2274
+ "",
2275
+ "| Claim | Minimum verification | Stronger verification |",
2276
+ "|---|---|---|",
2277
+ "| loop-agent governance is valid | `bash scripts/check-repo.sh` | `bash scripts/ci-governance.sh` |",
2278
+ "| target project tests are valid | `bash scripts/ci-tests.sh` | project-specific full test/build command |",
2279
+ "| full local delivery is valid | `bash scripts/ci.sh` | add deployment/package-specific checks when relevant |",
2280
+ "| docs/governance changed | `bash scripts/check-repo.sh` | `loop-agent docs audit` plus `bash scripts/ci-governance.sh` |",
2281
+ "| model writer changed files | `git status --short` + relevant verification | `bash scripts/ci.sh` |",
2282
+ "",
2283
+ "## Generated Commands",
2284
+ "",
2285
+ "```bash",
2286
+ "bash scripts/check-engineering-structure.sh",
2287
+ "bash scripts/check-doc-index.sh",
2288
+ "bash scripts/check-doc-links.sh",
2289
+ "bash scripts/check-active-plan-status.sh",
2290
+ "bash scripts/check-exec-plan-index-sync.sh",
2291
+ "bash scripts/check-harness-runtime-clean.sh",
2292
+ "bash scripts/check-architecture-boundaries.sh",
2293
+ "bash scripts/check-skill-entry.sh",
2294
+ "bash scripts/check-repo.sh",
2295
+ "bash scripts/ci-governance.sh",
2296
+ "bash scripts/ci-tests.sh",
2297
+ "bash scripts/ci.sh",
2298
+ "```",
2299
+ "",
2300
+ "## Project-Specific Verification",
2301
+ "",
2302
+ "`scripts/ci-tests.sh` detects common entrypoints such as package.json, Makefile, go.mod, Cargo.toml, Python test metadata, Maven, Gradle, and .NET projects. If no safe command is detected, it exits successfully with a clear message so the initialization model can adapt this file to the target project.",
2303
+ "",
2304
+ "After initialization, update this matrix with the target project's actual quick, standard, and full verification commands.",
2305
+ "",
2306
+ ].join("\n");
2307
+ }
2308
+ function buildTargetLoopAgentHarness(input) {
2309
+ return [
2310
+ "# loop-agent Harness",
2311
+ "",
2312
+ `This target project, \`${input.projectName}\`, is initialized to use loop-agent for governed task execution.`,
2313
+ "",
2314
+ "## Runtime Layout",
2315
+ "",
2316
+ "- `.harness/tasks/` stores task state and source materials.",
2317
+ "- `.harness/dag-runs/` stores DAG run facts.",
2318
+ "- `.harness/runs/` stores one-shot executor facts.",
2319
+ "- `.harness/task-pool/` stores optional agent-worker Task Pool state, batch artifacts, failure handoffs, and local Observe events; it is runtime state and normally ignored by Git.",
2320
+ `- \`${input.governanceRoot}/\` stores loop-agent generated governance, plans, reports, progress, and decisions.`,
2321
+ "- `.agents/skills/` stores project repo-local skill instructions; the CLI can fall back to bundled package skills when needed.",
2322
+ "",
2323
+ "## Default Workflow",
2324
+ "",
2325
+ "Use `loop-agent task advance` / `loop-agent task status` for non-trivial implementation work; advanced arbitrary DagSpec uses `loop-agent dag execute`.",
2326
+ "",
2327
+ "## Pi Model Matrix",
2328
+ "",
2329
+ "New DAG work reads only `harness.json.executors.pi.LOW`, `MED`, and `HIGH`. Each tier must be a model string or `{ model, thinking? }`; use explicit `provider/model` references when choosing a provider. Fresh init does not write `defaultModel`, `models`, `modelProfiles`, or `modelRouting`. `defaultModel` remains a runtime fallback only for compatible older projects.",
2330
+ "",
2331
+ "```json",
2332
+ '{ "executors": { "pi": { "LOW": "provider/low", "MED": "provider/medium", "HIGH": "provider/high" } } }',
2333
+ "```",
2334
+ "",
2335
+ "## Adaptive Liveness",
2336
+ "",
2337
+ "- Healthy Pi nodes are no longer stopped by a fixed 30-minute deadline. The default 4h absolute max is a final safety bound and cannot be extended by synthetic heartbeats.",
2338
+ "- Runner heartbeat proves only the runner lease. Provider, tool, or output activity records meaningful progress; prolonged inactivity can surface as `quiet`, `suspected-stall`, `probing`, or `needs-attention` in `dag status`, `dag doctor`, and Observe.",
2339
+ "- A silent transport is aborted in a controlled way. `termination-unconfirmed` means the old attempt may still exist, so loop-agent fails closed and does not start an automatic retry; inspect the run before any operator recovery.",
2340
+ "- `agent-worker` does not set an outer `task advance` / `dag execute` wall-clock by default. An explicit `worker.timeout_ms` remains a hard timeout.",
2341
+ "",
2342
+ "## Operator Recovery",
2343
+ "",
2344
+ "After a run, use `loop-agent dag report --run-id <run-id> --markdown` to read canonical run facts and `loop-agent dag doctor --run-id <run-id> --markdown` to diagnose failed or paused runs. Failed DAG runs should produce a failure handoff via `loop-agent dag closeout-draft --run-id <run-id>` instead of a successful closeout.",
2345
+ "",
2346
+ `Use \`${input.governanceRoot}/templates/production-readiness-checklist.md\` when claiming Production Readiness v0.1 for low/medium-risk single-repo DAG work.`,
2347
+ "",
2348
+ `For real product-line Worker samples, use \`${input.governanceRoot}/templates/worker-dogfood-setup.md\` and \`${input.governanceRoot}/templates/worker-dogfood-evidence.md\`. Explicit failed-task retries must preserve prior evidence and use a new worker run id.`,
2349
+ "",
2350
+ "## Script Matrix",
2351
+ "",
2352
+ "`scripts/check-repo.sh` verifies loop-agent governance. `scripts/ci-tests.sh` handles target project verification through conservative language/toolchain detection and should be adapted after reading the project.",
2353
+ "Copy or project only stack-agnostic governance scripts. For project-specific verification, packaging, release, or maintenance commands, generate the target-project version from templates plus the target repository's actual files instead of copying loop-agent's own TypeScript-specific scripts.",
2354
+ "",
2355
+ ].join("\n");
2356
+ }
2357
+ function buildGeneratedCoreDoc(input) {
2358
+ switch (input.doc) {
2359
+ case "README.md":
2360
+ return buildTargetDocsReadme({ projectName: input.projectName });
2361
+ case "architecture/runtime-boundaries.md":
2362
+ return buildTargetRuntimeBoundaries({ projectName: input.projectName });
2363
+ case "development-principles.md":
2364
+ return buildTargetDevelopmentPrinciples({
2365
+ projectName: input.projectName,
2366
+ governanceRoot: input.governanceRoot,
2367
+ });
2368
+ case "feature-workflow.md":
2369
+ return buildTargetFeatureWorkflow({
2370
+ governanceRoot: input.governanceRoot,
2371
+ });
2372
+ case "verification-matrix.md":
2373
+ return buildTargetVerificationMatrix();
2374
+ case "loop-agent-harness.md":
2375
+ return buildTargetLoopAgentHarness({
2376
+ projectName: input.projectName,
2377
+ governanceRoot: input.governanceRoot,
2378
+ });
2379
+ default:
2380
+ return undefined;
2381
+ }
2382
+ }
2383
+ function buildGovernanceDirectoryReadme(input) {
2384
+ const title = input.dir
2385
+ .split("/")
2386
+ .map((part) => part.replace(/-/g, " "))
2387
+ .join(" / ");
2388
+ if (input.dir === "decisions" || input.dir.endsWith("/decisions")) {
2389
+ return [
2390
+ `# ${title}`,
2391
+ "",
2392
+ `This directory stores ${input.projectName} architecture decision records (ADRs).`,
2393
+ "",
2394
+ "## When to write an ADR",
2395
+ "",
2396
+ "Write an ADR only when **all three** are true:",
2397
+ "",
2398
+ "1. **Hard to reverse** — changing the decision later has meaningful cost.",
2399
+ "2. **Surprising without context** — a future reader will ask why this path was chosen.",
2400
+ "3. **Real trade-off** — genuine alternatives existed and one was chosen for specific reasons.",
2401
+ "",
2402
+ "If any condition is missing, skip the ADR. Temporary scheduling, obvious implementation choices, and facts with no alternatives do not belong here.",
2403
+ "",
2404
+ "Use the ADR template under this governance root's `templates/adr.md` (or the package template when projecting). Update this README index when adding a new ADR.",
2405
+ "",
2406
+ "初始化只创建目录契约,不复制 loop-agent 源仓库的历史决策正文。目标项目中的具体 ADR 应由后续任务按真实取舍生成。",
2407
+ "",
2408
+ ].join("\n");
2409
+ }
2410
+ return [
2411
+ `# ${title}`,
2412
+ "",
2413
+ `This directory stores ${input.projectName} governance artifacts generated during real work.`,
2414
+ "",
2415
+ "初始化只创建目录契约,不复制 loop-agent 源仓库的历史任务正文或历史索引。目标项目中的具体 plan、progress、report、decision 应由后续任务按真实执行事实生成。",
2416
+ "",
2417
+ ].join("\n");
2418
+ }
2419
+ export function buildInitInstructions(input) {
2420
+ const projectName = input.projectName ?? path.basename(path.resolve(input.repoRoot));
2421
+ const governanceRoot = input.governanceRoot ?? DEFAULT_GOVERNANCE_ROOT;
2422
+ return [
2423
+ "# loop-agent Initialization Instructions",
2424
+ "",
2425
+ `Initialize the target repository as a loop-agent harness project: \`${input.repoRoot}\`.`,
2426
+ "",
2427
+ "## Confirm With User",
2428
+ "",
2429
+ `- Project name: default \`${projectName}\`.`,
2430
+ `- Governance root: default \`${governanceRoot}\`.`,
2431
+ "- Confirm the Pi LOW/MED/HIGH matrix. Explicit input must be `--model provider/model` or a paired `--provider provider --model model`; do not guess a provider for a bare model.",
2432
+ "- Default: merge existing AGENTS.md, harness.json, and loop-agent governance docs instead of overwriting user content.",
2433
+ "",
2434
+ "## Initialization Upgrade Routing",
2435
+ "",
2436
+ "When the user says `loop-agent初始化更新`, `loop-agent 初始化更新`, `更新 loop-agent 初始化内容`, or an equivalent write request, run `loop-agent init upgrade --repo-root . --json` as the single controller-owned entry. Complete returned single-file allowedPaths merge tasks and call `--continue` until a stable terminal result; do not stop at check-update, needs-safe-update, needs-model-merge, or verification-pending.",
2437
+ "Explicit `检查初始化更新`, `初始化更新校验`, or `只检查,不要修改` stays strictly read-only: run only `loop-agent init check-update --repo-root . --markdown` and do not create an upgrade run.",
2438
+ "Upgrade manages project `.opencode/plugins/`, `.pi/extensions/`, and `.pi/settings.json` by nested merge. Pi must trust the project before loading these settings; do not read or write `~/.pi/agent/settings.json` by default.",
2439
+ "The upgrade also writes a read-only gitignore migration assessment (`.harness/init-upgrades/<run-id>/gitignore-migration.json`): tracked `.harness/**` and `.agents/**` get index-only `git rm -r --cached --ignore-unmatch` guidance (working-tree files are kept). Pause for human review when `.agents` content is tracked or when staged changes / git query failures block safe guidance; never run git rm yourself.",
2440
+ "",
2441
+ "## Apply Defaults",
2442
+ "",
2443
+ '- Use the current loop-agent harness.json as the default template, but write target project name plus `adapter: "loop-agent"` and a Pi-only `executors.pi.LOW/MED/HIGH` matrix. Do not write `defaultModel` when all three tiers exist, and never project `model`, `models`, `modelProfiles`, `modelRouting`, `verify`, or `sequentialWorkflowRole`.',
2444
+ `- ${DAG_HARD_GATE_TRIGGER}`,
2445
+ "- Generate the target project's loop-agent script matrix from templates: structure check, docs index/link checks, active plan status, exec-plan index sync, harness runtime cleanliness, architecture boundaries, skill entry integrity, governance CI, project-test CI, and full CI.",
2446
+ "- Copy or project only stack-agnostic governance scripts. For project-specific verification, packaging, release, or maintenance commands, generate the target-project version from templates plus the target repository's actual files instead of copying loop-agent's own TypeScript-specific scripts.",
2447
+ "- Do not assume the target project is TypeScript, Node.js, frontend, backend, or any other specific stack.",
2448
+ "- `scripts/ci-tests.sh` must be language-neutral: detect common project verification entrypoints conservatively, run only commands that exist, and clearly report when project-specific verification needs model/user adaptation.",
2449
+ "- Default verification entries: quick `bash scripts/check-repo.sh`, standard `bash scripts/ci-governance.sh`, full `bash scripts/ci.sh`.",
2450
+ `- Generate target-project versions of \`${governanceRoot}/README.md\`, \`${governanceRoot}/development-principles.md\`, \`${governanceRoot}/architecture/runtime-boundaries.md\`, \`${governanceRoot}/feature-workflow.md\`, \`${governanceRoot}/verification-matrix.md\`, and \`${governanceRoot}/loop-agent-harness.md\`; copy only generic methodology/template material as-is.`,
2451
+ "- After deterministic initialization, inspect README/config/build files (for example package.json, pyproject.toml, go.mod, Cargo.toml, pom.xml, Gradle files, Makefile, .sln/.csproj, or project-specific scripts) and adapt `scripts/ci-tests.sh` plus the verification matrix to the real project.",
2452
+ "- Enrich the root `README.md`: keep the deterministic project title and the loop-agent managed block intact, and fill the human-authored sections (项目概览, 技术栈与目录结构, 开发与验证) from the target project's actual files. The root README must serve both as a human-first project entry and as an agent work entry; replace the initialization-model supplement comments when the project files provide the information.",
2453
+ `- Populate \`${governanceRoot}/verification-matrix.md\` with the target project's actual quick, standard, and full verification commands derived from its real language and toolchain, keeping the governance rows intact.`,
2454
+ "- Project repo-local skills live in `.agents/skills/`. Do not create a root `skills/` directory in the target project; the package's bundled `skills/` remains the built-in fallback.",
2455
+ "- Merge a loop-agent managed block into `.gitignore` that ignores exactly the four local, rebuildable runtime directories (`.harness/`, `.agents/`, `.task-pool/`, `.worktrees/`) while keeping `scripts/` and `ai_workspace/loop-agent/` on the shared commit surface; keep all user rules outside the block untouched.",
2456
+ "- Do not copy examples by default; examples stay bundled in the tool and are available through `loop-agent examples`.",
2457
+ "- Add or update a loop-agent managed block in AGENTS.md.",
2458
+ "- The generated AGENTS.md must include documentation convergence and structured DAG write-boundary rules so target projects keep the same working discipline as this repository.",
2459
+ "- The generated target docs must surface the production readiness checklist and the operator recovery path: `dag report`, `dag doctor --run-id --markdown`, and failed-run `dag closeout-draft` failure handoff.",
2460
+ "- Create `.harness/` runtime directories but do not create historical run facts.",
2461
+ "",
2462
+ "## Automatic Workflow For Model-Executed Init",
2463
+ "",
2464
+ "When a model/agent is asked to initialize a target project, treat initialization as one automated workflow. Do not run deterministic init and then wait for another user turn to fill project-specific content.",
2465
+ "",
2466
+ "1. Resolve only the inputs that are genuinely unsafe to assume: provider/model, non-default governance root, overwrite policy, credentials, cost, or deployment side effects.",
2467
+ "2. Run deterministic init with merge defaults.",
2468
+ "3. Continue immediately into project inspection and adaptation.",
2469
+ "4. Finish by running the init health checks and quick verification.",
2470
+ "",
2471
+ "If provider/model is not specified and the existing tier matrix is usable, proceed with it and record the assumption in the handoff. Ask the user only when the choice affects credentials, cost, availability, or an explicitly requested model backend. An ambiguous old model configuration must stay in a bounded harness.json model merge/human decision; never silently reinterpret it.",
2472
+ "",
2473
+ "## Required Model Adaptation",
2474
+ "",
2475
+ "Do not stop after the deterministic `loop-agent init` command. When these instructions are executed by an agent/model, the initialization is not complete until the target project has been inspected and the generated scaffolding has been adapted.",
2476
+ "",
2477
+ "Required post-init steps:",
2478
+ "",
2479
+ "1. Read the target project's existing README, manifest/build/config files, and top-level source/module directories.",
2480
+ "2. Replace the root README's `初始化模型补充` comments when the information can be inferred from files. At minimum, fill project overview, technology stack/directory structure, and development/verification commands.",
2481
+ `3. Update \`${governanceRoot}/verification-matrix.md\` with the target project's actual quick, standard, and full verification commands.`,
2482
+ `4. Confirm the generated docs expose \`${governanceRoot}/templates/production-readiness-checklist.md\` plus the operator recovery path: \`dag report\`, \`dag doctor --run-id --markdown\`, and failed-run \`dag closeout-draft\` failure handoff.`,
2483
+ "5. Update `scripts/ci-tests.sh` only when the conservative generated detector is insufficient for the target project.",
2484
+ "6. Run `loop-agent init doctor`, `loop-agent inspect`, `loop-agent docs audit`, and the quick verification command.",
2485
+ "",
2486
+ "Completion rule: if target files provide enough evidence, the final README should not leave generic `初始化模型补充` comments in the sections that can be filled. If a section truly cannot be inferred, write a short explicit note such as `尚未从仓库文件中识别到 ...` instead of asking the user to fill it later.",
2487
+ "",
2488
+ "## Suggested Command",
2489
+ "",
2490
+ "```bash",
2491
+ `loop-agent init --repo-root ${input.repoRoot} --profile full --merge`,
2492
+ "loop-agent init doctor --repo-root <target>",
2493
+ "loop-agent inspect --repo-root <target>",
2494
+ "loop-agent docs audit --repo-root <target>",
2495
+ "```",
2496
+ ].join("\n");
2497
+ }
2498
+ export async function initializeLoopAgentProject(options) {
2499
+ const repoRoot = path.resolve(options.repoRoot);
2500
+ const projectName = options.projectName ?? path.basename(repoRoot);
2501
+ const governanceRoot = options.governanceRoot ?? DEFAULT_GOVERNANCE_ROOT;
2502
+ const profile = options.profile ?? "full";
2503
+ const merge = options.merge ?? true;
2504
+ const model = normalizeInitModelReference(options);
2505
+ const written = [];
2506
+ const skipped = [];
2507
+ const assetRoot = await findPackageRoot();
2508
+ const template = await readJsonIfExists(path.join(assetRoot, "harness.json"));
2509
+ let existingHarness = await readJsonIfExists(path.join(repoRoot, "harness.json"));
2510
+ if (Object.keys(existingHarness).length > 0) {
2511
+ const migration = assessHarnessModelMigration(existingHarness);
2512
+ if (migration.kind === "ambiguous") {
2513
+ throw new Error(`init refuses to overwrite ambiguous legacy model configuration: ${migration.reason}; run init check-update and complete the harness.json model merge`);
2514
+ }
2515
+ if (migration.kind === "safe") {
2516
+ existingHarness = migrateHarnessModelFields(existingHarness);
2517
+ }
2518
+ }
2519
+ await mkdir(repoRoot, { recursive: true });
2520
+ const readmePath = path.join(repoRoot, "README.md");
2521
+ const existingReadme = (await exists(readmePath))
2522
+ ? await readFile(readmePath, "utf-8")
2523
+ : undefined;
2524
+ const readmeContent = existingReadme
2525
+ ? mergeManagedBlock(existingReadme, buildManagedReadmeBlock({ projectName, governanceRoot }))
2526
+ : buildTargetReadme({ projectName, governanceRoot });
2527
+ await writeText({
2528
+ repoRoot,
2529
+ relativePath: "README.md",
2530
+ content: readmeContent,
2531
+ merge: true,
2532
+ written,
2533
+ skipped,
2534
+ });
2535
+ const harness = buildHarness({
2536
+ existing: existingHarness,
2537
+ template,
2538
+ projectName,
2539
+ governanceRoot,
2540
+ model,
2541
+ });
2542
+ await writeText({
2543
+ repoRoot,
2544
+ relativePath: "harness.json",
2545
+ content: `${JSON.stringify(harness, null, 2)}\n`,
2546
+ merge: true,
2547
+ written,
2548
+ skipped,
2549
+ });
2550
+ const agentsPath = path.join(repoRoot, "AGENTS.md");
2551
+ const existingAgents = (await exists(agentsPath))
2552
+ ? await readFile(agentsPath, "utf-8")
2553
+ : `# AGENTS.md\n`;
2554
+ await writeText({
2555
+ repoRoot,
2556
+ relativePath: "AGENTS.md",
2557
+ content: mergeManagedBlock(existingAgents, await buildManagedAgentsBlock({
2558
+ assetRoot,
2559
+ projectName,
2560
+ governanceRoot,
2561
+ })),
2562
+ merge: true,
2563
+ written,
2564
+ skipped,
2565
+ });
2566
+ const gitignorePath = path.join(repoRoot, ".gitignore");
2567
+ const existingGitignore = (await exists(gitignorePath))
2568
+ ? await readFile(gitignorePath, "utf-8")
2569
+ : "";
2570
+ await writeText({
2571
+ repoRoot,
2572
+ relativePath: ".gitignore",
2573
+ content: mergeGitignoreManagedBlock(existingGitignore, buildManagedGitignoreBlock()),
2574
+ merge: true,
2575
+ written,
2576
+ skipped,
2577
+ });
2578
+ for (const doc of CORE_DOC_FILES) {
2579
+ const generated = buildGeneratedCoreDoc({
2580
+ doc: doc.target,
2581
+ projectName,
2582
+ governanceRoot,
2583
+ });
2584
+ if (generated) {
2585
+ await writeTextIfMissing({
2586
+ repoRoot,
2587
+ relativePath: path.join(governanceRoot, doc.target),
2588
+ content: generated,
2589
+ merge,
2590
+ written,
2591
+ skipped,
2592
+ });
2593
+ continue;
2594
+ }
2595
+ await copyFileIfMissing({
2596
+ assetRoot,
2597
+ repoRoot,
2598
+ sourceRelativePath: path.join("docs", doc.source),
2599
+ targetRelativePath: path.join(governanceRoot, doc.target),
2600
+ merge,
2601
+ written,
2602
+ skipped,
2603
+ });
2604
+ }
2605
+ for (const dir of GOVERNANCE_README_DIRS) {
2606
+ await writeTextIfMissing({
2607
+ repoRoot,
2608
+ relativePath: path.join(governanceRoot, dir, "README.md"),
2609
+ content: buildGovernanceDirectoryReadme({ dir, projectName }),
2610
+ merge,
2611
+ written,
2612
+ skipped,
2613
+ });
2614
+ }
2615
+ await copyDirMerge({
2616
+ assetRoot,
2617
+ repoRoot,
2618
+ sourceRelativePath: "docs/templates",
2619
+ targetRelativePath: path.join(governanceRoot, "templates"),
2620
+ written,
2621
+ });
2622
+ if (profile === "full") {
2623
+ // Project repo-local skills live only under `.agents/skills/`.
2624
+ // The package still ships `skills/` as the built-in fallback source.
2625
+ await copyDirMerge({
2626
+ assetRoot,
2627
+ repoRoot,
2628
+ sourceRelativePath: "skills",
2629
+ targetRelativePath: ".agents/skills",
2630
+ written,
2631
+ });
2632
+ }
2633
+ else {
2634
+ skipped.push(".agents/skills/");
2635
+ }
2636
+ skipped.push("examples/");
2637
+ for (const [relativePath, content] of Object.entries(buildInitScriptFiles(governanceRoot))) {
2638
+ await writeText({
2639
+ repoRoot,
2640
+ relativePath,
2641
+ content,
2642
+ merge: true,
2643
+ written,
2644
+ skipped,
2645
+ });
2646
+ }
2647
+ await ensureHarnessDirs(repoRoot, written);
2648
+ await writeCompatPrompts({ repoRoot, merge, written, skipped });
2649
+ const clientRecoveryMode = options.clientRecovery ?? "auto";
2650
+ const clientRecovery = clientRecoveryMode === "off"
2651
+ ? undefined
2652
+ : await runClientRecovery({
2653
+ repoRoot,
2654
+ mode: clientRecoveryMode,
2655
+ });
2656
+ const clientRecoveryPaths = [
2657
+ OPENCODE_TRANSIENT_RETRY_PLUGIN_PATH,
2658
+ OPENCODE_CONTEXT_OVERFLOW_COMPACT_PLUGIN_PATH,
2659
+ PI_CONTEXT_OVERFLOW_EXTENSION_PATH,
2660
+ PI_PROJECT_SETTINGS_PATH,
2661
+ ];
2662
+ if (clientRecoveryMode === "off") {
2663
+ for (const recoveryPath of clientRecoveryPaths)
2664
+ skipped.push(recoveryPath);
2665
+ }
2666
+ else if (clientRecovery) {
2667
+ const installResults = [
2668
+ clientRecovery.plugin,
2669
+ clientRecovery.overflowPlugin,
2670
+ clientRecovery.piExtension,
2671
+ { written: clientRecovery.pi?.wrote === true },
2672
+ ];
2673
+ for (const [index, recoveryPath] of clientRecoveryPaths.entries()) {
2674
+ if (installResults[index]?.written)
2675
+ written.push(recoveryPath);
2676
+ }
2677
+ }
2678
+ await writeInitSurfaceState({
2679
+ repoRoot,
2680
+ projectName,
2681
+ governanceRoot,
2682
+ stateKind: "recorded",
2683
+ acceptCurrentHarnessAsMerge: model !== undefined,
2684
+ });
2685
+ written.push(INIT_SURFACE_STATE_PATH);
2686
+ return {
2687
+ repoRoot,
2688
+ projectName,
2689
+ governanceRoot,
2690
+ profile,
2691
+ written,
2692
+ skipped,
2693
+ clientRecovery,
2694
+ };
2695
+ }
2696
+ function actionForMissing(pathName, state) {
2697
+ if (state.mode === "state")
2698
+ return undefined;
2699
+ if (state.mode === "directory") {
2700
+ return {
2701
+ type: "create-directory",
2702
+ path: pathName,
2703
+ reason: "required init directory is missing",
2704
+ };
2705
+ }
2706
+ if (state.mode === "copied") {
2707
+ return {
2708
+ type: "copy-missing",
2709
+ path: pathName,
2710
+ reason: "required bundled init file is missing",
2711
+ };
2712
+ }
2713
+ if (state.mode === "generated") {
2714
+ return {
2715
+ type: "write-generated-missing",
2716
+ path: pathName,
2717
+ reason: "required generated init file is missing",
2718
+ };
2719
+ }
2720
+ return {
2721
+ type: "refresh-managed-block",
2722
+ path: pathName,
2723
+ reason: "managed block file is missing or stale",
2724
+ };
2725
+ }
2726
+ function modelMergeTaskFor(pathName, state, allPaths, recordedState) {
2727
+ return {
2728
+ taskId: `init-merge-${sha256Text(pathName).slice(0, 12)}`,
2729
+ path: pathName,
2730
+ reason: "target file exists but does not match the current loop-agent initialization surface",
2731
+ sourcePath: state.sourcePath,
2732
+ allowedPaths: [pathName],
2733
+ forbiddenPaths: [
2734
+ ...allPaths.filter((candidate) => candidate !== pathName),
2735
+ ".harness/dag-runs/**",
2736
+ ".harness/runs/**",
2737
+ ".git/**",
2738
+ ],
2739
+ mergeRules: [
2740
+ "Preserve target-project user content and project-specific commands.",
2741
+ "Adopt the current loop-agent initialization structure where it does not conflict with local intent.",
2742
+ "Do not delete user-authored sections merely because they differ from the package template.",
2743
+ "Keep writes inside allowedPaths only.",
2744
+ ],
2745
+ verification: [
2746
+ "loop-agent init check-update --repo-root . --json",
2747
+ "loop-agent init doctor --repo-root .",
2748
+ "loop-agent inspect --repo-root .",
2749
+ "loop-agent docs audit --repo-root .",
2750
+ "bash scripts/check-repo.sh",
2751
+ ],
2752
+ evidence: {
2753
+ baseSha256: recordedState?.files[pathName]?.currentSha256,
2754
+ currentSha256: state.currentSha256,
2755
+ desiredSha256: state.sourceSha256,
2756
+ sourceAnchorSha256: state.sourceAnchorSha256,
2757
+ },
2758
+ };
2759
+ }
2760
+ function recommendedNextFor(report) {
2761
+ const next = [];
2762
+ if (report.surfaceState === "missing") {
2763
+ next.push("loop-agent init update --repo-root <target> --bootstrap-surface");
2764
+ }
2765
+ if (report.deterministicActions.some((action) => action.type !== "bootstrap-surface")) {
2766
+ next.push("loop-agent init update --repo-root <target> --apply-safe");
2767
+ }
2768
+ if (report.modelMergeTasks.length > 0) {
2769
+ next.push("loop-agent init check-update --repo-root <target> --markdown");
2770
+ }
2771
+ if (report.humanDecisions.length > 0) {
2772
+ next.push("Review humanDecisions before applying semantic or policy-sensitive changes.");
2773
+ }
2774
+ if (next.length === 0)
2775
+ next.push("No init update actions are needed.");
2776
+ return next;
2777
+ }
2778
+ const CLIENT_RECOVERY_SURFACE_PATHS = new Set([
2779
+ OPENCODE_TRANSIENT_RETRY_PLUGIN_PATH,
2780
+ OPENCODE_CONTEXT_OVERFLOW_COMPACT_PLUGIN_PATH,
2781
+ PI_CONTEXT_OVERFLOW_EXTENSION_PATH,
2782
+ PI_PROJECT_SETTINGS_PATH,
2783
+ ]);
2784
+ function isClientRecoverySurfacePath(pathName) {
2785
+ return CLIENT_RECOVERY_SURFACE_PATHS.has(pathName);
2786
+ }
2787
+ export async function checkInitUpdate(input) {
2788
+ const repoRoot = path.resolve(input.repoRoot);
2789
+ const { projectName, governanceRoot } = await resolveInitProjectContext({
2790
+ repoRoot,
2791
+ projectName: input.projectName,
2792
+ governanceRoot: input.governanceRoot,
2793
+ });
2794
+ const clientRecoveryMode = input.clientRecovery ?? "auto";
2795
+ const skipClientRecoverySurfaces = clientRecoveryMode === "off";
2796
+ const recordedState = await readExistingSurfaceState(repoRoot);
2797
+ const assetRoot = await findPackageRoot();
2798
+ const surfaceState = recordedState?.stateKind ?? "missing";
2799
+ const currentState = await buildCurrentSurfaceState({
2800
+ repoRoot,
2801
+ projectName,
2802
+ governanceRoot,
2803
+ stateKind: recordedState?.stateKind ?? "inferred-baseline",
2804
+ });
2805
+ const allPaths = Object.keys(currentState.files).sort();
2806
+ const deterministicActions = [];
2807
+ const modelMergeTasks = [];
2808
+ const humanDecisions = [];
2809
+ const retiredLayoutResult = await collectRetiredLayoutActions({
2810
+ assetRoot,
2811
+ repoRoot,
2812
+ projectName,
2813
+ governanceRoot,
2814
+ recordedState,
2815
+ currentState,
2816
+ humanDecisions,
2817
+ });
2818
+ const retiredLayoutActions = retiredLayoutResult.actions;
2819
+ const retiredTargetPaths = new Set(retiredLayoutActions
2820
+ .filter((action) => action.type === "migrate-owned-file" && action.targetPath)
2821
+ .map((action) => action.targetPath));
2822
+ // Targets occupied by a user-modified legacy copy must not be silently
2823
+ // (re)installed from the package; preserve them for a human/model merge.
2824
+ for (const blockedPath of retiredLayoutResult.blockedTargetPaths) {
2825
+ retiredTargetPaths.add(blockedPath);
2826
+ }
2827
+ if (!recordedState) {
2828
+ deterministicActions.push({
2829
+ type: "bootstrap-surface",
2830
+ path: INIT_SURFACE_STATE_PATH,
2831
+ reason: "target project has no recorded init surface baseline",
2832
+ });
2833
+ }
2834
+ for (const [pathName, state] of Object.entries(currentState.files)) {
2835
+ // Mirror fresh init: client-recovery=off never reinstalls recovery surfaces.
2836
+ if (skipClientRecoverySurfaces && isClientRecoverySurfacePath(pathName)) {
2837
+ continue;
2838
+ }
2839
+ // `.pi/settings.json` is a structured project configuration: fill only
2840
+ // absent nested recovery fields and preserve every explicit project value.
2841
+ if (pathName === PI_PROJECT_SETTINGS_PATH &&
2842
+ state.relationship === "local-existing-unknown") {
2843
+ const projectPi = await inspectProjectPiSettings({ repoRoot });
2844
+ if (projectPi.action === "write") {
2845
+ deterministicActions.push({
2846
+ type: "merge-pi-project-settings",
2847
+ path: pathName,
2848
+ reason: "fill missing project Pi retry/compaction fields without overwriting explicit values",
2849
+ });
2850
+ continue;
2851
+ }
2852
+ if (projectPi.reason === "matches" || projectPi.reason === "enabled-false") {
2853
+ continue;
2854
+ }
2855
+ }
2856
+ if (state.relationship === "missing-from-target") {
2857
+ if (retiredTargetPaths.has(pathName))
2858
+ continue;
2859
+ const action = actionForMissing(pathName, state);
2860
+ if (action)
2861
+ deterministicActions.push(action);
2862
+ continue;
2863
+ }
2864
+ if (state.relationship === "managed-block-present") {
2865
+ deterministicActions.push({
2866
+ type: "refresh-managed-block",
2867
+ path: pathName,
2868
+ reason: "managed block is missing or differs from the current package block",
2869
+ });
2870
+ continue;
2871
+ }
2872
+ if (state.relationship === "local-existing-unknown") {
2873
+ const acceptedMerge = recordedState?.files[pathName]?.acceptedMerge;
2874
+ if (acceptedMerge) {
2875
+ // Generated acceptance is bound to the stable source anchor so that
2876
+ // project-derived desired drift (projectName/governanceRoot changes)
2877
+ // cannot invalidate an already accepted merge. Copied files remain
2878
+ // byte-strict; desiredSha256 stays the audit receipt in both cases.
2879
+ const anchorAccepted = state.mode === "generated" &&
2880
+ state.sourceAnchorSha256 !== undefined &&
2881
+ acceptedMerge.sourceAnchorSha256 === state.sourceAnchorSha256;
2882
+ if (acceptedMerge.currentSha256 === state.currentSha256 &&
2883
+ (anchorAccepted ||
2884
+ acceptedMerge.desiredSha256 === state.sourceSha256)) {
2885
+ continue;
2886
+ }
2887
+ // A previously accepted semantic merge is user-preserving ownership.
2888
+ // A later desired/current change must be merged again, never refreshed
2889
+ // as an unchanged package-owned file.
2890
+ modelMergeTasks.push(modelMergeTaskFor(pathName, state, allPaths, recordedState));
2891
+ continue;
2892
+ }
2893
+ if (state.mode === "directory") {
2894
+ humanDecisions.push({
2895
+ path: pathName,
2896
+ reason: "required init directory path exists but is not a directory",
2897
+ });
2898
+ }
2899
+ else if (recordedState?.stateKind === "recorded" &&
2900
+ (state.mode === "copied" || state.mode === "generated") &&
2901
+ state.currentSha256 !== undefined &&
2902
+ recordedState.files[pathName]?.currentSha256 === state.currentSha256) {
2903
+ deterministicActions.push({
2904
+ type: "refresh-owned-file",
2905
+ path: pathName,
2906
+ mode: state.mode,
2907
+ reason: "refresh an unchanged recorded init file to the current generated or bundled content",
2908
+ });
2909
+ }
2910
+ else {
2911
+ modelMergeTasks.push(modelMergeTaskFor(pathName, state, allPaths, recordedState));
2912
+ }
2913
+ }
2914
+ }
2915
+ deterministicActions.unshift(...retiredLayoutActions);
2916
+ // Safe harness hygiene: strip obsolete pi.requiresApiKey without full harness rewrite.
2917
+ // Cursor requiresApiKey is intentionally kept when present.
2918
+ const harnessPath = path.join(repoRoot, "harness.json");
2919
+ if (await exists(harnessPath)) {
2920
+ try {
2921
+ const harness = JSON.parse(await readFile(harnessPath, "utf-8"));
2922
+ if (recordedState &&
2923
+ isRecord(harness) &&
2924
+ harness.$schema !== expectedHarnessSchemaRef(governanceRoot)) {
2925
+ deterministicActions.push({
2926
+ type: "add-harness-schema-ref",
2927
+ path: "harness.json",
2928
+ reason: "harness.json is missing or has a stale IDE JSON Schema reference",
2929
+ });
2930
+ }
2931
+ if (recordedState &&
2932
+ isRecord(harness) &&
2933
+ governanceRoot !== "docs" &&
2934
+ (harness.governanceRoot === "docs" ||
2935
+ hasLegacyHarnessGovernancePaths(harness))) {
2936
+ deterministicActions.push({
2937
+ type: "migrate-harness-governance-root",
2938
+ path: "harness.json",
2939
+ reason: `replace the legacy default governanceRoot docs with ${governanceRoot}`,
2940
+ });
2941
+ }
2942
+ // A harness that already matches the current generated surface except for
2943
+ // opaque Pi routing fields is modern, not a legacy-routing candidate.
2944
+ // Keep the explicit migration assessment for every other harness shape.
2945
+ if (isRecord(harness) &&
2946
+ currentState.files["harness.json"]?.relationship !==
2947
+ "matches-current-generated") {
2948
+ const modelMigration = assessHarnessModelMigration(harness);
2949
+ if (modelMigration.kind === "safe") {
2950
+ deterministicActions.push({
2951
+ type: "migrate-harness-model-fields",
2952
+ path: "harness.json",
2953
+ reason: "expand one proven-equivalent legacy model into the Pi LOW/MED/HIGH matrix",
2954
+ });
2955
+ }
2956
+ else if (modelMigration.kind === "ambiguous") {
2957
+ const state = currentState.files["harness.json"];
2958
+ const mergeTask = modelMergeTaskFor("harness.json", state, allPaths, recordedState);
2959
+ mergeTask.reason = `legacy model configuration requires a human/model merge: ${modelMigration.reason}`;
2960
+ mergeTask.mergeRules = [
2961
+ "Preserve every existing harness.json field unless the user explicitly approves its migration.",
2962
+ "Map only unambiguous complete provider/model references to executors.pi.LOW, MED, and HIGH.",
2963
+ "Do not retain defaultModel when all three tiers are present; do not invent a provider for bare model ids.",
2964
+ "Do not restore models, modelProfiles, modelRouting, cursor, or other legacy routing fields.",
2965
+ ];
2966
+ mergeTask.verification = [
2967
+ "loop-agent init check-update --repo-root . --json",
2968
+ "loop-agent inspect --repo-root .",
2969
+ "bash scripts/check-repo.sh",
2970
+ ];
2971
+ const existingTask = modelMergeTasks.findIndex((task) => task.path === "harness.json");
2972
+ if (existingTask >= 0)
2973
+ modelMergeTasks[existingTask] = mergeTask;
2974
+ else
2975
+ modelMergeTasks.push(mergeTask);
2976
+ }
2977
+ }
2978
+ }
2979
+ catch {
2980
+ // leave malformed harness for model merge / doctor
2981
+ }
2982
+ }
2983
+ const partial = {
2984
+ repoRoot,
2985
+ controllerVersion: currentState.controllerVersion,
2986
+ surfaceState,
2987
+ deterministicActions,
2988
+ modelMergeTasks,
2989
+ humanDecisions,
2990
+ summary: {
2991
+ missing: deterministicActions.filter((action) => action.type !== "bootstrap-surface").length,
2992
+ modelMerge: modelMergeTasks.length,
2993
+ humanDecision: humanDecisions.length,
2994
+ },
2995
+ };
2996
+ const recommendedNext = recommendedNextFor(partial);
2997
+ // Project settings are part of the surface. Do not inspect user home by default.
2998
+ const piInspection = await inspectProjectPiSettings({ repoRoot });
2999
+ return {
3000
+ ...partial,
3001
+ ok: deterministicActions.length === 0 &&
3002
+ modelMergeTasks.length === 0 &&
3003
+ humanDecisions.length === 0,
3004
+ recommendedNext,
3005
+ clientRecovery: {
3006
+ pi: piInspection,
3007
+ },
3008
+ };
3009
+ }
3010
+ async function applySafeAction(input) {
3011
+ if (input.action.type === "merge-pi-project-settings") {
3012
+ return (await applyProjectPiSettingsMerge({ repoRoot: input.repoRoot })).wrote === true;
3013
+ }
3014
+ if (input.action.type === "add-harness-schema-ref") {
3015
+ const target = path.join(input.repoRoot, "harness.json");
3016
+ if (!(await exists(target)))
3017
+ return false;
3018
+ const harness = JSON.parse(await readFile(target, "utf-8"));
3019
+ if (!isRecord(harness))
3020
+ return false;
3021
+ if (harness.$schema === expectedHarnessSchemaRef(input.governanceRoot))
3022
+ return false;
3023
+ const next = {
3024
+ ...harness,
3025
+ $schema: expectedHarnessSchemaRef(input.governanceRoot),
3026
+ };
3027
+ await writeFile(target, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
3028
+ return true;
3029
+ }
3030
+ if (input.action.type === "strip-pi-requires-api-key") {
3031
+ const target = path.join(input.repoRoot, "harness.json");
3032
+ if (!(await exists(target)))
3033
+ return false;
3034
+ const harness = JSON.parse(await readFile(target, "utf-8"));
3035
+ if (!isRecord(harness) ||
3036
+ !isRecord(harness.executors) ||
3037
+ !isRecord(harness.executors.pi)) {
3038
+ return false;
3039
+ }
3040
+ if (!Object.hasOwn(harness.executors.pi, "requiresApiKey")) {
3041
+ return false;
3042
+ }
3043
+ const nextPi = { ...harness.executors.pi };
3044
+ delete nextPi.requiresApiKey;
3045
+ const next = {
3046
+ ...harness,
3047
+ executors: {
3048
+ ...harness.executors,
3049
+ pi: nextPi,
3050
+ },
3051
+ };
3052
+ await writeFile(target, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
3053
+ return true;
3054
+ }
3055
+ if (input.action.type === "migrate-harness-model-fields") {
3056
+ const target = path.join(input.repoRoot, "harness.json");
3057
+ if (!(await exists(target)))
3058
+ return false;
3059
+ const harness = JSON.parse(await readFile(target, "utf-8"));
3060
+ if (!isRecord(harness) || !needsHarnessModelMigration(harness))
3061
+ return false;
3062
+ await writeFile(target, `${JSON.stringify(migrateHarnessModelFields(harness), null, 2)}\n`, "utf-8");
3063
+ return true;
3064
+ }
3065
+ if (input.action.type === "migrate-harness-governance-root") {
3066
+ const target = path.join(input.repoRoot, "harness.json");
3067
+ if (!(await exists(target)))
3068
+ return false;
3069
+ const harness = JSON.parse(await readFile(target, "utf-8"));
3070
+ if (!isRecord(harness) ||
3071
+ (harness.governanceRoot !== "docs" &&
3072
+ !hasLegacyHarnessGovernancePaths(harness))) {
3073
+ return false;
3074
+ }
3075
+ const next = {
3076
+ ...harness,
3077
+ governanceRoot: input.governanceRoot,
3078
+ };
3079
+ for (const sectionName of ["entrypoints", "artifacts"]) {
3080
+ const section = harness[sectionName];
3081
+ if (!isRecord(section))
3082
+ continue;
3083
+ next[sectionName] = Object.fromEntries(Object.entries(section).map(([key, value]) => [
3084
+ key,
3085
+ typeof value === "string" && value.startsWith("docs/")
3086
+ ? `${input.governanceRoot}/${value.slice("docs/".length)}`
3087
+ : value,
3088
+ ]));
3089
+ }
3090
+ await writeFile(target, `${JSON.stringify(next, null, 2)}\n`, "utf-8");
3091
+ return true;
3092
+ }
3093
+ if (input.action.type === "migrate-owned-file") {
3094
+ if (!input.action.targetPath)
3095
+ return false;
3096
+ const source = path.join(input.repoRoot, input.action.path);
3097
+ const target = path.join(input.repoRoot, input.action.targetPath);
3098
+ if (!(await exists(source)) || (await exists(target)))
3099
+ return false;
3100
+ await mkdir(path.dirname(target), { recursive: true });
3101
+ await rename(source, target);
3102
+ return true;
3103
+ }
3104
+ if (input.action.type === "remove-owned-file") {
3105
+ const target = path.join(input.repoRoot, input.action.path);
3106
+ if (!(await exists(target)))
3107
+ return false;
3108
+ await rm(target, { force: true });
3109
+ return true;
3110
+ }
3111
+ if (input.action.type === "remove-empty-directory") {
3112
+ const target = path.join(input.repoRoot, input.action.path);
3113
+ if (!(await isDirectoryEmpty(target)))
3114
+ return false;
3115
+ await rmdir(target);
3116
+ return true;
3117
+ }
3118
+ const assetRoot = await findPackageRoot();
3119
+ const entry = {
3120
+ path: input.action.path,
3121
+ mode: input.action.mode ??
3122
+ (input.action.type === "create-directory"
3123
+ ? "directory"
3124
+ : input.action.type === "copy-missing"
3125
+ ? "copied"
3126
+ : input.action.type === "refresh-managed-block"
3127
+ ? "managed-block"
3128
+ : "generated"),
3129
+ };
3130
+ const target = path.join(input.repoRoot, input.action.path);
3131
+ if (input.action.type === "create-directory") {
3132
+ if (await exists(target))
3133
+ return false;
3134
+ await mkdir(target, { recursive: true });
3135
+ return true;
3136
+ }
3137
+ const desired = await buildDesiredSurfaceContent({
3138
+ assetRoot,
3139
+ repoRoot: input.repoRoot,
3140
+ projectName: input.projectName,
3141
+ governanceRoot: input.governanceRoot,
3142
+ entry,
3143
+ });
3144
+ if (desired.content === undefined)
3145
+ return false;
3146
+ await mkdir(path.dirname(target), { recursive: true });
3147
+ if (input.action.type === "refresh-managed-block") {
3148
+ const existing = (await exists(target))
3149
+ ? await readFile(target, "utf-8")
3150
+ : "";
3151
+ const next = input.action.path === ".gitignore"
3152
+ ? mergeGitignoreManagedBlock(existing, desired.content)
3153
+ : input.action.path === "README.md" && existing.trim().length === 0
3154
+ ? buildTargetReadme({
3155
+ projectName: input.projectName,
3156
+ governanceRoot: input.governanceRoot,
3157
+ })
3158
+ : input.action.path === "AGENTS.md" && existing.trim().length === 0
3159
+ ? `# AGENTS.md\n\n${desired.content}\n`
3160
+ : mergeManagedBlock(existing, desired.content);
3161
+ await writeFile(target, next, "utf-8");
3162
+ return true;
3163
+ }
3164
+ if (input.action.type === "refresh-owned-file") {
3165
+ await writeFile(target, desired.content, "utf-8");
3166
+ return true;
3167
+ }
3168
+ if (await exists(target))
3169
+ return false;
3170
+ await writeFile(target, desired.content, "utf-8");
3171
+ return true;
3172
+ }
3173
+ export async function applyInitUpdate(input) {
3174
+ const repoRoot = path.resolve(input.repoRoot);
3175
+ const { projectName, governanceRoot } = await resolveInitProjectContext({
3176
+ repoRoot,
3177
+ projectName: input.projectName,
3178
+ governanceRoot: input.governanceRoot,
3179
+ });
3180
+ const applied = [];
3181
+ const skipped = [];
3182
+ const clientRecoveryMode = input.clientRecovery ?? "auto";
3183
+ if (input.bootstrapSurface) {
3184
+ await writeInitSurfaceState({
3185
+ repoRoot,
3186
+ projectName,
3187
+ governanceRoot,
3188
+ stateKind: "inferred-baseline",
3189
+ });
3190
+ applied.push({
3191
+ type: "bootstrap-surface",
3192
+ path: INIT_SURFACE_STATE_PATH,
3193
+ reason: "wrote inferred init surface baseline",
3194
+ });
3195
+ }
3196
+ if (input.applySafe) {
3197
+ const existingSurface = await readExistingSurfaceState(repoRoot);
3198
+ // Preserve source strength: recorded stays recorded so unchanged owned files
3199
+ // remain deterministic refresh candidates; bootstrap/inferred stays inferred.
3200
+ const preservedStateKind = existingSurface?.stateKind === "recorded"
3201
+ ? "recorded"
3202
+ : "inferred-baseline";
3203
+ const report = await checkInitUpdate({
3204
+ repoRoot,
3205
+ projectName,
3206
+ governanceRoot,
3207
+ clientRecovery: clientRecoveryMode,
3208
+ homeDir: input.homeDir,
3209
+ });
3210
+ for (const action of report.deterministicActions) {
3211
+ if (action.type === "bootstrap-surface") {
3212
+ skipped.push(action);
3213
+ continue;
3214
+ }
3215
+ // The report was produced immediately before this batch. Each action also
3216
+ // performs its own target check; missing-file writes refuse existing paths,
3217
+ // while managed blocks merge into the latest user-authored content.
3218
+ if (await applySafeAction({ repoRoot, projectName, governanceRoot, action }))
3219
+ applied.push(action);
3220
+ else
3221
+ skipped.push(action);
3222
+ }
3223
+ await writeInitSurfaceState({
3224
+ repoRoot,
3225
+ projectName,
3226
+ governanceRoot,
3227
+ stateKind: preservedStateKind,
3228
+ preserveOwnershipFrom: existingSurface,
3229
+ });
3230
+ }
3231
+ // Pi user config is only mutated with explicit --client-recovery=user.
3232
+ // Do not reinstall the project plugin here — that would bypass ownership and
3233
+ // clobber user-modified plugins; plugin updates stay on deterministic actions.
3234
+ if (clientRecoveryMode === "user") {
3235
+ await applyPiRetryMerge({
3236
+ homeDir: input.homeDir ?? os.homedir(),
3237
+ });
3238
+ }
3239
+ return {
3240
+ applied,
3241
+ skipped,
3242
+ report: await checkInitUpdate({
3243
+ repoRoot,
3244
+ projectName,
3245
+ governanceRoot,
3246
+ clientRecovery: clientRecoveryMode,
3247
+ homeDir: input.homeDir,
3248
+ }),
3249
+ };
3250
+ }
3251
+ function formatClientRecoverySummary(report) {
3252
+ if (!report.clientRecovery?.pi)
3253
+ return [];
3254
+ const pi = report.clientRecovery.pi;
3255
+ return [
3256
+ `clientRecovery.pi.reason: ${pi.reason}`,
3257
+ `clientRecovery.pi.action: ${pi.action}`,
3258
+ `clientRecovery.pi.path: ${pi.path}`,
3259
+ ];
3260
+ }
3261
+ function formatCheckUpdateText(report) {
3262
+ return [
3263
+ `loop-agent init update check for ${report.repoRoot}`,
3264
+ `controllerVersion: ${report.controllerVersion}`,
3265
+ `surfaceState: ${report.surfaceState}`,
3266
+ `deterministicActions: ${report.deterministicActions.length}`,
3267
+ `modelMergeTasks: ${report.modelMergeTasks.length}`,
3268
+ `humanDecisions: ${report.humanDecisions.length}`,
3269
+ ...formatClientRecoverySummary(report),
3270
+ "recommendedNext:",
3271
+ ...report.recommendedNext.map((item) => `- ${item}`),
3272
+ ].join("\n");
3273
+ }
3274
+ function formatCheckUpdateMarkdown(report) {
3275
+ const piLines = formatClientRecoverySummary(report).map((line) => `- ${line}`);
3276
+ const lines = [
3277
+ "# loop-agent init check-update",
3278
+ "",
3279
+ `- repoRoot: \`${report.repoRoot}\``,
3280
+ `- controllerVersion: \`${report.controllerVersion}\``,
3281
+ `- surfaceState: \`${report.surfaceState}\``,
3282
+ ...(piLines.length > 0
3283
+ ? ["", "## Client Recovery (Pi user config, read-only)", "", ...piLines]
3284
+ : []),
3285
+ "",
3286
+ "## Deterministic Actions",
3287
+ "",
3288
+ ...(report.deterministicActions.length
3289
+ ? report.deterministicActions.map((action) => `- \`${action.type}\` \`${action.path}\`: ${action.reason}`)
3290
+ : ["- None"]),
3291
+ "",
3292
+ "## Model Merge Tasks",
3293
+ "",
3294
+ ];
3295
+ if (report.modelMergeTasks.length === 0) {
3296
+ lines.push("- None", "");
3297
+ }
3298
+ else {
3299
+ for (const task of report.modelMergeTasks) {
3300
+ lines.push(`### ${task.path}`, "", task.reason, "", "allowedPaths:", ...task.allowedPaths.map((item) => `- ${item}`), "", "forbiddenPaths:", ...task.forbiddenPaths.map((item) => `- ${item}`), "", "mergeRules:", ...task.mergeRules.map((item) => `- ${item}`), "", "verification:", ...task.verification.map((item) => `- ${item}`), "");
3301
+ }
3302
+ }
3303
+ lines.push("## Human Decisions", "", ...(report.humanDecisions.length
3304
+ ? report.humanDecisions.map((decision) => `- \`${decision.path}\`: ${decision.reason}`)
3305
+ : ["- None"]), "", "## Recommended Next", "", ...report.recommendedNext.map((item) => `- ${item}`), "");
3306
+ return lines.join("\n");
3307
+ }
3308
+ export async function runInitDoctor(input) {
3309
+ const repoRoot = path.resolve(input.repoRoot);
3310
+ const checks = [];
3311
+ const add = (name, ok, message) => checks.push({ name, ok, message });
3312
+ try {
3313
+ const manifest = await loadHarnessManifest(repoRoot);
3314
+ add("harness.json", manifest.adapter === "loop-agent" || manifest.project === "loop-agent", `project=${manifest.project}`);
3315
+ add("governance root", await exists(path.join(repoRoot, manifest.governanceRoot, "README.md")), manifest.governanceRoot);
3316
+ }
3317
+ catch (error) {
3318
+ add("harness.json", false, error instanceof Error ? error.message : String(error));
3319
+ }
3320
+ const agents = path.join(repoRoot, "AGENTS.md");
3321
+ add("AGENTS.md loop-agent block", (await exists(agents)) &&
3322
+ (await readFile(agents, "utf-8")).includes(MANAGED_BLOCK_START), "managed block present");
3323
+ const readme = path.join(repoRoot, "README.md");
3324
+ add("README loop-agent block", (await exists(readme)) &&
3325
+ (await readFile(readme, "utf-8")).includes(MANAGED_BLOCK_START), "managed block present");
3326
+ add("repo-local skills", await exists(path.join(repoRoot, ".agents", "skills", "loop-agent", "SKILL.md")), ".agents/skills/loop-agent/SKILL.md");
3327
+ const gitignorePath = path.join(repoRoot, ".gitignore");
3328
+ const gitignoreContent = (await exists(gitignorePath))
3329
+ ? await readFile(gitignorePath, "utf-8")
3330
+ : "";
3331
+ add("gitignore loop-agent block", gitignoreContent.includes(GITIGNORE_BLOCK_START) &&
3332
+ gitignoreContent.includes(".harness/"), ".gitignore managed runtime ignores");
3333
+ const requiredScripts = Object.keys(INIT_SCRIPT_FILES);
3334
+ const missingScripts = [];
3335
+ for (const script of requiredScripts) {
3336
+ if (!(await exists(path.join(repoRoot, script))))
3337
+ missingScripts.push(script);
3338
+ }
3339
+ add("script matrix", missingScripts.length === 0, missingScripts.length === 0
3340
+ ? `${requiredScripts.length} scripts`
3341
+ : `missing: ${missingScripts.join(", ")}`);
3342
+ add("compat prompts", (await exists(path.join(repoRoot, ".harness", "prompts", "analyze.md"))) &&
3343
+ (await exists(path.join(repoRoot, ".harness", "prompts", "plan.md"))), ".harness/prompts/analyze.md");
3344
+ add("harness runtime dirs", await exists(path.join(repoRoot, ".harness", "dag-runs", "active")), ".harness/dag-runs/active");
3345
+ return {
3346
+ ok: checks.every((check) => check.ok),
3347
+ repoRoot,
3348
+ checks,
3349
+ };
3350
+ }
3351
+ export async function runInitReconcile(input) {
3352
+ const repoRoot = path.resolve(input.repoRoot);
3353
+ const preReport = await checkInitUpdate({
3354
+ repoRoot,
3355
+ projectName: input.projectName,
3356
+ governanceRoot: input.governanceRoot,
3357
+ });
3358
+ if (preReport.surfaceState === "missing") {
3359
+ return { status: "needs-baseline", report: preReport };
3360
+ }
3361
+ if (preReport.humanDecisions.length > 0) {
3362
+ return { status: "needs-human-decision", report: preReport };
3363
+ }
3364
+ const runtimeActivity = await input.readRuntimeActivity(repoRoot);
3365
+ if (isInitRuntimeActive(runtimeActivity)) {
3366
+ return {
3367
+ status: "blocked-active-runtime",
3368
+ report: preReport,
3369
+ runtimeActivity,
3370
+ };
3371
+ }
3372
+ const update = await applyInitUpdate({
3373
+ repoRoot,
3374
+ projectName: input.projectName,
3375
+ governanceRoot: input.governanceRoot,
3376
+ applySafe: true,
3377
+ });
3378
+ const status = update.report.ok
3379
+ ? "clean"
3380
+ : update.report.modelMergeTasks.length > 0
3381
+ ? "needs-model-merge"
3382
+ : "needs-safe-update";
3383
+ return {
3384
+ status,
3385
+ report: update.report,
3386
+ applied: update.applied,
3387
+ skipped: update.skipped,
3388
+ };
3389
+ }
3390
+ function parseInitArgs(repoRoot, args) {
3391
+ let subcommand;
3392
+ let projectName;
3393
+ let governanceRoot;
3394
+ let profile = "full";
3395
+ let merge = true;
3396
+ let provider;
3397
+ let model;
3398
+ let clientRecovery = "auto";
3399
+ let json = false;
3400
+ let markdown = false;
3401
+ let bootstrapSurface = false;
3402
+ let applySafe = false;
3403
+ let runId;
3404
+ let upgradeStatus = false;
3405
+ let upgradeContinue = false;
3406
+ let upgradeReport = false;
3407
+ let versionChoice;
3408
+ for (let i = 0; i < args.length; i += 1) {
3409
+ const arg = args[i];
3410
+ if ((arg === "instructions" ||
3411
+ arg === "doctor" ||
3412
+ arg === "check-update" ||
3413
+ arg === "update" ||
3414
+ arg === "reconcile" ||
3415
+ arg === "upgrade") &&
3416
+ !subcommand) {
3417
+ subcommand = arg;
3418
+ continue;
3419
+ }
3420
+ if (arg === "--project-name")
3421
+ projectName = args[++i];
3422
+ else if (arg.startsWith("--project-name="))
3423
+ projectName = arg.slice("--project-name=".length);
3424
+ else if (arg === "--governance-root")
3425
+ governanceRoot = args[++i] ?? governanceRoot;
3426
+ else if (arg.startsWith("--governance-root="))
3427
+ governanceRoot = arg.slice("--governance-root=".length);
3428
+ else if (arg === "--profile")
3429
+ profile = args[++i] ?? profile;
3430
+ else if (arg.startsWith("--profile="))
3431
+ profile = arg.slice("--profile=".length);
3432
+ else if (arg === "--merge")
3433
+ merge = true;
3434
+ else if (arg === "--no-merge")
3435
+ merge = false;
3436
+ else if (arg === "--provider")
3437
+ provider = args[++i];
3438
+ else if (arg.startsWith("--provider="))
3439
+ provider = arg.slice("--provider=".length);
3440
+ else if (arg === "--model")
3441
+ model = args[++i];
3442
+ else if (arg.startsWith("--model="))
3443
+ model = arg.slice("--model=".length);
3444
+ else if (arg === "--client-recovery")
3445
+ clientRecovery = parseClientRecoveryMode(args[++i]);
3446
+ else if (arg.startsWith("--client-recovery="))
3447
+ clientRecovery = parseClientRecoveryMode(arg.slice("--client-recovery=".length));
3448
+ else if (arg === "--json")
3449
+ json = true;
3450
+ else if (arg === "--markdown")
3451
+ markdown = true;
3452
+ else if (arg === "--bootstrap-surface")
3453
+ bootstrapSurface = true;
3454
+ else if (arg === "--apply-safe")
3455
+ applySafe = true;
3456
+ else if (arg === "--run-id")
3457
+ runId = args[++i];
3458
+ else if (arg.startsWith("--run-id="))
3459
+ runId = arg.slice("--run-id=".length);
3460
+ else if (arg === "--status")
3461
+ upgradeStatus = true;
3462
+ else if (arg === "--continue")
3463
+ upgradeContinue = true;
3464
+ else if (arg === "--report")
3465
+ upgradeReport = true;
3466
+ else if (arg === "--version-choice")
3467
+ versionChoice = args[++i];
3468
+ else if (arg.startsWith("--version-choice="))
3469
+ versionChoice = arg.slice("--version-choice=".length);
3470
+ else if (arg.startsWith("-"))
3471
+ throw new Error(`unknown init flag: ${arg}`);
3472
+ else
3473
+ throw new Error(`unexpected init argument: ${arg}`);
3474
+ }
3475
+ if (profile !== "full" && profile !== "minimal")
3476
+ throw new Error("init --profile must be full or minimal");
3477
+ if (versionChoice && !["current", "upgrade", "cancel", "retry"].includes(versionChoice)) {
3478
+ throw new Error("init upgrade --version-choice must be current|upgrade|cancel|retry");
3479
+ }
3480
+ const normalizedModel = normalizeInitModelReference({ provider, model });
3481
+ return {
3482
+ repoRoot,
3483
+ projectName,
3484
+ governanceRoot,
3485
+ profile,
3486
+ merge,
3487
+ provider,
3488
+ model: normalizedModel,
3489
+ clientRecovery,
3490
+ subcommand,
3491
+ json,
3492
+ markdown,
3493
+ bootstrapSurface,
3494
+ applySafe,
3495
+ runId,
3496
+ upgradeStatus,
3497
+ upgradeContinue,
3498
+ upgradeReport,
3499
+ versionChoice,
3500
+ };
3501
+ }
3502
+ export async function runInit(repoRoot, rawArgs, dependencies) {
3503
+ const parsed = parseInitArgs(repoRoot, rawArgs);
3504
+ if (parsed.subcommand === "instructions") {
3505
+ console.log(buildInitInstructions(parsed));
3506
+ return;
3507
+ }
3508
+ if (parsed.subcommand === "doctor") {
3509
+ const report = await runInitDoctor({ repoRoot: parsed.repoRoot });
3510
+ console.log(JSON.stringify(report, null, 2));
3511
+ if (!report.ok)
3512
+ process.exitCode = 1;
3513
+ return;
3514
+ }
3515
+ if (parsed.subcommand === "check-update") {
3516
+ const report = await checkInitUpdate(parsed);
3517
+ if (parsed.json)
3518
+ console.log(JSON.stringify(report, null, 2));
3519
+ else if (parsed.markdown)
3520
+ console.log(formatCheckUpdateMarkdown(report));
3521
+ else
3522
+ console.log(formatCheckUpdateText(report));
3523
+ return;
3524
+ }
3525
+ if (parsed.subcommand === "update") {
3526
+ if (!parsed.bootstrapSurface && !parsed.applySafe) {
3527
+ throw new Error("usage: init update [--bootstrap-surface] [--apply-safe]");
3528
+ }
3529
+ const result = await applyInitUpdate(parsed);
3530
+ console.log(parsed.json
3531
+ ? JSON.stringify(result, null, 2)
3532
+ : formatInitUpdateResult(result));
3533
+ return;
3534
+ }
3535
+ if (parsed.subcommand === "upgrade") {
3536
+ const { runInitUpgrade } = await import("./init-upgrade.js");
3537
+ const selected = [parsed.upgradeStatus, parsed.upgradeContinue, parsed.upgradeReport].filter(Boolean).length;
3538
+ if (selected > 1)
3539
+ throw new Error("init upgrade accepts only one of --status, --continue, or --report");
3540
+ const result = await runInitUpgrade({
3541
+ repoRoot: parsed.repoRoot,
3542
+ runId: parsed.runId,
3543
+ mode: parsed.upgradeStatus ? "status" : parsed.upgradeContinue ? "continue" : parsed.upgradeReport ? "report" : "start",
3544
+ versionChoice: parsed.versionChoice,
3545
+ readRuntimeActivity: dependencies.readRuntimeActivity,
3546
+ });
3547
+ if ("markdown" in result)
3548
+ console.log(result.markdown);
3549
+ else
3550
+ console.log(parsed.json ? JSON.stringify(result, null, 2) : formatInitUpgradeResult(result));
3551
+ return;
3552
+ }
3553
+ if (parsed.subcommand === "reconcile") {
3554
+ const result = await runInitReconcile({
3555
+ ...parsed,
3556
+ readRuntimeActivity: dependencies.readRuntimeActivity,
3557
+ });
3558
+ console.log(parsed.json
3559
+ ? JSON.stringify(result, null, 2)
3560
+ : formatReconcileResult(result));
3561
+ return;
3562
+ }
3563
+ const result = await initializeLoopAgentProject(parsed);
3564
+ console.log(parsed.json ? JSON.stringify(result, null, 2) : formatInitResult(result));
3565
+ }
3566
+ function formatInitUpgradeResult(result) {
3567
+ return [
3568
+ `loop-agent init upgrade: ${result.status}`,
3569
+ `runId: ${result.runId}`,
3570
+ `phase: ${result.phase}`,
3571
+ `modelMergeTasks: ${result.modelMergeTasks.length}`,
3572
+ `humanDecisions: ${result.humanDecisions.length}`,
3573
+ `next: ${result.nextAction}`,
3574
+ ].join("\n");
3575
+ }
3576
+ function formatInitUpdateResult(result) {
3577
+ return [
3578
+ "loop-agent init update complete",
3579
+ `applied: ${result.applied.length}`,
3580
+ `skipped: ${result.skipped.length}`,
3581
+ `remaining deterministicActions: ${result.report.deterministicActions.length}`,
3582
+ `remaining modelMergeTasks: ${result.report.modelMergeTasks.length}`,
3583
+ "recommendedNext:",
3584
+ ...result.report.recommendedNext.map((item) => `- ${item}`),
3585
+ ].join("\n");
3586
+ }
3587
+ function formatReconcileResult(result) {
3588
+ const lines = [
3589
+ `loop-agent init reconcile: ${result.status}`,
3590
+ `surfaceState: ${result.report.surfaceState}`,
3591
+ ];
3592
+ if (result.applied && result.skipped) {
3593
+ lines.push(`applied: ${result.applied.length}`, `skipped: ${result.skipped.length}`);
3594
+ }
3595
+ if (result.runtimeActivity) {
3596
+ lines.push(`activeDagRuns: ${result.runtimeActivity.activeDagRunIds.length}`, `activeWorkerTasks: ${result.runtimeActivity.activeWorkerTasks}`, `activeWorkerBatches: ${result.runtimeActivity.activeWorkerBatches}`);
3597
+ if (result.runtimeActivity.workerProjectionError) {
3598
+ lines.push(`workerProjectionError: ${result.runtimeActivity.workerProjectionError}`);
3599
+ }
3600
+ }
3601
+ lines.push(`deterministicActions: ${result.report.deterministicActions.length}`, `modelMergeTasks: ${result.report.modelMergeTasks.length}`, `humanDecisions: ${result.report.humanDecisions.length}`, "recommendedNext:", ...result.report.recommendedNext.map((item) => `- ${item}`));
3602
+ return lines.join("\n");
3603
+ }
3604
+ function formatInitResult(result) {
3605
+ return [
3606
+ `Initialized loop-agent harness at ${result.repoRoot}`,
3607
+ `project: ${result.projectName}`,
3608
+ `governanceRoot: ${result.governanceRoot}`,
3609
+ `profile: ${result.profile}`,
3610
+ `written: ${result.written.length}`,
3611
+ `skipped: ${result.skipped.length}`,
3612
+ "next: loop-agent init doctor --repo-root <target>",
3613
+ `model-executed init: continue automatically by inspecting the target project, adapting README.md, ${result.governanceRoot}/verification-matrix.md, and scripts/ci-tests.sh when needed, then run inspect/docs audit/quick verification.`,
3614
+ ].join("\n");
3615
+ }
3616
+ export async function listInitializedFiles(repoRoot) {
3617
+ const files = [];
3618
+ async function walk(dir) {
3619
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
3620
+ const full = path.join(dir, entry.name);
3621
+ if (entry.isDirectory())
3622
+ await walk(full);
3623
+ else
3624
+ files.push(repoRelative(repoRoot, full));
3625
+ }
3626
+ }
3627
+ await access(repoRoot);
3628
+ await walk(repoRoot);
3629
+ return files.sort();
3630
+ }