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,398 @@
1
+ from __future__ import annotations
2
+
3
+ # ruff: noqa: F403,F405
4
+
5
+ from agentic_project_kit.cli_commands.transfer_shared import *
6
+
7
+
8
+ def _clean_post_merge_status_is_noop() -> bool:
9
+ status = repo_status(short=True)
10
+ if status.returncode != 0 or str(status.stdout).strip():
11
+ return False
12
+ post_merge = post_merge_check()
13
+ return post_merge.returncode == 0 and "result=NOOP" in post_merge.stdout
14
+
15
+ def _evaluate_llm_context_freshness(
16
+ root: Path,
17
+ *,
18
+ max_age_minutes: int,
19
+ allow_clean_post_merge_carrier_staleness: bool = False,
20
+ ) -> dict[str, object]:
21
+ from datetime import datetime, timezone
22
+
23
+ from agentic_project_kit.llm_execution_context import build_llm_execution_context
24
+
25
+ paths = {
26
+ "outbox": root / ".agentic/transfer/outbox/last_result.txt",
27
+ "latest_handoff_report": root / "docs/reports/terminal/transfer_handoff_reports/latest-transfer-handoff-report.json",
28
+ }
29
+ required_context_keys = [
30
+ "source_hashes",
31
+ "context_quality",
32
+ "running_chat_refresh_contract",
33
+ "shell_placeholder_policy",
34
+ "terminal_resilience",
35
+ "patch_generation_policy",
36
+ "command_reference",
37
+ ]
38
+ required_markers = [
39
+ "agentic-kit transfer pr-create-complete --post-merge-complete",
40
+ "verify-llm-context-refresh",
41
+ ]
42
+ forbidden_fragments = ["<PR_NUMMER>", "<PR_NUMBER>"]
43
+ now = datetime.now(timezone.utc)
44
+ blockers: list[str] = []
45
+ warnings: list[str] = []
46
+ checked: dict[str, dict[str, object]] = {}
47
+ valid_contexts: list[str] = []
48
+
49
+ try:
50
+ current_context = build_llm_execution_context(root)
51
+ current_source_hashes = current_context.get("source_hashes", {})
52
+ except Exception as exc: # noqa: BLE001
53
+ current_source_hashes = {}
54
+ blockers.append("current_context_generation_failed")
55
+ checked["current_context"] = {"ok": False, "error": str(exc)}
56
+
57
+ for name, path in paths.items():
58
+ status: dict[str, object] = {"path": str(path), "exists": path.exists()}
59
+ checked[name] = status
60
+ local_blockers: list[str] = []
61
+ if not path.exists():
62
+ local_blockers.append("missing")
63
+ status["blockers"] = local_blockers
64
+ blockers.append(name + "_missing")
65
+ continue
66
+ try:
67
+ data = json.loads(path.read_text(encoding="utf-8"))
68
+ except json.JSONDecodeError as exc:
69
+ status["json_error"] = str(exc)
70
+ local_blockers.append("json_invalid")
71
+ status["blockers"] = local_blockers
72
+ blockers.append(name + "_json_invalid")
73
+ continue
74
+ if not isinstance(data, dict):
75
+ local_blockers.append("not_object")
76
+ status["blockers"] = local_blockers
77
+ blockers.append(name + "_not_object")
78
+ continue
79
+ ctx = data.get("llm_execution_context")
80
+ status["llm_execution_context_present"] = isinstance(ctx, dict)
81
+ if not isinstance(ctx, dict):
82
+ local_blockers.append("llm_execution_context_missing")
83
+ status["blockers"] = local_blockers
84
+ blockers.append(name + "_llm_execution_context_missing")
85
+ continue
86
+ ctx_text = json.dumps(ctx, sort_keys=True, ensure_ascii=False)
87
+ missing_keys = [key for key in required_context_keys if key not in ctx]
88
+ missing_markers = [marker for marker in required_markers if marker not in ctx_text]
89
+ forbidden_hits = [fragment for fragment in forbidden_fragments if fragment in ctx_text]
90
+ context_quality = ctx.get("context_quality") if isinstance(ctx.get("context_quality"), dict) else {}
91
+ refresh_contract = ctx.get("running_chat_refresh_contract") if isinstance(ctx.get("running_chat_refresh_contract"), dict) else {}
92
+ source_hashes = ctx.get("source_hashes") if isinstance(ctx.get("source_hashes"), dict) else {}
93
+ status["missing_context_keys"] = missing_keys
94
+ status["missing_markers"] = missing_markers
95
+ status["forbidden_fragments"] = forbidden_hits
96
+ status["source_hashes_complete"] = bool(context_quality.get("source_hashes_complete"))
97
+ status["refresh_required_for_running_chats"] = bool(refresh_contract.get("refresh_required_for_running_chats"))
98
+ status["source_hashes_match_current_repo"] = bool(source_hashes) and source_hashes == current_source_hashes
99
+ generated_at = ctx.get("generated_at_utc")
100
+ status["generated_at_utc"] = generated_at
101
+ age_only_stale = False
102
+ if isinstance(generated_at, str) and generated_at:
103
+ try:
104
+ generated_time = datetime.fromisoformat(generated_at.replace("Z", "+00:00"))
105
+ age_seconds = (now - generated_time).total_seconds()
106
+ status["age_seconds"] = age_seconds
107
+ status["fresh"] = age_seconds <= max_age_minutes * 60
108
+ age_only_stale = not status["fresh"]
109
+ except ValueError:
110
+ status["fresh"] = False
111
+ local_blockers.append("generated_at_invalid")
112
+ else:
113
+ status["fresh"] = False
114
+ local_blockers.append("generated_at_missing")
115
+ if missing_keys:
116
+ local_blockers.append("context_keys_missing")
117
+ if missing_markers:
118
+ local_blockers.append("markers_missing")
119
+ if forbidden_hits:
120
+ local_blockers.append("forbidden_placeholder_present")
121
+ if not status["source_hashes_complete"]:
122
+ local_blockers.append("source_hashes_incomplete")
123
+ if not status["source_hashes_match_current_repo"]:
124
+ local_blockers.append("source_hashes_mismatch")
125
+ if not status["refresh_required_for_running_chats"]:
126
+ local_blockers.append("running_chat_refresh_contract_missing")
127
+ age_warning_only = (
128
+ age_only_stale
129
+ and status["source_hashes_complete"]
130
+ and status["source_hashes_match_current_repo"]
131
+ )
132
+ status["age_warning_only"] = age_warning_only
133
+ if age_warning_only:
134
+ warning = name + "_age_exceeds_max_but_source_hashes_match"
135
+ status.setdefault("warnings", []).append("age_exceeds_max_but_source_hashes_match")
136
+ warnings.append(warning)
137
+ elif not status["fresh"]:
138
+ local_blockers.append("stale_or_not_fresh")
139
+ status["blockers"] = local_blockers
140
+ if local_blockers:
141
+ blockers.extend(name + "_" + item for item in local_blockers)
142
+ else:
143
+ valid_contexts.append(name)
144
+
145
+ blockers = list(dict.fromkeys(blockers))
146
+ # A single fresh, hash-consistent carrier is sufficient. Other stale or
147
+ # missing carriers remain diagnostic findings, but must not block when at
148
+ # least one authoritative local handoff context is valid.
149
+ clean_post_merge_carrier_staleness_allowed = (
150
+ allow_clean_post_merge_carrier_staleness
151
+ and not valid_contexts
152
+ and set(blockers).issubset(
153
+ {
154
+ "outbox_missing",
155
+ "latest_handoff_report_source_hashes_mismatch",
156
+ "latest_handoff_report_stale_or_not_fresh",
157
+ }
158
+ )
159
+ and _clean_post_merge_status_is_noop()
160
+ )
161
+ result_status = (
162
+ "PASS"
163
+ if valid_contexts
164
+ else "WARN"
165
+ if clean_post_merge_carrier_staleness_allowed
166
+ else "BLOCKED"
167
+ )
168
+ return {
169
+ "schema_version": 1,
170
+ "kind": "transfer_require_fresh_llm_context_result",
171
+ "action": "require-fresh-llm-context",
172
+ "result_status": result_status,
173
+ "final_signal": "d" if result_status in {"PASS", "WARN"} else "f",
174
+ "next_action": (
175
+ "Fresh LLM context gate passed; planning may continue."
176
+ if result_status == "PASS"
177
+ else "Clean post-merge state detected; stale volatile LLM-context carriers are a warning, not a product blocker."
178
+ if result_status == "WARN"
179
+ else "Regenerate and publish fresh LLM context before planning: " + ", ".join(blockers)
180
+ ),
181
+ "max_age_minutes": max_age_minutes,
182
+ "valid_contexts": valid_contexts,
183
+ "blockers": blockers,
184
+ "warnings": list(dict.fromkeys(warnings)),
185
+ "checked": checked,
186
+ "allow_clean_post_merge_carrier_staleness": allow_clean_post_merge_carrier_staleness,
187
+ "clean_post_merge_carrier_staleness_allowed": clean_post_merge_carrier_staleness_allowed,
188
+ }
189
+
190
+ def _emit_llm_context_gate_result(payload: dict[str, object], *, json_output: bool) -> None:
191
+ if json_output:
192
+ typer.echo(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False))
193
+ return
194
+ _echo_transfer_payload_summary(
195
+ title="TRANSFER_REQUIRE_FRESH_LLM_CONTEXT",
196
+ result_status=str(payload.get("result_status", "BLOCKED")),
197
+ final_signal=str(payload.get("final_signal", "f")),
198
+ next_action=str(payload.get("next_action", "Regenerate fresh LLM context before planning.")),
199
+ fields={
200
+ "VALID_CONTEXTS": len(payload.get("valid_contexts", [])),
201
+ "BLOCKERS": len(payload.get("blockers", [])),
202
+ "MAX_AGE_MINUTES": payload.get("max_age_minutes", 0),
203
+ },
204
+ )
205
+
206
+ def _require_fresh_llm_context_or_exit(*, max_age_minutes: int, json_output: bool) -> None:
207
+ evaluate = _public_transfer_attr("_evaluate_llm_context_freshness", _evaluate_llm_context_freshness)
208
+ payload = evaluate(Path("."), max_age_minutes=max_age_minutes)
209
+ if payload.get("result_status") == "PASS":
210
+ return
211
+ _emit_llm_context_gate_result(payload, json_output=json_output)
212
+ raise typer.Exit(code=2)
213
+
214
+ def _branch_publishes_communication_rule_carrier() -> bool:
215
+ completed = subprocess.run(
216
+ ["git", "diff", "--name-only", "origin/main...HEAD"],
217
+ text=True,
218
+ stdout=subprocess.PIPE,
219
+ stderr=subprocess.PIPE,
220
+ check=False,
221
+ )
222
+ if completed.returncode != 0:
223
+ return False
224
+ return "docs/reports/communication_rules/CURRENT_COMMUNICATION_RULES.md" in set(
225
+ completed.stdout.splitlines()
226
+ )
227
+
228
+ def _require_current_communication_context_or_exit(
229
+ *,
230
+ json_output: bool,
231
+ allow_rule_carrier_publish: bool = False,
232
+ ) -> None:
233
+ result = require_current_communication_context(Path("."))
234
+ if result.result_status == "PASS":
235
+ return
236
+ if allow_rule_carrier_publish and _branch_publishes_communication_rule_carrier():
237
+ return
238
+ payload = result.as_json_data()
239
+ if json_output:
240
+ typer.echo(json.dumps(payload, indent=2, sort_keys=True))
241
+ else:
242
+ typer.echo("COMMUNICATION_CONTEXT_GATE")
243
+ typer.echo(f"result_status={result.result_status}")
244
+ typer.echo(f"reason={result.reason}")
245
+ typer.echo(f"required_next_reply={result.required_next_reply or '<none>'}")
246
+ typer.echo(f"remote_path={result.remote_path}")
247
+ typer.echo(f"next_action={result.next_action}")
248
+ raise typer.Exit(code=2)
249
+
250
+ def _ensure_fresh_llm_context_or_exit(*, max_age_minutes: int, json_output: bool) -> None:
251
+ evaluate = _public_transfer_attr("_evaluate_llm_context_freshness", _evaluate_llm_context_freshness)
252
+ payload = evaluate(Path("."), max_age_minutes=max_age_minutes)
253
+ if payload.get("result_status") == "PASS":
254
+ return
255
+
256
+ initial_blockers = list(payload.get("blockers", []))
257
+ try:
258
+ refresh = _public_transfer_attr("refresh_llm_context_carriers", refresh_llm_context_carriers)
259
+ refresh_result = refresh(Path("."))
260
+ except (FileNotFoundError, ValueError, OSError) as exc:
261
+ payload["auto_refresh_attempted"] = True
262
+ payload["auto_refresh_result_status"] = "BLOCKED"
263
+ payload["auto_refresh_error"] = str(exc)
264
+ payload["next_action"] = "Inspect LLM context carrier refresh failure before planning: " + str(exc)
265
+ _emit_llm_context_gate_result(payload, json_output=json_output)
266
+ raise typer.Exit(code=2) from exc
267
+
268
+ evaluate = _public_transfer_attr("_evaluate_llm_context_freshness", _evaluate_llm_context_freshness)
269
+ payload = evaluate(Path("."), max_age_minutes=max_age_minutes)
270
+ payload["auto_refresh_attempted"] = True
271
+ payload["auto_refresh_result_status"] = refresh_result.get("result_status")
272
+ payload["initial_blockers"] = initial_blockers
273
+ if payload.get("result_status") == "PASS":
274
+ return
275
+
276
+ _emit_llm_context_gate_result(payload, json_output=json_output)
277
+ raise typer.Exit(code=2)
278
+
279
+ KNOWN_VOLATILE_TRANSFER_PATHS = [
280
+ ".agentic/transfer/inbox/next_command.py.txt",
281
+ ".agentic/transfer/outbox/last_result.txt",
282
+ "docs/reports/terminal/transfer_handoff_reports/latest-transfer-handoff-report.json",
283
+ "docs/reports/terminal/transfer_handoff_reports/latest-transfer-handoff-report.log",
284
+ ]
285
+
286
+
287
+ def _completed_process_payload(completed: subprocess.CompletedProcess[str]) -> dict[str, object]:
288
+ return {
289
+ "argv": list(completed.args),
290
+ "returncode": completed.returncode,
291
+ "stdout": completed.stdout,
292
+ "stderr": completed.stderr,
293
+ "ok": completed.returncode == 0,
294
+ }
295
+
296
+ def _restore_known_volatile_paths(root: Path | str = ".") -> dict[str, object]:
297
+ root_path = Path(root)
298
+ tracked_paths: list[str] = []
299
+ removed_untracked: list[str] = []
300
+ skipped_missing: list[str] = []
301
+ checks: list[dict[str, object]] = []
302
+ errors: list[str] = []
303
+
304
+ for relative_path in KNOWN_VOLATILE_TRANSFER_PATHS:
305
+ check = subprocess.run(
306
+ ["git", "ls-files", "--error-unmatch", relative_path],
307
+ cwd=root_path,
308
+ text=True,
309
+ capture_output=True,
310
+ check=False,
311
+ )
312
+ checks.append(_completed_process_payload(check))
313
+ target = root_path / relative_path
314
+ if check.returncode == 0:
315
+ tracked_paths.append(relative_path)
316
+ continue
317
+ if target.exists():
318
+ try:
319
+ if target.is_dir():
320
+ shutil.rmtree(target)
321
+ else:
322
+ target.unlink()
323
+ removed_untracked.append(relative_path)
324
+ except OSError as exc:
325
+ errors.append(f"remove_untracked_failed:{relative_path}:{exc}")
326
+ else:
327
+ skipped_missing.append(relative_path)
328
+
329
+ restore_result: dict[str, object] | None = None
330
+ if tracked_paths:
331
+ restore = subprocess.run(
332
+ ["git", "restore", "--", *tracked_paths],
333
+ cwd=root_path,
334
+ text=True,
335
+ capture_output=True,
336
+ check=False,
337
+ )
338
+ restore_result = _completed_process_payload(restore)
339
+ if restore.returncode != 0:
340
+ errors.append("git_restore_failed")
341
+
342
+ return {
343
+ "ok": not errors,
344
+ "tracked_paths": tracked_paths,
345
+ "removed_untracked": removed_untracked,
346
+ "skipped_missing": skipped_missing,
347
+ "checks": checks,
348
+ "restore": restore_result,
349
+ "errors": errors,
350
+ }
351
+
352
+ def _run_transfer_subprocess(argv: list[str], *, cwd: Path | None = None) -> dict[str, object]:
353
+ completed = subprocess.run(argv, cwd=cwd or Path("."), text=True, capture_output=True, check=False)
354
+ return _completed_process_payload(completed)
355
+
356
+ def _echo_transfer_payload_terminal_summary(payload: dict[str, object], *, title: str | None = None) -> None:
357
+ action = str(payload.get("action") or "transfer")
358
+ summary_title = title or f"TRANSFER_{action.upper().replace('-', '_')}"
359
+ result_status = str(payload.get("result_status") or "UNKNOWN")
360
+ final_signal = str(payload.get("final_signal") or ("d" if result_status == "PASS" else "f"))
361
+ next_action = str(payload.get("next_action") or "Inspect command result before continuing.")
362
+
363
+ fields: dict[str, object] = {}
364
+ for key, label in (
365
+ ("kind", "KIND"),
366
+ ("changed_files", "CHANGED_FILES"),
367
+ ("blockers", "BLOCKERS"),
368
+ ("missing_sources", "MISSING_SOURCES"),
369
+ ("outbox_written", "OUTBOX_WRITTEN"),
370
+ ):
371
+ if key not in payload:
372
+ continue
373
+ value = payload.get(key)
374
+ if isinstance(value, list):
375
+ value = len(value)
376
+ fields[label] = value
377
+
378
+ _echo_transfer_payload_summary(
379
+ title=summary_title,
380
+ result_status=result_status,
381
+ final_signal=final_signal,
382
+ fields=fields,
383
+ next_action=next_action,
384
+ )
385
+
386
+ def _echo_transfer_payload_json_or_summary(
387
+ payload: dict[str, object],
388
+ *,
389
+ json_output: bool,
390
+ title: str | None = None,
391
+ ) -> None:
392
+ if json_output:
393
+ typer.echo(json.dumps(payload, indent=2, ensure_ascii=False))
394
+ else:
395
+ _echo_transfer_payload_terminal_summary(payload, title=title)
396
+
397
+
398
+ __all__ = [name for name in globals() if not name.startswith("__")]