@guanlin4924/devflow-pi 0.1.93 → 0.1.95

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 (369) hide show
  1. package/README.md +10 -10
  2. package/node_modules/@playwright/test/LICENSE +202 -0
  3. package/node_modules/@playwright/test/NOTICE +5 -0
  4. package/node_modules/@playwright/test/README.md +318 -0
  5. package/node_modules/@playwright/test/cli.js +19 -0
  6. package/node_modules/@playwright/test/index.d.ts +18 -0
  7. package/node_modules/@playwright/test/index.js +17 -0
  8. package/node_modules/@playwright/test/index.mjs +18 -0
  9. package/node_modules/@playwright/test/package.json +35 -0
  10. package/node_modules/@playwright/test/reporter.d.ts +17 -0
  11. package/node_modules/@playwright/test/reporter.js +17 -0
  12. package/node_modules/@playwright/test/reporter.mjs +17 -0
  13. package/node_modules/playwright/LICENSE +202 -0
  14. package/node_modules/playwright/NOTICE +5 -0
  15. package/node_modules/playwright/README.md +318 -0
  16. package/node_modules/playwright/ThirdPartyNotices.txt +14 -0
  17. package/node_modules/playwright/cli.js +19 -0
  18. package/node_modules/playwright/index.d.ts +17 -0
  19. package/node_modules/playwright/index.js +17 -0
  20. package/node_modules/playwright/index.mjs +18 -0
  21. package/node_modules/playwright/jsx-runtime.js +42 -0
  22. package/node_modules/playwright/jsx-runtime.mjs +21 -0
  23. package/node_modules/playwright/lib/agents/agentParser.js +89 -0
  24. package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +34 -0
  25. package/node_modules/playwright/lib/agents/generateAgents.js +395 -0
  26. package/node_modules/playwright/lib/agents/playwright-test-coverage.prompt.md +31 -0
  27. package/node_modules/playwright/lib/agents/playwright-test-generate.prompt.md +8 -0
  28. package/node_modules/playwright/lib/agents/playwright-test-generator.agent.md +88 -0
  29. package/node_modules/playwright/lib/agents/playwright-test-heal.prompt.md +6 -0
  30. package/node_modules/playwright/lib/agents/playwright-test-healer.agent.md +56 -0
  31. package/node_modules/playwright/lib/agents/playwright-test-plan.prompt.md +9 -0
  32. package/node_modules/playwright/lib/agents/playwright-test-planner.agent.md +74 -0
  33. package/node_modules/playwright/lib/cli/reportActions.js +78 -0
  34. package/node_modules/playwright/lib/cli/testActions.js +214 -0
  35. package/node_modules/playwright/lib/common/index.js +2923 -0
  36. package/node_modules/playwright/lib/common/index.js.txt +34 -0
  37. package/node_modules/playwright/lib/errorContext.js +129 -0
  38. package/node_modules/playwright/lib/globals.js +58 -0
  39. package/node_modules/playwright/lib/index.js +815 -0
  40. package/node_modules/playwright/lib/isomorphic.js +260 -0
  41. package/node_modules/playwright/lib/isomorphic.js.txt +9 -0
  42. package/node_modules/playwright/lib/loader/loaderProcessEntry.js +34 -0
  43. package/node_modules/playwright/lib/loader/loaderProcessEntry.js.txt +9 -0
  44. package/node_modules/playwright/lib/matchers/expect.js +13490 -0
  45. package/node_modules/playwright/lib/matchers/expect.js.LICENSE +640 -0
  46. package/node_modules/playwright/lib/matchers/expect.js.txt +67 -0
  47. package/node_modules/playwright/lib/mcp/test/browserBackend.js +135 -0
  48. package/node_modules/playwright/lib/mcp/test/generatorTools.js +122 -0
  49. package/node_modules/playwright/lib/mcp/test/plannerTools.js +150 -0
  50. package/node_modules/playwright/lib/mcp/test/seed.js +82 -0
  51. package/node_modules/playwright/lib/mcp/test/streams.js +44 -0
  52. package/node_modules/playwright/lib/mcp/test/testBackend.js +99 -0
  53. package/node_modules/playwright/lib/mcp/test/testContext.js +306 -0
  54. package/node_modules/playwright/lib/mcp/test/testTool.js +30 -0
  55. package/node_modules/playwright/lib/mcp/test/testTools.js +98 -0
  56. package/node_modules/playwright/lib/package.js +47 -0
  57. package/node_modules/playwright/lib/program.js +241 -0
  58. package/node_modules/playwright/lib/runner/index.js +8527 -0
  59. package/node_modules/playwright/lib/runner/index.js.txt +61 -0
  60. package/node_modules/playwright/lib/transform/babelBundle.js +72274 -0
  61. package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +2384 -0
  62. package/node_modules/playwright/lib/transform/babelBundle.js.txt +325 -0
  63. package/node_modules/playwright/lib/transform/esmLoader.js +8054 -0
  64. package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +355 -0
  65. package/node_modules/playwright/lib/transform/esmLoader.js.txt +60 -0
  66. package/node_modules/playwright/lib/util.js +373 -0
  67. package/node_modules/playwright/lib/worker/workerProcessEntry.js +1861 -0
  68. package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +19 -0
  69. package/node_modules/playwright/package.json +55 -0
  70. package/node_modules/playwright/test.d.ts +18 -0
  71. package/node_modules/playwright/test.js +24 -0
  72. package/node_modules/playwright/test.mjs +35 -0
  73. package/node_modules/playwright/types/test.d.ts +11074 -0
  74. package/node_modules/playwright/types/testReporter.d.ts +950 -0
  75. package/node_modules/playwright-core/LICENSE +202 -0
  76. package/node_modules/playwright-core/NOTICE +5 -0
  77. package/node_modules/playwright-core/README.md +3 -0
  78. package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
  79. package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
  80. package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
  81. package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
  82. package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
  83. package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
  84. package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
  85. package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
  86. package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
  87. package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
  88. package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
  89. package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
  90. package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
  91. package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
  92. package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
  93. package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
  94. package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
  95. package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
  96. package/node_modules/playwright-core/browsers.json +52 -0
  97. package/node_modules/playwright-core/cli.js +21 -0
  98. package/node_modules/playwright-core/index.d.ts +17 -0
  99. package/node_modules/playwright-core/index.js +17 -0
  100. package/node_modules/playwright-core/index.mjs +28 -0
  101. package/node_modules/playwright-core/lib/bootstrap.js +88 -0
  102. package/node_modules/playwright-core/lib/coreBundle.js +75813 -0
  103. package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
  104. package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
  105. package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
  106. package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
  107. package/node_modules/playwright-core/lib/package.js +50 -0
  108. package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
  109. package/node_modules/playwright-core/lib/server/electron/loader.js +116 -0
  110. package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
  111. package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
  112. package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
  113. package/node_modules/playwright-core/lib/tools/cli-client/cli.js +6 -0
  114. package/node_modules/playwright-core/lib/tools/cli-client/help.json +721 -0
  115. package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
  116. package/node_modules/playwright-core/lib/tools/cli-client/output.js +351 -0
  117. package/node_modules/playwright-core/lib/tools/cli-client/program.js +418 -0
  118. package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
  119. package/node_modules/playwright-core/lib/tools/cli-client/session.js +259 -0
  120. package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
  121. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/SKILL.md +425 -0
  122. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/element-attributes.md +23 -0
  123. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/playwright-tests.md +39 -0
  124. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/request-mocking.md +87 -0
  125. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/running-code.md +241 -0
  126. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/session-management.md +225 -0
  127. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/storage-state.md +275 -0
  128. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/test-generation.md +433 -0
  129. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/tracing.md +139 -0
  130. package/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/video-recording.md +143 -0
  131. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +136 -0
  132. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
  133. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +89 -0
  134. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +69 -0
  135. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/typing.md +173 -0
  136. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +77 -0
  137. package/node_modules/playwright-core/lib/tools/skills/playwright-trace/SKILL.md +174 -0
  138. package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
  139. package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
  140. package/node_modules/playwright-core/lib/utilsBundle.js +85357 -0
  141. package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
  142. package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
  143. package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
  144. package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
  145. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
  146. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
  147. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
  148. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
  149. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
  150. package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
  151. package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
  152. package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
  153. package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
  154. package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
  155. package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
  156. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
  157. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CVQWJZAA.js +32 -0
  158. package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
  159. package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
  160. package/node_modules/playwright-core/lib/vite/recorder/assets/index-hrlLqLtq.js +129 -0
  161. package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
  162. package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
  163. package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BbkfBe3n.js +32 -0
  164. package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Ds6CBOo0.js +182 -0
  165. package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
  166. package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-DywYcAf8.js +7 -0
  167. package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
  168. package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
  169. package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.Bqk9acqE.css +1 -0
  170. package/node_modules/playwright-core/lib/vite/traceViewer/index.B4cLoZK3.js +1 -0
  171. package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
  172. package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
  173. package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
  174. package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
  175. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
  176. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
  177. package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
  178. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CU5KtEkS.js +5 -0
  179. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
  180. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
  181. package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
  182. package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
  183. package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
  184. package/node_modules/playwright-core/lib/xdg-open +1267 -0
  185. package/node_modules/playwright-core/package.json +34 -0
  186. package/node_modules/playwright-core/types/protocol.d.ts +24645 -0
  187. package/node_modules/playwright-core/types/structs.d.ts +52 -0
  188. package/node_modules/playwright-core/types/types.d.ts +26632 -0
  189. package/package.json +12 -2
  190. package/plugins/devflow-core/extensions/devflow-core.ts +518 -40
  191. package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +3 -3
  192. package/plugins/devflow-core/runtime/hooks/hook_common.py +334 -11
  193. package/plugins/devflow-core/runtime/hooks/index_gate.py +29 -16
  194. package/plugins/devflow-core/runtime/hooks/start.py +13 -13
  195. package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +2 -2
  196. package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +4 -4
  197. package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +19 -19
  198. package/plugins/devflow-core/skills/workflow-orchestrator/agents/openai.yaml +2 -2
  199. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_runner.py +412 -76
  200. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +60 -9
  201. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/api_test_matrix.py +2 -2
  202. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/approval_pins.py +433 -0
  203. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/approval_store.py +137 -0
  204. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +1 -1
  205. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +1 -1
  206. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +1 -1
  207. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_invalidation.rb +5 -5
  208. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_commit.rb +4 -4
  209. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_validate.py +1 -1
  210. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_source_resolver.rb +4 -4
  211. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/checker_adapter.rb +7 -7
  212. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +51 -7
  213. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/cross_track_consistency_check.rb +1 -1
  214. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dsl_coverage.py +87 -0
  215. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/dsl_v1.py +318 -0
  216. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/environment_profile.py +134 -0
  217. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +7 -6
  218. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_surface.py +138 -0
  219. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +3 -3
  220. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +5 -5
  221. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_workflow.rb +5 -5
  222. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +13 -13
  223. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +3 -3
  224. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/manage_lessons.rb +7 -7
  225. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +2 -2
  226. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_run.rb +3 -3
  227. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/phase_ownership.rb +6 -6
  228. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/playwright_adapter.py +515 -0
  229. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/repair_control_plane.rb +1 -1
  230. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_coverage.rb +1 -1
  231. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +3 -3
  232. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_preflight.py +3 -3
  233. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_run.py +18 -18
  234. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/runtime_context_collector.rb +3 -3
  235. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/session_rollover.rb +4 -4
  236. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/static_runtime_context_collector.rb +2 -2
  237. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +5 -5
  238. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tc_hash.py +132 -0
  239. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tdd_evidence.py +3 -3
  240. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_command_policy.py +104 -10
  241. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_task_binding_check.py +3 -3
  242. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/update_triage_route.rb +3 -3
  243. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +19 -6
  244. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +5 -5
  245. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py +1 -1
  246. package/plugins/devflow-core/skills/workflow-orchestrator/templates/acceptance-remediation.yaml +12 -12
  247. package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +201 -222
  248. package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +6 -4
  249. package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +68 -140
  250. package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +14 -14
  251. package/plugins/devflow-core/skills/workflow-project-context/assets/agents-md-template.md +7 -7
  252. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-backend-spec-template.md +4 -4
  253. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-frontend-spec-template.md +4 -4
  254. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-navigation-template.md +1 -1
  255. package/plugins/devflow-core/skills/workflow-project-context/assets/context-domain-index-template.md +4 -4
  256. package/plugins/devflow-core/skills/workflow-project-context/assets/context-spec-index-template.md +10 -10
  257. package/plugins/devflow-core/skills/workflow-project-context/assets/execution-contract-template.md +2 -2
  258. package/plugins/devflow-core/skills/workflow-project-context/assets/navigation-index-template.md +18 -18
  259. package/plugins/devflow-core/skills/workflow-project-context/assets/project-initialization-template.md +32 -32
  260. package/plugins/devflow-core/skills/workflow-project-context/assets/rules-index-template.md +30 -30
  261. package/plugins/devflow-core/skills/workflow-project-context/assets/state-template.md +7 -7
  262. package/plugins/devflow-core/skills/workflow-project-context/references/navigation-guide.md +37 -37
  263. package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization.py +16 -16
  264. package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization_database.py +1 -1
  265. package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +4 -4
  266. package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +8 -8
  267. package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +5 -5
  268. package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +1 -1
  269. package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +39 -39
  270. package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/backend-spec-template.md +10 -10
  271. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/affected-module-exploration.md +2 -2
  272. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/code-explore-guide.md +1 -1
  273. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/project-rule-contract.md +20 -20
  274. package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +8 -8
  275. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/index.md +3 -3
  276. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/accessibility.md +2 -2
  277. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/component-library.md +2 -2
  278. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/design-systems.md +1 -1
  279. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/responsive-grid.md +2 -2
  280. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/stack-guidelines.md +6 -6
  281. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/ux-patterns.md +2 -2
  282. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tokens/project-tokens.md +1 -1
  283. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/SKILL.md +12 -12
  284. package/plugins/devflow-design/skills/workflow-design-rule-pack/scripts/install.py +2 -2
  285. package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +30 -30
  286. package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/output-template.md +2 -2
  287. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +41 -41
  288. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/assets/frontend-spec-template.md +15 -15
  289. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/affected-module-exploration.md +2 -2
  290. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/project-rule-contract.md +25 -25
  291. package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +13 -13
  292. package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +23 -23
  293. package/plugins/devflow-design/skills/workflow-ui-spec/assets/ui-spec-template.md +3 -3
  294. package/plugins/devflow-implementation/agents/implementation-worker.md +2 -2
  295. package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +9 -9
  296. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/lesson-format.md +2 -2
  297. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/root-cause-ladder.md +1 -1
  298. package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +17 -17
  299. package/plugins/devflow-implementation/skills/workflow-change-plan/references/code-conventions.md +1 -1
  300. package/plugins/devflow-implementation/skills/workflow-change-plan/references/output-template.md +3 -3
  301. package/plugins/devflow-implementation/skills/workflow-change-plan/references/project-rule-contract.md +16 -16
  302. package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +38 -38
  303. package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +4 -2
  304. package/plugins/devflow-intel/agents/project-scout.md +1 -1
  305. package/plugins/devflow-intel/extensions/devflow-intel.ts +20 -19
  306. package/plugins/devflow-intel/skills/code-index-query/SKILL.md +6 -6
  307. package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +12 -12
  308. package/plugins/devflow-intel/skills/workflow-code-index/references/discovery.md +2 -2
  309. package/plugins/devflow-intel/skills/workflow-code-index/references/query-guide.md +1 -1
  310. package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +31 -12
  311. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/discovery.py +2 -2
  312. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +4 -1
  313. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/project_binding_check.py +4 -4
  314. package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +14 -14
  315. package/plugins/devflow-intel/skills/workflow-context-sync/scripts/context_sync_cursor.py +2 -2
  316. package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +26 -26
  317. package/plugins/devflow-intel/skills/workflow-update-navigation/references/incremental-update-guide.md +2 -2
  318. package/plugins/devflow-quality/runtime/playwright/devflow-test-dsl-v1.schema.json +872 -0
  319. package/plugins/devflow-quality/runtime/playwright/playwright.config.mjs +46 -0
  320. package/plugins/devflow-quality/runtime/playwright/reporter.mjs +311 -0
  321. package/plugins/devflow-quality/runtime/playwright/runner.spec.mjs +1308 -0
  322. package/plugins/devflow-quality/runtime/playwright/runtime-manifest.json +11 -0
  323. package/plugins/devflow-quality/skills/code-review/SKILL.md +12 -12
  324. package/plugins/devflow-quality/skills/code-review/references/spec-checklist.md +1 -1
  325. package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +11 -8
  326. package/plugins/devflow-quality/skills/workflow-gen-test-guide/SKILL.md +6 -6
  327. package/plugins/devflow-quality/skills/workflow-gen-test-guide/legacy/SKILL.md +14 -14
  328. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +82 -69
  329. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/api-mainflow-rules.md +20 -15
  330. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +12 -10
  331. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/intermediate-format.md +115 -192
  332. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/mainflow-selection.md +27 -23
  333. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +78 -153
  334. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/ui-mainflow-rules.md +36 -83
  335. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +497 -556
  336. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/discover_codebases.py +2 -2
  337. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/export_legacy_testagent.py +647 -0
  338. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/normalize_case_payloads.py +10 -3
  339. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/validate_case_payloads.py +165 -275
  340. package/plugins/devflow-quality/skills/workflow-human-acceptance/SKILL.md +61 -0
  341. package/plugins/devflow-quality/skills/workflow-release-closeout/SKILL.md +18 -18
  342. package/plugins/devflow-quality/skills/workflow-run-acceptance-tests/SKILL.md +20 -14
  343. package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +1 -1
  344. package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +10 -10
  345. package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +25 -25
  346. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +49 -49
  347. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/assets/baseline-template.md +22 -22
  348. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/antipatterns.md +3 -3
  349. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/challenge-review-guide.md +3 -3
  350. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/project-context-guide.md +11 -11
  351. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/questioning.md +1 -1
  352. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/requirement-review-checklist.md +4 -4
  353. package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +16 -16
  354. package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +6 -6
  355. package/src/approval-store.ts +340 -0
  356. package/src/cli.ts +15 -0
  357. package/src/constants.ts +1 -1
  358. package/src/doctor.ts +259 -2
  359. package/src/initialization-store.ts +51 -3
  360. package/src/paths.ts +12 -1
  361. package/src/project-context.ts +60 -15
  362. package/src/project-documents.ts +61 -61
  363. package/src/project-initializer.ts +9 -8
  364. package/src/project-layout.ts +217 -0
  365. package/src/runtime.ts +3 -3
  366. package/templates/commands/start.md +4 -4
  367. package/templates/omp/AGENTS.md +1 -1
  368. package/templates/omp/hook-config.json +13 -7
  369. package/templates/omp/index-policy.json +8 -8
@@ -16,6 +16,21 @@ import { runProjectInitialization } from "../../../src/project-initializer.ts";
16
16
  import { projectControlDatabasePath } from "../../../src/paths.ts";
17
17
  import { generateProjectDocuments } from "../../../src/project-documents.ts";
18
18
  import { exportInitializationSnapshot, importLegacyInitialization, switchInitializationAuthority } from "../../../src/project-migration.ts";
19
+ import { migrateLegacyProjectLayout } from "../../../src/project-layout.ts";
20
+ import {
21
+ APPROVAL_KINDS,
22
+ confirmApproval,
23
+ databaseAuthority,
24
+ listApprovals,
25
+ listProposals,
26
+ proposalIsExpired,
27
+ proposeApprovals,
28
+ rejectProposal,
29
+ type ApprovalKind,
30
+ type CurrentPins,
31
+ type HumanResultStatus,
32
+ type ProposalRecord,
33
+ } from "../../../src/approval-store.ts";
19
34
 
20
35
 
21
36
  export type DevflowIntent = "idle" | "explain-state" | "project-init" | "requirement-draft" | "workflow-execute";
@@ -209,6 +224,28 @@ const STATE_MUTATION_TOOLS = new Set([
209
224
  "devflow_run_acceptance_tests",
210
225
  ]);
211
226
 
227
+ /** Maps devflow_run_acceptance_tests parameters to acceptance_test_runner.py argv; gc_runs is a dry-run unless write=true. */
228
+ export function acceptanceRunnerArgs(params: {
229
+ action: "validate" | "validate_human" | "run" | "validate_results" | "gc_runs";
230
+ feature: string;
231
+ write?: boolean;
232
+ retentionDays?: number;
233
+ unreferencedTtlDays?: number;
234
+ }): string[] {
235
+ const scriptAction = params.action === "validate_results"
236
+ ? "validate-results"
237
+ : params.action === "validate_human" ? "validate-human"
238
+ : params.action === "gc_runs" ? "gc-runs" : params.action;
239
+ const args = [scriptAction, "--feature", params.feature, "--json"];
240
+ if (params.action === "run") args.push("--write");
241
+ if (params.action === "gc_runs") {
242
+ if (params.write === true) args.push("--write");
243
+ if (params.retentionDays !== undefined) args.push("--retention-days", String(params.retentionDays));
244
+ if (params.unreferencedTtlDays !== undefined) args.push("--unreferenced-ttl-days", String(params.unreferencedTtlDays));
245
+ }
246
+ return args;
247
+ }
248
+
212
249
  export function initialControlState(): SessionControlState {
213
250
  return { intent: "idle", phase: "idle", label: "对话(不自动执行)" };
214
251
  }
@@ -351,7 +388,7 @@ export function discoverExistingProjectInitialization(cwd: string, options: { al
351
388
  }
352
389
  }
353
390
  if (!options.allowLegacyImport) return null;
354
- const sources = [".planning/PROJECT.md", ".planning/navigation/INDEX.md"];
391
+ const sources = [".devflow/planning/PROJECT.md", ".devflow/planning/navigation/INDEX.md"];
355
392
  const records: Array<ExistingProjectInitialization> = [];
356
393
  for (const sourcePath of sources) {
357
394
  const absolute = path.join(cwd, sourcePath);
@@ -531,13 +568,13 @@ type DoctorContext = {
531
568
  };
532
569
 
533
570
  const CONTROL_PATHS = [
534
- ".planning/STATE.md",
535
- ".planning/.next-intent",
536
- ".planning/.devflow.lock",
537
- ".planning/artifacts/index.yaml",
571
+ ".devflow/planning/STATE.md",
572
+ ".devflow/planning/.next-intent",
573
+ ".devflow/planning/.devflow.lock",
574
+ ".devflow/planning/artifacts/index.yaml",
538
575
  ];
539
576
 
540
- const CONTROL_PREFIXES = [".planning/workflows/", ".planning/contracts/", ".planning/evidence/", ".planning/review-runs/"];
577
+ const CONTROL_PREFIXES = [".devflow/planning/workflows/", ".devflow/planning/contracts/", ".devflow/planning/evidence/", ".devflow/planning/review-runs/"];
541
578
 
542
579
  const REQUIRED_EXTENSION_TOOLS = [
543
580
  "devflow_doctor",
@@ -560,6 +597,7 @@ const REQUIRED_EXTENSION_TOOLS = [
560
597
  "devflow_context_cursor",
561
598
  "devflow_design_rule",
562
599
  "devflow_mainflow",
600
+ "devflow_approval",
563
601
  ];
564
602
 
565
603
  const REQUIRED_SKILLS = [
@@ -583,6 +621,7 @@ const REQUIRED_SKILLS = [
583
621
  "workflow-frontend-tech-spec",
584
622
  "workflow-gen-test-guide",
585
623
  "workflow-generate-mainflow-cases",
624
+ "workflow-human-acceptance",
586
625
  "workflow-integration-verify",
587
626
  "workflow-orchestrator",
588
627
  "workflow-pipeline",
@@ -703,16 +742,63 @@ function assertRuntimeProjectBoundary(cwd: string): void {
703
742
  if (configured && realpathSync(configured) !== resolvedCwd) {
704
743
  throw new Error(`Devflow project-root mismatch: session=${resolvedCwd}, configured=${configured}`);
705
744
  }
706
- const planningRoot = path.join(resolvedCwd, ".planning");
745
+ const legacyPlanning = path.join(resolvedCwd, ".planning");
746
+ const legacyContext = path.join(resolvedCwd, ".context");
747
+ const planningMarkers = ["STATE.md", "PROJECT.md", ".devflow.lock", "navigation/INDEX.md", "code-index/INDEX.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"];
748
+ const contextMarkers = ["spec/index.md", "index.md", "project/index.md", "knowledge/index.md"];
749
+ const legacyMarker = planningMarkers.some(relative => existsSync(path.join(legacyPlanning, relative)))
750
+ || contextMarkers.some(relative => existsSync(path.join(legacyContext, relative)));
751
+ const activeLegacy = ["STATE.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"]
752
+ .find(relative => existsSync(path.join(legacyPlanning, relative)));
753
+ if (legacyMarker) {
754
+ const manifestPath = path.join(resolvedCwd, ".devflow", "project.json");
755
+ let manifestValid = false;
756
+ try {
757
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>;
758
+ manifestValid = manifest.schemaVersion === 1
759
+ && typeof manifest.projectId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(manifest.projectId)
760
+ && typeof manifest.name === "string" && manifest.name.trim().length > 0
761
+ && typeof manifest.createdAt === "string" && Number.isFinite(Date.parse(manifest.createdAt))
762
+ && [undefined, 1, 2].includes(manifest.layoutVersion as undefined | number);
763
+ } catch { /* reported below */ }
764
+ if (!manifestValid) throw new Error("DEVFLOW_LEGACY_LAYOUT_CONFIRMATION_REQUIRED: valid .devflow/project.json is required before legacy layout migration");
765
+ let migrationCompletePrecheck = false;
766
+ try {
767
+ const marker = JSON.parse(readFileSync(path.join(resolvedCwd, ".devflow", "layout-migration.json"), "utf8")) as Record<string, unknown>;
768
+ migrationCompletePrecheck = marker.schemaVersion === 1 && marker.status === "complete" && Array.isArray(marker.migrated);
769
+ } catch { /* reported by the normal conflict checks below */ }
770
+ if (activeLegacy && !migrationCompletePrecheck) {
771
+ throw new Error(`DEVFLOW_ACTIVE_LEGACY_LAYOUT: .planning/${activeLegacy} requires dedicated control-plane migration`);
772
+ }
773
+ const canonicalExists = existsSync(path.join(resolvedCwd, ".devflow", "planning")) || existsSync(path.join(resolvedCwd, ".devflow", "context"));
774
+ const migrationComplete = (() => {
775
+ try {
776
+ const marker = JSON.parse(readFileSync(path.join(resolvedCwd, ".devflow", "layout-migration.json"), "utf8")) as Record<string, unknown>;
777
+ return marker.schemaVersion === 1 && marker.status === "complete" && Array.isArray(marker.migrated);
778
+ } catch { return false; }
779
+ })();
780
+ if (manifestValid && (() => {
781
+ try { return (JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>).layoutVersion === 2; } catch { return false; }
782
+ })() && migrationComplete) {
783
+ const planningLegacy = planningMarkers.some(relative => existsSync(path.join(legacyPlanning, relative)));
784
+ const contextLegacy = contextMarkers.some(relative => existsSync(path.join(legacyContext, relative)));
785
+ if ((planningLegacy && !existsSync(path.join(resolvedCwd, ".devflow", "planning"))) || (contextLegacy && !existsSync(path.join(resolvedCwd, ".devflow", "context")))) {
786
+ throw new Error("DEVFLOW_LEGACY_REINTRODUCTION: v2 project will not backfill a missing canonical directory");
787
+ }
788
+ }
789
+ if (canonicalExists && !migrationComplete) throw new Error("DEVFLOW_LAYOUT_CONFLICT: canonical and legacy layouts coexist without a completed migration marker");
790
+ if (!canonicalExists) throw new Error("DEVFLOW_LAYOUT_MIGRATION_REQUIRED: run project initialization to migrate legacy read-only artifacts");
791
+ }
792
+ const planningRoot = path.join(resolvedCwd, ".devflow/planning");
707
793
  if (existsSync(planningRoot) && lstatSync(planningRoot).isSymbolicLink()) {
708
794
  throw new Error(`Refusing symlinked Devflow planning root: ${planningRoot}`);
709
795
  }
710
796
  for (const relative of [
711
- ".planning/STATE.md",
712
- ".planning/.devflow.lock",
713
- ".planning/workflows",
714
- ".planning/artifacts",
715
- ".planning/review-runs",
797
+ ".devflow/planning/STATE.md",
798
+ ".devflow/planning/.devflow.lock",
799
+ ".devflow/planning/workflows",
800
+ ".devflow/planning/artifacts",
801
+ ".devflow/planning/review-runs",
716
802
  ]) {
717
803
  const target = path.join(resolvedCwd, relative);
718
804
  try {
@@ -744,29 +830,30 @@ function safeControlPath(cwd: string, raw: string | undefined, kind: "state" | "
744
830
  const relative = normalizedProjectPath(raw, cwd);
745
831
  const allowed =
746
832
  kind === "state"
747
- ? relative === ".planning/STATE.md"
748
- : Boolean(relative?.startsWith(".planning/workflows/") && relative.endsWith(".yaml"));
833
+ ? relative === ".devflow/planning/STATE.md"
834
+ : Boolean(relative?.startsWith(".devflow/planning/workflows/") && relative.endsWith(".yaml"));
749
835
  if (!relative || !allowed) throw new Error(`Invalid ${kind} control path: ${raw}`);
750
836
  return relative;
751
837
  }
752
838
 
753
839
  function isCanonicalRequirementGraphPath(filePath: string): boolean {
754
- return /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|tests|traceability|graph\.meta)\.json$/u.test(filePath)
755
- || /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
840
+ return /^\.devflow\/planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|tests|traceability|graph\.meta)\.json$/u.test(filePath)
841
+ || /^\.devflow\/planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
756
842
  }
757
843
 
758
844
  function isCoreOwnedGovernancePath(filePath: string): boolean {
759
845
  // 规范需求图谱只能由 Core 的 devflow_requirement_graph writer 写入;the
760
846
  // same ownership extends to its views, machine evidence, and audit records.
761
847
  return isCanonicalRequirementGraphPath(filePath)
762
- || /^\.planning\/evidence\/[^/\\\x00-\x1F\x7F]+\/[^/\\]+\.machine\.json$/u.test(filePath)
763
- || /^\.planning\/contracts\/[^/\\\x00-\x1F\x7F]+\/(?:EC-[A-Fa-f0-9]+\.json|control-events\.jsonl)$/u.test(filePath)
764
- || filePath.startsWith(".planning/review-runs/")
765
- || /^\.planning\/phases\/[^/\\\x00-\x1F\x7F]+\/REVIEW\.md$/u.test(filePath);
848
+ || /^\.devflow\/planning\/test-runs\/[^/\\\x00-\x1F\x7F]+\/acceptance-results(?:\.meta)?\.json$/u.test(filePath)
849
+ || /^\.devflow\/planning\/evidence\/[^/\\\x00-\x1F\x7F]+\/[^/\\]+\.machine\.json$/u.test(filePath)
850
+ || /^\.devflow\/planning\/contracts\/[^/\\\x00-\x1F\x7F]+\/(?:EC-[A-Fa-f0-9]+\.json|control-events\.jsonl)$/u.test(filePath)
851
+ || filePath.startsWith(".devflow/planning/review-runs/")
852
+ || /^\.devflow\/planning\/phases\/[^/\\\x00-\x1F\x7F]+\/REVIEW\.md$/u.test(filePath);
766
853
  }
767
854
 
768
855
  function planningFeatureFromPath(filePath: string): string | null {
769
- const match = filePath.match(/^\.planning\/(?:requirements|features)\/([^/\\\x00-\x1F\x7F]+)(?:\/|$)/u);
856
+ const match = filePath.match(/^\.devflow\/planning\/(?:requirements|features)\/([^/\\\x00-\x1F\x7F]+)(?:\/|$)/u);
770
857
  return match?.[1] ?? null;
771
858
  }
772
859
 
@@ -1083,8 +1170,8 @@ function projectInitializationDirective(summary: string): string {
1083
1170
  return [
1084
1171
  "<devflow-project-initialization-directive>",
1085
1172
  "用户已批准完整项目初始化。现在处于执行模式;不要再创建 Todo、不要读取或写入 xd://devflow_contract。",
1086
- "执行 skill://workflow-project-context,按已确认的项目边界建立 .planning/PROJECT.md、.planning/navigation/INDEX.md、.context/spec/index.md,并按用户选择建立 full 代码索引。",
1087
- "只写项目初始化所需的 .planning/ 与 .context/ 控制产物;不得改动业务源码、依赖或配置。完成后运行项目初始化校验,报告生成文件和验证结果并停止,等待用户审查。",
1173
+ "执行 skill://workflow-project-context,按已确认的项目边界建立 .devflow/planning/PROJECT.md、.devflow/planning/navigation/INDEX.md、.devflow/context/spec/index.md,并按用户选择建立 full 代码索引。",
1174
+ "只写项目初始化所需的 .devflow/planning/ 与 .devflow/context/ 控制产物;不得改动业务源码、依赖或配置。完成后运行项目初始化校验,报告生成文件和验证结果并停止,等待用户审查。",
1088
1175
  summary,
1089
1176
  "</devflow-project-initialization-directive>",
1090
1177
  ].join("\n");
@@ -1177,7 +1264,7 @@ export function isShellControlMutation(toolName: string, input: unknown): boolea
1177
1264
  /workflow-orchestrator\/scripts\/(?:advance_state|init_state|init_workflow|sync_artifacts|requirement_graph|machine_evidence|execution_contract)\.rb[^\n]*--write/.test(
1178
1265
  command,
1179
1266
  );
1180
- const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.planning\/requirements\//.test(command);
1267
+ const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.devflow\/planning\/requirements\//.test(command);
1181
1268
  // `2>` and `2>>` only redirect stderr. They frequently accompany read-only
1182
1269
  // probes of control-plane files and must not be treated as a file mutation.
1183
1270
  // Keep bare `>`/`>>` and explicit stdout `1>`/`1>>` guarded.
@@ -1244,7 +1331,9 @@ function stepScopedMutationOwner(toolName: string, input: unknown): string | nul
1244
1331
  if (normalized === "devflow_api_test_matrix") return action === "validate" ? undefined : "acceptance-test-design";
1245
1332
  if (normalized === "devflow_tdd_evidence") return action === "inspect" ? undefined : "execute";
1246
1333
  if (normalized === "devflow_run_acceptance_tests") return action === "run" ? "run-acceptance-tests" : undefined;
1247
- if (normalized === "devflow_mainflow") return "generate-mainflow-cases";
1334
+ // Mainflow generation is an optional developer handoff inside the explicit
1335
+ // human-acceptance step, not a standalone workflow step.
1336
+ if (normalized === "devflow_mainflow") return "human-acceptance";
1248
1337
  if (normalized === "devflow_wireframe" || normalized === "devflow_design_rule") return "ui-spec";
1249
1338
  if (normalized.startsWith("devflow_architecture_") || normalized === "devflow_workload_constraint") {
1250
1339
  return action === "inspect" ? undefined : "architecture-feasibility";
@@ -1301,6 +1390,198 @@ export function stepScopedMutationBlockReason(
1301
1390
  return undefined;
1302
1391
  }
1303
1392
 
1393
+ // ---------------------------------------------------------------------------
1394
+ // Core human approvals (change list P03/P04). The agent may only propose or
1395
+ // list; confirmation exists solely in the /devflow control panel callback,
1396
+ // which writes control.db directly from this extension process.
1397
+ // ---------------------------------------------------------------------------
1398
+
1399
+ export const APPROVAL_TOOL_ACTIONS = ["propose", "list"] as const;
1400
+
1401
+ /** Parameter schema of `devflow_approval`; there is deliberately no confirm action. */
1402
+ export function approvalToolParameters(z: any) {
1403
+ const item = z.object({
1404
+ subject: z.string().min(1).max(128),
1405
+ dsl_file: z.string().min(1).max(512).optional(),
1406
+ evidence_files: z.array(z.string().min(1).max(512)).max(50).optional(),
1407
+ note: z.string().max(4000).optional(),
1408
+ review_issues: z.array(z.string().min(1).max(2000)).max(100).optional(),
1409
+ }).strict();
1410
+ return z.object({
1411
+ action: z.enum(APPROVAL_TOOL_ACTIONS),
1412
+ feature: z.string().min(1).max(200).optional(),
1413
+ kind: z.enum(APPROVAL_KINDS).optional(),
1414
+ items: z.array(item).min(1).max(200).optional(),
1415
+ status: z.enum(["proposed", "consumed", "rejected", "expired"]).optional(),
1416
+ }).strict();
1417
+ }
1418
+
1419
+ export type ApprovalProposeItem = {
1420
+ subject: string;
1421
+ dsl_file?: string;
1422
+ evidence_files?: string[];
1423
+ note?: string;
1424
+ review_issues?: string[];
1425
+ };
1426
+
1427
+ /**
1428
+ * Splits one agent item into the pin-computation request (re-used verbatim for
1429
+ * the confirm-time CAS) and agent-supplied context that is only displayed.
1430
+ */
1431
+ export function approvalProposeRequest(kind: ApprovalKind, item: ApprovalProposeItem): { request: Record<string, unknown>; agent: Record<string, unknown> } {
1432
+ if (item.dsl_file !== undefined && kind !== "execution_plan") throw new Error(`dsl_file is only valid for execution_plan proposals`);
1433
+ if (item.review_issues !== undefined && kind !== "execution_plan") throw new Error(`review_issues are only valid for execution_plan proposals`);
1434
+ if (item.evidence_files !== undefined && kind !== "human_result") throw new Error(`evidence_files are only valid for human_result proposals`);
1435
+ const request: Record<string, unknown> = {};
1436
+ if (kind === "execution_plan" && item.dsl_file) request.dsl_file = item.dsl_file;
1437
+ if (kind === "human_result") request.evidence_files = item.evidence_files ?? [];
1438
+ const agent: Record<string, unknown> = {};
1439
+ if (item.note?.trim()) agent.note = item.note.trim();
1440
+ if (item.review_issues?.length) agent.review_issues = item.review_issues;
1441
+ return { request, agent };
1442
+ }
1443
+
1444
+ export function approvalPinsScript(): string {
1445
+ // Always the packaged helper next to this extension: never a project-local
1446
+ // `.skills` copy the agent could edit to forge the confirm-time CAS.
1447
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../skills/workflow-orchestrator/scripts/approval_pins.py");
1448
+ }
1449
+
1450
+ export function approvalPinsArgv(root: string, feature: string, kind: ApprovalKind, subject: string, request: Record<string, unknown>): string[] {
1451
+ return [
1452
+ "python3", "-I", approvalPinsScript(), "compute",
1453
+ "--root", root, "--feature", feature, "--kind", kind, "--subject", subject,
1454
+ "--payload-json", JSON.stringify(request), "--json",
1455
+ ];
1456
+ }
1457
+
1458
+ export type ApprovalPinsResult = { ok: true; pins: Record<string, unknown>; stateRevision: number; display: Record<string, unknown> } | { ok: false; errors: string[] };
1459
+
1460
+ export function parseApprovalPinsOutput(stdout: string, stderr = ""): ApprovalPinsResult {
1461
+ let parsed: Record<string, unknown> | null = null;
1462
+ try { parsed = JSON.parse(stdout) as Record<string, unknown>; } catch { parsed = null; }
1463
+ if (!parsed || typeof parsed !== "object") return { ok: false, errors: [`approval pin helper returned invalid JSON: ${(stderr || stdout).slice(0, 500)}`] };
1464
+ if (parsed.ok !== true) {
1465
+ const errors = Array.isArray(parsed.errors) ? parsed.errors.map(String) : ["approval pin helper failed"];
1466
+ return { ok: false, errors };
1467
+ }
1468
+ if (!parsed.pins || typeof parsed.pins !== "object" || !Number.isInteger(parsed.state_revision)) {
1469
+ return { ok: false, errors: ["approval pin helper returned no pins"] };
1470
+ }
1471
+ return {
1472
+ ok: true,
1473
+ pins: parsed.pins as Record<string, unknown>,
1474
+ stateRevision: Number(parsed.state_revision),
1475
+ display: parsed.display && typeof parsed.display === "object" ? parsed.display as Record<string, unknown> : {},
1476
+ };
1477
+ }
1478
+
1479
+ /** Synchronous recompute used inside the confirm transaction. */
1480
+ export function recomputeApprovalPinsSync(root: string, proposal: ProposalRecord): CurrentPins {
1481
+ const request = proposal.payload.request && typeof proposal.payload.request === "object" ? proposal.payload.request as Record<string, unknown> : {};
1482
+ const child = Bun.spawnSync(approvalPinsArgv(root, proposal.feature, proposal.kind, proposal.subjectKey, request), {
1483
+ cwd: root, stdout: "pipe", stderr: "pipe", env: { ...process.env, OMP_PROJECT_DIR: root },
1484
+ });
1485
+ const result = parseApprovalPinsOutput(child.stdout.toString(), child.stderr.toString());
1486
+ if (!result.ok) throw new Error(result.errors.join("; "));
1487
+ return { pins: result.pins, stateRevision: result.stateRevision };
1488
+ }
1489
+
1490
+ export const APPROVAL_KIND_LABELS: Record<ApprovalKind, string> = {
1491
+ execution_plan: "执行计划",
1492
+ environment_profile: "环境配置",
1493
+ mode_override: "降级为人工",
1494
+ human_result: "人工验收结果",
1495
+ };
1496
+
1497
+ export const HUMAN_RESULT_CHOICES: Array<{ label: string; status: HumanResultStatus }> = [
1498
+ { label: "passed(通过)", status: "passed" },
1499
+ { label: "failed(失败)", status: "failed" },
1500
+ { label: "blocked(阻塞)", status: "blocked" },
1501
+ ];
1502
+
1503
+ /** Builds a human_result decision only from the user's own selection and input. */
1504
+ export function humanResultDecision(choice: string | undefined, actualResult: string | undefined): { status: HumanResultStatus; actual_result: string } | null {
1505
+ const status = HUMAN_RESULT_CHOICES.find(item => item.label === choice)?.status;
1506
+ const actual = actualResult?.trim();
1507
+ if (!status || !actual) return null;
1508
+ return { status, actual_result: actual };
1509
+ }
1510
+
1511
+ export function pendingApprovalProposals(proposals: ProposalRecord[], now = new Date()): ProposalRecord[] {
1512
+ return proposals.filter(item => item.status === "proposed" && !proposalIsExpired(item, now));
1513
+ }
1514
+
1515
+ export function approvalProposalLabel(proposal: ProposalRecord): string {
1516
+ return `${APPROVAL_KIND_LABELS[proposal.kind] ?? proposal.kind} · ${proposal.feature} · ${proposal.subjectKey} · ${proposal.createdAt.slice(0, 16).replace("T", " ")} · ${proposal.id.slice(0, 8)}`;
1517
+ }
1518
+
1519
+ function approvalText(value: unknown, limit = 600): string {
1520
+ const text = typeof value === "string" ? value : value === undefined || value === null ? "(无)" : JSON.stringify(value);
1521
+ return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
1522
+ }
1523
+
1524
+ function approvalList(values: unknown, empty = "(无)"): string[] {
1525
+ if (!Array.isArray(values) || values.length === 0) return [` ${empty}`];
1526
+ return values.map((value, index) => ` ${index + 1}. ${approvalText(value, 400)}`);
1527
+ }
1528
+
1529
+ /** Pure display assembly for one pending proposal (tested without a UI). */
1530
+ export function approvalReviewText(proposal: ProposalRecord): string {
1531
+ const display = proposal.payload.display && typeof proposal.payload.display === "object" ? proposal.payload.display as Record<string, any> : {};
1532
+ const agent = proposal.payload.agent && typeof proposal.payload.agent === "object" ? proposal.payload.agent as Record<string, any> : {};
1533
+ const lines = [
1534
+ `类型:${APPROVAL_KIND_LABELS[proposal.kind] ?? proposal.kind}`,
1535
+ `功能:${proposal.feature}`,
1536
+ `对象:${proposal.subjectKey}`,
1537
+ `提案:${proposal.id}(${proposal.createdAt},状态版本 ${proposal.stateRevision})`,
1538
+ "",
1539
+ ];
1540
+ const tcLines = () => [
1541
+ `TC 目标:${approvalText(display.target)}`,
1542
+ `Oracle 原文:${approvalText(display.oracle, 2000)}`,
1543
+ "前置条件:", ...approvalList(display.preconditions),
1544
+ "步骤:", ...approvalList(display.steps),
1545
+ ];
1546
+ if (proposal.kind === "execution_plan") {
1547
+ lines.push(...tcLines());
1548
+ lines.push(`DSL:${approvalText(display.dsl_file)} sha256=${approvalText(proposal.pins.dsl_sha256)}`);
1549
+ lines.push("覆盖 oracle 的断言:", ...approvalList(display.oracle_assertions, "(无 — 请勿批准)"));
1550
+ const uncovered = [
1551
+ ...(Array.isArray(display.uncovered_preconditions) && display.uncovered_preconditions.length ? [`前置条件 ${display.uncovered_preconditions.join(", ")}`] : []),
1552
+ ...(Array.isArray(display.uncovered_steps) && display.uncovered_steps.length ? [`步骤 ${display.uncovered_steps.join(", ")}`] : []),
1553
+ ];
1554
+ lines.push(`未被覆盖:${uncovered.length ? uncovered.join(";") : "无"}`);
1555
+ if (display.structural_validation === "unavailable") lines.push("注意:Core 结构覆盖校验器不可用,本次只展示覆盖声明。");
1556
+ lines.push("AI 审查问题(建议性):", ...approvalList(agent.review_issues, "(未提供独立 AI 审查结果)"));
1557
+ } else if (proposal.kind === "environment_profile") {
1558
+ lines.push(`Profile:${approvalText(display.profile)}(${approvalText(display.path)})`);
1559
+ lines.push(`base_url:${approvalText(display.base_url)}`);
1560
+ lines.push("允许的页面导航 origin:", ...approvalList(display.navigation_origins));
1561
+ lines.push("允许的 API origin:", ...approvalList(display.api_origins));
1562
+ lines.push(`子资源策略:${approvalText(display.subresources)}`);
1563
+ const secrets = display.secret_env && typeof display.secret_env === "object" ? Object.entries(display.secret_env as Record<string, unknown>) : [];
1564
+ lines.push("密钥环境变量(仅名称):", ...(secrets.length ? secrets.map(([alias, name]) => ` ${alias} ← ${String(name)}`) : [" (无)"]));
1565
+ lines.push(`trace:${approvalText(display.trace)}`);
1566
+ lines.push(`profile sha256:${approvalText(proposal.pins.profile_sha256)}`);
1567
+ } else if (proposal.kind === "mode_override") {
1568
+ lines.push(...tcLines());
1569
+ lines.push(`降级:${approvalText(display.from)} → ${approvalText(display.to)}`);
1570
+ lines.push(`绑定条目:${approvalText(display.binding_entry, 1000)}`);
1571
+ } else if (proposal.kind === "human_result") {
1572
+ lines.push(...tcLines());
1573
+ const evidence = Array.isArray(display.evidence) ? display.evidence as Array<Record<string, unknown>> : [];
1574
+ lines.push("证据文件(批准时固定哈希):", ...(evidence.length ? evidence.map(item => ` ${String(item.path)} sha256=${String(item.sha256)}`) : [" (无证据文件;无法选择 passed)"]));
1575
+ lines.push("结论未预填:请根据你自己的执行结果选择 passed / failed / blocked,并填写实际结果。");
1576
+ }
1577
+ if (agent.note && proposal.kind !== "execution_plan") {
1578
+ lines.push("", `Agent 说明(仅供参考,不是结论):${approvalText(agent.note, 2000)}`);
1579
+ } else if (agent.note) {
1580
+ lines.push("", `Agent 说明:${approvalText(agent.note, 2000)}`);
1581
+ }
1582
+ return lines.join("\n");
1583
+ }
1584
+
1304
1585
  export default function devflowCore(pi: ExtensionAPI) {
1305
1586
  const { z } = pi.zod;
1306
1587
  pi.registerTool({
@@ -1424,7 +1705,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1424
1705
  const feature = activeContract.feature;
1425
1706
  const step = activeContract.step;
1426
1707
  if (!/^[\p{L}\p{N}._-]{1,160}$/u.test(feature) || !/^[\p{L}\p{N}._-]{1,160}$/u.test(step)) return;
1427
- const directory = path.join(cwd, ".planning", "evidence", feature);
1708
+ const directory = path.join(cwd, ".devflow/planning", "evidence", feature);
1428
1709
  const attempt = typeof extra.attempt === "number" && Number.isInteger(extra.attempt) && extra.attempt >= 0
1429
1710
  ? extra.attempt
1430
1711
  : undefined;
@@ -1619,7 +1900,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1619
1900
  ? (rawStep as { id: string }).id
1620
1901
  : undefined;
1621
1902
  if (!feature || !step) return undefined;
1622
- const contractsDir = path.join(cwd, ".planning", "contracts", feature);
1903
+ const contractsDir = path.join(cwd, ".devflow/planning", "contracts", feature);
1623
1904
  if (!existsSync(contractsDir)) return undefined;
1624
1905
  const candidates = readdirSync(contractsDir)
1625
1906
  .filter(name => name.startsWith("EC-") && name.endsWith(".json"))
@@ -1755,7 +2036,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1755
2036
  `初始化方式:${modeSummary}`,
1756
2037
  ].join("\n");
1757
2038
  const consequence = mode === "apply"
1758
- ? "确认后会创建 Devflow 的 .planning/ 与 .context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
2039
+ ? "确认后会创建 Devflow 的 .devflow/planning/ 与 .devflow/context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
1759
2040
  : "确认后只进入只读规划,不会创建工作流或写入项目文件。";
1760
2041
  const confirmed = await ctx.ui.confirm("确认项目初始化范围", `${summary}\n\n${consequence}`);
1761
2042
  if (!confirmed) {
@@ -2004,7 +2285,7 @@ export default function devflowCore(pi: ExtensionAPI) {
2004
2285
  const runtimeBootstrapAuthorizations = new Set<string>();
2005
2286
 
2006
2287
  async function enqueueControlledRolloverRequest(cwd: string, rolloverId: string, sourceSessionId: string) {
2007
- const requestPath = path.join(cwd, ".planning", "rollover-request.json");
2288
+ const requestPath = path.join(cwd, ".devflow/planning", "rollover-request.json");
2008
2289
  const temporary = `${requestPath}.${randomUUID()}.tmp`;
2009
2290
  await writeFile(temporary, JSON.stringify({ schema_version: 1, rollover_id: rolloverId, source_session_id: sourceSessionId }) + "\n", "utf8");
2010
2291
  await rename(temporary, requestPath);
@@ -2278,6 +2559,115 @@ export default function devflowCore(pi: ExtensionAPI) {
2278
2559
  ctx.ui.notify(`功能“${feature}”已取消;现有工件已保留。`, "info");
2279
2560
  }
2280
2561
 
2562
+ async function pendingApprovalCount(cwd: string): Promise<number | null> {
2563
+ try {
2564
+ const project = await loadProjectContext(cwd);
2565
+ const store = await InitializationStore.open(project);
2566
+ try {
2567
+ if (databaseAuthority(store.db, project.manifest.projectId) !== "database") return null;
2568
+ return pendingApprovalProposals(listProposals(store.db, project.manifest.projectId, { status: "proposed" })).length;
2569
+ } finally { store.close(); }
2570
+ } catch {
2571
+ return null;
2572
+ }
2573
+ }
2574
+
2575
+ /**
2576
+ * The only confirmation path for Core approvals. It runs in this extension
2577
+ * process, re-computes pins inside the control.db transaction and never
2578
+ * shells out to a script the agent could also call to confirm.
2579
+ */
2580
+ async function reviewPendingApprovalsFromPanel(ctx: ProjectControlCommandContext): Promise<void> {
2581
+ let project: Awaited<ReturnType<typeof loadProjectContext>>;
2582
+ try {
2583
+ project = await loadProjectContext(ctx.cwd);
2584
+ } catch (error) {
2585
+ ctx.ui.notify(`无法读取项目上下文:${error instanceof Error ? error.message : String(error)}`, "error");
2586
+ return;
2587
+ }
2588
+ const projectId = project.manifest.projectId;
2589
+ for (;;) {
2590
+ const store = await InitializationStore.open(project);
2591
+ try {
2592
+ if (databaseAuthority(store.db, projectId) !== "database") {
2593
+ ctx.ui.notify("approval_requires_database_authority:只有 database authority 模式支持人工批准。", "error");
2594
+ return;
2595
+ }
2596
+ const pending = pendingApprovalProposals(listProposals(store.db, projectId, { status: "proposed" }));
2597
+ if (pending.length === 0) {
2598
+ ctx.ui.notify("当前没有待审批的提案。", "info");
2599
+ return;
2600
+ }
2601
+ const labels = pending.map(approvalProposalLabel);
2602
+ const selected = await ctx.ui.select("待审批", [...labels, "返回"]);
2603
+ if (!selected || selected === "返回") return;
2604
+ const proposal = pending[labels.indexOf(selected)];
2605
+ if (!proposal) return;
2606
+ const review = approvalReviewText(proposal);
2607
+ let decision: Record<string, unknown> | null = null;
2608
+ if (proposal.kind === "human_result") {
2609
+ ctx.ui.notify(review.slice(0, 10000), "info");
2610
+ const choice = await ctx.ui.select(`人工验收结论(${proposal.subjectKey})`, [...HUMAN_RESULT_CHOICES.map(item => item.label), "拒绝该提案", "返回"]);
2611
+ if (!choice || choice === "返回") continue;
2612
+ if (choice === "拒绝该提案") {
2613
+ if (await ctx.ui.confirm("拒绝提案", `${approvalProposalLabel(proposal)}
2614
+
2615
+ 拒绝后该提案不能再被确认。继续吗?`)) {
2616
+ rejectProposal(store.db, projectId, proposal.id);
2617
+ ctx.ui.notify("已拒绝该提案。", "info");
2618
+ }
2619
+ continue;
2620
+ }
2621
+ const actual = await ctx.ui.input(`实际结果(${proposal.subjectKey})`, "请填写你实际观察到的结果");
2622
+ decision = humanResultDecision(choice, actual);
2623
+ if (!decision) {
2624
+ ctx.ui.notify("必须选择结论并填写实际结果;未写入任何批准。", "error");
2625
+ continue;
2626
+ }
2627
+ const confirmed = await ctx.ui.confirm(
2628
+ "确认人工验收结果",
2629
+ `${proposal.subjectKey}
2630
+ 结论:${String(decision.status)}
2631
+ 实际结果:${String(decision.actual_result)}
2632
+
2633
+ 确认后 Core 将固定当前 TC 与证据哈希并记录该结果。继续吗?`,
2634
+ );
2635
+ if (!confirmed) continue;
2636
+ } else {
2637
+ const action = await ctx.ui.select(`审查:${approvalProposalLabel(proposal)}`, ["查看详情并批准…", "拒绝该提案", "返回"]);
2638
+ if (!action || action === "返回") continue;
2639
+ if (action === "拒绝该提案") {
2640
+ if (await ctx.ui.confirm("拒绝提案", `${approvalProposalLabel(proposal)}
2641
+
2642
+ 拒绝后该提案不能再被确认。继续吗?`)) {
2643
+ rejectProposal(store.db, projectId, proposal.id);
2644
+ ctx.ui.notify("已拒绝该提案。", "info");
2645
+ }
2646
+ continue;
2647
+ }
2648
+ const confirmed = await ctx.ui.confirm(`批准${APPROVAL_KIND_LABELS[proposal.kind]}`, `${review.slice(0, 12000)}
2649
+
2650
+ 确认批准吗?`);
2651
+ if (!confirmed) continue;
2652
+ decision = { approved: true };
2653
+ }
2654
+ const result = confirmApproval(store.db, {
2655
+ projectId,
2656
+ proposalId: proposal.id,
2657
+ decision,
2658
+ recompute: current => recomputeApprovalPinsSync(project.root, current),
2659
+ });
2660
+ if (result.ok) {
2661
+ ctx.ui.notify(`已批准:${approvalProposalLabel(proposal)}${result.supersededIds.length ? `(替代 ${result.supersededIds.length} 条旧批准)` : ""}`, "info");
2662
+ } else {
2663
+ ctx.ui.notify(`批准失败(${result.code}):${result.message}`, "error");
2664
+ }
2665
+ } finally {
2666
+ store.close();
2667
+ }
2668
+ }
2669
+ }
2670
+
2281
2671
  async function openProjectControlPanel(ctx: ProjectControlCommandContext): Promise<void> {
2282
2672
  for (;;) {
2283
2673
  // The control panel is a read-only projection. Each action below reads a
@@ -2293,6 +2683,9 @@ export default function devflowCore(pi: ExtensionAPI) {
2293
2683
  if (snapshot.action === "await_user_confirmation") options.push("审查并批准推进…");
2294
2684
  if (hasSafeRecoveryPlan(snapshot)) options.push("执行安全恢复…");
2295
2685
  if (typeof snapshot.current_feature === "string" && !["completed", "cancelled", "failed"].includes(String(snapshot.status))) options.push("取消当前功能…");
2686
+ const approvalCount = await pendingApprovalCount(ctx.cwd);
2687
+ const approvalOption = approvalCount === null ? null : `待审批(${approvalCount})…`;
2688
+ if (approvalOption) options.push(approvalOption);
2296
2689
  options.push("关闭");
2297
2690
 
2298
2691
  const selected = await ctx.ui.select("项目控制操作", options);
@@ -2323,6 +2716,10 @@ export default function devflowCore(pi: ExtensionAPI) {
2323
2716
  await cancelFeatureFromPanel(ctx);
2324
2717
  return;
2325
2718
  }
2719
+ if (approvalOption && selected === approvalOption) {
2720
+ await reviewPendingApprovalsFromPanel(ctx);
2721
+ continue;
2722
+ }
2326
2723
  }
2327
2724
  }
2328
2725
 
@@ -2403,6 +2800,12 @@ export default function devflowCore(pi: ExtensionAPI) {
2403
2800
  pi.on("session_start", async (_event, ctx) => {
2404
2801
  projectRoot = ctx.cwd;
2405
2802
  sessionId = randomUUID();
2803
+ try {
2804
+ await migrateLegacyProjectLayout(ctx.cwd);
2805
+ } catch (error) {
2806
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
2807
+ return;
2808
+ }
2406
2809
  const result = await runHook(ctx.cwd, "start.py", {
2407
2810
  session_id: sessionId,
2408
2811
  cwd: ctx.cwd,
@@ -2736,7 +3139,7 @@ export default function devflowCore(pi: ExtensionAPI) {
2736
3139
  text: [
2737
3140
  "<devflow-planner-directive>",
2738
3141
  "用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
2739
- "不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
3142
+ "不得创建 .devflow/planning 文件、不得调用状态迁移、不得执行实现。",
2740
3143
  "先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
2741
3144
  initialization.summary,
2742
3145
  "</devflow-planner-directive>",
@@ -2841,7 +3244,7 @@ export default function devflowCore(pi: ExtensionAPI) {
2841
3244
  "--root", ctx.cwd,
2842
3245
  "--feature", activeContract.feature,
2843
3246
  "--step", activeContract.step,
2844
- "--contract", `.planning/contracts/${activeContract.feature}/${activeContract.contractId}.json`,
3247
+ "--contract", `.devflow/planning/contracts/${activeContract.feature}/${activeContract.contractId}.json`,
2845
3248
  "--obligations", "[]",
2846
3249
  "--review-agent", "devflow-code-reviewer",
2847
3250
  "--stage", "entry",
@@ -3375,16 +3778,22 @@ export default function devflowCore(pi: ExtensionAPI) {
3375
3778
  pi.registerTool({
3376
3779
  name: "devflow_run_acceptance_tests",
3377
3780
  label: "Devflow Run Acceptance Tests",
3378
- description: "Validate or execute only command or portable HTTP adapters bound to the frozen TC acceptance suite.",
3781
+ description: [
3782
+ "Validate or execute the adapters bound to the frozen TC acceptance suite: command, portable HTTP, and devflow_playwright (the Core-owned Playwright Runtime driven by a pinned DSL).",
3783
+ "Actions: validate (bindings and suite), validate_human (manual TCs), run (executes every automated TC and writes the canonical acceptance-results.json; Core is the only writer), validate_results (re-checks recorded results against current approvals and hashes), gc_runs (prunes .devflow/runs/<feature> execution directories per retention rules; dry-run by default, pass write=true to delete; optional retentionDays / unreferencedTtlDays).",
3784
+ "devflow_playwright bindings require CURRENT human approvals created with devflow_approval and confirmed by the user in /devflow control: an execution_plan approval per TC (pins the DSL, TC and suite) and an environment_profile approval for the target profile; manual or downgraded TCs need a human_result / mode_override approval. You cannot confirm approvals yourself.",
3785
+ "Hard gate: a missing, superseded or expired approval, or a DSL/profile/suite/evidence hash that changed after the approval or the run, makes run and validate_results return ok=false and blocks run-acceptance-tests from completing; there is no soft override and results must never be written by hand.",
3786
+ ].join(" "),
3379
3787
  parameters: z.object({
3380
- action: z.enum(["validate", "run", "validate_results"]),
3788
+ action: z.enum(["validate", "validate_human", "run", "validate_results", "gc_runs"]),
3381
3789
  feature: z.string().min(1),
3790
+ write: z.boolean().optional().describe("gc_runs only: actually delete expired run directories. Omit for a dry-run."),
3791
+ retentionDays: z.number().int().min(0).optional().describe("gc_runs only: days referenced runs are kept after release-closeout."),
3792
+ unreferencedTtlDays: z.number().int().min(0).optional().describe("gc_runs only: days unreferenced runs are kept."),
3382
3793
  }),
3383
- approval: args => args.action === "run" ? "write" : "read",
3794
+ approval: args => args.action === "run" || (args.action === "gc_runs" && args.write === true) ? "write" : "read",
3384
3795
  async execute(_id, params, _signal, _onUpdate, ctx) {
3385
- const scriptAction = params.action === "validate_results" ? "validate-results" : params.action;
3386
- const args = [scriptAction, "--feature", params.feature, "--json"];
3387
- if (params.action === "run") args.push("--write");
3796
+ const args = acceptanceRunnerArgs(params);
3388
3797
  const process = await runPython(ctx.cwd, "workflow-orchestrator/scripts/acceptance_test_runner.py", args);
3389
3798
  const payload = parseJsonResult(process);
3390
3799
  const failed = payload && typeof payload === "object" && (payload as { ok?: unknown }).ok === false;
@@ -3395,6 +3804,75 @@ export default function devflowCore(pi: ExtensionAPI) {
3395
3804
  },
3396
3805
  });
3397
3806
 
3807
+ pi.registerTool({
3808
+ name: "devflow_approval",
3809
+ label: "Devflow Approval",
3810
+ description: "Propose or list Core human approvals (execution_plan, environment_profile, mode_override, human_result). Core computes and pins the current TC/DSL/binding/profile/evidence hashes. Only the user can confirm, in /devflow control → 待审批; this tool has no confirm action. human_result proposals carry evidence only, never a verdict. Requires database authority.",
3811
+ parameters: approvalToolParameters(z),
3812
+ approval: args => (args.action === "list" ? "read" : "write"),
3813
+ async execute(_id, params, _signal, _onUpdate, ctx) {
3814
+ const fail = (message: string, details: Record<string, unknown> = {}) => ({
3815
+ content: [{ type: "text" as const, text: message }],
3816
+ details: { action: params.action, error: message, ...details },
3817
+ isError: true,
3818
+ });
3819
+ try {
3820
+ if (!(APPROVAL_TOOL_ACTIONS as readonly string[]).includes(params.action)) {
3821
+ return fail(`devflow_approval only supports ${APPROVAL_TOOL_ACTIONS.join("|")}; approvals are confirmed by the user in /devflow control`);
3822
+ }
3823
+ const project = await loadProjectContext(ctx.cwd);
3824
+ const projectId = project.manifest.projectId;
3825
+ const store = await InitializationStore.open(project);
3826
+ try {
3827
+ if (databaseAuthority(store.db, projectId) !== "database") {
3828
+ return fail("approval_requires_database_authority: approvals are only supported when the project uses database authority", { code: "approval_requires_database_authority" });
3829
+ }
3830
+ if (params.action === "list") {
3831
+ const proposals = listProposals(store.db, projectId, { feature: params.feature, kind: params.kind, status: params.status ?? "proposed" })
3832
+ .map(item => ({ proposalId: item.id, feature: item.feature, kind: item.kind, subject: item.subjectKey, status: item.status, createdAt: item.createdAt, resolvedAt: item.resolvedAt, expired: item.status === "proposed" && proposalIsExpired(item) }));
3833
+ const approvals = listApprovals(store.db, projectId, { feature: params.feature, kind: params.kind })
3834
+ .map(item => ({ approvalId: item.id, proposalId: item.proposalId, feature: item.feature, kind: item.kind, subject: item.subjectKey, pins: item.pins, decision: item.decision, approvedAt: item.approvedAt }));
3835
+ const value = { proposals, approvals, note: "Approvals listed here are current records; validity is re-checked against current pins by the reader." };
3836
+ return { content: [{ type: "text" as const, text: JSON.stringify(value, null, 2) }], details: value };
3837
+ }
3838
+ if (!params.feature || !params.kind || !params.items?.length) return fail("propose requires feature, kind and at least one item");
3839
+ const kind = params.kind as ApprovalKind;
3840
+ const subjects = new Set<string>();
3841
+ const prepared: Array<{ subject: string; request: Record<string, unknown>; agent: Record<string, unknown>; pins: Record<string, unknown>; stateRevision: number; display: Record<string, unknown> }> = [];
3842
+ const errors: string[] = [];
3843
+ for (const item of params.items as ApprovalProposeItem[]) {
3844
+ if (subjects.has(item.subject)) { errors.push(`${item.subject}: duplicate subject`); continue; }
3845
+ subjects.add(item.subject);
3846
+ let split: { request: Record<string, unknown>; agent: Record<string, unknown> };
3847
+ try { split = approvalProposeRequest(kind, item); } catch (error) { errors.push(`${item.subject}: ${error instanceof Error ? error.message : String(error)}`); continue; }
3848
+ const result = await runProcess(approvalPinsArgv(project.root, params.feature, kind, item.subject, split.request), project.root, { OMP_PROJECT_DIR: project.root });
3849
+ const pins = parseApprovalPinsOutput(result.stdout, result.stderr);
3850
+ if (!pins.ok) { errors.push(`${item.subject}: ${pins.errors.join("; ")}`); continue; }
3851
+ prepared.push({ subject: item.subject, ...split, pins: pins.pins, stateRevision: pins.stateRevision, display: pins.display });
3852
+ }
3853
+ // All-or-nothing: a partially recorded execution plan would be misleading to review.
3854
+ if (errors.length) return fail(`No proposal was recorded:\n${errors.join("\n")}`, { errors });
3855
+ // One immediate transaction: any store-level error rolls back every insert and every "expire older proposal" update.
3856
+ const recorded = proposeApprovals(store.db, prepared.map(item => ({
3857
+ projectId, feature: params.feature!, kind, subjectKey: item.subject,
3858
+ payload: { request: item.request, agent: item.agent, display: item.display },
3859
+ pins: item.pins, stateRevision: item.stateRevision,
3860
+ })));
3861
+ const created = prepared.map((item, index) => ({
3862
+ proposalId: recorded[index]!.proposalId, subject: item.subject, pins: item.pins, replacedProposals: recorded[index]!.expiredIds,
3863
+ }));
3864
+ const value = {
3865
+ kind, feature: params.feature, proposals: created,
3866
+ next: "Ask the user to review and decide in /devflow control → 待审批. You cannot confirm approvals; do not write verdicts or approval records yourself.",
3867
+ };
3868
+ return { content: [{ type: "text" as const, text: JSON.stringify(value, null, 2) }], details: value };
3869
+ } finally { store.close(); }
3870
+ } catch (error) {
3871
+ return fail(error instanceof Error ? error.message : String(error));
3872
+ }
3873
+ },
3874
+ });
3875
+
3398
3876
  pi.registerTool({
3399
3877
  name: "devflow_runtime_context",
3400
3878
  label: "Devflow Runtime Context",
@@ -3625,7 +4103,7 @@ export default function devflowCore(pi: ExtensionAPI) {
3625
4103
  async execute(_id, params, _signal, _onUpdate, ctx) {
3626
4104
  const feature = normalizeContractSegment(params.feature, "feature");
3627
4105
  const taskId = normalizeContractSegment(params.taskId, "taskId");
3628
- const root = path.join(ctx.cwd, ".planning", "evidence", feature);
4106
+ const root = path.join(ctx.cwd, ".devflow/planning", "evidence", feature);
3629
4107
  const destination = path.join(root, `${taskId}.json`);
3630
4108
  const validatePack = (pack: unknown): string[] => {
3631
4109
  if (!pack || typeof pack !== "object" || Array.isArray(pack)) return ["pack must be an object"];
@@ -3718,7 +4196,7 @@ export default function devflowCore(pi: ExtensionAPI) {
3718
4196
  const written = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/execution_contract.rb", createArgs);
3719
4197
  if (!controlPlaneSucceeded(written)) return renderResult(written, { action: input.action });
3720
4198
  const writtenPayload = parseJsonResult(written);
3721
- const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".planning/contracts/";
4199
+ const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".devflow/planning/contracts/";
3722
4200
  contract.contractPath = destination;
3723
4201
  activeContract = contract;
3724
4202
  contractAgentCalls = 0;
@@ -4264,7 +4742,7 @@ export default function devflowCore(pi: ExtensionAPI) {
4264
4742
  [
4265
4743
  "<devflow-planner-directive>",
4266
4744
  "用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
4267
- "不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
4745
+ "不得创建 .devflow/planning 文件、不得调用状态迁移、不得执行实现。",
4268
4746
  "先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
4269
4747
  initialization.summary,
4270
4748
  "</devflow-planner-directive>",