@blxzer/cursor-trellis 0.4.2 → 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 (315) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +23 -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 +137 -40
  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 +2 -4
  33. package/dist/configurators/workflow.js.map +1 -1
  34. package/dist/constants/paths.d.ts +9 -0
  35. package/dist/constants/paths.d.ts.map +1 -1
  36. package/dist/constants/paths.js +13 -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-meta/references/platform-files/skills-and-commands.md +2 -4
  40. package/dist/templates/common/bundled-skills/cstl-skill-creator/SKILL.md +10 -7
  41. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/authoring-rules.md +93 -19
  42. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/general-authoring.md +189 -0
  43. package/dist/templates/common/bundled-skills/cstl-skill-creator/references/review-checklist.md +10 -5
  44. package/dist/templates/common/commands/continue.md +5 -4
  45. package/dist/templates/common/index.d.ts +20 -0
  46. package/dist/templates/common/index.d.ts.map +1 -1
  47. package/dist/templates/common/index.js +21 -2
  48. package/dist/templates/common/index.js.map +1 -1
  49. package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
  50. package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
  51. package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +903 -0
  52. package/dist/templates/cursor/agents/cstl-research.md +1 -1
  53. package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
  54. package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
  55. package/dist/templates/cursor/fixtures/expected-rules.js +7 -24
  56. package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
  57. package/dist/templates/cursor/hooks.json +4 -0
  58. package/dist/templates/cursor/index.d.ts +1 -1
  59. package/dist/templates/cursor/index.js +1 -1
  60. package/dist/templates/cursor/rules/cstl-bootstrap.mdc +17 -0
  61. package/dist/templates/markdown/agents.md +15 -10
  62. package/dist/templates/markdown/framework/cursor-native-modes-guide.md.txt +12 -9
  63. package/dist/templates/markdown/framework/cursor-subagent-policy.md.txt +62 -126
  64. package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +4 -6
  65. package/dist/templates/markdown/framework/execution-strategy.md.txt +2 -0
  66. package/dist/templates/markdown/framework/index.md.txt +4 -0
  67. package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +4 -3
  68. package/dist/templates/markdown/framework/middleware-protocol.md.txt +83 -0
  69. package/dist/templates/markdown/framework/parallel-first-execution.md.txt +78 -0
  70. package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
  71. package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +1 -1
  72. package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
  73. package/dist/templates/markdown/index.d.ts +4 -0
  74. package/dist/templates/markdown/index.d.ts.map +1 -1
  75. package/dist/templates/markdown/index.js +17 -0
  76. package/dist/templates/markdown/index.js.map +1 -1
  77. package/dist/templates/markdown/spec/guides/index.md.txt +0 -6
  78. package/dist/templates/shared-hooks/event-bridge.py +60 -0
  79. package/dist/templates/shared-hooks/index.d.ts +1 -1
  80. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  81. package/dist/templates/shared-hooks/index.js +1 -0
  82. package/dist/templates/shared-hooks/index.js.map +1 -1
  83. package/dist/templates/shared-hooks/session-start.py +18 -1
  84. package/dist/templates/trellis/CONTEXT.md +63 -3
  85. package/dist/templates/trellis/gitignore.txt +39 -39
  86. package/dist/templates/trellis/index.d.ts +12 -0
  87. package/dist/templates/trellis/index.d.ts.map +1 -1
  88. package/dist/templates/trellis/index.js +73 -15
  89. package/dist/templates/trellis/index.js.map +1 -1
  90. package/dist/templates/trellis/pool/README.md +12 -3
  91. package/dist/templates/trellis/pool/plan.md +24 -5
  92. package/dist/templates/trellis/scripts/common/adapter_middleware.py +158 -0
  93. package/dist/templates/trellis/scripts/common/artifact_locale.py +12 -3
  94. package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +5 -2
  95. package/dist/templates/trellis/scripts/common/full_quality.py +237 -0
  96. package/dist/templates/trellis/scripts/common/git.py +6 -0
  97. package/dist/templates/trellis/scripts/common/git_context.py +17 -5
  98. package/dist/templates/trellis/scripts/common/kernel_command.py +367 -0
  99. package/dist/templates/trellis/scripts/common/lite_context.py +280 -0
  100. package/dist/templates/trellis/scripts/common/ondemand_topology.py +225 -0
  101. package/dist/templates/trellis/scripts/common/parent_orchestration.py +1 -1
  102. package/dist/templates/trellis/scripts/common/pool_store.py +16 -4
  103. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +2 -2
  104. package/dist/templates/trellis/scripts/common/session_context.py +28 -2
  105. package/dist/templates/trellis/scripts/common/task_gates.py +73 -12
  106. package/dist/templates/trellis/scripts/common/task_map.py +42 -9
  107. package/dist/templates/trellis/scripts/common/task_store.py +286 -56
  108. package/dist/templates/trellis/scripts/common/test_adapter_middleware.py +56 -0
  109. package/dist/templates/trellis/scripts/common/test_depends_mode_block.py +9 -0
  110. package/dist/templates/trellis/scripts/common/test_full_quality.py +197 -0
  111. package/dist/templates/trellis/scripts/common/test_kernel_command.py +326 -0
  112. package/dist/templates/trellis/scripts/common/test_lite_path.py +190 -0
  113. package/dist/templates/trellis/scripts/common/test_ondemand_topology.py +122 -0
  114. package/dist/templates/trellis/scripts/common/test_pool_store.py +17 -0
  115. package/dist/templates/trellis/scripts/common/test_task_store_kernel_patch.py +163 -0
  116. package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -0
  117. package/dist/templates/trellis/scripts/get_context.py +1 -0
  118. package/dist/templates/trellis/scripts/pool.py +5 -0
  119. package/dist/templates/trellis/scripts/task.py +83 -7
  120. package/dist/templates/trellis/workflow.md +100 -18
  121. package/dist/utils/cursor-retrieval-env.d.ts +2 -1
  122. package/dist/utils/cursor-retrieval-env.d.ts.map +1 -1
  123. package/dist/utils/cursor-retrieval-env.js +3 -2
  124. package/dist/utils/cursor-retrieval-env.js.map +1 -1
  125. package/dist/utils/cursor2plus-residue-cleanup.d.ts +38 -0
  126. package/dist/utils/cursor2plus-residue-cleanup.d.ts.map +1 -0
  127. package/dist/utils/cursor2plus-residue-cleanup.js +145 -0
  128. package/dist/utils/cursor2plus-residue-cleanup.js.map +1 -0
  129. package/dist/utils/manifest-prune.d.ts.map +1 -1
  130. package/dist/utils/manifest-prune.js +7 -2
  131. package/dist/utils/manifest-prune.js.map +1 -1
  132. package/dist/utils/p36-upgrade.d.ts +62 -0
  133. package/dist/utils/p36-upgrade.d.ts.map +1 -0
  134. package/dist/utils/p36-upgrade.js +160 -0
  135. package/dist/utils/p36-upgrade.js.map +1 -0
  136. package/dist/utils/project-capabilities.d.ts +1 -12
  137. package/dist/utils/project-capabilities.d.ts.map +1 -1
  138. package/dist/utils/project-capabilities.js +3 -100
  139. package/dist/utils/project-capabilities.js.map +1 -1
  140. package/dist/utils/readiness.d.ts +10 -0
  141. package/dist/utils/readiness.d.ts.map +1 -1
  142. package/dist/utils/readiness.js +58 -26
  143. package/dist/utils/readiness.js.map +1 -1
  144. package/dist/utils/retrieval-agent-instructions.js +1 -1
  145. package/dist/utils/retrieval-agent-instructions.js.map +1 -1
  146. package/dist/utils/template-hash.d.ts.map +1 -1
  147. package/dist/utils/template-hash.js +11 -1
  148. package/dist/utils/template-hash.js.map +1 -1
  149. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  150. package/dist/utils/uninstall-scrubbers.js +52 -14
  151. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  152. package/dist/utils/update-rollout-report.d.ts +14 -0
  153. package/dist/utils/update-rollout-report.d.ts.map +1 -1
  154. package/dist/utils/update-rollout-report.js +1 -0
  155. package/dist/utils/update-rollout-report.js.map +1 -1
  156. package/dist/utils/workflow-ownership.d.ts +1 -1
  157. package/dist/utils/workflow-ownership.js +4 -4
  158. package/dist/utils/workflow-ownership.js.map +1 -1
  159. package/package.json +5 -4
  160. package/scripts/postinstall.js +6 -7
  161. package/dist/commands/campaign/canvas-render.d.ts +0 -68
  162. package/dist/commands/campaign/canvas-render.d.ts.map +0 -1
  163. package/dist/commands/campaign/canvas-render.js +0 -414
  164. package/dist/commands/campaign/canvas-render.js.map +0 -1
  165. package/dist/commands/campaign/compose.d.ts +0 -15
  166. package/dist/commands/campaign/compose.d.ts.map +0 -1
  167. package/dist/commands/campaign/compose.js +0 -110
  168. package/dist/commands/campaign/compose.js.map +0 -1
  169. package/dist/commands/campaign/index.d.ts +0 -13
  170. package/dist/commands/campaign/index.d.ts.map +0 -1
  171. package/dist/commands/campaign/index.js +0 -134
  172. package/dist/commands/campaign/index.js.map +0 -1
  173. package/dist/commands/campaign/kind-map.d.ts +0 -6
  174. package/dist/commands/campaign/kind-map.d.ts.map +0 -1
  175. package/dist/commands/campaign/kind-map.js +0 -21
  176. package/dist/commands/campaign/kind-map.js.map +0 -1
  177. package/dist/commands/campaign/mcp-server.d.ts +0 -6
  178. package/dist/commands/campaign/mcp-server.d.ts.map +0 -1
  179. package/dist/commands/campaign/mcp-server.js +0 -167
  180. package/dist/commands/campaign/mcp-server.js.map +0 -1
  181. package/dist/commands/campaign/render.d.ts +0 -5
  182. package/dist/commands/campaign/render.d.ts.map +0 -1
  183. package/dist/commands/campaign/render.js +0 -103
  184. package/dist/commands/campaign/render.js.map +0 -1
  185. package/dist/commands/campaign/trellis-load.d.ts +0 -13
  186. package/dist/commands/campaign/trellis-load.d.ts.map +0 -1
  187. package/dist/commands/campaign/trellis-load.js +0 -122
  188. package/dist/commands/campaign/trellis-load.js.map +0 -1
  189. package/dist/commands/campaign/types.d.ts +0 -61
  190. package/dist/commands/campaign/types.d.ts.map +0 -1
  191. package/dist/commands/campaign/types.js +0 -3
  192. package/dist/commands/campaign/types.js.map +0 -1
  193. package/dist/commands/goal/index.d.ts +0 -3
  194. package/dist/commands/goal/index.d.ts.map +0 -1
  195. package/dist/commands/goal/index.js +0 -159
  196. package/dist/commands/goal/index.js.map +0 -1
  197. package/dist/commands/goal/review.d.ts +0 -6
  198. package/dist/commands/goal/review.d.ts.map +0 -1
  199. package/dist/commands/goal/review.js +0 -28
  200. package/dist/commands/goal/review.js.map +0 -1
  201. package/dist/commands/rpc/client.d.ts +0 -51
  202. package/dist/commands/rpc/client.d.ts.map +0 -1
  203. package/dist/commands/rpc/client.js +0 -147
  204. package/dist/commands/rpc/client.js.map +0 -1
  205. package/dist/commands/rpc/index.d.ts +0 -10
  206. package/dist/commands/rpc/index.d.ts.map +0 -1
  207. package/dist/commands/rpc/index.js +0 -157
  208. package/dist/commands/rpc/index.js.map +0 -1
  209. package/dist/commands/rpc/serve.d.ts +0 -23
  210. package/dist/commands/rpc/serve.d.ts.map +0 -1
  211. package/dist/commands/rpc/serve.js +0 -60
  212. package/dist/commands/rpc/serve.js.map +0 -1
  213. package/dist/commands/rpc/smoke.d.ts +0 -16
  214. package/dist/commands/rpc/smoke.d.ts.map +0 -1
  215. package/dist/commands/rpc/smoke.js +0 -92
  216. package/dist/commands/rpc/smoke.js.map +0 -1
  217. package/dist/commands/sdk/index.d.ts +0 -9
  218. package/dist/commands/sdk/index.d.ts.map +0 -1
  219. package/dist/commands/sdk/index.js +0 -58
  220. package/dist/commands/sdk/index.js.map +0 -1
  221. package/dist/commands/sdk/run.d.ts +0 -42
  222. package/dist/commands/sdk/run.d.ts.map +0 -1
  223. package/dist/commands/sdk/run.js +0 -220
  224. package/dist/commands/sdk/run.js.map +0 -1
  225. package/dist/commands/sdk/status.d.ts +0 -14
  226. package/dist/commands/sdk/status.d.ts.map +0 -1
  227. package/dist/commands/sdk/status.js +0 -69
  228. package/dist/commands/sdk/status.js.map +0 -1
  229. package/dist/configurators/cursor2plus-local.d.ts +0 -6
  230. package/dist/configurators/cursor2plus-local.d.ts.map +0 -1
  231. package/dist/configurators/cursor2plus-local.js +0 -21
  232. package/dist/configurators/cursor2plus-local.js.map +0 -1
  233. package/dist/goal/action-packet.d.ts +0 -89
  234. package/dist/goal/action-packet.d.ts.map +0 -1
  235. package/dist/goal/action-packet.js +0 -113
  236. package/dist/goal/action-packet.js.map +0 -1
  237. package/dist/goal/audit.d.ts +0 -14
  238. package/dist/goal/audit.d.ts.map +0 -1
  239. package/dist/goal/audit.js +0 -12
  240. package/dist/goal/audit.js.map +0 -1
  241. package/dist/goal/constants.d.ts +0 -26
  242. package/dist/goal/constants.d.ts.map +0 -1
  243. package/dist/goal/constants.js +0 -24
  244. package/dist/goal/constants.js.map +0 -1
  245. package/dist/goal/paths.d.ts +0 -8
  246. package/dist/goal/paths.d.ts.map +0 -1
  247. package/dist/goal/paths.js +0 -36
  248. package/dist/goal/paths.js.map +0 -1
  249. package/dist/goal/preflight.d.ts +0 -21
  250. package/dist/goal/preflight.d.ts.map +0 -1
  251. package/dist/goal/preflight.js +0 -97
  252. package/dist/goal/preflight.js.map +0 -1
  253. package/dist/goal/review-seam.d.ts +0 -44
  254. package/dist/goal/review-seam.d.ts.map +0 -1
  255. package/dist/goal/review-seam.js +0 -153
  256. package/dist/goal/review-seam.js.map +0 -1
  257. package/dist/goal/reviewer-rules.d.ts +0 -13
  258. package/dist/goal/reviewer-rules.d.ts.map +0 -1
  259. package/dist/goal/reviewer-rules.js +0 -153
  260. package/dist/goal/reviewer-rules.js.map +0 -1
  261. package/dist/goal/reviewer.d.ts +0 -9
  262. package/dist/goal/reviewer.d.ts.map +0 -1
  263. package/dist/goal/reviewer.js +0 -54
  264. package/dist/goal/reviewer.js.map +0 -1
  265. package/dist/goal/runtime.d.ts +0 -32
  266. package/dist/goal/runtime.d.ts.map +0 -1
  267. package/dist/goal/runtime.js +0 -242
  268. package/dist/goal/runtime.js.map +0 -1
  269. package/dist/goal/sdk-client.d.ts +0 -10
  270. package/dist/goal/sdk-client.d.ts.map +0 -1
  271. package/dist/goal/sdk-client.js +0 -34
  272. package/dist/goal/sdk-client.js.map +0 -1
  273. package/dist/goal/state.d.ts +0 -40
  274. package/dist/goal/state.d.ts.map +0 -1
  275. package/dist/goal/state.js +0 -37
  276. package/dist/goal/state.js.map +0 -1
  277. package/dist/goal/task-triggers.d.ts +0 -15
  278. package/dist/goal/task-triggers.d.ts.map +0 -1
  279. package/dist/goal/task-triggers.js +0 -23
  280. package/dist/goal/task-triggers.js.map +0 -1
  281. package/dist/goal/walls.d.ts +0 -12
  282. package/dist/goal/walls.d.ts.map +0 -1
  283. package/dist/goal/walls.js +0 -65
  284. package/dist/goal/walls.js.map +0 -1
  285. package/dist/goal/worker-prompt.d.ts +0 -13
  286. package/dist/goal/worker-prompt.d.ts.map +0 -1
  287. package/dist/goal/worker-prompt.js +0 -41
  288. package/dist/goal/worker-prompt.js.map +0 -1
  289. package/dist/goal/worker.d.ts +0 -42
  290. package/dist/goal/worker.d.ts.map +0 -1
  291. package/dist/goal/worker.js +0 -180
  292. package/dist/goal/worker.js.map +0 -1
  293. package/dist/templates/common/bundled-skills/cstl-cursor2plus-setup/SKILL.md +0 -63
  294. package/dist/templates/cursor/commands/cursor2plus-setup.md +0 -82
  295. package/dist/templates/cursor/rules/cstl-cursor-modes.mdc +0 -32
  296. package/dist/templates/cursor/rules/cstl-session-rename.mdc +0 -31
  297. package/dist/templates/cursor/rules/cstl-subagent-dispatch.mdc +0 -34
  298. package/dist/templates/cursor/rules/cstl-triage.mdc +0 -36
  299. package/dist/templates/cursor/rules/retrieval-routing.mdc +0 -78
  300. package/dist/templates/trellis/local/README.md +0 -85
  301. package/dist/templates/trellis/local/config.local.json.example +0 -6
  302. package/dist/templates/trellis/local/index.d.ts +0 -8
  303. package/dist/templates/trellis/local/index.d.ts.map +0 -1
  304. package/dist/templates/trellis/local/index.js +0 -15
  305. package/dist/templates/trellis/local/index.js.map +0 -1
  306. package/dist/templates/trellis/local/patch_wpelc8.py +0 -421
  307. package/dist/templates/trellis/local/smoke.py +0 -94
  308. package/dist/templates/trellis/local/subagent-models.json.example +0 -4
  309. package/dist/templates/trellis/local/trellis-task-models.json.example +0 -10
  310. package/dist/templates/trellis/local/trellis-task-models.json5.example +0 -45
  311. package/dist/templates/trellis/local/trellis_task_models_config.py +0 -200
  312. package/dist/utils/cursor-sdk-gate.d.ts +0 -10
  313. package/dist/utils/cursor-sdk-gate.d.ts.map +0 -1
  314. package/dist/utils/cursor-sdk-gate.js +0 -32
  315. package/dist/utils/cursor-sdk-gate.js.map +0 -1
@@ -40,8 +40,17 @@ from .config import (
40
40
  validate_package,
41
41
  )
42
42
  from .cli_environment import format_git_repo_errors, print_environment_repair_hints
43
- from .git import run_git
44
- from .io import read_json, write_json
43
+ from .git import is_git_worktree, run_git
44
+ from .io import read_json
45
+ from .kernel_command import (
46
+ KernelCliNotFound,
47
+ KernelCommandError,
48
+ kernel_archive,
49
+ kernel_create,
50
+ kernel_expected_revision,
51
+ kernel_patch,
52
+ print_kernel_error,
53
+ )
45
54
  from .log import Colors, colored
46
55
  from .paths import (
47
56
  DIR_ARCHIVE,
@@ -62,10 +71,13 @@ from .task_gates import (
62
71
  BASELINE_GATE,
63
72
  archive_repair_hints,
64
73
  build_spec_update_scaffold,
74
+ collect_kernel_projection_extras,
65
75
  prepare_archive_evidence,
76
+ task_closeout_profile,
66
77
  validate_archive,
67
78
  write_gate_record,
68
79
  )
80
+ from .ondemand_topology import TopologyError, assign_parent, default_topology
69
81
  from .task_map import (
70
82
  CHILD_STATES,
71
83
  CHILD_REPORT_STATES,
@@ -345,6 +357,35 @@ def _default_prd_content(
345
357
  return default_prd_content(title, description, locale, root)
346
358
 
347
359
 
360
+ def _kernel_patch_task_json(
361
+ task_dir: Path,
362
+ data: dict,
363
+ *,
364
+ actor: str,
365
+ op: str,
366
+ evidence: str,
367
+ ) -> bool:
368
+ """Persist mutated task.json fields through Kernel patch (not task-map.md)."""
369
+ extras = collect_kernel_projection_extras(data)
370
+ if "depends_on" in data:
371
+ extras["depends_on"] = data["depends_on"]
372
+ try:
373
+ expected = kernel_expected_revision(task_dir)
374
+ kernel_patch(
375
+ task_dir,
376
+ data,
377
+ extras,
378
+ expected_revision=expected,
379
+ actor=actor,
380
+ idempotency_key=f"patch:{op}:{task_dir.name}:r{expected}",
381
+ evidence=evidence,
382
+ )
383
+ return True
384
+ except (KernelCliNotFound, KernelCommandError) as err:
385
+ print_kernel_error(err)
386
+ return False
387
+
388
+
348
389
  # =============================================================================
349
390
  # Command: create
350
391
  # =============================================================================
@@ -445,7 +486,33 @@ def cmd_create(args: argparse.Namespace) -> int:
445
486
  "meta": {},
446
487
  }
447
488
 
448
- write_json(task_json_path, task_data)
489
+ parent_dir = None
490
+ parent_data = None
491
+ if args.parent:
492
+ parent_dir = resolve_task_dir(args.parent, repo_root)
493
+ parent_json_path = parent_dir / FILE_TASK_JSON
494
+ if not parent_json_path.is_file():
495
+ print(colored(f"Warning: Parent task.json not found: {args.parent}", Colors.YELLOW), file=sys.stderr)
496
+ else:
497
+ parent_data = read_json(parent_json_path)
498
+ if parent_data:
499
+ task_data["parent"] = parent_dir.name
500
+
501
+ try:
502
+ kernel_create(
503
+ task_dir,
504
+ task_data,
505
+ actor="task.py create",
506
+ idempotency_key=f"create:{slug}",
507
+ evidence="task.py create",
508
+ )
509
+ except (KernelCliNotFound, KernelCommandError) as err:
510
+ print_kernel_error(err)
511
+ return 1
512
+
513
+ persisted = read_json(task_json_path)
514
+ if persisted:
515
+ task_data = persisted
449
516
 
450
517
  prd_path = task_dir / "prd.md"
451
518
  if not prd_path.exists():
@@ -473,39 +540,35 @@ def cmd_create(args: argparse.Namespace) -> int:
473
540
  _write_seed_jsonl(jsonl_path, repo_root, task_dir, task_data)
474
541
  seeded_jsonl = True
475
542
 
476
- # Handle --parent: establish bidirectional link
477
- if args.parent:
478
- parent_dir = resolve_task_dir(args.parent, repo_root)
479
- parent_json_path = parent_dir / FILE_TASK_JSON
480
- if not parent_json_path.is_file():
481
- print(colored(f"Warning: Parent task.json not found: {args.parent}", Colors.YELLOW), file=sys.stderr)
482
- else:
483
- parent_data = read_json(parent_json_path)
484
- if parent_data:
485
- # Add child to parent's children list
486
- parent_children = parent_data.get("children", [])
487
- if dir_name not in parent_children:
488
- parent_children.append(dir_name)
489
- parent_data["children"] = parent_children
490
- write_json(parent_json_path, parent_data)
491
-
492
- # Set parent in child's task.json
493
- task_data["parent"] = parent_dir.name
494
- write_json(task_json_path, task_data)
495
- ensure_task_map(
496
- parent_dir,
497
- parent_data,
498
- list(parent_data.get("children", [])),
499
- f"Linked Child `{dir_name}`.",
500
- )
501
-
502
- print(colored(f"Linked as child of: {parent_dir.name}", Colors.GREEN), file=sys.stderr)
543
+ # Handle --parent: child's `parent` is in the Kernel create record above;
544
+ # parent `children[]` goes through Kernel patch. task-map.md stays Python.
545
+ if parent_dir is not None and parent_data:
546
+ parent_children = parent_data.get("children", [])
547
+ if dir_name not in parent_children:
548
+ parent_children.append(dir_name)
549
+ parent_data["children"] = parent_children
550
+ if not _kernel_patch_task_json(
551
+ parent_dir,
552
+ parent_data,
553
+ actor="task.py create",
554
+ op="create-parent-link",
555
+ evidence="children[]",
556
+ ):
557
+ return 1
558
+ ensure_task_map(
559
+ parent_dir,
560
+ parent_data,
561
+ list(parent_data.get("children", [])),
562
+ f"Linked Child `{dir_name}`.",
563
+ )
564
+ print(colored(f"Linked as child of: {parent_dir.name}", Colors.GREEN), file=sys.stderr)
503
565
 
504
566
  print(colored(f"Created task: {dir_name}", Colors.GREEN), file=sys.stderr)
505
567
  print("", file=sys.stderr)
506
568
  print(colored("Next steps:", Colors.BLUE), file=sys.stderr)
507
569
  print(" - Fill prd.md with requirements and acceptance criteria", file=sys.stderr)
508
570
  print(f" - Select it when ready: python ./.cstl/scripts/task.py select {DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}", file=sys.stderr)
571
+ print(" - Personal Lite: Definition=prd.md, Evidence=verify.md; start-execution --check is not approval", file=sys.stderr)
509
572
  print(" - Lightweight task: PRD-only is valid", file=sys.stderr)
510
573
  print(" - Complex task: add design.md and implement.md before task.py start-execution --check", file=sys.stderr)
511
574
  if seeded_jsonl:
@@ -636,7 +699,21 @@ def _archive_one_task(
636
699
  write_gate_record(data, "full-task-complete", BASELINE_GATE, guard.baseline_record)
637
700
  data["status"] = "completed"
638
701
  data["completedAt"] = today
639
- write_json(task_json_path, data)
702
+ extras = collect_kernel_projection_extras(data)
703
+ try:
704
+ expected = kernel_expected_revision(task_dir)
705
+ kernel_archive(
706
+ task_dir,
707
+ data,
708
+ extras,
709
+ expected_revision=expected,
710
+ actor="task.py archive",
711
+ idempotency_key=f"archive:{dir_name}:{today}",
712
+ evidence="task.py archive",
713
+ )
714
+ except (KernelCliNotFound, KernelCommandError) as err:
715
+ print_kernel_error(err)
716
+ return False, [], None
640
717
 
641
718
  task_children = data.get("children", [])
642
719
  if task_children:
@@ -648,14 +725,32 @@ def _archive_one_task(
648
725
  child_data = read_json(child_json)
649
726
  if child_data:
650
727
  child_data["parent"] = None
651
- write_json(child_json, child_data)
728
+ if not _kernel_patch_task_json(
729
+ child_dir_path,
730
+ child_data,
731
+ actor="task.py archive",
732
+ op="archive-clear-parent",
733
+ evidence="parent",
734
+ ):
735
+ return False, modified_children, None
652
736
  modified_children.append(child_dir_path.name)
653
737
 
654
738
  from .active_task import clear_task_from_sessions
655
739
 
740
+ profile = task_closeout_profile(task_dir, task_data) if task_data else "lite"
656
741
  clear_task_from_sessions(str(task_dir), repo_root)
657
742
  result = archive_task_complete(task_dir, repo_root)
658
743
  if "archived_to" not in result:
744
+ if profile == "lite":
745
+ print(
746
+ colored(
747
+ "Close Outcome written; physical archive/retention skipped "
748
+ "(not a Lite completion condition).",
749
+ Colors.YELLOW,
750
+ ),
751
+ file=sys.stderr,
752
+ )
753
+ return True, modified_children, f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
659
754
  return False, modified_children, None
660
755
 
661
756
  archive_dest = Path(result["archived_to"])
@@ -667,15 +762,25 @@ def _archive_one_task(
667
762
 
668
763
  if not no_commit:
669
764
  if not _auto_commit_archive(dir_name, repo_root, modified_children):
670
- print(
671
- colored(
672
- "Archive moved on disk, but git auto-commit did not complete. "
673
- "Resolve `git status` before continuing.",
674
- Colors.RED,
675
- ),
676
- file=sys.stderr,
677
- )
678
- return False, modified_children, None
765
+ if profile == "lite":
766
+ print(
767
+ colored(
768
+ "Lite Close Outcome stands; git auto-commit skipped or failed "
769
+ "(VCS is On-demand, not a completion condition).",
770
+ Colors.YELLOW,
771
+ ),
772
+ file=sys.stderr,
773
+ )
774
+ else:
775
+ print(
776
+ colored(
777
+ "Archive moved on disk, but git auto-commit did not complete. "
778
+ "Resolve `git status` before continuing.",
779
+ Colors.RED,
780
+ ),
781
+ file=sys.stderr,
782
+ )
783
+ return False, modified_children, None
679
784
 
680
785
  rel = f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}/{year_month}/{dir_name}"
681
786
  archived_json = archive_dest / FILE_TASK_JSON
@@ -858,6 +963,14 @@ def _auto_commit_archive(
858
963
  set to ``false``, this function returns immediately without
859
964
  touching git (the archive directory move on disk is unaffected).
860
965
  """
966
+ if not is_git_worktree(repo_root):
967
+ print(
968
+ "[OK] no Git worktree — skipping archive auto-commit "
969
+ "(Close Outcome already written).",
970
+ file=sys.stderr,
971
+ )
972
+ return True
973
+
861
974
  if not get_session_auto_commit(repo_root):
862
975
  print(
863
976
  "[OK] session_auto_commit: false — skipping git stage/commit.",
@@ -967,9 +1080,49 @@ def cmd_add_subtask(args: argparse.Namespace) -> int:
967
1080
  # Set parent in child's task.json
968
1081
  child_data["parent"] = parent_dir.name
969
1082
 
970
- # Write both
971
- write_json(parent_json_path, parent_data)
972
- write_json(child_json_path, child_data)
1083
+ parent_id = str(parent_data.get("id") or parent_dir.name)
1084
+ parent_existing = (
1085
+ parent_data.get("topology")
1086
+ if isinstance(parent_data.get("topology"), dict)
1087
+ else {}
1088
+ )
1089
+ parent_data["topology"] = default_topology(
1090
+ parent=parent_existing.get("parent_id") or parent_data.get("parent"),
1091
+ children=parent_children,
1092
+ )
1093
+ child_existing = (
1094
+ child_data.get("topology")
1095
+ if isinstance(child_data.get("topology"), dict)
1096
+ else {}
1097
+ )
1098
+ child_children = list(
1099
+ child_existing.get("children") or child_data.get("children") or []
1100
+ )
1101
+ try:
1102
+ child_data["topology"] = assign_parent(
1103
+ default_topology(parent=None, children=child_children),
1104
+ parent_id,
1105
+ )
1106
+ except TopologyError as err:
1107
+ print(colored(f"Error: {err}", Colors.RED), file=sys.stderr)
1108
+ return 1
1109
+
1110
+ if not _kernel_patch_task_json(
1111
+ parent_dir,
1112
+ parent_data,
1113
+ actor="task.py add-subtask",
1114
+ op="add-subtask",
1115
+ evidence="children[]",
1116
+ ):
1117
+ return 1
1118
+ if not _kernel_patch_task_json(
1119
+ child_dir,
1120
+ child_data,
1121
+ actor="task.py add-subtask",
1122
+ op="add-subtask",
1123
+ evidence="parent",
1124
+ ):
1125
+ return 1
973
1126
  ensure_task_map(
974
1127
  parent_dir,
975
1128
  parent_data,
@@ -1020,9 +1173,41 @@ def cmd_remove_subtask(args: argparse.Namespace) -> int:
1020
1173
  # Clear parent in child's task.json
1021
1174
  child_data["parent"] = None
1022
1175
 
1023
- # Write both
1024
- write_json(parent_json_path, parent_data)
1025
- write_json(child_json_path, child_data)
1176
+ parent_existing = (
1177
+ parent_data.get("topology")
1178
+ if isinstance(parent_data.get("topology"), dict)
1179
+ else {}
1180
+ )
1181
+ parent_data["topology"] = default_topology(
1182
+ parent=parent_existing.get("parent_id") or parent_data.get("parent"),
1183
+ children=parent_children,
1184
+ )
1185
+ child_existing = (
1186
+ child_data.get("topology")
1187
+ if isinstance(child_data.get("topology"), dict)
1188
+ else {}
1189
+ )
1190
+ child_data["topology"] = default_topology(
1191
+ parent=None,
1192
+ children=list(child_existing.get("children") or child_data.get("children") or []),
1193
+ )
1194
+
1195
+ if not _kernel_patch_task_json(
1196
+ parent_dir,
1197
+ parent_data,
1198
+ actor="task.py remove-subtask",
1199
+ op="remove-subtask",
1200
+ evidence="children[]",
1201
+ ):
1202
+ return 1
1203
+ if not _kernel_patch_task_json(
1204
+ child_dir,
1205
+ child_data,
1206
+ actor="task.py remove-subtask",
1207
+ op="remove-subtask",
1208
+ evidence="parent",
1209
+ ):
1210
+ return 1
1026
1211
  remove_child_from_task_map(parent_dir, parent_data, child_dir_name)
1027
1212
 
1028
1213
  print(colored(f"Unlinked: {child_dir.name} from {parent_dir.name}", Colors.GREEN), file=sys.stderr)
@@ -1108,7 +1293,14 @@ def cmd_prepare_child_worktree(args: argparse.Namespace) -> int:
1108
1293
 
1109
1294
  child_data["branch"] = branch
1110
1295
  child_data["worktree_path"] = worktree_rel
1111
- write_json(child_json_path, child_data)
1296
+ if not _kernel_patch_task_json(
1297
+ child_dir,
1298
+ child_data,
1299
+ actor="task.py prepare-child-worktree",
1300
+ op="prepare-child-worktree",
1301
+ evidence="branch,worktree_path",
1302
+ ):
1303
+ return 1
1112
1304
 
1113
1305
  ok, map_errors = record_child_worktree(
1114
1306
  parent_dir,
@@ -1147,7 +1339,7 @@ def append_depends_ignore_event(
1147
1339
  """Append a depends-ignore audit event to meta.depends_ignore_events.
1148
1340
 
1149
1341
  Mutates task_data in memory; the caller persists it in the SAME
1150
- write_json as the mutation it accompanies (execution approval / child
1342
+ Kernel patch as the mutation it accompanies (execution approval / child
1151
1343
  state), so the event can never dangle as a separate stale write. The
1152
1344
  list is capped at MAX_IGNORE_EVENTS (FIFO drops the oldest entry).
1153
1345
  """
@@ -1236,8 +1428,13 @@ def _guard_child_working_deps(
1236
1428
  evidence=args.evidence,
1237
1429
  reason=getattr(args, "reason", None),
1238
1430
  )
1239
- child_json_path = child_dir / FILE_TASK_JSON
1240
- if not write_json(child_json_path, child_data):
1431
+ if not _kernel_patch_task_json(
1432
+ child_dir,
1433
+ child_data,
1434
+ actor="task.py set-child-state",
1435
+ op="depends-ignore",
1436
+ evidence=str(args.evidence or "ignore-deps"),
1437
+ ):
1241
1438
  print(
1242
1439
  colored("Error: failed to write task.json (ignore event)", Colors.RED),
1243
1440
  file=sys.stderr,
@@ -1772,7 +1969,14 @@ def cmd_set_branch(args: argparse.Namespace) -> int:
1772
1969
  return 1
1773
1970
 
1774
1971
  data["branch"] = branch
1775
- write_json(task_json, data)
1972
+ if not _kernel_patch_task_json(
1973
+ target_dir,
1974
+ data,
1975
+ actor="task.py set-branch",
1976
+ op="set-branch",
1977
+ evidence="branch",
1978
+ ):
1979
+ return 1
1776
1980
 
1777
1981
  print(colored(f"✓ Branch set to: {branch}", Colors.GREEN))
1778
1982
  return 0
@@ -1806,7 +2010,14 @@ def cmd_set_base_branch(args: argparse.Namespace) -> int:
1806
2010
  return 1
1807
2011
 
1808
2012
  data["base_branch"] = base_branch
1809
- write_json(task_json, data)
2013
+ if not _kernel_patch_task_json(
2014
+ target_dir,
2015
+ data,
2016
+ actor="task.py set-base-branch",
2017
+ op="set-base-branch",
2018
+ evidence="base_branch",
2019
+ ):
2020
+ return 1
1810
2021
 
1811
2022
  print(colored(f"✓ Base branch set to: {base_branch}", Colors.GREEN))
1812
2023
  print(f" PR will target: {base_branch}")
@@ -1838,7 +2049,14 @@ def cmd_set_scope(args: argparse.Namespace) -> int:
1838
2049
  return 1
1839
2050
 
1840
2051
  data["scope"] = scope
1841
- write_json(task_json, data)
2052
+ if not _kernel_patch_task_json(
2053
+ target_dir,
2054
+ data,
2055
+ actor="task.py set-scope",
2056
+ op="set-scope",
2057
+ evidence="scope",
2058
+ ):
2059
+ return 1
1842
2060
 
1843
2061
  print(colored(f"✓ Scope set to: {scope}", Colors.GREEN))
1844
2062
  return 0
@@ -1880,7 +2098,13 @@ def cmd_set_deps(args: argparse.Namespace) -> int:
1880
2098
  if resolve_dep_ref(ref, repo_root=repo_root).kind == KIND_MISSING
1881
2099
  ]
1882
2100
 
1883
- if not write_json(task_json, data):
2101
+ if not _kernel_patch_task_json(
2102
+ target_dir,
2103
+ data,
2104
+ actor="task.py set-deps",
2105
+ op="set-deps",
2106
+ evidence="depends_on",
2107
+ ):
1884
2108
  print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
1885
2109
  return 1
1886
2110
 
@@ -1950,7 +2174,13 @@ def cmd_set_depends_mode(args: argparse.Namespace) -> int:
1950
2174
  data["meta"] = meta
1951
2175
  meta["depends_mode"] = args.mode
1952
2176
 
1953
- if not write_json(task_json, data):
2177
+ if not _kernel_patch_task_json(
2178
+ target_dir,
2179
+ data,
2180
+ actor="task.py set-depends-mode",
2181
+ op="set-depends-mode",
2182
+ evidence="meta.depends_mode",
2183
+ ):
1954
2184
  print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
1955
2185
  return 1
1956
2186
 
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ """Stage 6 Event Bridge / Middleware Python projection."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
12
+
13
+ from common.adapter_middleware import (
14
+ EXTERNAL_KNOWLEDGE_CAPABILITY,
15
+ RETRIEVAL_INTENTS,
16
+ AdapterMiddlewareError,
17
+ assert_external_knowledge,
18
+ dispatch_hook_event,
19
+ normalize_capability_router,
20
+ probe_smart_search_readiness,
21
+ )
22
+
23
+
24
+ def test_dispatch_skips_unsubscribed_modules() -> None:
25
+ last = dispatch_hook_event(
26
+ {
27
+ "subscriptions": [
28
+ {"event": "sessionStart", "module": "observability-local"},
29
+ {"event": "stop", "module": "retrieval-extended"},
30
+ ]
31
+ },
32
+ "sessionStart",
33
+ source="cursor-hooks",
34
+ )
35
+ assert last["delivered"] == ["observability-local"]
36
+ assert last["skipped"] == ["retrieval-extended"]
37
+
38
+
39
+ def test_missing_smart_search_is_not_ready() -> None:
40
+ readiness = probe_smart_search_readiness(available=False)
41
+ assert readiness["status"] == "missing"
42
+ assert readiness["capability"] == EXTERNAL_KNOWLEDGE_CAPABILITY
43
+ extras = {"required_capabilities": [EXTERNAL_KNOWLEDGE_CAPABILITY]}
44
+ with pytest.raises(AdapterMiddlewareError, match="not ready"):
45
+ assert_external_knowledge(extras, readiness, phase="start")
46
+ extras["external_knowledge_policy"] = "degrade"
47
+ assert_external_knowledge(extras, readiness, phase="start")
48
+ assert extras["profile_health"] == "degraded"
49
+ assert_external_knowledge({}, readiness, phase="archive")
50
+
51
+
52
+ def test_capability_router_rejects_optional_tool_names() -> None:
53
+ router = normalize_capability_router(None)
54
+ assert tuple(key for key in RETRIEVAL_INTENTS if router.get(key)) == RETRIEVAL_INTENTS
55
+ with pytest.raises(AdapterMiddlewareError, match="Optional tool"):
56
+ normalize_capability_router({"codegraph": True})
@@ -25,6 +25,8 @@ from common.task_dependencies import (
25
25
  read_depends_mode,
26
26
  )
27
27
  from common.task_gates import validate_start_execution, validate_start_execution_check
28
+ from common import kernel_command as kernel_command_mod
29
+ from common.test_kernel_command import FAKE_KERNEL
28
30
 
29
31
 
30
32
  # =============================================================================
@@ -69,6 +71,13 @@ def sandbox(tmp_path, monkeypatch):
69
71
  tasks_dir.mkdir(parents=True)
70
72
  monkeypatch.setattr("common.task_dependencies.get_repo_root", lambda: repo)
71
73
  monkeypatch.setattr("common.task_store.get_repo_root", lambda: repo)
74
+ script = tmp_path / "fake_kernel.py"
75
+ script.write_text(FAKE_KERNEL, encoding="utf-8")
76
+ monkeypatch.setattr(
77
+ kernel_command_mod,
78
+ "kernel_cli_argv",
79
+ lambda: [sys.executable, str(script)],
80
+ )
72
81
  return repo, tasks_dir
73
82
 
74
83