taskledger 0.3.0__tar.gz → 0.3.1__tar.gz

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 (213) hide show
  1. {taskledger-0.3.0 → taskledger-0.3.1}/.taskledger.toml +1 -1
  2. {taskledger-0.3.0 → taskledger-0.3.1}/API.md +3 -0
  3. {taskledger-0.3.0 → taskledger-0.3.1}/CHANGELOG.md +63 -0
  4. {taskledger-0.3.0/taskledger.egg-info → taskledger-0.3.1}/PKG-INFO +21 -1
  5. {taskledger-0.3.0 → taskledger-0.3.1}/README.md +20 -0
  6. {taskledger-0.3.0 → taskledger-0.3.1}/docs/api.rst +3 -0
  7. {taskledger-0.3.0 → taskledger-0.3.1}/docs/command_contract.rst +30 -0
  8. {taskledger-0.3.0 → taskledger-0.3.1}/docs/public_surface.rst +1 -1
  9. {taskledger-0.3.0 → taskledger-0.3.1}/docs/transfer.rst +32 -0
  10. {taskledger-0.3.0 → taskledger-0.3.1}/docs/usage.rst +25 -2
  11. {taskledger-0.3.0 → taskledger-0.3.1}/pyproject.toml +1 -1
  12. {taskledger-0.3.0 → taskledger-0.3.1}/skills/taskledger/SKILL.md +6 -2
  13. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/_version.py +3 -3
  14. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/plans.py +8 -0
  15. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/project.py +7 -0
  16. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli.py +97 -5
  17. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_implement.py +5 -1
  18. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_plan.py +84 -3
  19. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/command_inventory.py +9 -0
  20. taskledger-0.3.1/taskledger/domain/check.py +111 -0
  21. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/models.py +1 -0
  22. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/run.py +3 -0
  23. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/exchange.py +415 -22
  24. taskledger-0.3.1/taskledger/services/check_tracking.py +126 -0
  25. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/dashboard.py +43 -5
  26. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/handoff.py +40 -5
  27. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/implementation_flow.py +13 -7
  28. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/navigation.py +12 -9
  29. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/plan_materialization.py +4 -0
  30. taskledger-0.3.1/taskledger/services/plan_review.py +429 -0
  31. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/planning_flow.py +4 -0
  32. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/serve_read_model.py +4 -0
  33. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/task_reports.py +70 -2
  34. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/tasks.py +12 -9
  35. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/task_store.py +42 -0
  36. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/web_assets/dashboard.js +41 -0
  37. {taskledger-0.3.0 → taskledger-0.3.1/taskledger.egg-info}/PKG-INFO +21 -1
  38. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger.egg-info/SOURCES.txt +5 -0
  39. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_command_inventory.py +8 -0
  40. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_docs_and_skill.py +4 -0
  41. taskledger-0.3.1/tests/test_implementation_checks.py +349 -0
  42. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_no_log_feature.py +6 -0
  43. taskledger-0.3.1/tests/test_plan_review.py +267 -0
  44. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_plan_revision_workflow.py +6 -0
  45. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_service_boundaries.py +2 -2
  46. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_taskledger_v2_cli.py +22 -3
  47. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_taskledger_v2_exchange.py +549 -1
  48. {taskledger-0.3.0 → taskledger-0.3.1}/.codecrate.toml +0 -0
  49. {taskledger-0.3.0 → taskledger-0.3.1}/.github/workflows/codecov.yml +0 -0
  50. {taskledger-0.3.0 → taskledger-0.3.1}/.github/workflows/pre-commit.yml +0 -0
  51. {taskledger-0.3.0 → taskledger-0.3.1}/.github/workflows/python-publish.yml +0 -0
  52. {taskledger-0.3.0 → taskledger-0.3.1}/.github/workflows/tests.yml +0 -0
  53. {taskledger-0.3.0 → taskledger-0.3.1}/.gitignore +0 -0
  54. {taskledger-0.3.0 → taskledger-0.3.1}/.pre-commit-config.yaml +0 -0
  55. {taskledger-0.3.0 → taskledger-0.3.1}/.readthedocs.yaml +0 -0
  56. {taskledger-0.3.0 → taskledger-0.3.1}/.ruff.toml +0 -0
  57. {taskledger-0.3.0 → taskledger-0.3.1}/AGENTS.md +0 -0
  58. {taskledger-0.3.0 → taskledger-0.3.1}/LICENSE +0 -0
  59. {taskledger-0.3.0 → taskledger-0.3.1}/Makefile +0 -0
  60. {taskledger-0.3.0 → taskledger-0.3.1}/docs/Makefile +0 -0
  61. {taskledger-0.3.0 → taskledger-0.3.1}/docs/architecture_taskledger_split.rst +0 -0
  62. {taskledger-0.3.0 → taskledger-0.3.1}/docs/build.sh +0 -0
  63. {taskledger-0.3.0 → taskledger-0.3.1}/docs/conf.py +0 -0
  64. {taskledger-0.3.0 → taskledger-0.3.1}/docs/full_task_cycle.rst +0 -0
  65. {taskledger-0.3.0 → taskledger-0.3.1}/docs/index.rst +0 -0
  66. {taskledger-0.3.0 → taskledger-0.3.1}/docs/multi_repo.rst +0 -0
  67. {taskledger-0.3.0 → taskledger-0.3.1}/docs/requirements.txt +0 -0
  68. {taskledger-0.3.0 → taskledger-0.3.1}/docs/service_boundary_whitelist.rst +0 -0
  69. {taskledger-0.3.0 → taskledger-0.3.1}/setup.cfg +0 -0
  70. {taskledger-0.3.0 → taskledger-0.3.1}/setup.py +0 -0
  71. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/__init__.py +0 -0
  72. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/__main__.py +0 -0
  73. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/__init__.py +0 -0
  74. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/handoff.py +0 -0
  75. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/introductions.py +0 -0
  76. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/locks.py +0 -0
  77. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/questions.py +0 -0
  78. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/releases.py +0 -0
  79. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/search.py +0 -0
  80. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/task_runs.py +0 -0
  81. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/api/tasks.py +0 -0
  82. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_actor.py +0 -0
  83. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_common.py +0 -0
  84. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_ledger.py +0 -0
  85. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_migrate.py +0 -0
  86. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_misc.py +0 -0
  87. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_question.py +0 -0
  88. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_release.py +0 -0
  89. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_task.py +0 -0
  90. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/cli_validate.py +0 -0
  91. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/__init__.py +0 -0
  92. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/_model_utils.py +0 -0
  93. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/active_state.py +0 -0
  94. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/actor.py +0 -0
  95. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/change.py +0 -0
  96. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/event.py +0 -0
  97. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/handoff.py +0 -0
  98. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/lock.py +0 -0
  99. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/plan.py +0 -0
  100. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/policies.py +0 -0
  101. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/question.py +0 -0
  102. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/release.py +0 -0
  103. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/sidecars.py +0 -0
  104. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/states.py +0 -0
  105. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/domain/task.py +0 -0
  106. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/errors.py +0 -0
  107. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/ids.py +0 -0
  108. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/launcher.py +0 -0
  109. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/py.typed +0 -0
  110. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/search.py +0 -0
  111. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/__init__.py +0 -0
  112. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/actors.py +0 -0
  113. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/agent_logging.py +0 -0
  114. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/agent_transcripts.py +0 -0
  115. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/change_tracking.py +0 -0
  116. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/command_runner.py +0 -0
  117. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/doctor.py +0 -0
  118. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/doctor_checks/migration_checks.py +0 -0
  119. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/doctor_checks/project_scan.py +0 -0
  120. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/doctor_checks/task_checks.py +0 -0
  121. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/handoff_lifecycle.py +0 -0
  122. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/next_action_model.py +0 -0
  123. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/phase5_lock_transfer.py +0 -0
  124. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/plan_editing.py +0 -0
  125. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/plan_hash.py +0 -0
  126. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/plan_lint.py +0 -0
  127. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/releases.py +0 -0
  128. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/run_store.py +0 -0
  129. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/task_archive.py +0 -0
  130. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/task_collections.py +0 -0
  131. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/task_lifecycle.py +0 -0
  132. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/task_queries.py +0 -0
  133. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/task_repair.py +0 -0
  134. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/tree.py +0 -0
  135. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/validation.py +0 -0
  136. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/validation_flow.py +0 -0
  137. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/web_dashboard.py +0 -0
  138. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/services/workflow_guidance.py +0 -0
  139. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/__init__.py +0 -0
  140. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/agent_logs.py +0 -0
  141. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/atomic.py +0 -0
  142. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/common.py +0 -0
  143. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/events.py +0 -0
  144. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/frontmatter.py +0 -0
  145. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/indexes.py +0 -0
  146. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/init.py +0 -0
  147. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/ledger_config.py +0 -0
  148. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/locks.py +0 -0
  149. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/meta.py +0 -0
  150. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/migrations.py +0 -0
  151. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/paths.py +0 -0
  152. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/project_config.py +0 -0
  153. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/project_identity.py +0 -0
  154. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/storage/repos.py +0 -0
  155. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/timeutils.py +0 -0
  156. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/web_assets/__init__.py +0 -0
  157. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger/web_assets/dashboard.css +0 -0
  158. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger.egg-info/dependency_links.txt +0 -0
  159. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger.egg-info/entry_points.txt +0 -0
  160. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger.egg-info/requires.txt +0 -0
  161. {taskledger-0.3.0 → taskledger-0.3.1}/taskledger.egg-info/top_level.txt +0 -0
  162. {taskledger-0.3.0 → taskledger-0.3.1}/tests/__init__.py +0 -0
  163. {taskledger-0.3.0 → taskledger-0.3.1}/tests/conftest.py +0 -0
  164. {taskledger-0.3.0 → taskledger-0.3.1}/tests/support/__init__.py +0 -0
  165. {taskledger-0.3.0 → taskledger-0.3.1}/tests/support/builders.py +0 -0
  166. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_active_task.py +0 -0
  167. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_actor_harness_state.py +0 -0
  168. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_agent_command_logging.py +0 -0
  169. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_agent_session_protocol.py +0 -0
  170. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_atomic_fast_io.py +0 -0
  171. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_cli_command_contract.py +0 -0
  172. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_cli_import_resilience.py +0 -0
  173. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_command_example_linter.py +0 -0
  174. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_compact_mutation_output.py +0 -0
  175. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_delta_remaining_contracts.py +0 -0
  176. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_doctor.py +0 -0
  177. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_domain_policies.py +0 -0
  178. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_events.py +0 -0
  179. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_handoff_lifecycle.py +0 -0
  180. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_help_subprocess.py +0 -0
  181. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_implementation_change_scan.py +0 -0
  182. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_json_contracts.py +0 -0
  183. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_legacy_cleanup_contracts.py +0 -0
  184. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_lifecycle_policies.py +0 -0
  185. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_locks_audit.py +0 -0
  186. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_models_v1_schema.py +0 -0
  187. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_next_action_expired_lock.py +0 -0
  188. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_plan_approval_contract.py +0 -0
  189. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_plan_lint.py +0 -0
  190. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_plan_todo_materialization.py +0 -0
  191. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_project_root_config.py +0 -0
  192. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_question_add_many.py +0 -0
  193. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_question_filter_answers.py +0 -0
  194. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_question_plan_regeneration.py +0 -0
  195. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_release_changelog.py +0 -0
  196. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_search.py +0 -0
  197. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_serve_dashboard.py +0 -0
  198. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_services_dashboard.py +0 -0
  199. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_sidecar_collections.py +0 -0
  200. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_storage_bundle_layout.py +0 -0
  201. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_storage_common.py +0 -0
  202. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_storage_init.py +0 -0
  203. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_storage_migration.py +0 -0
  204. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_storage_repos.py +0 -0
  205. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_task_archive.py +0 -0
  206. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_task_events.py +0 -0
  207. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_task_report.py +0 -0
  208. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_taskledger_branch_scoped_ledgers.py +0 -0
  209. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_taskledger_cli_api_parity.py +0 -0
  210. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_tasks_service_static.py +0 -0
  211. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_todo_implementation_gate.py +0 -0
  212. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_tree_command.py +0 -0
  213. {taskledger-0.3.0 → taskledger-0.3.1}/tests/test_workflow_guidance.py +0 -0
@@ -9,7 +9,7 @@ project_uuid = "081c7c05-2d10-42b7-9b37-3d814c2f400a"
9
9
  # Taskledger branch-scoped state. This block is intentionally safe to commit.
10
10
  ledger_ref = "main"
11
11
  ledger_parent_ref = ""
12
- ledger_next_task_number = 81
12
+ ledger_next_task_number = 84
13
13
  ledger_branch_guard = "off"
14
14
 
15
15
  [prompt_profiles.planning]
@@ -104,6 +104,9 @@ from taskledger.errors import (
104
104
  - `propose_plan`
105
105
  - `plan_template`
106
106
  - `upsert_plan`
107
+ - `PlanReviewOptions`
108
+ - `build_plan_review_payload`
109
+ - `render_plan_review`
107
110
  - `export_plan`
108
111
  - `amend_plan`
109
112
  - `regenerate_plan_from_answers`
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.3.1 - 2026-05-12
4
+
5
+ ### Added
6
+
7
+ - Added task-scoped export/import with `--task` positional ref on export, safe ID remapping (`--id-policy preserve|renumber`), counter repair, and artifact path remapping on import. Full-ledger export remains the default; single-task archives filter task-scoped records and clear `active_task`.
8
+ - Added `taskledger plan review` command to show the current or specified plan version with structured JSON output and content rendering.
9
+ - Added `next-action` routing to recommend `plan review --version N` when a plan is awaiting approval.
10
+
11
+ ### Documentation
12
+
13
+ - Updated README, command contract, and SKILL.md for task-scoped transfer workflow and `plan review` command.
14
+
15
+ ### Quality
16
+
17
+ - Added regression coverage for task-scoped export/import ID mapping, conflict policy, counter repair, artifact remap, and plan review end-to-end.
18
+ - Full suite: 878 tests passing, ruff and mypy clean.
19
+
20
+ ## v0.3.0 - 2026-05-05
21
+
22
+ ### Added
23
+
24
+ - Added agent command transcript logging: opt-in config, append-only NDJSON storage, CLI stdout/stderr tee capture, managed-shell capture, `task transcript` command, and `task report --include command-log` section. Export/import preserves transcript archives.
25
+ - Added planning guidance profiles: `plan guidance` command, `--include-guidance` plan template injection, and `prompt_profiles.planning` config with advisory required-fields rendering.
26
+ - Added transcript review mode as the default `task transcript` output, with `--raw` flag for the original table view, duplicate log ID warnings, and logical-row grouping for wrapper/managed-shell pairs.
27
+ - Added enriched command metadata: tier, deprecated, replaced_by, ledger_effect, workspace_effect, external_effect, and agent_safe fields on CommandSpec. Added `--tier` and `--include-deprecated` CLI filters. Deprecated `lock break` in favor of `repair lock`.
28
+ - Added first-class expired-lock resume path: `implement resume --repair-expired-lock` releases expired implementation locks with audit trail, and `next-action` emits `expired-lock-resume` when applicable.
29
+ - Added task-resource positional refs for read-only commands (`task show`, `task view`, etc.) with explicit `--task` required for destructive commands (`task cancel`, `task uncancel`, `task edit`).
30
+ - Added JSON usage-error envelopes for workflow positional-ref rejection and CLI parse errors.
31
+ - Added soft task archive: `task archive`/`task unarchive` commands, archived-task visibility filtering across list/tree/status, and slug reuse semantics.
32
+ - Added export/import project metadata guard, dry-run safety, and include-flag controls.
33
+ - Added plan revision workflow: `plan export`, `plan amend`, and `--auto-revise` with safe plan input path guard and plan.amended audit events.
34
+ - Added plan approval provenance: approval_source and approved_plan_hash stored on acceptance, with hash-mismatch warnings in reports.
35
+ - Added implement finish warning for missing git change scans.
36
+
37
+ ### Changed
38
+
39
+ - Split run/lock helpers into `services/run_store.py` from the tasks.py monolith; `tasks.py` re-exports for backward compatibility.
40
+ - Wrapper commands now mirror inner exit status by default instead of always succeeding.
41
+ - Planning guidance recommendation is now integrated into `plan start` and `next-action` with a one-time viewed marker.
42
+ - Plan lint human output now renders summary and issue details instead of bare pass/fail.
43
+ - Question `answer-many` now validates repeat inputs, aliases, and provenance.
44
+ - Doctor mismatch guidance and verbose output improved with actionable repair hints.
45
+
46
+ ### Fixed
47
+
48
+ - Fixed task report Plans section so non-accepted plans show reviewable details instead of being omitted.
49
+ - Fixed pre-commit `--all-files` regressions across test files.
50
+
51
+ ### Documentation
52
+
53
+ - Documented planning guidance profiles in README, usage, command contract, API, and skill.
54
+ - Documented transcript logging, managed command capture, and review mode in usage and skill.
55
+ - Documented expired-lock-resume path and `--repair-expired-lock` in SKILL.md.
56
+ - Documented command-surface safety guidance, task-resource positional refs, and destructive-target rules in SKILL.md and command contract.
57
+ - Documented plan revision workflow commands and safety semantics in SKILL.md and command examples.
58
+ - Updated failure-review remediation hints for known mistakes in docs and skill.
59
+
60
+ ### Quality
61
+
62
+ - Added regression test modules for agent command logging, expired-lock resume, task archive, and plan revision workflow.
63
+ - Expanded command inventory, CLI contract, JSON contract, and docs/skill tests for metadata enrichment, deprecation, targeting, and envelope behavior.
64
+ - Full suite: 770+ tests passing, ruff and mypy clean.
65
+
3
66
  ## v0.2.0 - 2026-05-03
4
67
 
5
68
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskledger
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Durable project-state storage and CLI for coding workflows
5
5
  Author: Taskledger Contributors
6
6
  Maintainer: Holger Nahrstaedt
@@ -164,6 +164,7 @@ taskledger question answer-many --text $'q-0001: Yes.\nq-0002: No.'
164
164
  taskledger question status
165
165
  taskledger plan template --from-answers --file ./plan.md
166
166
  taskledger plan upsert --from-answers --file ./plan.md
167
+ taskledger plan review --version 1
167
168
  taskledger plan lint --version 1
168
169
  taskledger plan accept --version 1 --note "Ready."
169
170
 
@@ -512,7 +513,10 @@ for task-first handoff guidance.
512
513
  ```bash
513
514
  taskledger init --project-name "Taskledger"
514
515
  taskledger export
516
+ taskledger export --task task-0040
517
+ taskledger export task-0040
515
518
  taskledger import ./taskledger-transfer.tar.gz --dry-run
519
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
516
520
  taskledger import ./taskledger-transfer.tar.gz --replace
517
521
  taskledger snapshot ./artifacts
518
522
  ```
@@ -521,6 +525,7 @@ Default export filenames use this policy:
521
525
 
522
526
  ```text
523
527
  taskledger-export-{project_slug}-{ledger_ref}-{timestamp}.tar.gz
528
+ taskledger-task-{project_slug}-{ledger_ref}-{task_id}-{timestamp}.tar.gz
524
529
  ```
525
530
 
526
531
  `project_slug` is derived from `project_name` in `taskledger.toml`. If
@@ -543,6 +548,21 @@ taskledger implement resume --reason "Continue imported implementation."
543
548
 
544
549
  Use `--lock-policy keep` only for diagnostic full-fidelity lock restoration.
545
550
 
551
+ Single-task transfer from a config-only checkout:
552
+
553
+ ```bash
554
+ # fresh checkout on another PC
555
+ taskledger init
556
+ taskledger task create "Fix import edge case" --slug fix-import-edge-case --description "..."
557
+ # ... normal plan / implementation / validation lifecycle ...
558
+ taskledger export task-0040
559
+
560
+ # main dev repo
561
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
562
+ taskledger task list
563
+ taskledger task show task-0040
564
+ ```
565
+
546
566
  ## Skill packaging
547
567
 
548
568
  Agent workflows work best when the `taskledger` skill is installed in the
@@ -121,6 +121,7 @@ taskledger question answer-many --text $'q-0001: Yes.\nq-0002: No.'
121
121
  taskledger question status
122
122
  taskledger plan template --from-answers --file ./plan.md
123
123
  taskledger plan upsert --from-answers --file ./plan.md
124
+ taskledger plan review --version 1
124
125
  taskledger plan lint --version 1
125
126
  taskledger plan accept --version 1 --note "Ready."
126
127
 
@@ -469,7 +470,10 @@ for task-first handoff guidance.
469
470
  ```bash
470
471
  taskledger init --project-name "Taskledger"
471
472
  taskledger export
473
+ taskledger export --task task-0040
474
+ taskledger export task-0040
472
475
  taskledger import ./taskledger-transfer.tar.gz --dry-run
476
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
473
477
  taskledger import ./taskledger-transfer.tar.gz --replace
474
478
  taskledger snapshot ./artifacts
475
479
  ```
@@ -478,6 +482,7 @@ Default export filenames use this policy:
478
482
 
479
483
  ```text
480
484
  taskledger-export-{project_slug}-{ledger_ref}-{timestamp}.tar.gz
485
+ taskledger-task-{project_slug}-{ledger_ref}-{task_id}-{timestamp}.tar.gz
481
486
  ```
482
487
 
483
488
  `project_slug` is derived from `project_name` in `taskledger.toml`. If
@@ -500,6 +505,21 @@ taskledger implement resume --reason "Continue imported implementation."
500
505
 
501
506
  Use `--lock-policy keep` only for diagnostic full-fidelity lock restoration.
502
507
 
508
+ Single-task transfer from a config-only checkout:
509
+
510
+ ```bash
511
+ # fresh checkout on another PC
512
+ taskledger init
513
+ taskledger task create "Fix import edge case" --slug fix-import-edge-case --description "..."
514
+ # ... normal plan / implementation / validation lifecycle ...
515
+ taskledger export task-0040
516
+
517
+ # main dev repo
518
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
519
+ taskledger task list
520
+ taskledger task show task-0040
521
+ ```
522
+
503
523
  ## Skill packaging
504
524
 
505
525
  Agent workflows work best when the `taskledger` skill is installed in the
@@ -85,6 +85,9 @@ Plan API
85
85
  - ``propose_plan``
86
86
  - ``plan_template``
87
87
  - ``upsert_plan``
88
+ - ``PlanReviewOptions``
89
+ - ``build_plan_review_payload``
90
+ - ``render_plan_review``
88
91
  - ``export_plan``
89
92
  - ``amend_plan``
90
93
  - ``list_plan_versions``
@@ -84,6 +84,22 @@ storage, and ``taskledger plan amend`` applies structured plan-review edits:
84
84
  Plan proposal commands that accept ``--file`` reject file paths under
85
85
  ``.taskledger/`` because that directory is private durable ledger state.
86
86
 
87
+ Plan review command
88
+ -------------------
89
+
90
+ ``taskledger plan review`` is a read-only approval-facing plan renderer:
91
+
92
+ .. code-block:: bash
93
+
94
+ taskledger plan review [--task TASK_REF] [--version N] [--format markdown|json] [-o PATH]
95
+
96
+ Rules:
97
+
98
+ * defaults to the active task when ``--task`` is omitted;
99
+ * defaults to the latest proposed plan in ``plan_review`` stage when ``--version`` is omitted;
100
+ * renders Markdown by default and can emit JSON-formatted content with ``--format json``;
101
+ * when ``-o/--output`` is provided, writes rendered content to a file path.
102
+
87
103
  Archive import lock policy
88
104
  --------------------------
89
105
 
@@ -92,6 +108,7 @@ Archive imports support explicit lock handling:
92
108
  .. code-block:: bash
93
109
 
94
110
  taskledger import ./taskledger-transfer.tar.gz --replace [--lock-policy drop|quarantine|keep]
111
+ taskledger import ./taskledger-task-<project>-<ledger>-task-0040-<ts>.tar.gz [--id-policy preserve|renumber-on-conflict|fail-on-conflict]
95
112
 
96
113
  The default is ``quarantine`` so imported source-machine runtime locks are not
97
114
  restored as active ``lock.yaml`` files. ``keep`` is diagnostic-only behavior
@@ -122,6 +139,19 @@ still authoritative for import safety.
122
139
  ``taskledger import --dry-run`` must not mutate taskledger state for either
123
140
  archive or JSON payload imports.
124
141
 
142
+ Export task selection
143
+ ---------------------
144
+
145
+ Taskledger export supports full-ledger and task-scoped archives:
146
+
147
+ .. code-block:: bash
148
+
149
+ taskledger export
150
+ taskledger export ./backup.tar.gz
151
+ taskledger export --task task-0040
152
+ taskledger export task-0040
153
+ taskledger export task-0040 -o ./task0040.tar.gz
154
+
125
155
  Positional Resource Refs
126
156
  ------------------------
127
157
 
@@ -39,7 +39,7 @@ question subcommands
39
39
  plan subcommands
40
40
  ----------------
41
41
 
42
- - ``plan start``, ``plan propose``, ``plan template``, ``plan upsert``, ``plan lint``, ``plan approve``, ``plan accept``, ``plan reject``, ``plan show``, ``plan diff``
42
+ - ``plan start``, ``plan propose``, ``plan template``, ``plan upsert``, ``plan review``, ``plan lint``, ``plan approve``, ``plan accept``, ``plan reject``, ``plan show``, ``plan diff``
43
43
  - ``plan regenerate --from-answers``, ``plan materialize-todos``, ``plan command -- ...``
44
44
 
45
45
  task reporting and transcripts
@@ -24,9 +24,41 @@ When no output path is passed to ``taskledger export``, taskledger writes:
24
24
 
25
25
  taskledger-export-{project_slug}-{ledger_ref}-{timestamp}.tar.gz
26
26
 
27
+ For task-scoped exports:
28
+
29
+ .. code-block:: text
30
+
31
+ taskledger-task-{project_slug}-{ledger_ref}-{task_id}-{timestamp}.tar.gz
32
+
27
33
  ``project_slug`` comes from ``project_name`` (or workspace fallback). Import
28
34
  safety still depends on UUID checks, not name matching.
29
35
 
36
+ Single-task transfer from a config-only checkout
37
+ ------------------------------------------------
38
+
39
+ .. code-block:: bash
40
+
41
+ # fresh checkout on another PC
42
+ taskledger init
43
+ taskledger task create "Fix import edge case" --slug fix-import-edge-case --description "..."
44
+ # ... normal plan/implement/validate workflow ...
45
+ taskledger export task-0040
46
+
47
+ # main dev repo
48
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
49
+ taskledger task list
50
+ taskledger task show task-0040
51
+
52
+ Rules:
53
+
54
+ - Keep ``project_uuid`` committed in ``taskledger.toml`` / ``.taskledger.toml``.
55
+ - ``.taskledger/`` is local operational state and can be absent on another PC.
56
+ - Run ``taskledger init`` after cloning to create local state.
57
+ - ``taskledger export --task TASK_REF`` and ``taskledger export TASK_REF`` export task-scoped archives.
58
+ - Task-scoped import is additive by default; if the task id already exists locally, import renumbers and reports an id map.
59
+ - ``--replace`` is for full-state replacement, not the normal single-task workflow.
60
+ - Import repairs ``ledger_next_task_number`` so future ``task create`` ids remain unique.
61
+
30
62
  Dry-run import
31
63
  --------------
32
64
 
@@ -56,8 +56,9 @@ Task-first workflow
56
56
  taskledger question add --text "Should exports include v2?"
57
57
  taskledger question answer-many --text "q-0001: Yes."
58
58
  taskledger plan upsert --from-answers --criterion "Accepted workflow is implemented." --file ./plan.md
59
- taskledger plan lint --version 1
60
- taskledger plan accept --version 1 --note "Ready."
59
+ taskledger plan review --version 1
60
+ taskledger plan lint --version 1
61
+ taskledger plan accept --version 1 --note "Ready."
61
62
 
62
63
  Planning guidance profiles
63
64
  --------------------------
@@ -192,6 +193,7 @@ parent and run the normal lifecycle on that child:
192
193
  taskledger task follow-up rewrite-v2 "Rename submit label" --description "Small post-completion delta." --activate
193
194
  taskledger plan start
194
195
  taskledger plan upsert --file ./plan.md
196
+ taskledger plan review --version 1
195
197
  taskledger plan accept --version 1 --note "Ready."
196
198
  taskledger implement start
197
199
  taskledger validate start
@@ -474,7 +476,11 @@ Export and snapshots
474
476
 
475
477
  taskledger init --project-name "Taskledger"
476
478
  taskledger export
479
+ taskledger export --task task-0040
480
+ taskledger export task-0040
477
481
  taskledger import ./taskledger-transfer.tar.gz --dry-run
482
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
483
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz --id-policy fail-on-conflict
478
484
  taskledger import ./taskledger-transfer.tar.gz --replace
479
485
  taskledger snapshot ./artifacts
480
486
 
@@ -483,6 +489,7 @@ Default export filenames are project-specific:
483
489
  .. code-block:: text
484
490
 
485
491
  taskledger-export-{project_slug}-{ledger_ref}-{timestamp}.tar.gz
492
+ taskledger-task-{project_slug}-{ledger_ref}-{task_id}-{timestamp}.tar.gz
486
493
 
487
494
  ``project_slug`` is derived from ``project_name`` in ``taskledger.toml``.
488
495
  If unset, taskledger falls back to the workspace directory name.
@@ -503,3 +510,19 @@ runtime locks by default. For an imported in-progress implementation, run:
503
510
 
504
511
  Use ``--lock-policy keep`` only when you explicitly want diagnostic lock
505
512
  restoration behavior.
513
+
514
+ Single-task transfer from a config-only checkout
515
+ ------------------------------------------------
516
+
517
+ .. code-block:: bash
518
+
519
+ # fresh checkout on another PC
520
+ taskledger init
521
+ taskledger task create "Fix import edge case" --slug fix-import-edge-case --description "..."
522
+ # ... normal plan / implementation / validation lifecycle ...
523
+ taskledger export task-0040
524
+
525
+ # main dev repo
526
+ taskledger import ./taskledger-task-planledger-main-task-0040-20260509T101500Z.tar.gz
527
+ taskledger task list
528
+ taskledger task show task-0040
@@ -14,7 +14,7 @@ taskledger = [
14
14
  ]
15
15
 
16
16
  [tool.pytest.ini_options]
17
- addopts = "-p no:odoo --strict-markers"
17
+ addopts = "--strict-markers"
18
18
  testpaths = ["tests"]
19
19
  python_files = "test_*.py"
20
20
  markers = [
@@ -121,7 +121,7 @@ If any `taskledger ...` command fails with a Python traceback before taskledger
121
121
  19. A proposed plan must include concrete `acceptance_criteria` and `todos` in front matter unless the user explicitly says the task is trivial.
122
122
  20. After writing the plan, do not run `taskledger repair lock`; planning locks are released by plan proposal/upsert. Run `taskledger next-action`.
123
123
  21. After `taskledger plan upsert --from-answers`, run `taskledger question status`. If it still reports `Plan regeneration needed: True`, do not ask for approval. Inspect `taskledger question answers`, `taskledger plan show --version N`, and `taskledger doctor`.
124
- 22. Before asking the user to approve, run `taskledger plan lint --version N` and fix lint errors. Lint waiver flags are on `plan approve`/`plan accept`, not on `plan lint`.
124
+ 22. Before asking the user to approve, run `taskledger plan review --version N` and paste or summarize the rendered review. Then run `taskledger plan lint --version N` if the review did not include lint. Do not record approval until the user explicitly approves.
125
125
  23. Record approval only with clear user intent such as approve, accept, go ahead, or start implementation. Prefer `taskledger plan accept --version N --note "User approved in harness: ..."` for normal chat approval. Use `taskledger plan approve --version N --actor user --approval-source explicit_chat --note "..."` only when explicit actor/source metadata is needed (advanced).
126
126
  24. Never replace a user-provided rich plan with only generated YAML criteria and todos. Preserve the user's plan body after the front matter and use front matter only to expose machine-readable fields to Taskledger.
127
127
  25. A Taskledger plan is not just YAML front matter. The YAML block is for machine-readable `goal`, `acceptance_criteria`, `todos`, file links, and test commands. The rich human plan must remain as Markdown after the second `---`. Before approval, inspect `taskledger plan show --version N` or the saved `plan-vN.md` and verify that the accepted plan body is non-empty and contains the implementation rationale.
@@ -142,7 +142,7 @@ The plan file should use version ids like `plan-v1`, `plan-v2` in references. Do
142
142
  - `taskledger implement change --path ... --kind edit --summary "..."`
143
143
  - Run verification through `taskledger implement command -- ...` so exit code and output are recorded.
144
144
  - `implement command` mirrors the inner command exit code by default. Use `--allow-failure` when you intentionally want to record a non-zero command without failing the wrapper command.
145
- - Mark each todo done only after the relevant command or inspection evidence exists: `taskledger todo done <todo-id> --evidence "implement command change-NNNN exited 0"`.
145
+ - Mark each todo done only after the relevant command or inspection evidence exists: `taskledger todo done <todo-id> --evidence "check-NNNN exited 0"`.
146
146
  - Optional: add `--source planner|implementer|user` to override the inferred source, though this is rarely needed.
147
147
  6. `taskledger implement checklist` after each meaningful change to track progress.
148
148
  7. In Git workspaces, prefer `taskledger implement scan-changes --from-git --summary "..."` before `implement finish` so change evidence includes a git-backed reconciliation checkpoint.
@@ -321,6 +321,7 @@ taskledger next-action
321
321
  taskledger plan guidance
322
322
  taskledger plan template --from-answers --file ./plan.md
323
323
  taskledger plan upsert --from-answers --file ./plan.md
324
+ taskledger plan review --version 1
324
325
  taskledger plan lint --version 1
325
326
  taskledger plan accept --version 1 --note "User approved in harness."
326
327
  taskledger context --for implementation --format markdown
@@ -330,6 +331,9 @@ taskledger context --for code-reviewer --run run-0008
330
331
  taskledger release tag 0.4.1 --at-task task-0030 --note "0.4.1 released"
331
332
  taskledger release changelog 0.4.2 --since 0.4.1 --until-task task-0035 --output /tmp/taskledger-0.4.2-changelog-source.md
332
333
  taskledger import ./taskledger-transfer.tar.gz --dry-run
334
+ taskledger export --task task-0040
335
+ taskledger export task-0040 -o ./task0040.tar.gz
336
+ taskledger import ./task0040.tar.gz --id-policy renumber-on-conflict
333
337
  taskledger ledger status
334
338
  taskledger ledger fork feature-a
335
339
  taskledger ledger switch main
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.0'
22
- __version_tuple__ = version_tuple = (0, 3, 0)
21
+ __version__ = version = '0.3.1'
22
+ __version_tuple__ = version_tuple = (0, 3, 1)
23
23
 
24
- __commit_id__ = commit_id = 'gabec514e0'
24
+ __commit_id__ = commit_id = 'g9f557bd45'
@@ -1,6 +1,11 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from taskledger.services.plan_lint import lint_plan
4
+ from taskledger.services.plan_review import (
5
+ PlanReviewOptions,
6
+ build_plan_review_payload,
7
+ render_plan_review,
8
+ )
4
9
  from taskledger.services.tasks import (
5
10
  amend_plan,
6
11
  approve_plan,
@@ -36,6 +41,9 @@ __all__ = [
36
41
  "revise_plan",
37
42
  "run_planning_command",
38
43
  "lint_plan",
44
+ "PlanReviewOptions",
45
+ "build_plan_review_payload",
46
+ "render_plan_review",
39
47
  "plan_guidance",
40
48
  ]
41
49
 
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Sequence
3
4
  from pathlib import Path
4
5
  from typing import Any, cast
5
6
 
@@ -265,6 +266,8 @@ def project_export_archive(
265
266
  output_path: Path | None = None,
266
267
  include_bodies: bool = True,
267
268
  include_run_artifacts: bool = False,
269
+ task_refs: Sequence[str] = (),
270
+ overwrite: bool = False,
268
271
  ) -> dict[str, object]:
269
272
  """Export current-ledger state into a compressed archive file."""
270
273
  return write_project_archive(
@@ -272,6 +275,8 @@ def project_export_archive(
272
275
  output_path=output_path,
273
276
  include_bodies=include_bodies,
274
277
  include_run_artifacts=include_run_artifacts,
278
+ task_refs=task_refs,
279
+ overwrite=overwrite,
275
280
  )
276
281
 
277
282
 
@@ -282,6 +287,7 @@ def project_import_archive(
282
287
  replace: bool = False,
283
288
  dry_run: bool = False,
284
289
  lock_policy: str = "quarantine",
290
+ id_policy: str = "preserve",
285
291
  ) -> dict[str, object]:
286
292
  """Import a taskledger archive into the current project."""
287
293
  return import_project_archive(
@@ -290,6 +296,7 @@ def project_import_archive(
290
296
  replace=replace,
291
297
  dry_run=dry_run,
292
298
  lock_policy=lock_policy,
299
+ id_policy=id_policy,
293
300
  )
294
301
 
295
302
 
@@ -1090,12 +1090,41 @@ def repair_task_dirs_command(ctx: typer.Context) -> None:
1090
1090
  )
1091
1091
 
1092
1092
 
1093
+ def _looks_like_archive_output_target(value: str) -> bool:
1094
+ candidate = value.strip()
1095
+ lowered = candidate.lower()
1096
+ if (
1097
+ lowered.endswith(".tar.gz")
1098
+ or lowered.endswith(".tgz")
1099
+ or lowered.endswith(".json")
1100
+ ):
1101
+ return True
1102
+ path = Path(candidate)
1103
+ if path.is_absolute():
1104
+ return True
1105
+ if "/" in candidate or "\\" in candidate:
1106
+ return True
1107
+ if path.parent != Path("."):
1108
+ return True
1109
+ return False
1110
+
1111
+
1093
1112
  @app.command("export")
1094
1113
  def export_command(
1095
1114
  ctx: typer.Context,
1115
+ target_or_output: Annotated[
1116
+ str | None,
1117
+ typer.Argument(
1118
+ help="Task ref convenience selector or output archive path (.tar.gz)."
1119
+ ),
1120
+ ] = None,
1121
+ task_ref: Annotated[
1122
+ str | None,
1123
+ typer.Option("--task", help="Task ref to export as task-scoped archive."),
1124
+ ] = None,
1096
1125
  output: Annotated[
1097
1126
  Path | None,
1098
- typer.Argument(help="Output archive path (.tar.gz)."),
1127
+ typer.Option("--output", "-o", help="Output archive path (.tar.gz)."),
1099
1128
  ] = None,
1100
1129
  include_bodies: Annotated[
1101
1130
  bool,
@@ -1118,20 +1147,61 @@ def export_command(
1118
1147
  ) -> None:
1119
1148
  state = ctx.obj
1120
1149
  assert isinstance(state, CLIState)
1121
- if output is not None and output.exists() and not overwrite:
1150
+ resolved_output = output
1151
+ task_refs: list[str] = []
1152
+ if task_ref is not None:
1153
+ task_refs = [resolve_cli_task(state.cwd, task_ref).id]
1154
+ if target_or_output is not None:
1155
+ if output is not None:
1156
+ emit_error(
1157
+ ctx,
1158
+ LaunchError(
1159
+ "export received both positional output and --output. Use one."
1160
+ ),
1161
+ )
1162
+ raise typer.Exit(code=2)
1163
+ resolved_output = Path(target_or_output)
1164
+ elif target_or_output is not None:
1165
+ if _looks_like_archive_output_target(target_or_output):
1166
+ if output is not None:
1167
+ emit_error(
1168
+ ctx,
1169
+ LaunchError(
1170
+ "export received both positional output and --output. Use one."
1171
+ ),
1172
+ )
1173
+ raise typer.Exit(code=2)
1174
+ resolved_output = Path(target_or_output)
1175
+ else:
1176
+ try:
1177
+ task_refs = [resolve_cli_task(state.cwd, target_or_output).id]
1178
+ except LaunchError as exc:
1179
+ emit_error(
1180
+ ctx,
1181
+ LaunchError(
1182
+ f"No task found for '{target_or_output}'. To write an archive "
1183
+ "to that filename, use: taskledger export -o "
1184
+ f"{target_or_output}.tar.gz"
1185
+ ),
1186
+ )
1187
+ raise typer.Exit(code=launch_error_exit_code(exc)) from exc
1188
+ if resolved_output is not None and resolved_output.exists() and not overwrite:
1122
1189
  emit_error(
1123
1190
  ctx,
1124
1191
  LaunchError(
1125
- f"Output file already exists: {output}. Use --overwrite to replace."
1192
+ "Output file already exists: "
1193
+ f"{resolved_output}. Use --overwrite to replace."
1126
1194
  ),
1127
1195
  )
1128
1196
  raise typer.Exit(code=1)
1129
1197
  try:
1130
1198
  payload = project_export_archive(
1131
1199
  state.cwd,
1132
- output_path=output,
1200
+ output_path=resolved_output,
1133
1201
  include_bodies=include_bodies,
1134
1202
  include_run_artifacts=include_run_artifacts,
1203
+ task_refs=task_refs,
1204
+ overwrite=overwrite,
1135
1205
  )
1136
1206
  except LaunchError as exc:
1137
1207
  emit_error(ctx, exc)
@@ -1148,6 +1218,7 @@ def export_command(
1148
1218
  f"exported taskledger archive: {payload['path']}\n"
1149
1219
  f"project: {project_label}\n"
1150
1220
  f"ledger: {payload['ledger_ref']}\n"
1221
+ f"scope: {payload.get('archive_scope', 'ledger')}\n"
1151
1222
  f"tasks: {counts.get('tasks', 0)}"
1152
1223
  )
1153
1224
  emit_payload(ctx, payload, human=human)
@@ -1172,6 +1243,16 @@ def import_command(
1172
1243
  help="How imported live locks are handled: drop, quarantine, keep.",
1173
1244
  ),
1174
1245
  ] = "quarantine",
1246
+ id_policy: Annotated[
1247
+ str,
1248
+ typer.Option(
1249
+ "--id-policy",
1250
+ help=(
1251
+ "Task ID conflict policy for task archives: "
1252
+ "preserve, renumber-on-conflict, fail-on-conflict."
1253
+ ),
1254
+ ),
1255
+ ] = "preserve",
1175
1256
  ) -> None:
1176
1257
  state = ctx.obj
1177
1258
  assert isinstance(state, CLIState)
@@ -1210,6 +1291,7 @@ def import_command(
1210
1291
  replace=replace,
1211
1292
  dry_run=dry_run,
1212
1293
  lock_policy=lock_policy,
1294
+ id_policy=id_policy,
1213
1295
  )
1214
1296
  except LaunchError as exc:
1215
1297
  emit_error(ctx, exc)
@@ -1234,8 +1316,18 @@ def import_command(
1234
1316
  f"imported taskledger archive: {source}\n"
1235
1317
  f"project: {project_label}\n"
1236
1318
  f"ledger: {payload['ledger_ref']}\n"
1237
- f"replace: {payload['replace']}"
1319
+ f"replace: {payload['replace']}\n"
1320
+ f"scope: {payload.get('archive_scope', 'ledger')}"
1238
1321
  )
1322
+ task_id_map = payload.get("task_id_map")
1323
+ if isinstance(task_id_map, dict) and task_id_map:
1324
+ id_lines = ["id map:"]
1325
+ for source_id, target_id in sorted(task_id_map.items()):
1326
+ if source_id == target_id:
1327
+ id_lines.append(f" {source_id} -> {target_id}")
1328
+ else:
1329
+ id_lines.append(f" {source_id} -> {target_id} renumbered")
1330
+ human = f"{human}\n" + "\n".join(id_lines)
1239
1331
  if isinstance(payload.get("next_command"), str):
1240
1332
  human = f"{human}\nnext: {payload['next_command']}"
1241
1333
  emit_payload(ctx, payload, human=human)
@@ -307,10 +307,14 @@ def command_command(
307
307
  except LaunchError as exc:
308
308
  emit_error(ctx, exc)
309
309
  raise typer.Exit(code=launch_error_exit_code(exc)) from exc
310
+ check_data = payload.get("check") or {}
310
311
  emit_payload(
311
312
  ctx,
312
313
  payload,
313
- human=f"ran implementation command exit={payload['exit_code']}",
314
+ human=(
315
+ f"recorded check {check_data.get('check_id', '?')}"
316
+ f" exit={payload['exit_code']}"
317
+ ),
314
318
  )
315
319
  raw_exit_code = payload.get("exit_code", 0)
316
320
  if isinstance(raw_exit_code, int):