@pennixrv/trellis 0.7.0-beta.5 → 0.7.0-beta.50

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 (219) hide show
  1. package/dist/cli/index.d.ts.map +1 -1
  2. package/dist/cli/index.js +47 -3
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/backup.d.ts +25 -0
  5. package/dist/commands/backup.d.ts.map +1 -0
  6. package/dist/commands/backup.js +152 -0
  7. package/dist/commands/backup.js.map +1 -0
  8. package/dist/commands/channel/adapters/claude.d.ts.map +1 -1
  9. package/dist/commands/channel/adapters/claude.js +2 -0
  10. package/dist/commands/channel/adapters/claude.js.map +1 -1
  11. package/dist/commands/channel/adapters/codex.d.ts +5 -1
  12. package/dist/commands/channel/adapters/codex.d.ts.map +1 -1
  13. package/dist/commands/channel/adapters/codex.js +19 -9
  14. package/dist/commands/channel/adapters/codex.js.map +1 -1
  15. package/dist/commands/channel/adapters/index.d.ts +3 -0
  16. package/dist/commands/channel/adapters/index.d.ts.map +1 -1
  17. package/dist/commands/channel/adapters/index.js +3 -3
  18. package/dist/commands/channel/adapters/index.js.map +1 -1
  19. package/dist/commands/channel/adapters/types.d.ts +2 -0
  20. package/dist/commands/channel/adapters/types.d.ts.map +1 -1
  21. package/dist/commands/channel/guard.d.ts.map +1 -1
  22. package/dist/commands/channel/guard.js +38 -29
  23. package/dist/commands/channel/guard.js.map +1 -1
  24. package/dist/commands/channel/index.d.ts.map +1 -1
  25. package/dist/commands/channel/index.js +52 -16
  26. package/dist/commands/channel/index.js.map +1 -1
  27. package/dist/commands/channel/kill.d.ts.map +1 -1
  28. package/dist/commands/channel/kill.js +37 -15
  29. package/dist/commands/channel/kill.js.map +1 -1
  30. package/dist/commands/channel/messages.d.ts.map +1 -1
  31. package/dist/commands/channel/messages.js +3 -2
  32. package/dist/commands/channel/messages.js.map +1 -1
  33. package/dist/commands/channel/process-identity.d.ts +3 -0
  34. package/dist/commands/channel/process-identity.d.ts.map +1 -0
  35. package/dist/commands/channel/process-identity.js +24 -0
  36. package/dist/commands/channel/process-identity.js.map +1 -0
  37. package/dist/commands/channel/profiles.d.ts +26 -0
  38. package/dist/commands/channel/profiles.d.ts.map +1 -0
  39. package/dist/commands/channel/profiles.js +186 -0
  40. package/dist/commands/channel/profiles.js.map +1 -0
  41. package/dist/commands/channel/rm.d.ts +0 -1
  42. package/dist/commands/channel/rm.d.ts.map +1 -1
  43. package/dist/commands/channel/rm.js +203 -96
  44. package/dist/commands/channel/rm.js.map +1 -1
  45. package/dist/commands/channel/spawn.d.ts +3 -0
  46. package/dist/commands/channel/spawn.d.ts.map +1 -1
  47. package/dist/commands/channel/spawn.js +43 -15
  48. package/dist/commands/channel/spawn.js.map +1 -1
  49. package/dist/commands/channel/supervisor/idle.d.ts +2 -0
  50. package/dist/commands/channel/supervisor/idle.d.ts.map +1 -1
  51. package/dist/commands/channel/supervisor/idle.js +15 -7
  52. package/dist/commands/channel/supervisor/idle.js.map +1 -1
  53. package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -1
  54. package/dist/commands/channel/supervisor/inbox.js +48 -33
  55. package/dist/commands/channel/supervisor/inbox.js.map +1 -1
  56. package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -1
  57. package/dist/commands/channel/supervisor/stdout.js +6 -3
  58. package/dist/commands/channel/supervisor/stdout.js.map +1 -1
  59. package/dist/commands/channel/supervisor/warning.d.ts.map +1 -1
  60. package/dist/commands/channel/supervisor/warning.js +2 -3
  61. package/dist/commands/channel/supervisor/warning.js.map +1 -1
  62. package/dist/commands/channel/supervisor.d.ts +12 -0
  63. package/dist/commands/channel/supervisor.d.ts.map +1 -1
  64. package/dist/commands/channel/supervisor.js +88 -16
  65. package/dist/commands/channel/supervisor.js.map +1 -1
  66. package/dist/commands/channel/wait.d.ts +3 -1
  67. package/dist/commands/channel/wait.d.ts.map +1 -1
  68. package/dist/commands/channel/wait.js +59 -14
  69. package/dist/commands/channel/wait.js.map +1 -1
  70. package/dist/commands/init.d.ts.map +1 -1
  71. package/dist/commands/init.js +23 -10
  72. package/dist/commands/init.js.map +1 -1
  73. package/dist/commands/update.d.ts.map +1 -1
  74. package/dist/commands/update.js +446 -333
  75. package/dist/commands/update.js.map +1 -1
  76. package/dist/commands/workflow.d.ts +1 -0
  77. package/dist/commands/workflow.d.ts.map +1 -1
  78. package/dist/commands/workflow.js +56 -1
  79. package/dist/commands/workflow.js.map +1 -1
  80. package/dist/configurators/codex.d.ts +6 -0
  81. package/dist/configurators/codex.d.ts.map +1 -1
  82. package/dist/configurators/codex.js +93 -0
  83. package/dist/configurators/codex.js.map +1 -1
  84. package/dist/configurators/shared.js +2 -2
  85. package/dist/configurators/shared.js.map +1 -1
  86. package/dist/constants/paths.d.ts +2 -0
  87. package/dist/constants/paths.d.ts.map +1 -1
  88. package/dist/constants/paths.js +2 -0
  89. package/dist/constants/paths.js.map +1 -1
  90. package/dist/migrations/manifests/0.7.0-beta.10.json +9 -0
  91. package/dist/migrations/manifests/0.7.0-beta.11.json +9 -0
  92. package/dist/migrations/manifests/0.7.0-beta.12.json +9 -0
  93. package/dist/migrations/manifests/0.7.0-beta.13.json +9 -0
  94. package/dist/migrations/manifests/0.7.0-beta.14.json +9 -0
  95. package/dist/migrations/manifests/0.7.0-beta.15.json +9 -0
  96. package/dist/migrations/manifests/0.7.0-beta.16.json +9 -0
  97. package/dist/migrations/manifests/0.7.0-beta.17.json +9 -0
  98. package/dist/migrations/manifests/0.7.0-beta.18.json +9 -0
  99. package/dist/migrations/manifests/0.7.0-beta.19.json +9 -0
  100. package/dist/migrations/manifests/0.7.0-beta.20.json +9 -0
  101. package/dist/migrations/manifests/0.7.0-beta.21.json +17 -0
  102. package/dist/migrations/manifests/0.7.0-beta.22.json +9 -0
  103. package/dist/migrations/manifests/0.7.0-beta.23.json +9 -0
  104. package/dist/migrations/manifests/0.7.0-beta.24.json +9 -0
  105. package/dist/migrations/manifests/0.7.0-beta.25.json +9 -0
  106. package/dist/migrations/manifests/0.7.0-beta.26.json +9 -0
  107. package/dist/migrations/manifests/0.7.0-beta.27.json +9 -0
  108. package/dist/migrations/manifests/0.7.0-beta.28.json +9 -0
  109. package/dist/migrations/manifests/0.7.0-beta.29.json +9 -0
  110. package/dist/migrations/manifests/0.7.0-beta.30.json +9 -0
  111. package/dist/migrations/manifests/0.7.0-beta.31.json +9 -0
  112. package/dist/migrations/manifests/0.7.0-beta.32.json +9 -0
  113. package/dist/migrations/manifests/0.7.0-beta.33.json +9 -0
  114. package/dist/migrations/manifests/0.7.0-beta.34.json +9 -0
  115. package/dist/migrations/manifests/0.7.0-beta.35.json +9 -0
  116. package/dist/migrations/manifests/0.7.0-beta.36.json +9 -0
  117. package/dist/migrations/manifests/0.7.0-beta.37.json +9 -0
  118. package/dist/migrations/manifests/0.7.0-beta.38.json +9 -0
  119. package/dist/migrations/manifests/0.7.0-beta.39.json +9 -0
  120. package/dist/migrations/manifests/0.7.0-beta.40.json +9 -0
  121. package/dist/migrations/manifests/0.7.0-beta.41.json +9 -0
  122. package/dist/migrations/manifests/0.7.0-beta.42.json +9 -0
  123. package/dist/migrations/manifests/0.7.0-beta.43.json +9 -0
  124. package/dist/migrations/manifests/0.7.0-beta.44.json +9 -0
  125. package/dist/migrations/manifests/0.7.0-beta.45.json +9 -0
  126. package/dist/migrations/manifests/0.7.0-beta.46.json +9 -0
  127. package/dist/migrations/manifests/0.7.0-beta.47.json +9 -0
  128. package/dist/migrations/manifests/0.7.0-beta.48.json +9 -0
  129. package/dist/migrations/manifests/0.7.0-beta.49.json +13 -0
  130. package/dist/migrations/manifests/0.7.0-beta.50.json +12 -0
  131. package/dist/migrations/manifests/0.7.0-beta.6.json +9 -0
  132. package/dist/migrations/manifests/0.7.0-beta.7.json +9 -0
  133. package/dist/migrations/manifests/0.7.0-beta.8.json +9 -0
  134. package/dist/migrations/manifests/0.7.0-beta.9.json +9 -0
  135. package/dist/templates/codex/hooks/session-start.py +21 -17
  136. package/dist/templates/common/bundled-skills/trellis-channel/SKILL.md +3 -1
  137. package/dist/templates/common/bundled-skills/trellis-channel/references/command-reference.md +24 -16
  138. package/dist/templates/common/bundled-skills/trellis-channel/references/forum.md +4 -12
  139. package/dist/templates/common/bundled-skills/trellis-channel/references/multi-target-dispatch.md +139 -0
  140. package/dist/templates/common/bundled-skills/trellis-channel/references/progress-debugging.md +9 -5
  141. package/dist/templates/common/bundled-skills/trellis-channel/references/subnode-work.md +84 -14
  142. package/dist/templates/common/bundled-skills/trellis-channel/references/workers.md +9 -11
  143. package/dist/templates/common/bundled-skills/trellis-channel/references/workflows.md +25 -14
  144. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +13 -11
  145. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +7 -2
  146. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +6 -1
  147. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +6 -4
  148. package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +42 -3
  149. package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +1 -1
  150. package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +10 -2
  151. package/dist/templates/common/commands/continue.md +36 -8
  152. package/dist/templates/common/commands/finish-work.md +17 -2
  153. package/dist/templates/common/commands/start.md +8 -1
  154. package/dist/templates/common/skills/brainstorm.md +49 -22
  155. package/dist/templates/common/skills/break-loop.md +6 -10
  156. package/dist/templates/common/skills/check.md +34 -5
  157. package/dist/templates/copilot/hooks/session-start.py +19 -15
  158. package/dist/templates/copilot/prompts/brainstorm.prompt.md +27 -21
  159. package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +22 -22
  160. package/dist/templates/markdown/spec/backend/directory-structure.md.txt +20 -20
  161. package/dist/templates/markdown/spec/backend/error-handling.md.txt +22 -22
  162. package/dist/templates/markdown/spec/backend/index.md.txt +19 -19
  163. package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +22 -22
  164. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +22 -22
  165. package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +25 -25
  166. package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +20 -20
  167. package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +22 -22
  168. package/dist/templates/markdown/spec/frontend/index.md.txt +20 -20
  169. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +22 -22
  170. package/dist/templates/markdown/spec/frontend/state-management.md.txt +22 -22
  171. package/dist/templates/markdown/spec/frontend/type-safety.md.txt +22 -22
  172. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +80 -85
  173. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +164 -209
  174. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +161 -185
  175. package/dist/templates/markdown/spec/guides/index.md.txt +51 -51
  176. package/dist/templates/markdown/workspace-index.md +44 -44
  177. package/dist/templates/opencode/lib/session-utils.js +2 -2
  178. package/dist/templates/shared-hooks/inject-shell-session-context.py +3 -6
  179. package/dist/templates/shared-hooks/inject-workflow-state.py +3 -2
  180. package/dist/templates/shared-hooks/session-start.py +23 -18
  181. package/dist/templates/trellis/agents/subnode-profiles.json +13 -0
  182. package/dist/templates/trellis/agents/subnode.md +11 -5
  183. package/dist/templates/trellis/config.yaml +2 -0
  184. package/dist/templates/trellis/index.d.ts +2 -1
  185. package/dist/templates/trellis/index.d.ts.map +1 -1
  186. package/dist/templates/trellis/index.js +4 -2
  187. package/dist/templates/trellis/index.js.map +1 -1
  188. package/dist/templates/trellis/scripts/add_session.py +35 -32
  189. package/dist/templates/trellis/scripts/common/active_task.py +8 -8
  190. package/dist/templates/trellis/scripts/common/developer.py +18 -18
  191. package/dist/templates/trellis/scripts/common/ownership_record.py +13 -8
  192. package/dist/templates/trellis/scripts/common/safe_commit.py +38 -9
  193. package/dist/templates/trellis/scripts/common/task_planning.py +137 -0
  194. package/dist/templates/trellis/scripts/common/task_store.py +46 -14
  195. package/dist/templates/trellis/scripts/common/workflow_phase.py +4 -3
  196. package/dist/templates/trellis/scripts/subnode_artifact.py +470 -21
  197. package/dist/templates/trellis/scripts/task.py +175 -2
  198. package/dist/templates/trellis/workflow.md +407 -388
  199. package/dist/utils/managed-paths.d.ts +2 -0
  200. package/dist/utils/managed-paths.d.ts.map +1 -1
  201. package/dist/utils/managed-paths.js +24 -2
  202. package/dist/utils/managed-paths.js.map +1 -1
  203. package/dist/utils/template-fetcher.d.ts +1 -0
  204. package/dist/utils/template-fetcher.d.ts.map +1 -1
  205. package/dist/utils/template-fetcher.js +2 -1
  206. package/dist/utils/template-fetcher.js.map +1 -1
  207. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  208. package/dist/utils/uninstall-scrubbers.js +5 -0
  209. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  210. package/dist/utils/workflow-provenance.d.ts +24 -0
  211. package/dist/utils/workflow-provenance.d.ts.map +1 -0
  212. package/dist/utils/workflow-provenance.js +71 -0
  213. package/dist/utils/workflow-provenance.js.map +1 -0
  214. package/dist/utils/workflow-resolver.d.ts +9 -2
  215. package/dist/utils/workflow-resolver.d.ts.map +1 -1
  216. package/dist/utils/workflow-resolver.js +33 -7
  217. package/dist/utils/workflow-resolver.js.map +1 -1
  218. package/package.json +2 -2
  219. package/dist/templates/trellis/agents/subnode.env +0 -4
@@ -8,7 +8,11 @@ Usage:
8
8
  python3 task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
9
9
  python3 task.py validate <dir> # Validate jsonl files
10
10
  python3 task.py list-context <dir> # List jsonl entries
11
- python3 task.py start <dir> # Set active task, record current branch
11
+ python3 task.py select <dir> # Select an existing task without starting it
12
+ python3 task.py plan seal <dir> # Seal the current material plan
13
+ python3 task.py plan approve <dir> --revision N --basis "approval"
14
+ python3 task.py start <dir> # Start an approved task, record current branch
15
+ python3 task.py replan <dir> "<reason>" # Return an in-progress task to planning
12
16
  python3 task.py current [--source] [--json] # Show active task
13
17
  python3 task.py ownership <operation> ... # Manage formal handoff task ownership
14
18
  python3 task.py finish # Clear active task
@@ -29,7 +33,9 @@ from __future__ import annotations
29
33
 
30
34
  import argparse
31
35
  import json
36
+ import os
32
37
  import sys
38
+ from datetime import datetime, timezone
33
39
  from pathlib import Path
34
40
 
35
41
  from common.log import Colors, colored
@@ -56,7 +62,10 @@ from common.io import (
56
62
  read_json_checked,
57
63
  write_json,
58
64
  )
59
- from common.task_utils import resolve_task_dir, run_task_hooks
65
+ from common.task_utils import is_within_tasks_dir, resolve_task_dir, run_task_hooks
66
+ from common.task_planning import (
67
+ PlanningError, approve_plan, invalidate_plan, require_start_approval, seal_plan,
68
+ )
60
69
  from common.tasks import iter_active_tasks, children_progress
61
70
  from common.workflow_selection import WORKFLOW_ID_RE, workflow_md_for_task
62
71
 
@@ -191,6 +200,58 @@ def _record_start_state(
191
200
  )
192
201
 
193
202
 
203
+ def _active_task_target(task_input: str, repo_root: Path) -> tuple[Path, dict]:
204
+ """Resolve a live task and enforce the native mutation boundary."""
205
+ target = resolve_task_dir(task_input, repo_root)
206
+ if target is None or not is_within_tasks_dir(target, repo_root):
207
+ raise PlanningError("target must be a direct active task directory")
208
+ task_json = target / FILE_TASK_JSON
209
+ if task_json.is_symlink():
210
+ raise PlanningError("refusing symlinked task.json")
211
+ data, reason = read_json_checked(task_json)
212
+ if data is None:
213
+ raise PlanningError(describe_json_read_failure(task_json, reason)[0])
214
+ if data.get("status") not in {"planning", "in_progress"}:
215
+ raise PlanningError("target task must be planning or in_progress")
216
+ assert_task_mutation_allowed(repo_root, target)
217
+ return target, data
218
+
219
+
220
+ def cmd_select(args: argparse.Namespace) -> int:
221
+ """Bind context only; Channel drain is owned by the coordinator procedure."""
222
+ repo_root = get_repo_root()
223
+ try:
224
+ target, _data = _active_task_target(args.dir, repo_root)
225
+ active = set_active_task(target.relative_to(repo_root.resolve()).as_posix(), repo_root)
226
+ if not active:
227
+ raise PlanningError("failed to select current task")
228
+ except (PlanningError, OwnershipError, OSError, ValueError) as exc:
229
+ print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
230
+ return 1
231
+ print(colored(f"✓ Task selected without starting: {active.task_path}", Colors.GREEN))
232
+ return 0
233
+
234
+
235
+ def cmd_plan(args: argparse.Namespace) -> int:
236
+ repo_root = get_repo_root()
237
+ try:
238
+ target, data = _active_task_target(args.dir, repo_root)
239
+ if data.get("status") != "planning":
240
+ raise PlanningError("plan seal/approve requires planning; use replan for a material change")
241
+ if args.plan_command == "seal":
242
+ revision = seal_plan(data, target)
243
+ else:
244
+ approve_plan(data, target, args.revision, args.basis)
245
+ revision = args.revision
246
+ if not write_json(target / FILE_TASK_JSON, data):
247
+ raise PlanningError("could not write planning record")
248
+ except (PlanningError, OwnershipError, OSError, ValueError) as exc:
249
+ print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
250
+ return 1
251
+ print(colored(f"✓ Plan {args.plan_command}: revision {revision}", Colors.GREEN))
252
+ return 0
253
+
254
+
194
255
  def cmd_start(args: argparse.Namespace) -> int:
195
256
  """Set active task."""
196
257
  repo_root = get_repo_root()
@@ -220,6 +281,19 @@ def cmd_start(args: argparse.Namespace) -> int:
220
281
  print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
221
282
  return 2
222
283
 
284
+ # Reject before writing either the session pointer or task status. The
285
+ # manifest override below never supplies implementation authorization.
286
+ try:
287
+ if not is_within_tasks_dir(full_path, repo_root) or (full_path / FILE_TASK_JSON).is_symlink():
288
+ raise PlanningError("start requires a direct active task with regular task.json")
289
+ task_data, reason = read_json_checked(full_path / FILE_TASK_JSON)
290
+ if task_data is None:
291
+ raise PlanningError(describe_json_read_failure(full_path / FILE_TASK_JSON, reason)[0])
292
+ require_start_approval(task_data, full_path)
293
+ except (PlanningError, OSError, ValueError) as exc:
294
+ print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
295
+ return 1
296
+
223
297
  # Context-manifest gate (#573): a seeded-but-uncurated implement/check
224
298
  # manifest means every sub-agent dispatched for this task runs with zero
225
299
  # spec context, and nothing downstream surfaces that to the main session.
@@ -301,6 +375,80 @@ def cmd_start(args: argparse.Namespace) -> int:
301
375
  return 1
302
376
 
303
377
 
378
+ def cmd_replan(args: argparse.Namespace) -> int:
379
+ """Return an in-progress task to planning without losing its binding."""
380
+ repo_root = get_repo_root()
381
+ full_path = resolve_task_dir(args.dir, repo_root)
382
+ if full_path is None or not full_path.is_dir():
383
+ print(colored(f"Error: Task not found: {args.dir}", Colors.RED), file=sys.stderr)
384
+ return 1
385
+
386
+ try:
387
+ assert_task_mutation_allowed(repo_root, full_path)
388
+ except (OwnershipError, OSError) as exc:
389
+ print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
390
+ return 2
391
+
392
+ reason = " ".join(args.reason).strip()
393
+ if not reason:
394
+ print(colored("Error: replan reason must not be empty", Colors.RED), file=sys.stderr)
395
+ return 1
396
+
397
+ task_json_path = full_path / FILE_TASK_JSON
398
+ data, read_reason = read_json_checked(task_json_path)
399
+ if data is None:
400
+ problem, hint = describe_json_read_failure(task_json_path, read_reason)
401
+ print(colored(f"Error: {problem}", Colors.RED), file=sys.stderr)
402
+ print(hint, file=sys.stderr)
403
+ return 1
404
+ if data.get("status") != "in_progress":
405
+ print(
406
+ colored(
407
+ f"Error: replan requires status=in_progress (found {data.get('status')!r})",
408
+ Colors.RED,
409
+ ),
410
+ file=sys.stderr,
411
+ )
412
+ return 1
413
+
414
+ try:
415
+ invalidate_plan(data)
416
+ except PlanningError as exc:
417
+ print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
418
+ return 1
419
+
420
+ event = {
421
+ "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
422
+ "task": full_path.relative_to(repo_root).as_posix(),
423
+ "from_status": "in_progress",
424
+ "reason": reason,
425
+ "branch": data.get("branch"),
426
+ }
427
+ context_key = resolve_context_key()
428
+ if context_key:
429
+ event["session"] = context_key
430
+
431
+ replans_path = full_path / "replans.jsonl"
432
+ try:
433
+ with replans_path.open("a", encoding="utf-8") as handle:
434
+ handle.write(json.dumps(event, ensure_ascii=False) + "\n")
435
+ handle.flush()
436
+ os.fsync(handle.fileno())
437
+ except OSError as exc:
438
+ print(colored(f"Error: could not record replan: {exc}", Colors.RED), file=sys.stderr)
439
+ return 1
440
+
441
+ data["status"] = "planning"
442
+ if not write_json(task_json_path, data):
443
+ print(colored("Error: replan event recorded but task status was not changed", Colors.RED), file=sys.stderr)
444
+ return 1
445
+
446
+ print(colored(f"✓ Task returned to planning: {full_path.relative_to(repo_root)}", Colors.GREEN))
447
+ print("Reason:", reason)
448
+ run_task_hooks("after_replan", task_json_path, repo_root)
449
+ return 0
450
+
451
+
304
452
  def cmd_finish(args: argparse.Namespace) -> int:
305
453
  """Clear active task."""
306
454
  repo_root = get_repo_root()
@@ -373,6 +521,7 @@ def cmd_current(args: argparse.Namespace) -> int:
373
521
  payload = {
374
522
  "current_task": task_obj,
375
523
  "source": active.source,
524
+ "session_source": f"session:{active.context_key}" if active.context_key else None,
376
525
  "stale": active.stale,
377
526
  }
378
527
  if active.candidate_paths:
@@ -731,6 +880,9 @@ def show_usage() -> None:
731
880
 
732
881
  Usage:
733
882
  python3 task.py create <title> --description <desc> Create new task directory (both required, non-empty)
883
+ python3 task.py select <dir> Select context without starting
884
+ python3 task.py plan seal <dir> Seal the current material plan
885
+ python3 task.py plan approve <dir> --revision N --basis "approval"
734
886
  python3 task.py create <title> --description <desc> --package <pkg> Create task for a specific package
735
887
  python3 task.py create <title> --description <desc> --parent <dir> Create task as child of parent
736
888
  python3 task.py create <title> --description <desc> --no-start Create without making it active in this session
@@ -739,6 +891,7 @@ Usage:
739
891
  python3 task.py validate <dir> Validate jsonl files
740
892
  python3 task.py list-context <dir> List jsonl entries
741
893
  python3 task.py start <dir> Set active task; records the checked-out branch when unset
894
+ python3 task.py replan <dir> "<reason>" Return an in-progress task to planning
742
895
  python3 task.py current [--source] Show active task
743
896
  python3 task.py finish Clear active task
744
897
  python3 task.py workflow <id> Select workflow variant for active task
@@ -891,6 +1044,18 @@ def main() -> int:
891
1044
  p_listctx = subparsers.add_parser("list-context", help="List context entries")
892
1045
  p_listctx.add_argument("dir", help="Task directory")
893
1046
 
1047
+ p_select = subparsers.add_parser("select", help="Select a task without changing its phase")
1048
+ p_select.add_argument("dir", help="Existing active task directory")
1049
+
1050
+ p_plan = subparsers.add_parser("plan", help="Seal or record later approval of a material plan")
1051
+ plan_sub = p_plan.add_subparsers(dest="plan_command", required=True)
1052
+ p_seal = plan_sub.add_parser("seal", help="Seal the current material plan revision")
1053
+ p_seal.add_argument("dir", help="Planning task directory")
1054
+ p_approve = plan_sub.add_parser("approve", help="Record actual later user approval")
1055
+ p_approve.add_argument("dir", help="Planning task directory")
1056
+ p_approve.add_argument("--revision", type=int, required=True)
1057
+ p_approve.add_argument("--basis", required=True, help="Short non-sensitive basis for real approval")
1058
+
894
1059
  # start
895
1060
  p_start = subparsers.add_parser("start", help="Set active task")
896
1061
  p_start.add_argument("dir", help="Task directory")
@@ -900,6 +1065,11 @@ def main() -> int:
900
1065
  help="Start even when implement.jsonl / check.jsonl have no curated entries",
901
1066
  )
902
1067
 
1068
+ # replan
1069
+ p_replan = subparsers.add_parser("replan", help="Return an in-progress task to planning")
1070
+ p_replan.add_argument("dir", help="Task directory")
1071
+ p_replan.add_argument("reason", nargs="+", help="Material reason for returning to planning")
1072
+
903
1073
  # current
904
1074
  p_current = subparsers.add_parser("current", help="Show active task")
905
1075
  p_current.add_argument("--source", action="store_true",
@@ -1046,7 +1216,10 @@ def main() -> int:
1046
1216
  "add-context": cmd_add_context,
1047
1217
  "validate": cmd_validate,
1048
1218
  "list-context": cmd_list_context,
1219
+ "select": cmd_select,
1220
+ "plan": cmd_plan,
1049
1221
  "start": cmd_start,
1222
+ "replan": cmd_replan,
1050
1223
  "current": cmd_current,
1051
1224
  "continuity": cmd_continuity,
1052
1225
  "ownership": cmd_ownership,