@blxzer/cursor-trellis 0.4.1 → 0.4.3

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 (337) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +25 -14
  3. package/README.zh-CN.md +14 -10
  4. package/bin/smart-search.js +10 -2
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +36 -15
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/commands/init.d.ts +1 -1
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js +42 -153
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/commands/migrate.d.ts +10 -0
  13. package/dist/commands/migrate.d.ts.map +1 -0
  14. package/dist/commands/migrate.js +38 -0
  15. package/dist/commands/migrate.js.map +1 -0
  16. package/dist/commands/update.d.ts +5 -0
  17. package/dist/commands/update.d.ts.map +1 -1
  18. package/dist/commands/update.js +144 -41
  19. package/dist/commands/update.js.map +1 -1
  20. package/dist/configurators/cursor.d.ts +2 -1
  21. package/dist/configurators/cursor.d.ts.map +1 -1
  22. package/dist/configurators/cursor.js +4 -2
  23. package/dist/configurators/cursor.js.map +1 -1
  24. package/dist/configurators/index.js +4 -4
  25. package/dist/configurators/index.js.map +1 -1
  26. package/dist/configurators/shared.d.ts +8 -0
  27. package/dist/configurators/shared.d.ts.map +1 -1
  28. package/dist/configurators/shared.js +27 -1
  29. package/dist/configurators/shared.js.map +1 -1
  30. package/dist/configurators/workflow.d.ts +0 -9
  31. package/dist/configurators/workflow.d.ts.map +1 -1
  32. package/dist/configurators/workflow.js +12 -45
  33. package/dist/configurators/workflow.js.map +1 -1
  34. package/dist/constants/paths.d.ts +13 -0
  35. package/dist/constants/paths.d.ts.map +1 -1
  36. package/dist/constants/paths.js +17 -0
  37. package/dist/constants/paths.js.map +1 -1
  38. package/dist/migrations/manifests/0.3.0.json +1 -1
  39. package/dist/templates/common/bundled-skills/cstl-check/SKILL.md +1 -1
  40. package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/hooks-and-settings.md +2 -2
  41. package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/overview.md +1 -1
  42. package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/skills-and-commands.md +2 -4
  43. package/dist/templates/common/bundled-skills/cstl-skill-creator/SKILL.md +10 -7
  44. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/authoring-rules.md +93 -19
  45. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/general-authoring.md +189 -0
  46. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/review-checklist.md +10 -5
  47. package/dist/templates/common/commands/continue.md +6 -5
  48. package/dist/templates/common/index.d.ts +20 -0
  49. package/dist/templates/common/index.d.ts.map +1 -1
  50. package/dist/templates/common/index.js +21 -2
  51. package/dist/templates/common/index.js.map +1 -1
  52. package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
  53. package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
  54. package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +903 -0
  55. package/dist/templates/common/skills/brainstorm.md +2 -2
  56. package/dist/templates/cursor/agents/cstl-check.md +2 -2
  57. package/dist/templates/cursor/agents/cstl-implement.md +1 -1
  58. package/dist/templates/cursor/agents/cstl-research.md +2 -2
  59. package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
  60. package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
  61. package/dist/templates/cursor/fixtures/expected-rules.js +7 -24
  62. package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
  63. package/dist/templates/cursor/hooks.json +4 -0
  64. package/dist/templates/cursor/index.d.ts +1 -1
  65. package/dist/templates/cursor/index.js +1 -1
  66. package/dist/templates/cursor/rules/cstl-bootstrap.mdc +17 -0
  67. package/dist/templates/markdown/agents.md +36 -3
  68. package/dist/templates/markdown/{spec/guides → framework}/cursor-native-modes-guide.md.txt +13 -10
  69. package/dist/templates/markdown/{spec/guides → framework}/cursor-subagent-policy.md.txt +63 -127
  70. package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +29 -0
  71. package/dist/templates/markdown/{spec/guides → framework}/execution-strategy.md.txt +2 -0
  72. package/dist/templates/markdown/framework/index.md.txt +31 -0
  73. package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +45 -0
  74. package/dist/templates/markdown/framework/middleware-protocol.md.txt +83 -0
  75. package/dist/templates/markdown/framework/parallel-first-execution.md.txt +78 -0
  76. package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +83 -0
  77. package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
  78. package/dist/templates/markdown/{spec/guides → framework}/retrieval-daily-guide.md.txt +1 -1
  79. package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
  80. package/dist/templates/markdown/{spec/guides → framework}/verification-strength-guide.md.txt +2 -2
  81. package/dist/templates/markdown/index.d.ts +26 -10
  82. package/dist/templates/markdown/index.d.ts.map +1 -1
  83. package/dist/templates/markdown/index.js +87 -11
  84. package/dist/templates/markdown/index.js.map +1 -1
  85. package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +1 -1
  86. package/dist/templates/markdown/spec/guides/index.md.txt +5 -19
  87. package/dist/templates/markdown/spec/guides/prototype-guide.md.txt +10 -10
  88. package/dist/templates/markdown/spec/guides/test-discipline-guide.md.txt +1 -1
  89. package/dist/templates/shared-hooks/event-bridge.py +60 -0
  90. package/dist/templates/shared-hooks/index.d.ts +1 -1
  91. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  92. package/dist/templates/shared-hooks/index.js +1 -0
  93. package/dist/templates/shared-hooks/index.js.map +1 -1
  94. package/dist/templates/shared-hooks/session-start.py +19 -2
  95. package/dist/templates/trellis/CONTEXT.md +63 -3
  96. package/dist/templates/trellis/config.yaml +1 -1
  97. package/dist/templates/trellis/gitignore.txt +39 -39
  98. package/dist/templates/trellis/index.d.ts +12 -0
  99. package/dist/templates/trellis/index.d.ts.map +1 -1
  100. package/dist/templates/trellis/index.js +73 -15
  101. package/dist/templates/trellis/index.js.map +1 -1
  102. package/dist/templates/trellis/pool/README.md +12 -3
  103. package/dist/templates/trellis/pool/plan.md +24 -5
  104. package/dist/templates/trellis/scripts/common/adapter_middleware.py +158 -0
  105. package/dist/templates/trellis/scripts/common/artifact_locale.py +12 -3
  106. package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +5 -2
  107. package/dist/templates/trellis/scripts/common/full_quality.py +237 -0
  108. package/dist/templates/trellis/scripts/common/git.py +6 -0
  109. package/dist/templates/trellis/scripts/common/git_context.py +17 -5
  110. package/dist/templates/trellis/scripts/common/kernel_command.py +367 -0
  111. package/dist/templates/trellis/scripts/common/lite_context.py +280 -0
  112. package/dist/templates/trellis/scripts/common/ondemand_topology.py +225 -0
  113. package/dist/templates/trellis/scripts/common/parent_orchestration.py +1 -1
  114. package/dist/templates/trellis/scripts/common/pool_store.py +16 -4
  115. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +2 -2
  116. package/dist/templates/trellis/scripts/common/session_context.py +28 -2
  117. package/dist/templates/trellis/scripts/common/task_gates.py +73 -12
  118. package/dist/templates/trellis/scripts/common/task_map.py +42 -9
  119. package/dist/templates/trellis/scripts/common/task_store.py +293 -61
  120. package/dist/templates/trellis/scripts/common/test_adapter_middleware.py +56 -0
  121. package/dist/templates/trellis/scripts/common/test_depends_mode_block.py +9 -0
  122. package/dist/templates/trellis/scripts/common/test_full_quality.py +197 -0
  123. package/dist/templates/trellis/scripts/common/test_kernel_command.py +326 -0
  124. package/dist/templates/trellis/scripts/common/test_lite_path.py +190 -0
  125. package/dist/templates/trellis/scripts/common/test_observable_defaults.py +10 -4
  126. package/dist/templates/trellis/scripts/common/test_ondemand_topology.py +122 -0
  127. package/dist/templates/trellis/scripts/common/test_pool_store.py +17 -0
  128. package/dist/templates/trellis/scripts/common/test_task_store_kernel_patch.py +163 -0
  129. package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -0
  130. package/dist/templates/trellis/scripts/cursor_retrieval_probe_prompt.md +4 -5
  131. package/dist/templates/trellis/scripts/get_context.py +1 -0
  132. package/dist/templates/trellis/scripts/pool.py +5 -0
  133. package/dist/templates/trellis/scripts/task.py +83 -7
  134. package/dist/templates/trellis/workflow.md +109 -23
  135. package/dist/utils/cursor-retrieval-env.d.ts +2 -1
  136. package/dist/utils/cursor-retrieval-env.d.ts.map +1 -1
  137. package/dist/utils/cursor-retrieval-env.js +3 -2
  138. package/dist/utils/cursor-retrieval-env.js.map +1 -1
  139. package/dist/utils/cursor2plus-residue-cleanup.d.ts +38 -0
  140. package/dist/utils/cursor2plus-residue-cleanup.d.ts.map +1 -0
  141. package/dist/utils/cursor2plus-residue-cleanup.js +145 -0
  142. package/dist/utils/cursor2plus-residue-cleanup.js.map +1 -0
  143. package/dist/utils/manifest-prune.d.ts.map +1 -1
  144. package/dist/utils/manifest-prune.js +7 -2
  145. package/dist/utils/manifest-prune.js.map +1 -1
  146. package/dist/utils/p36-upgrade.d.ts +62 -0
  147. package/dist/utils/p36-upgrade.d.ts.map +1 -0
  148. package/dist/utils/p36-upgrade.js +160 -0
  149. package/dist/utils/p36-upgrade.js.map +1 -0
  150. package/dist/utils/project-capabilities.d.ts +1 -12
  151. package/dist/utils/project-capabilities.d.ts.map +1 -1
  152. package/dist/utils/project-capabilities.js +3 -100
  153. package/dist/utils/project-capabilities.js.map +1 -1
  154. package/dist/utils/readiness.d.ts +10 -0
  155. package/dist/utils/readiness.d.ts.map +1 -1
  156. package/dist/utils/readiness.js +58 -26
  157. package/dist/utils/readiness.js.map +1 -1
  158. package/dist/utils/retrieval-agent-instructions.js +1 -1
  159. package/dist/utils/retrieval-agent-instructions.js.map +1 -1
  160. package/dist/utils/retrieval-execution-telemetry.js +1 -1
  161. package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
  162. package/dist/utils/template-hash.d.ts.map +1 -1
  163. package/dist/utils/template-hash.js +11 -1
  164. package/dist/utils/template-hash.js.map +1 -1
  165. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  166. package/dist/utils/uninstall-scrubbers.js +52 -14
  167. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  168. package/dist/utils/update-rollout-report.d.ts +14 -0
  169. package/dist/utils/update-rollout-report.d.ts.map +1 -1
  170. package/dist/utils/update-rollout-report.js +1 -0
  171. package/dist/utils/update-rollout-report.js.map +1 -1
  172. package/dist/utils/workflow-ownership.d.ts +1 -1
  173. package/dist/utils/workflow-ownership.js +4 -4
  174. package/dist/utils/workflow-ownership.js.map +1 -1
  175. package/package.json +5 -4
  176. package/scripts/postinstall.js +6 -7
  177. package/dist/commands/campaign/canvas-render.d.ts +0 -68
  178. package/dist/commands/campaign/canvas-render.d.ts.map +0 -1
  179. package/dist/commands/campaign/canvas-render.js +0 -414
  180. package/dist/commands/campaign/canvas-render.js.map +0 -1
  181. package/dist/commands/campaign/compose.d.ts +0 -15
  182. package/dist/commands/campaign/compose.d.ts.map +0 -1
  183. package/dist/commands/campaign/compose.js +0 -110
  184. package/dist/commands/campaign/compose.js.map +0 -1
  185. package/dist/commands/campaign/index.d.ts +0 -13
  186. package/dist/commands/campaign/index.d.ts.map +0 -1
  187. package/dist/commands/campaign/index.js +0 -134
  188. package/dist/commands/campaign/index.js.map +0 -1
  189. package/dist/commands/campaign/kind-map.d.ts +0 -6
  190. package/dist/commands/campaign/kind-map.d.ts.map +0 -1
  191. package/dist/commands/campaign/kind-map.js +0 -21
  192. package/dist/commands/campaign/kind-map.js.map +0 -1
  193. package/dist/commands/campaign/mcp-server.d.ts +0 -6
  194. package/dist/commands/campaign/mcp-server.d.ts.map +0 -1
  195. package/dist/commands/campaign/mcp-server.js +0 -167
  196. package/dist/commands/campaign/mcp-server.js.map +0 -1
  197. package/dist/commands/campaign/render.d.ts +0 -5
  198. package/dist/commands/campaign/render.d.ts.map +0 -1
  199. package/dist/commands/campaign/render.js +0 -103
  200. package/dist/commands/campaign/render.js.map +0 -1
  201. package/dist/commands/campaign/trellis-load.d.ts +0 -13
  202. package/dist/commands/campaign/trellis-load.d.ts.map +0 -1
  203. package/dist/commands/campaign/trellis-load.js +0 -122
  204. package/dist/commands/campaign/trellis-load.js.map +0 -1
  205. package/dist/commands/campaign/types.d.ts +0 -61
  206. package/dist/commands/campaign/types.d.ts.map +0 -1
  207. package/dist/commands/campaign/types.js +0 -3
  208. package/dist/commands/campaign/types.js.map +0 -1
  209. package/dist/commands/goal/index.d.ts +0 -3
  210. package/dist/commands/goal/index.d.ts.map +0 -1
  211. package/dist/commands/goal/index.js +0 -159
  212. package/dist/commands/goal/index.js.map +0 -1
  213. package/dist/commands/goal/review.d.ts +0 -6
  214. package/dist/commands/goal/review.d.ts.map +0 -1
  215. package/dist/commands/goal/review.js +0 -28
  216. package/dist/commands/goal/review.js.map +0 -1
  217. package/dist/commands/rpc/client.d.ts +0 -51
  218. package/dist/commands/rpc/client.d.ts.map +0 -1
  219. package/dist/commands/rpc/client.js +0 -147
  220. package/dist/commands/rpc/client.js.map +0 -1
  221. package/dist/commands/rpc/index.d.ts +0 -10
  222. package/dist/commands/rpc/index.d.ts.map +0 -1
  223. package/dist/commands/rpc/index.js +0 -157
  224. package/dist/commands/rpc/index.js.map +0 -1
  225. package/dist/commands/rpc/serve.d.ts +0 -23
  226. package/dist/commands/rpc/serve.d.ts.map +0 -1
  227. package/dist/commands/rpc/serve.js +0 -60
  228. package/dist/commands/rpc/serve.js.map +0 -1
  229. package/dist/commands/rpc/smoke.d.ts +0 -16
  230. package/dist/commands/rpc/smoke.d.ts.map +0 -1
  231. package/dist/commands/rpc/smoke.js +0 -92
  232. package/dist/commands/rpc/smoke.js.map +0 -1
  233. package/dist/commands/sdk/index.d.ts +0 -9
  234. package/dist/commands/sdk/index.d.ts.map +0 -1
  235. package/dist/commands/sdk/index.js +0 -58
  236. package/dist/commands/sdk/index.js.map +0 -1
  237. package/dist/commands/sdk/run.d.ts +0 -42
  238. package/dist/commands/sdk/run.d.ts.map +0 -1
  239. package/dist/commands/sdk/run.js +0 -220
  240. package/dist/commands/sdk/run.js.map +0 -1
  241. package/dist/commands/sdk/status.d.ts +0 -14
  242. package/dist/commands/sdk/status.d.ts.map +0 -1
  243. package/dist/commands/sdk/status.js +0 -69
  244. package/dist/commands/sdk/status.js.map +0 -1
  245. package/dist/configurators/cursor2plus-local.d.ts +0 -6
  246. package/dist/configurators/cursor2plus-local.d.ts.map +0 -1
  247. package/dist/configurators/cursor2plus-local.js +0 -21
  248. package/dist/configurators/cursor2plus-local.js.map +0 -1
  249. package/dist/goal/action-packet.d.ts +0 -89
  250. package/dist/goal/action-packet.d.ts.map +0 -1
  251. package/dist/goal/action-packet.js +0 -113
  252. package/dist/goal/action-packet.js.map +0 -1
  253. package/dist/goal/audit.d.ts +0 -14
  254. package/dist/goal/audit.d.ts.map +0 -1
  255. package/dist/goal/audit.js +0 -12
  256. package/dist/goal/audit.js.map +0 -1
  257. package/dist/goal/constants.d.ts +0 -26
  258. package/dist/goal/constants.d.ts.map +0 -1
  259. package/dist/goal/constants.js +0 -24
  260. package/dist/goal/constants.js.map +0 -1
  261. package/dist/goal/paths.d.ts +0 -8
  262. package/dist/goal/paths.d.ts.map +0 -1
  263. package/dist/goal/paths.js +0 -36
  264. package/dist/goal/paths.js.map +0 -1
  265. package/dist/goal/preflight.d.ts +0 -21
  266. package/dist/goal/preflight.d.ts.map +0 -1
  267. package/dist/goal/preflight.js +0 -97
  268. package/dist/goal/preflight.js.map +0 -1
  269. package/dist/goal/review-seam.d.ts +0 -44
  270. package/dist/goal/review-seam.d.ts.map +0 -1
  271. package/dist/goal/review-seam.js +0 -153
  272. package/dist/goal/review-seam.js.map +0 -1
  273. package/dist/goal/reviewer-rules.d.ts +0 -13
  274. package/dist/goal/reviewer-rules.d.ts.map +0 -1
  275. package/dist/goal/reviewer-rules.js +0 -153
  276. package/dist/goal/reviewer-rules.js.map +0 -1
  277. package/dist/goal/reviewer.d.ts +0 -9
  278. package/dist/goal/reviewer.d.ts.map +0 -1
  279. package/dist/goal/reviewer.js +0 -54
  280. package/dist/goal/reviewer.js.map +0 -1
  281. package/dist/goal/runtime.d.ts +0 -32
  282. package/dist/goal/runtime.d.ts.map +0 -1
  283. package/dist/goal/runtime.js +0 -242
  284. package/dist/goal/runtime.js.map +0 -1
  285. package/dist/goal/sdk-client.d.ts +0 -10
  286. package/dist/goal/sdk-client.d.ts.map +0 -1
  287. package/dist/goal/sdk-client.js +0 -34
  288. package/dist/goal/sdk-client.js.map +0 -1
  289. package/dist/goal/state.d.ts +0 -40
  290. package/dist/goal/state.d.ts.map +0 -1
  291. package/dist/goal/state.js +0 -37
  292. package/dist/goal/state.js.map +0 -1
  293. package/dist/goal/task-triggers.d.ts +0 -15
  294. package/dist/goal/task-triggers.d.ts.map +0 -1
  295. package/dist/goal/task-triggers.js +0 -23
  296. package/dist/goal/task-triggers.js.map +0 -1
  297. package/dist/goal/walls.d.ts +0 -12
  298. package/dist/goal/walls.d.ts.map +0 -1
  299. package/dist/goal/walls.js +0 -65
  300. package/dist/goal/walls.js.map +0 -1
  301. package/dist/goal/worker-prompt.d.ts +0 -13
  302. package/dist/goal/worker-prompt.d.ts.map +0 -1
  303. package/dist/goal/worker-prompt.js +0 -41
  304. package/dist/goal/worker-prompt.js.map +0 -1
  305. package/dist/goal/worker.d.ts +0 -42
  306. package/dist/goal/worker.d.ts.map +0 -1
  307. package/dist/goal/worker.js +0 -180
  308. package/dist/goal/worker.js.map +0 -1
  309. package/dist/templates/common/bundled-skills/cstl-cursor2plus-setup/SKILL.md +0 -63
  310. package/dist/templates/cursor/commands/cursor2plus-setup.md +0 -82
  311. package/dist/templates/cursor/rules/cstl-cursor-modes.mdc +0 -32
  312. package/dist/templates/cursor/rules/cstl-session-rename.mdc +0 -31
  313. package/dist/templates/cursor/rules/cstl-subagent-dispatch.mdc +0 -34
  314. package/dist/templates/cursor/rules/cstl-triage.mdc +0 -36
  315. package/dist/templates/cursor/rules/retrieval-routing.mdc +0 -78
  316. package/dist/templates/markdown/spec/guides/cursor-trellis-release-coexistence-guide.md.txt +0 -166
  317. package/dist/templates/markdown/spec/guides/goal-release-regression-runbook.md.txt +0 -132
  318. package/dist/templates/trellis/local/README.md +0 -85
  319. package/dist/templates/trellis/local/config.local.json.example +0 -6
  320. package/dist/templates/trellis/local/index.d.ts +0 -8
  321. package/dist/templates/trellis/local/index.d.ts.map +0 -1
  322. package/dist/templates/trellis/local/index.js +0 -15
  323. package/dist/templates/trellis/local/index.js.map +0 -1
  324. package/dist/templates/trellis/local/patch_wpelc8.py +0 -421
  325. package/dist/templates/trellis/local/smoke.py +0 -94
  326. package/dist/templates/trellis/local/subagent-models.json.example +0 -4
  327. package/dist/templates/trellis/local/trellis-task-models.json.example +0 -10
  328. package/dist/templates/trellis/local/trellis-task-models.json5.example +0 -45
  329. package/dist/templates/trellis/local/trellis_task_models_config.py +0 -200
  330. package/dist/utils/cursor-sdk-gate.d.ts +0 -10
  331. package/dist/utils/cursor-sdk-gate.d.ts.map +0 -1
  332. package/dist/utils/cursor-sdk-gate.js +0 -32
  333. package/dist/utils/cursor-sdk-gate.js.map +0 -1
  334. /package/dist/templates/markdown/{spec/guides → framework}/artifact-locale-guide.md.txt +0 -0
  335. /package/dist/templates/markdown/{spec/guides → framework}/cursor-context-injection-guide.md.txt +0 -0
  336. /package/dist/templates/markdown/{spec/guides → framework}/cursor-semantic-compliance.md.txt +0 -0
  337. /package/dist/templates/markdown/{spec/guides → framework}/injection-budget-guide.md.txt +0 -0
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env python3
2
+ """Stage 4 Full Quality: required_controls, ledger, graded Independent Check."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
10
+
11
+ from common.full_quality import (
12
+ FULL_BASELINE_CONTROLS,
13
+ evaluate_independent_check,
14
+ full_quality_archive_errors,
15
+ resolve_required_controls,
16
+ rigor_from_required_controls,
17
+ )
18
+ from common.task_gates import (
19
+ task_closeout_profile,
20
+ validate_archive,
21
+ wave_c_stop_read_confirmed,
22
+ )
23
+
24
+ LITE_VERIFY = """# Verification Evidence
25
+
26
+ - validation: python -c "print('ok')"
27
+ - acceptance: Lite path completed without Parent or Git
28
+ - durable learning: no durable learning
29
+ """
30
+
31
+
32
+ def test_resolve_controls_does_not_require_design_for_standard_full() -> None:
33
+ standard = resolve_required_controls(rigor="full", verification_profile="standard")
34
+ assert standard["controls"] == list(FULL_BASELINE_CONTROLS)
35
+ assert "design" not in standard["controls"]
36
+
37
+ architecture = resolve_required_controls(
38
+ rigor="full", verification_profile="architecture"
39
+ )
40
+ assert "design" in architecture["controls"]
41
+
42
+
43
+ def test_closeout_does_not_infer_from_files_or_children(tmp_path: Path) -> None:
44
+ task_dir = tmp_path / "inferred"
45
+ task_dir.mkdir()
46
+ (task_dir / "design.md").write_text("# design\n", encoding="utf-8")
47
+ (task_dir / "implement.md").write_text("# implement\n", encoding="utf-8")
48
+ assert task_closeout_profile(task_dir, {"children": ["c1"]}) == "lite"
49
+ assert task_closeout_profile(task_dir, {}) == "lite"
50
+
51
+
52
+ def test_closeout_reads_legacy_classification_but_prefers_topology(
53
+ tmp_path: Path,
54
+ ) -> None:
55
+ task_dir = tmp_path / "legacy"
56
+ task_dir.mkdir()
57
+ assert (
58
+ task_closeout_profile(
59
+ task_dir,
60
+ {"meta": {"classification": "parent"}, "children": []},
61
+ )
62
+ == "parent"
63
+ )
64
+ assert (
65
+ task_closeout_profile(
66
+ task_dir,
67
+ {
68
+ "required_controls": resolve_required_controls(rigor="lite"),
69
+ "topology": {"kind": "parent-child", "parent_id": None, "children": ["c1"]},
70
+ },
71
+ )
72
+ == "parent"
73
+ )
74
+
75
+
76
+ def test_closeout_stops_leftover_after_wave_c_confirm(tmp_path: Path) -> None:
77
+ (tmp_path / ".cstl").mkdir()
78
+ (tmp_path / ".cstl" / ".p36-wave-c.json").write_text(
79
+ '{"schema_version": 1, "wave": "C", "confirmed": true, "confirmedAt": "2026-08-29T00:00:00Z"}\n',
80
+ encoding="utf-8",
81
+ )
82
+ child = tmp_path / ".cstl" / "tasks" / "linked-child"
83
+ child.mkdir(parents=True)
84
+ assert wave_c_stop_read_confirmed(child) is True
85
+ assert (
86
+ task_closeout_profile(
87
+ child,
88
+ {
89
+ "parent": "parent-a",
90
+ "children": [],
91
+ "meta": {"classification": "parent"},
92
+ "kind": "parent",
93
+ "topology": {"kind": "single", "parent_id": "parent-a", "children": []},
94
+ },
95
+ )
96
+ == "lite"
97
+ )
98
+ parent = tmp_path / ".cstl" / "tasks" / "controller"
99
+ parent.mkdir(parents=True)
100
+ assert (
101
+ task_closeout_profile(
102
+ parent,
103
+ {
104
+ "required_controls": resolve_required_controls(rigor="lite"),
105
+ "topology": {
106
+ "kind": "parent-child",
107
+ "parent_id": None,
108
+ "children": ["linked-child"],
109
+ },
110
+ },
111
+ )
112
+ == "parent"
113
+ )
114
+
115
+
116
+ def test_closeout_profile_prefers_required_controls_over_files(tmp_path: Path) -> None:
117
+ task_dir = tmp_path / "task"
118
+ task_dir.mkdir()
119
+ (task_dir / "design.md").write_text("# design\n", encoding="utf-8")
120
+ (task_dir / "implement.md").write_text("# implement\n", encoding="utf-8")
121
+ lite = {
122
+ "children": [],
123
+ "required_controls": resolve_required_controls(rigor="lite"),
124
+ }
125
+ assert task_closeout_profile(task_dir, lite) == "lite"
126
+ assert rigor_from_required_controls(lite) == "lite"
127
+
128
+ full = {
129
+ "children": [],
130
+ "required_controls": resolve_required_controls(rigor="full"),
131
+ }
132
+ assert task_closeout_profile(tmp_path / "no-design", full) == "full"
133
+
134
+
135
+ def test_archive_full_requires_ledger_and_check(tmp_path: Path) -> None:
136
+ task_dir = tmp_path / "full"
137
+ task_dir.mkdir()
138
+ (task_dir / "prd.md").write_text(
139
+ "# Full\n\n## Acceptance Criteria\n\n- [x] Ledger maps AC\n",
140
+ encoding="utf-8",
141
+ )
142
+ (task_dir / "implement.md").write_text("execution_mode: inline\n", encoding="utf-8")
143
+ (task_dir / "verify.md").write_text(LITE_VERIFY, encoding="utf-8")
144
+ data = {
145
+ "status": "in_progress",
146
+ "children": [],
147
+ "parent": None,
148
+ "required_controls": resolve_required_controls(rigor="full"),
149
+ }
150
+ missing = full_quality_archive_errors(task_dir, data)
151
+ assert any("ledger" in err for err in missing)
152
+
153
+ data["ac_evidence_ledger"] = {
154
+ "items": [{"ac_id": "AC-1", "evidence_ref": "verify.md#validation"}],
155
+ "source_fingerprint": "stale",
156
+ }
157
+ data["independent_check"] = evaluate_independent_check(
158
+ mode="self-review",
159
+ independent_worker_available=False,
160
+ evidence="self-review notes",
161
+ code_fingerprint="code-v1",
162
+ )
163
+ stale = full_quality_archive_errors(task_dir, data)
164
+ assert any("stale" in err for err in stale)
165
+
166
+ blocked = evaluate_independent_check(
167
+ mode="true-independent",
168
+ independent_worker_available=False,
169
+ evidence="would-be independent",
170
+ code_fingerprint="code-v1",
171
+ )
172
+ assert blocked["result"] == "BLOCKED"
173
+ data["independent_check"] = blocked
174
+ blocked_errors = full_quality_archive_errors(task_dir, data)
175
+ assert any("PASS" in err or "independent worker" in err for err in blocked_errors)
176
+
177
+
178
+ def test_lite_archive_does_not_require_independent_check(tmp_path: Path) -> None:
179
+ task_dir = tmp_path / "lite-quality"
180
+ task_dir.mkdir()
181
+ (task_dir / "prd.md").write_text(
182
+ "# Lite\n\n## Acceptance Criteria\n\n- [x] Close\n",
183
+ encoding="utf-8",
184
+ )
185
+ (task_dir / "verify.md").write_text(LITE_VERIFY, encoding="utf-8")
186
+ data = {
187
+ "id": "lite-quality",
188
+ "name": "lite-quality",
189
+ "title": "Lite",
190
+ "status": "in_progress",
191
+ "children": [],
192
+ "parent": None,
193
+ }
194
+ guard = validate_archive(task_dir, data)
195
+ assert guard.ok
196
+ assert guard.closeout_profile == "lite"
197
+ assert full_quality_archive_errors(task_dir, data) == []
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env python3
2
+ """Characterization tests for Stage 2 Python → Kernel Command shim."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+
12
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
13
+
14
+ from common import kernel_command as kernel_command_mod
15
+ from common.kernel_command import (
16
+ KernelCommandError,
17
+ kernel_archive,
18
+ kernel_create,
19
+ kernel_expected_revision,
20
+ kernel_patch,
21
+ kernel_projection_extras,
22
+ kernel_record_gate,
23
+ kernel_start,
24
+ run_kernel_command,
25
+ to_kernel_record,
26
+ )
27
+ from common.artifact_locale import set_task_artifact_locale
28
+ from common.task_gates import collect_kernel_projection_extras, write_gate_record
29
+
30
+ FAKE_KERNEL = r"""
31
+ import json
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ req = json.loads(sys.stdin.read())
36
+ op = req.get("op")
37
+ task_dir = Path(req["taskDir"])
38
+ task_dir.mkdir(parents=True, exist_ok=True)
39
+ kernel_path = task_dir / "kernel.json"
40
+ task_path = task_dir / "task.json"
41
+
42
+ def dump(payload, code=0):
43
+ sys.stdout.write(json.dumps(payload) + "\n")
44
+ sys.exit(code)
45
+
46
+ def read_kernel():
47
+ if kernel_path.is_file():
48
+ return json.loads(kernel_path.read_text(encoding="utf-8"))
49
+ return {
50
+ "schemaVersion": 1,
51
+ "identity": {"taskId": "missing"},
52
+ "revision": 0,
53
+ "phase": "define",
54
+ "condition": "ready",
55
+ "outcome": None,
56
+ "audit": [],
57
+ "gates": {"schemaVersion": 1, "transitions": {}},
58
+ "projection": None,
59
+ }
60
+
61
+ if op == "read":
62
+ kernel = read_kernel()
63
+ status = "planning"
64
+ if task_path.is_file():
65
+ status = json.loads(task_path.read_text(encoding="utf-8")).get("status", "planning")
66
+ dump({
67
+ "ok": True,
68
+ "op": "read",
69
+ "kernel": kernel,
70
+ "persisted": kernel_path.is_file(),
71
+ "legacy": {
72
+ "status": status,
73
+ "id": kernel["identity"]["taskId"],
74
+ "name": kernel["identity"]["taskId"],
75
+ "title": "",
76
+ },
77
+ })
78
+
79
+ record = req.get("record") or {}
80
+ existing_task = {}
81
+ if task_path.is_file():
82
+ existing_task = json.loads(task_path.read_text(encoding="utf-8"))
83
+
84
+ status_by_op = {
85
+ "create": "planning",
86
+ "start": "in_progress",
87
+ "archive": "completed",
88
+ }
89
+ if op == "patch":
90
+ status = existing_task.get("status") or (record.get("status") if isinstance(record, dict) else None) or "planning"
91
+ elif op == "record-gate" and existing_task:
92
+ status = existing_task.get("status", "planning")
93
+ else:
94
+ status = status_by_op.get(op, "planning")
95
+
96
+ prev = read_kernel()
97
+ revision = int(prev.get("revision") or 0) + 1
98
+ task_id = "task"
99
+ if isinstance(record, dict) and record.get("id"):
100
+ task_id = record["id"]
101
+ elif prev.get("identity", {}).get("taskId"):
102
+ task_id = prev["identity"]["taskId"]
103
+
104
+ phase_by_op = {
105
+ "create": "define",
106
+ "start": "execute",
107
+ "archive": "close",
108
+ "record-gate": prev.get("phase", "define"),
109
+ "patch": prev.get("phase", "define"),
110
+ }
111
+ kernel = {
112
+ "schemaVersion": 1,
113
+ "identity": {"taskId": task_id},
114
+ "revision": revision,
115
+ "phase": phase_by_op.get(op, "define"),
116
+ "condition": "ready",
117
+ "outcome": "completed" if op == "archive" else None,
118
+ "audit": list(prev.get("audit") or []) + [{
119
+ "id": "a1",
120
+ "idempotencyKey": req.get("idempotencyKey"),
121
+ "actor": req.get("actor"),
122
+ }],
123
+ "gates": prev.get("gates") or {"schemaVersion": 1, "transitions": {}},
124
+ "projection": {"status": status, "record": record, "extras": req.get("extras") or {}},
125
+ }
126
+ if op == "record-gate":
127
+ kernel["gates"].setdefault("transitions", {}).setdefault(req["transition"], {})[req["gate"]] = req["record"]
128
+
129
+ kernel_path.write_text(json.dumps(kernel, indent=2), encoding="utf-8")
130
+ projected = dict(existing_task)
131
+ if op != "record-gate" and isinstance(record, dict) and record:
132
+ projected.update(record)
133
+ if isinstance(record.get("meta"), dict) and isinstance(existing_task.get("meta"), dict):
134
+ projected["meta"] = {**existing_task["meta"], **record["meta"]}
135
+ projected["status"] = status
136
+ projected.update(req.get("extras") or {})
137
+ if projected:
138
+ task_path.write_text(json.dumps(projected, indent=2), encoding="utf-8")
139
+
140
+ dump({
141
+ "ok": True,
142
+ "op": op,
143
+ "kernel": kernel,
144
+ "persisted": True,
145
+ "projected": True,
146
+ "idempotent": False,
147
+ "legacy": {"status": status, "id": task_id, "name": task_id, "title": ""},
148
+ "audit": kernel["audit"][-1],
149
+ })
150
+ """
151
+
152
+
153
+ @pytest.fixture
154
+ def fake_kernel(tmp_path, monkeypatch):
155
+ script = tmp_path / "fake_kernel.py"
156
+ script.write_text(FAKE_KERNEL, encoding="utf-8")
157
+ monkeypatch.setattr(
158
+ kernel_command_mod,
159
+ "kernel_cli_argv",
160
+ lambda: [sys.executable, str(script)],
161
+ )
162
+ return script
163
+
164
+
165
+ def _record(**overrides):
166
+ data = {
167
+ "id": "shim-demo",
168
+ "name": "shim-demo",
169
+ "title": "Shim Demo",
170
+ "description": "",
171
+ "status": "planning",
172
+ "dev_type": None,
173
+ "scope": None,
174
+ "package": None,
175
+ "priority": "P2",
176
+ "creator": "tester",
177
+ "assignee": "tester",
178
+ "createdAt": "2026-08-28",
179
+ "completedAt": None,
180
+ "branch": None,
181
+ "base_branch": "main",
182
+ "worktree_path": None,
183
+ "commit": None,
184
+ "pr_url": None,
185
+ "subtasks": [],
186
+ "children": [],
187
+ "parent": None,
188
+ "relatedFiles": [],
189
+ "notes": "",
190
+ "meta": {},
191
+ }
192
+ data.update(overrides)
193
+ return data
194
+
195
+
196
+ def test_to_kernel_record_fills_canonical_fields():
197
+ record = to_kernel_record({"id": "a", "name": "a", "title": "A"})
198
+ assert record["status"] == "planning"
199
+ assert record["priority"] == "P2"
200
+ assert record["children"] == []
201
+ assert record["meta"] == {}
202
+
203
+
204
+ def test_create_updates_kernel_json(fake_kernel, tmp_path):
205
+ task_dir = tmp_path / "task"
206
+ result = kernel_create(
207
+ task_dir,
208
+ _record(),
209
+ actor="task.py create",
210
+ idempotency_key="create:shim-demo",
211
+ )
212
+ assert result["ok"] is True
213
+ assert (task_dir / "kernel.json").is_file()
214
+ kernel = json.loads((task_dir / "kernel.json").read_text(encoding="utf-8"))
215
+ assert kernel["revision"] >= 1
216
+ task = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
217
+ assert task["status"] == "planning"
218
+
219
+
220
+ def test_start_projects_in_progress(fake_kernel, tmp_path):
221
+ task_dir = tmp_path / "task"
222
+ kernel_create(task_dir, _record(), actor="a", idempotency_key="create:shim-demo")
223
+ rev = kernel_expected_revision(task_dir)
224
+ started = kernel_start(
225
+ task_dir,
226
+ _record(status="in_progress"),
227
+ {"execution_approval": {"approved_by": "user"}},
228
+ expected_revision=rev,
229
+ actor="task.py start-execution --approved",
230
+ idempotency_key="start:shim-demo",
231
+ )
232
+ assert started["legacy"]["status"] == "in_progress"
233
+ task = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
234
+ assert task["status"] == "in_progress"
235
+ assert task["execution_approval"]["approved_by"] == "user"
236
+
237
+
238
+ def test_record_gate_and_archive_go_through_kernel(fake_kernel, tmp_path):
239
+ task_dir = tmp_path / "task"
240
+ kernel_create(task_dir, _record(), actor="a", idempotency_key="create:shim-demo")
241
+ data = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
242
+ gate = {"result": "PASS", "evidence": "verify.md", "reviewer": "tester"}
243
+ write_gate_record(data, "start-execution", "code-review", gate)
244
+ extras = collect_kernel_projection_extras(data)
245
+ kernel_record_gate(
246
+ task_dir,
247
+ expected_revision=kernel_expected_revision(task_dir),
248
+ actor="task.py record-gate",
249
+ idempotency_key="gate:code-review",
250
+ transition="start-execution",
251
+ gate="code-review",
252
+ record=gate,
253
+ extras=extras,
254
+ evidence="verify.md",
255
+ )
256
+ kernel = json.loads((task_dir / "kernel.json").read_text(encoding="utf-8"))
257
+ assert kernel["gates"]["transitions"]["start-execution"]["code-review"]["result"] == "PASS"
258
+
259
+ archived = kernel_archive(
260
+ task_dir,
261
+ _record(status="completed", completedAt="2026-08-28"),
262
+ extras,
263
+ expected_revision=kernel_expected_revision(task_dir),
264
+ actor="task.py archive",
265
+ idempotency_key="archive:shim-demo",
266
+ )
267
+ assert archived["kernel"]["phase"] == "close"
268
+ task = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
269
+ assert task["status"] == "completed"
270
+
271
+
272
+ def test_half_conversion_is_not_success(tmp_path, monkeypatch):
273
+ script = tmp_path / "failing_kernel.py"
274
+ script.write_text(
275
+ "import json,sys\n"
276
+ "json.loads(sys.stdin.read())\n"
277
+ "sys.stdout.write(json.dumps({"
278
+ '"ok": False, "error": {"code": "HALF_CONVERSION", "message": "projection failed"},'
279
+ '"halfConversion": {"kernelPersisted": True, "projectionPersisted": False}'
280
+ "})+'\\n')\n"
281
+ "sys.exit(1)\n",
282
+ encoding="utf-8",
283
+ )
284
+ monkeypatch.setattr(
285
+ kernel_command_mod,
286
+ "kernel_cli_argv",
287
+ lambda: [sys.executable, str(script)],
288
+ )
289
+ with pytest.raises(KernelCommandError) as exc:
290
+ run_kernel_command({"op": "create", "taskDir": str(tmp_path / "t")})
291
+ assert "HALF_CONVERSION" in str(exc.value)
292
+ assert exc.value.result["halfConversion"]["kernelPersisted"] is True
293
+ assert exc.value.result["halfConversion"]["projectionPersisted"] is False
294
+
295
+
296
+ def test_kernel_patch_updates_meta_and_kernel_revision(fake_kernel, tmp_path):
297
+ task_dir = tmp_path / "task"
298
+ kernel_create(task_dir, _record(), actor="a", idempotency_key="create:shim-demo")
299
+ data = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
300
+ data["meta"] = {"pool_items": ["P28"]}
301
+ patched = kernel_patch(
302
+ task_dir,
303
+ data,
304
+ kernel_projection_extras(data),
305
+ expected_revision=kernel_expected_revision(task_dir),
306
+ actor="pool.py link",
307
+ idempotency_key="patch:pool-link:task:P28",
308
+ )
309
+ assert patched["ok"] is True
310
+ kernel = json.loads((task_dir / "kernel.json").read_text(encoding="utf-8"))
311
+ assert kernel["revision"] >= 2
312
+ assert kernel["audit"][-1]["idempotencyKey"] == "patch:pool-link:task:P28"
313
+ task = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
314
+ assert task["status"] == "planning"
315
+ assert task["meta"]["pool_items"] == ["P28"]
316
+
317
+
318
+ def test_set_task_artifact_locale_goes_through_kernel(fake_kernel, tmp_path):
319
+ task_dir = tmp_path / "task"
320
+ kernel_create(task_dir, _record(), actor="a", idempotency_key="create:shim-demo")
321
+ set_task_artifact_locale(task_dir, "en")
322
+ kernel = json.loads((task_dir / "kernel.json").read_text(encoding="utf-8"))
323
+ assert kernel["revision"] >= 2
324
+ task = json.loads((task_dir / "task.json").read_text(encoding="utf-8"))
325
+ assert task["meta"]["artifact_locale"] == "en"
326
+ assert task["status"] == "planning"