agentic-project-kit 1.0.1__py3-none-any.whl

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 (310) hide show
  1. agentic_project_kit/__init__.py +1 -0
  2. agentic_project_kit/absolute_path_portability_audit.py +241 -0
  3. agentic_project_kit/access_level_policy.py +14 -0
  4. agentic_project_kit/access_levels.py +22 -0
  5. agentic_project_kit/action_registry.py +195 -0
  6. agentic_project_kit/action_specs.py +286 -0
  7. agentic_project_kit/agent_command_runner.py +516 -0
  8. agentic_project_kit/audit_test_matrix.py +36 -0
  9. agentic_project_kit/audit_test_matrix_cli.py +43 -0
  10. agentic_project_kit/chat_bootloader.py +257 -0
  11. agentic_project_kit/chat_entrypoint_contract.py +297 -0
  12. agentic_project_kit/checks.py +511 -0
  13. agentic_project_kit/ci_readiness.py +211 -0
  14. agentic_project_kit/cli.py +147 -0
  15. agentic_project_kit/cli_commands/__init__.py +1 -0
  16. agentic_project_kit/cli_commands/absolute_path_audit.py +25 -0
  17. agentic_project_kit/cli_commands/actions.py +21 -0
  18. agentic_project_kit/cli_commands/artifact_gc.py +240 -0
  19. agentic_project_kit/cli_commands/boot.py +55 -0
  20. agentic_project_kit/cli_commands/chat.py +34 -0
  21. agentic_project_kit/cli_commands/checks.py +305 -0
  22. agentic_project_kit/cli_commands/cockpit.py +103 -0
  23. agentic_project_kit/cli_commands/command_taxonomy.py +25 -0
  24. agentic_project_kit/cli_commands/commands.py +129 -0
  25. agentic_project_kit/cli_commands/dev.py +23 -0
  26. agentic_project_kit/cli_commands/direction.py +92 -0
  27. agentic_project_kit/cli_commands/doc_currency_audit.py +25 -0
  28. agentic_project_kit/cli_commands/doc_registry.py +126 -0
  29. agentic_project_kit/cli_commands/docs.py +223 -0
  30. agentic_project_kit/cli_commands/dpa.py +1338 -0
  31. agentic_project_kit/cli_commands/evidence.py +251 -0
  32. agentic_project_kit/cli_commands/github.py +34 -0
  33. agentic_project_kit/cli_commands/governance.py +14 -0
  34. agentic_project_kit/cli_commands/gui.py +23 -0
  35. agentic_project_kit/cli_commands/gui_readiness_gate.py +27 -0
  36. agentic_project_kit/cli_commands/handoff.py +183 -0
  37. agentic_project_kit/cli_commands/human_workflows.py +430 -0
  38. agentic_project_kit/cli_commands/init.py +132 -0
  39. agentic_project_kit/cli_commands/instruction.py +75 -0
  40. agentic_project_kit/cli_commands/mutation_lock_audit.py +24 -0
  41. agentic_project_kit/cli_commands/ns_legacy_audit.py +25 -0
  42. agentic_project_kit/cli_commands/pass_already_done.py +66 -0
  43. agentic_project_kit/cli_commands/patch_failure_discipline_audit.py +26 -0
  44. agentic_project_kit/cli_commands/patch_preflight.py +37 -0
  45. agentic_project_kit/cli_commands/path_literal_audit.py +39 -0
  46. agentic_project_kit/cli_commands/patterns.py +40 -0
  47. agentic_project_kit/cli_commands/planning_docs_audit.py +25 -0
  48. agentic_project_kit/cli_commands/pr.py +133 -0
  49. agentic_project_kit/cli_commands/pr_hygiene.py +27 -0
  50. agentic_project_kit/cli_commands/profiles.py +26 -0
  51. agentic_project_kit/cli_commands/program_redundancy_audit.py +25 -0
  52. agentic_project_kit/cli_commands/project_direction.py +25 -0
  53. agentic_project_kit/cli_commands/release.py +392 -0
  54. agentic_project_kit/cli_commands/release_publish_orchestration.py +36 -0
  55. agentic_project_kit/cli_commands/remote_branch_hygiene.py +152 -0
  56. agentic_project_kit/cli_commands/remote_next.py +57 -0
  57. agentic_project_kit/cli_commands/removed_ns_commands.py +65 -0
  58. agentic_project_kit/cli_commands/rule_registry.py +129 -0
  59. agentic_project_kit/cli_commands/rules.py +222 -0
  60. agentic_project_kit/cli_commands/scaffold.py +46 -0
  61. agentic_project_kit/cli_commands/slice.py +21 -0
  62. agentic_project_kit/cli_commands/standard_gates_audit_suite.py +27 -0
  63. agentic_project_kit/cli_commands/state.py +51 -0
  64. agentic_project_kit/cli_commands/status_current_state_audit.py +30 -0
  65. agentic_project_kit/cli_commands/todo.py +53 -0
  66. agentic_project_kit/cli_commands/transfer.py +19 -0
  67. agentic_project_kit/cli_commands/transfer_context_flow.py +398 -0
  68. agentic_project_kit/cli_commands/transfer_context_helpers.py +398 -0
  69. agentic_project_kit/cli_commands/transfer_diagnostics.py +767 -0
  70. agentic_project_kit/cli_commands/transfer_evidence_flow.py +368 -0
  71. agentic_project_kit/cli_commands/transfer_handoff_flow.py +594 -0
  72. agentic_project_kit/cli_commands/transfer_order_contract.py +74 -0
  73. agentic_project_kit/cli_commands/transfer_post_merge_complete.py +442 -0
  74. agentic_project_kit/cli_commands/transfer_post_merge_settle.py +171 -0
  75. agentic_project_kit/cli_commands/transfer_pr_closeout_complete.py +124 -0
  76. agentic_project_kit/cli_commands/transfer_pr_create_flow.py +743 -0
  77. agentic_project_kit/cli_commands/transfer_pr_merge_flow.py +547 -0
  78. agentic_project_kit/cli_commands/transfer_repo_after_pr.py +160 -0
  79. agentic_project_kit/cli_commands/transfer_repo_pre_pr.py +664 -0
  80. agentic_project_kit/cli_commands/transfer_runner_flow.py +131 -0
  81. agentic_project_kit/cli_commands/transfer_runtime_initial.py +194 -0
  82. agentic_project_kit/cli_commands/transfer_shared.py +274 -0
  83. agentic_project_kit/cli_commands/validation.py +200 -0
  84. agentic_project_kit/cli_commands/version.py +27 -0
  85. agentic_project_kit/cli_commands/work_orders.py +219 -0
  86. agentic_project_kit/cli_commands/workflow.py +471 -0
  87. agentic_project_kit/cli_commands/workflow_guard.py +26 -0
  88. agentic_project_kit/cli_commands/workspace.py +252 -0
  89. agentic_project_kit/cockpit.py +608 -0
  90. agentic_project_kit/cockpit_readiness.py +67 -0
  91. agentic_project_kit/command_authority_audit.py +192 -0
  92. agentic_project_kit/command_inbox_check.py +103 -0
  93. agentic_project_kit/command_manifest.py +591 -0
  94. agentic_project_kit/command_queue_contract.py +161 -0
  95. agentic_project_kit/command_selector.py +245 -0
  96. agentic_project_kit/command_taxonomy.py +264 -0
  97. agentic_project_kit/commit_guard.py +67 -0
  98. agentic_project_kit/communication_artifact_gc.py +495 -0
  99. agentic_project_kit/communication_rule_context.py +437 -0
  100. agentic_project_kit/communication_state.py +115 -0
  101. agentic_project_kit/composed_wrapper_invariants.py +97 -0
  102. agentic_project_kit/composite_short_circuit.py +140 -0
  103. agentic_project_kit/contract.py +251 -0
  104. agentic_project_kit/control_state.py +83 -0
  105. agentic_project_kit/doc_currency_audit.py +536 -0
  106. agentic_project_kit/doc_lifecycle.py +1179 -0
  107. agentic_project_kit/doc_lifecycle_signals.py +446 -0
  108. agentic_project_kit/doc_lifecycle_sweep.py +639 -0
  109. agentic_project_kit/doc_mesh.py +491 -0
  110. agentic_project_kit/doctor.py +379 -0
  111. agentic_project_kit/documentation_registry.py +1122 -0
  112. agentic_project_kit/documentation_system_audit.py +310 -0
  113. agentic_project_kit/dpa_current_handoff_lifecycle.py +549 -0
  114. agentic_project_kit/dpa_dp2_decision_readiness.py +362 -0
  115. agentic_project_kit/dpa_dp3_dp4_adjudication.py +604 -0
  116. agentic_project_kit/dpa_dp5_block_new_gate.py +294 -0
  117. agentic_project_kit/dpa_dp5_stage_adoption.py +615 -0
  118. agentic_project_kit/dpa_dp5_strict_gate.py +187 -0
  119. agentic_project_kit/dpa_final_closeout.py +627 -0
  120. agentic_project_kit/dpa_fixture_evidence.py +680 -0
  121. agentic_project_kit/dpa_maintainer_record_check.py +711 -0
  122. agentic_project_kit/dpa_post_dp2_scope_assessment.py +734 -0
  123. agentic_project_kit/dpa_probe_002_readiness.py +509 -0
  124. agentic_project_kit/dpa_probe_003_readiness.py +417 -0
  125. agentic_project_kit/dpa_probe_004_readiness.py +437 -0
  126. agentic_project_kit/dpa_readiness.py +505 -0
  127. agentic_project_kit/dpa_readonly_probe_execution.py +500 -0
  128. agentic_project_kit/dpa_registry_contracts.py +312 -0
  129. agentic_project_kit/dpa_renderer_readiness.py +407 -0
  130. agentic_project_kit/dpa_repo_adoption_assessment.py +574 -0
  131. agentic_project_kit/dpa_stable_readiness.py +743 -0
  132. agentic_project_kit/dpa_successor_projection.py +168 -0
  133. agentic_project_kit/dpa_workspace_init_projection.py +93 -0
  134. agentic_project_kit/dpa_wrt_ch001_evidence.py +327 -0
  135. agentic_project_kit/entrypoint_slice_runner.py +153 -0
  136. agentic_project_kit/evidence_clean.py +152 -0
  137. agentic_project_kit/evidence_commit_paths.py +214 -0
  138. agentic_project_kit/evidence_finalize_log.py +204 -0
  139. agentic_project_kit/evidence_guard.py +114 -0
  140. agentic_project_kit/evidence_inspector.py +507 -0
  141. agentic_project_kit/evidence_state_contract.py +99 -0
  142. agentic_project_kit/execution_mode_state.py +148 -0
  143. agentic_project_kit/final_summary_contract.py +85 -0
  144. agentic_project_kit/finalize_guard.py +139 -0
  145. agentic_project_kit/gatekeeper_core.py +232 -0
  146. agentic_project_kit/github.py +25 -0
  147. agentic_project_kit/governance.py +59 -0
  148. agentic_project_kit/gui_action_execution.py +105 -0
  149. agentic_project_kit/gui_action_renderer.py +81 -0
  150. agentic_project_kit/gui_action_run_intent.py +98 -0
  151. agentic_project_kit/gui_action_views.py +195 -0
  152. agentic_project_kit/gui_activity_log.py +76 -0
  153. agentic_project_kit/gui_button_catalog.py +605 -0
  154. agentic_project_kit/gui_cockpit.py +365 -0
  155. agentic_project_kit/gui_cockpit_actions.py +695 -0
  156. agentic_project_kit/gui_cockpit_common.py +172 -0
  157. agentic_project_kit/gui_cockpit_header.py +501 -0
  158. agentic_project_kit/gui_cockpit_sidebar.py +387 -0
  159. agentic_project_kit/gui_cockpit_task.py +748 -0
  160. agentic_project_kit/gui_command_projection.py +355 -0
  161. agentic_project_kit/gui_communication_modes.py +130 -0
  162. agentic_project_kit/gui_dry_run.py +219 -0
  163. agentic_project_kit/gui_gatekeeper_status.py +259 -0
  164. agentic_project_kit/gui_layout_plan.py +148 -0
  165. agentic_project_kit/gui_open_folder.py +18 -0
  166. agentic_project_kit/gui_output_status_panel.py +61 -0
  167. agentic_project_kit/gui_panel_state.py +47 -0
  168. agentic_project_kit/gui_presenter.py +62 -0
  169. agentic_project_kit/gui_readiness_gate.py +206 -0
  170. agentic_project_kit/gui_release_flow.py +59 -0
  171. agentic_project_kit/gui_task_editor.py +1077 -0
  172. agentic_project_kit/gui_tk_widgets.py +177 -0
  173. agentic_project_kit/gui_tkinter_renderer.py +87 -0
  174. agentic_project_kit/gui_tkinter_shell.py +1176 -0
  175. agentic_project_kit/gui_transfer_contract.py +28 -0
  176. agentic_project_kit/gui_viewmodel.py +698 -0
  177. agentic_project_kit/gui_window_guard.py +46 -0
  178. agentic_project_kit/handoff_freshness.py +390 -0
  179. agentic_project_kit/handoff_prompt.py +180 -0
  180. agentic_project_kit/handoff_state.py +254 -0
  181. agentic_project_kit/instruction_lint.py +455 -0
  182. agentic_project_kit/llm_context_carriers.py +66 -0
  183. agentic_project_kit/llm_execution_context.py +285 -0
  184. agentic_project_kit/local_command_stack.py +133 -0
  185. agentic_project_kit/local_feature_gate.py +82 -0
  186. agentic_project_kit/local_garbage_collector.py +269 -0
  187. agentic_project_kit/models.py +18 -0
  188. agentic_project_kit/mutation_lock_audit.py +421 -0
  189. agentic_project_kit/next_turn_evidence.py +253 -0
  190. agentic_project_kit/next_turn_merge_if_green.py +459 -0
  191. agentic_project_kit/next_turn_pr_status.py +255 -0
  192. agentic_project_kit/next_turn_result.py +183 -0
  193. agentic_project_kit/next_turn_runner.py +159 -0
  194. agentic_project_kit/next_turn_slot.py +107 -0
  195. agentic_project_kit/next_turn_status.py +274 -0
  196. agentic_project_kit/ns_legacy_reference_audit.py +258 -0
  197. agentic_project_kit/ns_slice_runner.py +16 -0
  198. agentic_project_kit/operational_handoff_projection.py +124 -0
  199. agentic_project_kit/output_contract.py +80 -0
  200. agentic_project_kit/output_repair.py +61 -0
  201. agentic_project_kit/pass_already_done.py +216 -0
  202. agentic_project_kit/patch_artifact_preflight.py +147 -0
  203. agentic_project_kit/patch_cycle_workflow.py +440 -0
  204. agentic_project_kit/patch_failure_discipline_audit.py +223 -0
  205. agentic_project_kit/patch_preflight.py +215 -0
  206. agentic_project_kit/path_literal_audit.py +612 -0
  207. agentic_project_kit/patterns.py +99 -0
  208. agentic_project_kit/planning_docs_consolidation_audit.py +313 -0
  209. agentic_project_kit/portability_shell_gate.py +40 -0
  210. agentic_project_kit/post_merge_handoff_refresh.py +56 -0
  211. agentic_project_kit/post_release.py +252 -0
  212. agentic_project_kit/post_release_closeout.py +616 -0
  213. agentic_project_kit/pr_cleanup.py +172 -0
  214. agentic_project_kit/pr_closeout.py +50 -0
  215. agentic_project_kit/pr_closeout_complete.py +344 -0
  216. agentic_project_kit/pr_create_or_skip.py +71 -0
  217. agentic_project_kit/pr_hygiene.py +210 -0
  218. agentic_project_kit/program_redundancy_audit.py +251 -0
  219. agentic_project_kit/project_direction.py +840 -0
  220. agentic_project_kit/protected_change_planner.py +194 -0
  221. agentic_project_kit/release.py +401 -0
  222. agentic_project_kit/release_doi_safety.py +24 -0
  223. agentic_project_kit/release_gate_core.py +174 -0
  224. agentic_project_kit/release_metadata_authority_gate.py +328 -0
  225. agentic_project_kit/release_metadata_prep.py +43 -0
  226. agentic_project_kit/release_notes.py +823 -0
  227. agentic_project_kit/release_prep_core.py +204 -0
  228. agentic_project_kit/release_prepare.py +369 -0
  229. agentic_project_kit/release_process_guardrails.py +247 -0
  230. agentic_project_kit/release_publish_core.py +114 -0
  231. agentic_project_kit/release_publish_orchestration.py +469 -0
  232. agentic_project_kit/release_state.py +552 -0
  233. agentic_project_kit/release_verify_core.py +175 -0
  234. agentic_project_kit/remote_branch_hygiene.py +223 -0
  235. agentic_project_kit/remote_next.py +89 -0
  236. agentic_project_kit/remote_next_closeout.py +101 -0
  237. agentic_project_kit/remote_next_order_contract.py +391 -0
  238. agentic_project_kit/removed_ns_commands.py +458 -0
  239. agentic_project_kit/removed_source_audit.py +225 -0
  240. agentic_project_kit/rendering.py +13 -0
  241. agentic_project_kit/repair_report.py +79 -0
  242. agentic_project_kit/repo_identity.py +59 -0
  243. agentic_project_kit/result_report_classifier.py +57 -0
  244. agentic_project_kit/rule_ack.py +105 -0
  245. agentic_project_kit/rule_preservation.py +58 -0
  246. agentic_project_kit/rule_refresh.py +174 -0
  247. agentic_project_kit/rule_registry_registration.py +322 -0
  248. agentic_project_kit/rule_registry_report.py +206 -0
  249. agentic_project_kit/rule_registry_validator.py +704 -0
  250. agentic_project_kit/rule_snapshot.py +98 -0
  251. agentic_project_kit/rule_source_validator.py +133 -0
  252. agentic_project_kit/run_summary.py +68 -0
  253. agentic_project_kit/run_summary_renderer.py +238 -0
  254. agentic_project_kit/runtime_validator.py +68 -0
  255. agentic_project_kit/safe_file_replace.py +35 -0
  256. agentic_project_kit/safe_patch.py +47 -0
  257. agentic_project_kit/safe_push.py +218 -0
  258. agentic_project_kit/safe_remove_diagnostic.py +51 -0
  259. agentic_project_kit/scaffold.py +69 -0
  260. agentic_project_kit/site_claims.py +444 -0
  261. agentic_project_kit/site_generator.py +1140 -0
  262. agentic_project_kit/slice_gate.py +225 -0
  263. agentic_project_kit/slice_runner.py +75 -0
  264. agentic_project_kit/standard_gates_audit_suite.py +167 -0
  265. agentic_project_kit/state_freshness.py +135 -0
  266. agentic_project_kit/status_current_state_audit.py +762 -0
  267. agentic_project_kit/successor_handoff_package.py +1763 -0
  268. agentic_project_kit/templates.py +292 -0
  269. agentic_project_kit/terminal_block_guard.py +166 -0
  270. agentic_project_kit/terminal_logging.py +275 -0
  271. agentic_project_kit/todo.py +97 -0
  272. agentic_project_kit/transfer_closeout.py +136 -0
  273. agentic_project_kit/transfer_continue.py +215 -0
  274. agentic_project_kit/transfer_local_runner.py +101 -0
  275. agentic_project_kit/transfer_operation_monitor.py +236 -0
  276. agentic_project_kit/transfer_post_merge_lifecycle.py +356 -0
  277. agentic_project_kit/transfer_post_merge_settle.py +383 -0
  278. agentic_project_kit/transfer_pr_actions.py +164 -0
  279. agentic_project_kit/transfer_remote_next.py +959 -0
  280. agentic_project_kit/transfer_repo_actions.py +2253 -0
  281. agentic_project_kit/transfer_runner.py +347 -0
  282. agentic_project_kit/transfer_safety_context.py +291 -0
  283. agentic_project_kit/transfer_state.py +483 -0
  284. agentic_project_kit/transfer_uplink.py +471 -0
  285. agentic_project_kit/transfer_workflow_next.py +148 -0
  286. agentic_project_kit/typed_work_order_evidence.py +58 -0
  287. agentic_project_kit/typed_work_order_queue.py +166 -0
  288. agentic_project_kit/typed_work_order_runner.py +168 -0
  289. agentic_project_kit/work_cycle.py +317 -0
  290. agentic_project_kit/work_discard_changes.py +129 -0
  291. agentic_project_kit/work_order_patch.py +199 -0
  292. agentic_project_kit/work_order_runner.py +120 -0
  293. agentic_project_kit/work_order_uploader.py +220 -0
  294. agentic_project_kit/work_order_validator.py +185 -0
  295. agentic_project_kit/work_orders.py +246 -0
  296. agentic_project_kit/workflow_guard.py +262 -0
  297. agentic_project_kit/workflow_summary_runner.py +124 -0
  298. agentic_project_kit/workspace.py +665 -0
  299. agentic_project_kit/workspace_adopt.py +471 -0
  300. agentic_project_kit/workspace_dpa_intake.py +406 -0
  301. agentic_project_kit/workspace_init.py +487 -0
  302. agentic_project_kit/workspace_lock.py +129 -0
  303. agentic_project_kit/workspace_remove.py +381 -0
  304. agentic_project_kit/workspace_upgrade.py +324 -0
  305. agentic_project_kit/wrapper_live_status.py +104 -0
  306. agentic_project_kit-1.0.1.dist-info/METADATA +724 -0
  307. agentic_project_kit-1.0.1.dist-info/RECORD +310 -0
  308. agentic_project_kit-1.0.1.dist-info/WHEEL +4 -0
  309. agentic_project_kit-1.0.1.dist-info/entry_points.txt +3 -0
  310. agentic_project_kit-1.0.1.dist-info/licenses/LICENSE +10 -0
@@ -0,0 +1,257 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import subprocess
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from agentic_project_kit.operational_handoff_projection import render_current_operational_handoff_state
9
+ from agentic_project_kit.workspace import LEGACY_DEFAULTS, load_workspace
10
+
11
+ DEFAULT_BOOTSTRAP_PATH = Path(LEGACY_DEFAULTS.handoff_root) / "NEXT_CHAT_BOOTSTRAP.md"
12
+ START_PROMPT_PATH = "docs/handoff/START_NEW_CHAT_PROMPT.md"
13
+ CLOSEOUT_PROMPT_PATH = "docs/handoff/CLOSEOUT_BEFORE_CHAT_SWITCH_PROMPT.md"
14
+ BOOT_REPORT_PATH = Path(LEGACY_DEFAULTS.handoff_root) / "BOOT_REPORT.md"
15
+
16
+ MANDATORY_BOOT_SOURCES = (
17
+ ".agentic/compiled_agent_context.yaml",
18
+ ".agentic/handoff_state.yaml",
19
+ ".agentic/operational_handoff_state.yaml",
20
+ ".agentic/rule_mechanism_inventory.yaml",
21
+ ".agentic/rule_migrations.yaml",
22
+ ".agentic/rule_preservation.yaml",
23
+ "docs/STATUS.md",
24
+ "docs/handoff/CURRENT_HANDOFF.md",
25
+ "docs/handoff/START_NEW_CHAT_PROMPT.md",
26
+ "docs/handoff/CLOSEOUT_BEFORE_CHAT_SWITCH_PROMPT.md",
27
+ "docs/governance/FINAL_SUMMARY_CONTRACT.md",
28
+ "docs/governance/CHAT_COMMUNICATION_CONTRACT.md",
29
+ "docs/governance/PORTABLE_CHAT_EXECUTION_CONTRACT.md",
30
+ "docs/governance/CHAT_BOOTSTRAP_AND_DRIFT_CONTRACT.md",
31
+ "docs/DOCUMENTATION_REGISTRY.yaml",
32
+ "docs/planning/PROJECT_DIRECTION.yaml",
33
+ )
34
+
35
+ BOOTLOADER_RULES = (
36
+ "Start from repository artifacts instead of chat memory.",
37
+ "Read mandatory boot sources before repository changes.",
38
+ "Prefer Python runners for local workflow execution; shell remains a thin adapter.",
39
+ "Use run_summary_renderer for final summaries in evidence-bearing workflows.",
40
+ "Evidence-bearing local workflow finalization must use `agentic-kit evidence finalize-log` or a stricter successor. Freehand final PASS footers are not valid closeout evidence.",
41
+ "Treat d, f, and w as communication signals rather than evidence.",
42
+ "Run `agentic-kit evidence inspect --require-summary` or inspect equivalent remote/repo evidence before continuing after chat control signals.",
43
+ "Inspect repo or remote evidence before requesting pasted terminal output.",
44
+ (
45
+ "Use the supported protected-change planning route before protected YAML, JSON, "
46
+ "or Markdown control changes: `./ns protected-change-plan --diff-file <file>` "
47
+ "or `python -m agentic_project_kit.protected_change_planner --diff-file <file>`. "
48
+ "Do not use `agentic-kit protected-change-plan`; that package CLI command is not "
49
+ "registered."
50
+ ),
51
+ "Before a chat switch, run the closeout prompt and check whether START_NEW_CHAT_PROMPT.md, CLOSEOUT_BEFORE_CHAT_SWITCH_PROMPT.md, and NEXT_CHAT_BOOTSTRAP.md all need updates.",
52
+ )
53
+
54
+ def current_operational_handoff_state(root: Path | str = ".") -> tuple[str, ...]:
55
+ return render_current_operational_handoff_state(root)
56
+
57
+ NEXT_WORK_ITEMS = (
58
+ "Finish local sync after the bootloader/summary-runner merge and verify boot write/check plus targeted tests.",
59
+ "Use boot write to refresh docs/handoff/NEXT_CHAT_BOOTSTRAP.md before chat changes.",
60
+ "Harden no-op/PASS_ALREADY_DONE handling for already satisfied target states.",
61
+ "Use `agentic-kit evidence inspect --require-summary` after chat control signals so d/f/w do not rely on chat memory.",
62
+ "Use `agentic-kit evidence finalize-log` for evidence-bearing local workflow closeout so invalid summary fields cannot still end in a freehand final PASS.",
63
+ "Automate red CI failed-log diagnosis in the repo workflow path.",
64
+ "Resume Rule Registry Phase A only in small PRs: typed schema, generated projections, stronger assertions, query/impact analysis, and documentation integration.",
65
+ "Continue the document-management projection system: move operative truth into machine-readable sources and keep Markdown as generated or verified projection where possible.",
66
+ "Postpone GUI work until the workflow kernel, no-op handling, evidence inspection, and red-CI diagnosis are stable.",
67
+ )
68
+
69
+
70
+ @dataclass(frozen=True)
71
+ class BootCheckResult:
72
+ source: str
73
+ exists: bool
74
+
75
+
76
+ def _run_git(args: list[str], root: Path) -> str:
77
+ completed = subprocess.run(["git", *args], cwd=root, text=True, capture_output=True, check=False)
78
+ return completed.stdout.strip() if completed.returncode == 0 else "UNKNOWN"
79
+
80
+
81
+ def current_git_head(root: Path | str = ".") -> str:
82
+ return _run_git(["rev-parse", "HEAD"], Path(root))
83
+
84
+
85
+ def current_git_branch(root: Path | str = ".") -> str:
86
+ return _run_git(["branch", "--show-current"], Path(root))
87
+
88
+
89
+ def check_boot_sources(root: Path | str = ".") -> tuple[BootCheckResult, ...]:
90
+ root_path = Path(root)
91
+ return tuple(BootCheckResult(source, (root_path / source).exists()) for source in MANDATORY_BOOT_SOURCES)
92
+
93
+
94
+ def render_bootloader(root: Path | str = ".") -> str:
95
+ checks = check_boot_sources(root)
96
+ lines = [
97
+ "CHAT_BOOTLOADER",
98
+ "",
99
+ "Purpose: bootstrap a successor chat from repository truth.",
100
+ "",
101
+ "Mandatory boot sources:",
102
+ ]
103
+ for check in checks:
104
+ state = "present" if check.exists else "missing"
105
+ lines.append(f"- [{state}] {check.source}")
106
+ lines.extend(["", "Mandatory workflow rules:"])
107
+ for rule in BOOTLOADER_RULES:
108
+ lines.append(f"- {rule}")
109
+ lines.extend(
110
+ [
111
+ "",
112
+ "Required first action in a successor chat:",
113
+ "- Read these sources and verify main, open PRs, CI, STATUS, handoff, rule registry, and final-summary contracts before repository changes.",
114
+ "- If continuing after a chat control signal, run `agentic-kit evidence inspect --require-summary` or inspect equivalent remote/repo evidence first.",
115
+ "",
116
+ "### RESULT: PASS ###",
117
+ ]
118
+ )
119
+ return "\n".join(lines)
120
+
121
+
122
+ def render_next_chat_bootstrap(root: Path | str = ".", *, include_state: bool = False) -> str:
123
+ """Render the deterministic successor handoff bootstrap projection.
124
+
125
+ Prefer the committed latest successor package when present. This makes
126
+ bootstrap validation stable in CI merge-checkout contexts. The
127
+ chat-switch-complete command remains responsible for refreshing the package
128
+ from live local state.
129
+ """
130
+ package_context = Path(root) / "docs/reports/handoff-packages/latest/successor_context.yaml"
131
+ if package_context.exists():
132
+ from agentic_project_kit.successor_handoff_package import (
133
+ load_successor_context,
134
+ render_next_chat_bootstrap_from_context,
135
+ )
136
+
137
+ return render_next_chat_bootstrap_from_context(load_successor_context(package_context))
138
+
139
+ from agentic_project_kit.successor_handoff_package import build_successor_handoff_package
140
+
141
+ result = build_successor_handoff_package(root)
142
+ return result.next_chat_bootstrap
143
+
144
+
145
+
146
+ def write_next_chat_bootstrap(
147
+ path: Path | str = DEFAULT_BOOTSTRAP_PATH,
148
+ root: Path | str = ".",
149
+ *,
150
+ include_state: bool = False,
151
+ ) -> Path:
152
+ output_path = Path(path)
153
+ if output_path == DEFAULT_BOOTSTRAP_PATH:
154
+ output_path = load_workspace(Path(root)).handoff_file("NEXT_CHAT_BOOTSTRAP.md")
155
+ output_path.parent.mkdir(parents=True, exist_ok=True)
156
+ output_path.write_text(render_next_chat_bootstrap(root, include_state=include_state), encoding="utf-8")
157
+ return output_path
158
+
159
+
160
+ def validate_generated_bootstrap(path: Path | str = DEFAULT_BOOTSTRAP_PATH, root: Path | str = ".") -> list[str]:
161
+ output_path = Path(path)
162
+ if output_path == DEFAULT_BOOTSTRAP_PATH:
163
+ output_path = load_workspace(Path(root)).handoff_file("NEXT_CHAT_BOOTSTRAP.md")
164
+ elif not output_path.is_absolute():
165
+ output_path = Path(root) / output_path
166
+ if not output_path.exists():
167
+ return [f"missing bootstrap file: {path}"]
168
+ expected = render_next_chat_bootstrap(root)
169
+ actual = output_path.read_text(encoding="utf-8")
170
+ if actual != expected:
171
+ return ["bootstrap file is not generated from chat_bootloader.py"]
172
+ return []
173
+
174
+
175
+ def render_boot_report(root: Path | str = ".") -> str:
176
+ checks = check_boot_sources(root)
177
+ missing = [item.source for item in checks if not item.exists]
178
+ drift = validate_generated_bootstrap(root=root)
179
+ lines = [
180
+ "BOOT_REPORT",
181
+ "",
182
+ f"branch: {current_git_branch(root)}",
183
+ f"head: {current_git_head(root)}",
184
+ f"mandatory_sources_total: {len(checks)}",
185
+ f"mandatory_sources_missing: {len(missing)}",
186
+ "sources_read_required: yes",
187
+ "open_prs: inspect_remote_github_before_mutation",
188
+ "ci: inspect_remote_github_before_mutation",
189
+ "drift_findings:",
190
+ ]
191
+ lines.extend(f"- {finding}" for finding in drift) if drift else lines.append("- none")
192
+ lines.extend(
193
+ [
194
+ "next_safe_slice: verify remote PRs/CI, then continue with no-op/PASS_ALREADY_DONE hardening",
195
+ "### RESULT: PASS ###" if not missing and not drift else "### RESULT: FAIL ###",
196
+ ]
197
+ )
198
+ return "\n".join(lines)
199
+
200
+
201
+ def write_boot_report(path: Path | str = BOOT_REPORT_PATH, root: Path | str = ".") -> Path:
202
+ output_path = Path(path)
203
+ if output_path == BOOT_REPORT_PATH:
204
+ output_path = load_workspace(Path(root)).handoff_file("BOOT_REPORT.md")
205
+ output_path.parent.mkdir(parents=True, exist_ok=True)
206
+ output_path.write_text(render_boot_report(root), encoding="utf-8")
207
+ return output_path
208
+
209
+
210
+ def run_chat_switch_closeout(root: Path | str = ".") -> list[str]:
211
+ findings: list[str] = []
212
+ findings.extend(validate_generated_bootstrap(root=root))
213
+ missing = [item.source for item in check_boot_sources(root) if not item.exists]
214
+ findings.extend(f"missing mandatory boot source: {source}" for source in missing)
215
+ return findings
216
+
217
+
218
+ def build_parser() -> argparse.ArgumentParser:
219
+ parser = argparse.ArgumentParser(prog="chat-bootloader")
220
+ parser.add_argument("--root", default=".")
221
+ parser.add_argument("--check", action="store_true")
222
+ parser.add_argument("--write", action="store_true")
223
+ parser.add_argument("--path", default=str(DEFAULT_BOOTSTRAP_PATH))
224
+ parser.add_argument("--include-state", action="store_true")
225
+ parser.add_argument("--report", action="store_true")
226
+ parser.add_argument("--closeout", action="store_true")
227
+ return parser
228
+
229
+
230
+ def main(argv: list[str] | None = None) -> int:
231
+ args = build_parser().parse_args(argv)
232
+ status = 0
233
+ if args.write:
234
+ output_path = write_next_chat_bootstrap(args.path, args.root, include_state=args.include_state)
235
+ print(f"WROTE {output_path}")
236
+ elif args.report:
237
+ print(render_boot_report(args.root))
238
+ elif args.closeout:
239
+ findings = run_chat_switch_closeout(args.root)
240
+ if findings:
241
+ print("CHAT_SWITCH_CLOSEOUT: FAIL")
242
+ for finding in findings:
243
+ print(f"- {finding}")
244
+ status = 1
245
+ else:
246
+ print("CHAT_SWITCH_CLOSEOUT: PASS")
247
+ else:
248
+ print(render_bootloader(args.root))
249
+ if args.check:
250
+ missing = [item.source for item in check_boot_sources(args.root) if not item.exists]
251
+ if missing:
252
+ status = 1
253
+ return status
254
+
255
+
256
+ if __name__ == "__main__":
257
+ raise SystemExit(main())
@@ -0,0 +1,297 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ import hashlib
6
+ import re
7
+ from typing import Any
8
+
9
+ COMMAND_REFERENCE_JSON = "docs/reference/agentic-kit-commands.json"
10
+ COMMAND_REFERENCE_MARKDOWN = "docs/reference/AGENTIC_KIT_COMMANDS.md"
11
+ ENTRYPOINT_START = "<!-- command-manifest-entrypoint:start -->"
12
+ ENTRYPOINT_END = "<!-- command-manifest-entrypoint:end -->"
13
+ ENTRYPOINT_SOURCE_PATHS = (
14
+ "AGENTS.md",
15
+ "src/agentic_project_kit/workspace_init.py",
16
+ "src/agentic_project_kit/gui_task_editor.py",
17
+ )
18
+ CHAT_MODES = ("copy-paste", "remote", "file-transfer")
19
+ _SHA_REFERENCE_RE = re.compile(
20
+ r"(?:manifest_sha:\s*|COMMAND_MANIFEST_ACK\s+)(?P<sha>[0-9a-f]{12}|stale|UNKNOWN)"
21
+ )
22
+
23
+
24
+ def load_command_manifest(root: Path | str = ".") -> dict[str, Any]:
25
+ path = Path(root) / COMMAND_REFERENCE_JSON
26
+ if not path.exists():
27
+ return {"meta": {"manifest_sha": "UNKNOWN"}, "commands": []}
28
+ try:
29
+ return json.loads(path.read_text(encoding="utf-8"))
30
+ except json.JSONDecodeError:
31
+ return {"meta": {"manifest_sha": "UNKNOWN"}, "commands": []}
32
+
33
+
34
+ def command_manifest_sha(manifest: dict[str, Any]) -> str:
35
+ meta = manifest.get("meta") if isinstance(manifest.get("meta"), dict) else {}
36
+ return str(meta.get("manifest_sha") or "UNKNOWN")
37
+
38
+
39
+ def computed_command_manifest_sha(manifest: dict[str, Any]) -> str:
40
+ commands = manifest.get("commands")
41
+ if not isinstance(commands, list):
42
+ return command_manifest_sha(manifest)
43
+ canonical = json.dumps(commands, sort_keys=True, separators=(",", ":")).encode("utf-8")
44
+ return hashlib.sha256(canonical).hexdigest()[:12]
45
+
46
+
47
+ def command_manifest_ack_line(manifest: dict[str, Any]) -> str:
48
+ return f"COMMAND_MANIFEST_ACK {command_manifest_sha(manifest)}"
49
+
50
+
51
+ def mandatory_entrypoint_line(manifest: dict[str, Any]) -> str:
52
+ sha = command_manifest_sha(manifest)
53
+ return (
54
+ f"MANDATORY FIRST READ: {COMMAND_REFERENCE_JSON} (manifest_sha: {sha}). "
55
+ "Every reply containing commands MUST start with: "
56
+ f"COMMAND_MANIFEST_ACK {sha}. Consult `agentic-kit command-for` before proposing commands "
57
+ "and choose the most specific available Kit workflow command."
58
+ )
59
+
60
+
61
+ def mandatory_entrypoint_block(manifest: dict[str, Any]) -> str:
62
+ return "\n".join((ENTRYPOINT_START, mandatory_entrypoint_line(manifest), ENTRYPOINT_END))
63
+
64
+
65
+ def chat_refresher_lines(mode: str, manifest: dict[str, Any]) -> tuple[str, ...]:
66
+ normalized_mode = _normalize_chat_mode(mode)
67
+ sha = command_manifest_sha(manifest)
68
+ mode_lines = {
69
+ "copy-paste": "Mode copy-paste: your reply will be linted before execution.",
70
+ "remote": "Mode remote: read the manifest file first, it is the single source.",
71
+ "file-transfer": "Mode file-transfer: the carrier header pins the manifest sha.",
72
+ }
73
+ return (
74
+ command_manifest_ack_line(manifest),
75
+ f"Manifest: {COMMAND_REFERENCE_JSON} (manifest_sha: {sha}).",
76
+ "Before proposing ANY command run/consult `agentic-kit command-for` and choose the most specific available Kit workflow command.",
77
+ "raw git/gh commands with a mapped wrapper are rejected by instruction lint.",
78
+ mode_lines[normalized_mode],
79
+ f"Readable reference: {COMMAND_REFERENCE_MARKDOWN}.",
80
+ )
81
+
82
+
83
+ def render_chat_refresher(mode: str = "copy-paste", root: Path | str = ".") -> str:
84
+ return "\n".join(chat_refresher_lines(mode, load_command_manifest(root))) + "\n"
85
+
86
+
87
+ def render_chat_session_start(mode: str = "copy-paste", root: Path | str = ".") -> str:
88
+ manifest = load_command_manifest(root)
89
+ lines = [*chat_refresher_lines(mode, manifest), "", "Command manifest inline list:"]
90
+ for command in manifest.get("commands") or []:
91
+ if not isinstance(command, dict):
92
+ continue
93
+ lines.append(
94
+ f"{command.get('qualified_name', '')} · {command.get('safety', '')} · "
95
+ f"{command.get('when_to_use', '')}"
96
+ )
97
+ return "\n".join(lines).rstrip() + "\n"
98
+
99
+
100
+ def _normalize_chat_mode(mode: str) -> str:
101
+ normalized = str(mode or "").strip().lower().replace("_", "-")
102
+ if normalized not in CHAT_MODES:
103
+ raise ValueError(f"unsupported chat mode: {mode}")
104
+ return normalized
105
+
106
+
107
+ def command_reference_contract(
108
+ root: Path | str = ".",
109
+ *,
110
+ manifest: dict[str, Any] | None = None,
111
+ source_texts: dict[str, str] | None = None,
112
+ ) -> dict[str, Any]:
113
+ repo_root = Path(root)
114
+ manifest = manifest or load_command_manifest(repo_root)
115
+ source_hashes: dict[str, str] = {}
116
+ for rel in (COMMAND_REFERENCE_JSON, COMMAND_REFERENCE_MARKDOWN):
117
+ if source_texts is not None and rel in source_texts:
118
+ source_hashes[rel] = hashlib.sha256(source_texts[rel].encode("utf-8")).hexdigest()
119
+ elif (path := repo_root / rel).exists():
120
+ source_hashes[rel] = hashlib.sha256(path.read_bytes()).hexdigest()
121
+
122
+ return {
123
+ "json": COMMAND_REFERENCE_JSON,
124
+ "markdown": COMMAND_REFERENCE_MARKDOWN,
125
+ "manifest_sha": command_manifest_sha(manifest),
126
+ "ack": command_manifest_ack_line(manifest),
127
+ "must_not_reconstruct_commands_from_memory": True,
128
+ "source_hashes": source_hashes,
129
+ }
130
+
131
+
132
+ def command_reference_prompt_block(
133
+ root: Path | str = ".",
134
+ *,
135
+ manifest: dict[str, Any] | None = None,
136
+ source_texts: dict[str, str] | None = None,
137
+ ) -> str:
138
+ contract = command_reference_contract(root, manifest=manifest, source_texts=source_texts)
139
+ manifest = manifest or load_command_manifest(root)
140
+ hashes = contract["source_hashes"]
141
+ hash_lines = "\n".join(f"- {path}: {digest}" for path, digest in sorted(hashes.items()))
142
+ if not hash_lines:
143
+ hash_lines = "- source_hashes: unavailable; regenerate from repository root"
144
+
145
+ return (
146
+ "Command manifest entrypoint:\n"
147
+ f"- {mandatory_entrypoint_line(manifest)}\n"
148
+ f"- {chat_refresher_lines('remote', manifest)[2]}\n"
149
+ f"- {chat_refresher_lines('remote', manifest)[3]}\n\n"
150
+ "Command reference contract:\n"
151
+ f"- Read `{COMMAND_REFERENCE_JSON}` before composing agentic-kit commands.\n"
152
+ f"- Read `{COMMAND_REFERENCE_MARKDOWN}` before composing agentic-kit commands.\n"
153
+ "- `must_not_reconstruct_commands_from_memory: true`.\n"
154
+ "- Treat `source_hashes` as freshness evidence.\n"
155
+ "source_hashes:\n"
156
+ f"{hash_lines}"
157
+ )
158
+
159
+
160
+ def ensure_command_reference_in_prompt(
161
+ prompt_text: str,
162
+ root: Path | str = ".",
163
+ *,
164
+ manifest: dict[str, Any] | None = None,
165
+ source_texts: dict[str, str] | None = None,
166
+ ) -> str:
167
+ manifest = manifest or load_command_manifest(root)
168
+ if (
169
+ COMMAND_REFERENCE_JSON in prompt_text
170
+ and command_manifest_ack_line(manifest) in prompt_text
171
+ and "must_not_reconstruct_commands_from_memory" in prompt_text
172
+ ):
173
+ return prompt_text
174
+ return (
175
+ prompt_text.rstrip()
176
+ + "\n\n"
177
+ + command_reference_prompt_block(root, manifest=manifest, source_texts=source_texts)
178
+ + "\n"
179
+ )
180
+
181
+
182
+ _COMMAND_REFERENCE_PROMPT_BLOCK_RE = re.compile(
183
+ r"(?:\n\n)?Command manifest entrypoint:\n"
184
+ r".*?"
185
+ r"source_hashes:\n"
186
+ r"(?:- [^\n]+\n?)+",
187
+ re.DOTALL,
188
+ )
189
+
190
+
191
+ def upsert_command_reference_prompt_block(
192
+ prompt_text: str,
193
+ root: Path | str = ".",
194
+ *,
195
+ manifest: dict[str, Any] | None = None,
196
+ source_texts: dict[str, str] | None = None,
197
+ ) -> str:
198
+ block = command_reference_prompt_block(root, manifest=manifest, source_texts=source_texts)
199
+ if "Command manifest entrypoint:" in prompt_text and "Command reference contract:" in prompt_text:
200
+ updated = _COMMAND_REFERENCE_PROMPT_BLOCK_RE.sub("\n\n" + block + "\n", prompt_text, count=1)
201
+ if updated != prompt_text:
202
+ return updated.lstrip("\n")
203
+ return ensure_command_reference_in_prompt(prompt_text, root, manifest=manifest, source_texts=source_texts)
204
+
205
+
206
+ def upsert_mandatory_entrypoint_block(text: str, manifest: dict[str, Any]) -> str:
207
+ block = mandatory_entrypoint_block(manifest)
208
+ if ENTRYPOINT_START in text and ENTRYPOINT_END in text:
209
+ pattern = re.compile(
210
+ re.escape(ENTRYPOINT_START) + r".*?" + re.escape(ENTRYPOINT_END),
211
+ re.DOTALL,
212
+ )
213
+ return pattern.sub(block, text, count=1)
214
+ heading = "# Agent Instructions"
215
+ if heading in text:
216
+ return text.replace(heading, heading + "\n\n" + block, 1)
217
+ return block + "\n\n" + text
218
+
219
+
220
+ def sync_entrypoint_files(
221
+ root: Path | str,
222
+ *,
223
+ manifest: dict[str, Any],
224
+ markdown: str,
225
+ execute: bool = False,
226
+ ) -> dict[str, Any]:
227
+ repo_root = Path(root)
228
+ reference_targets: dict[str, str] = {
229
+ COMMAND_REFERENCE_JSON: json.dumps(manifest, indent=2, sort_keys=True) + "\n",
230
+ COMMAND_REFERENCE_MARKDOWN: markdown,
231
+ }
232
+ targets: dict[str, str] = dict(reference_targets)
233
+ agents_path = repo_root / "AGENTS.md"
234
+ if agents_path.exists():
235
+ targets["AGENTS.md"] = upsert_mandatory_entrypoint_block(
236
+ agents_path.read_text(encoding="utf-8"),
237
+ manifest,
238
+ )
239
+ start_prompt_path = repo_root / "docs/handoff/START_NEW_CHAT_PROMPT.md"
240
+ if start_prompt_path.exists():
241
+ targets["docs/handoff/START_NEW_CHAT_PROMPT.md"] = upsert_command_reference_prompt_block(
242
+ start_prompt_path.read_text(encoding="utf-8"),
243
+ repo_root,
244
+ manifest=manifest,
245
+ source_texts=reference_targets,
246
+ )
247
+
248
+ changed_paths: list[str] = []
249
+ for relative, desired in targets.items():
250
+ path = repo_root / relative
251
+ current = path.read_text(encoding="utf-8") if path.exists() else ""
252
+ if current != desired:
253
+ changed_paths.append(relative)
254
+ if execute:
255
+ path.parent.mkdir(parents=True, exist_ok=True)
256
+ path.write_text(desired, encoding="utf-8")
257
+
258
+ return {
259
+ "schema_version": 1,
260
+ "kind": "commands_sync_entrypoints_result",
261
+ "result_status": "PASS",
262
+ "manifest_sha": command_manifest_sha(manifest),
263
+ "changed": bool(changed_paths),
264
+ "execute": execute,
265
+ "changed_paths": changed_paths,
266
+ }
267
+
268
+
269
+ def audit_entrypoint_manifest_references(
270
+ root: Path | str,
271
+ manifest: dict[str, Any],
272
+ ) -> list[dict[str, str]]:
273
+ repo_root = Path(root)
274
+ expected_sha = computed_command_manifest_sha(manifest)
275
+ findings: list[dict[str, str]] = []
276
+ for relative in ENTRYPOINT_SOURCE_PATHS:
277
+ path = repo_root / relative
278
+ if not path.exists():
279
+ continue
280
+ text = path.read_text(encoding="utf-8")
281
+ if relative == "AGENTS.md" and mandatory_entrypoint_line(manifest) not in text:
282
+ findings.append(
283
+ {
284
+ "code": "ENTRYPOINT_MANIFEST_BLOCK_MISSING",
285
+ "message": f"{relative}: missing current command manifest entrypoint block",
286
+ }
287
+ )
288
+ for match in _SHA_REFERENCE_RE.finditer(text):
289
+ found = match.group("sha")
290
+ if found != expected_sha:
291
+ findings.append(
292
+ {
293
+ "code": "ENTRYPOINT_MANIFEST_SHA_MISMATCH",
294
+ "message": f"{relative}: expected {expected_sha}, found {found}",
295
+ }
296
+ )
297
+ return findings