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,140 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Iterable, Protocol
5
+
6
+
7
+ TERMINAL_BAD_STATUSES = {"FAIL", "BLOCKED"}
8
+ DEFAULT_EFFECTFUL_ACTIONS_AFTER_FAILURE = {
9
+ "commit",
10
+ "push",
11
+ "push-current",
12
+ "pr-create",
13
+ "pr-create-complete",
14
+ "pr-complete",
15
+ "pr-merge",
16
+ "pr-merge-safe",
17
+ "merge",
18
+ "admin-refresh-pr",
19
+ "post-merge-complete",
20
+ "prepare-successor-handoff",
21
+ "successor-handoff",
22
+ "handoff",
23
+ "release",
24
+ "publish",
25
+ }
26
+
27
+
28
+ class StepLike(Protocol):
29
+ name: str
30
+ result: object
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class CompositeShortCircuitViolation:
35
+ wrapper: str
36
+ failed_step_name: str
37
+ failed_step_status: str
38
+ later_step_name: str
39
+ later_action: str
40
+ message: str
41
+
42
+
43
+ def _status_of(result: object) -> str:
44
+ return str(getattr(result, "result_status", "") or "").upper()
45
+
46
+
47
+ def _action_of(step: StepLike) -> str:
48
+ result = getattr(step, "result", None)
49
+ action = getattr(result, "action", "")
50
+ if action:
51
+ return str(action)
52
+ return str(getattr(step, "name", ""))
53
+
54
+
55
+ def _is_allowed_recovery_step(action: str, allowed_recovery_actions: tuple[str, ...]) -> bool:
56
+ normalized = action.lower()
57
+ return any(item.lower() == normalized for item in allowed_recovery_actions)
58
+
59
+
60
+ def _is_effectful_after_failure(action: str, effectful_actions: tuple[str, ...]) -> bool:
61
+ normalized = action.lower()
62
+ return any(item.lower() in normalized for item in effectful_actions)
63
+
64
+
65
+ def validate_no_effectful_steps_after_required_failure(
66
+ *,
67
+ wrapper: str,
68
+ steps: Iterable[StepLike],
69
+ allowed_recovery_actions: Iterable[str] = (),
70
+ effectful_actions: Iterable[str] = DEFAULT_EFFECTFUL_ACTIONS_AFTER_FAILURE,
71
+ ) -> list[CompositeShortCircuitViolation]:
72
+ """Validate composite-wrapper short-circuit discipline.
73
+
74
+ After a required substep reports FAIL/BLOCKED, a composite command must not
75
+ execute later effectful steps such as commit, push, PR creation, merge,
76
+ handoff refresh, release, or publish. Explicit recovery steps may be
77
+ allowlisted by the wrapper.
78
+ """
79
+
80
+ steps_tuple = tuple(steps)
81
+ allowed = tuple(str(item) for item in allowed_recovery_actions)
82
+ effectful = tuple(str(item) for item in effectful_actions)
83
+ violations: list[CompositeShortCircuitViolation] = []
84
+
85
+ first_failure: tuple[str, str] | None = None
86
+ for step in steps_tuple:
87
+ status = _status_of(getattr(step, "result", None))
88
+ if status in TERMINAL_BAD_STATUSES:
89
+ first_failure = (str(getattr(step, "name", "")), status)
90
+ break
91
+
92
+ if first_failure is None:
93
+ return []
94
+
95
+ failed_step_name, failed_status = first_failure
96
+ failure_seen = False
97
+ for step in steps_tuple:
98
+ if not failure_seen:
99
+ if str(getattr(step, "name", "")) == failed_step_name:
100
+ failure_seen = True
101
+ continue
102
+
103
+ action = _action_of(step)
104
+ if _is_allowed_recovery_step(action, allowed):
105
+ continue
106
+ if not _is_effectful_after_failure(action, effectful):
107
+ continue
108
+
109
+ violations.append(
110
+ CompositeShortCircuitViolation(
111
+ wrapper=wrapper,
112
+ failed_step_name=failed_step_name,
113
+ failed_step_status=failed_status,
114
+ later_step_name=str(getattr(step, "name", "")),
115
+ later_action=action,
116
+ message=(
117
+ f"{wrapper} executed effectful step {getattr(step, 'name', '')!s} "
118
+ f"after {failed_step_name} returned {failed_status}."
119
+ ),
120
+ )
121
+ )
122
+
123
+ return violations
124
+
125
+
126
+ def assert_no_effectful_steps_after_required_failure(
127
+ *,
128
+ wrapper: str,
129
+ steps: Iterable[StepLike],
130
+ allowed_recovery_actions: Iterable[str] = (),
131
+ effectful_actions: Iterable[str] = DEFAULT_EFFECTFUL_ACTIONS_AFTER_FAILURE,
132
+ ) -> None:
133
+ violations = validate_no_effectful_steps_after_required_failure(
134
+ wrapper=wrapper,
135
+ steps=steps,
136
+ allowed_recovery_actions=allowed_recovery_actions,
137
+ effectful_actions=effectful_actions,
138
+ )
139
+ if violations:
140
+ raise AssertionError("; ".join(item.message for item in violations))
@@ -0,0 +1,251 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import yaml
8
+
9
+
10
+ CONTRACT_PATH = ".agentic/project.yaml"
11
+ CONTRACT_VERSION = 1
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ProfileDefinition:
16
+ id: str
17
+ title: str
18
+ description: str
19
+ required_files: tuple[str, ...] = ()
20
+ recommended_commands: tuple[str, ...] = ()
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class PolicyPackDefinition:
25
+ id: str
26
+ title: str
27
+ description: str
28
+ strictness: str
29
+ recommended_for: tuple[str, ...] = ()
30
+
31
+
32
+ PROFILE_DEFINITIONS: dict[str, ProfileDefinition] = {
33
+ "generic-git-repo": ProfileDefinition(
34
+ id="generic-git-repo",
35
+ title="Generic Git repository",
36
+ description="Language-neutral repository governance, status files, handoff files, and review gates.",
37
+ required_files=("README.md", "AGENTS.md", "docs/STATUS.md", "docs/TEST_GATES.md"),
38
+ recommended_commands=("agentic-kit check-docs", "agentic-kit doctor"),
39
+ ),
40
+ "python-cli": ProfileDefinition(
41
+ id="python-cli",
42
+ title="Python CLI",
43
+ description="Python package with command-line behavior, pytest tests, and optional ruff linting.",
44
+ required_files=("pyproject.toml", "tests"),
45
+ recommended_commands=("python -m pytest -q", "ruff check ."),
46
+ ),
47
+ "python-lib": ProfileDefinition(
48
+ id="python-lib",
49
+ title="Python library",
50
+ description="Python import package with pytest tests and packaging metadata.",
51
+ required_files=("pyproject.toml", "tests"),
52
+ recommended_commands=("python -m pytest -q", "ruff check ."),
53
+ ),
54
+ "markdown-docs": ProfileDefinition(
55
+ id="markdown-docs",
56
+ title="Markdown documentation",
57
+ description="Documentation-heavy repository with required state docs and coverage checks.",
58
+ required_files=("docs/STATUS.md", "docs/TEST_GATES.md", "docs/handoff/CURRENT_HANDOFF.md"),
59
+ recommended_commands=("agentic-kit check-docs",),
60
+ ),
61
+ "git-github": ProfileDefinition(
62
+ id="git-github",
63
+ title="GitHub workflow",
64
+ description="GitHub-oriented review workflow with PR templates, Actions, and branch discipline.",
65
+ required_files=(".github/pull_request_template.md",),
66
+ recommended_commands=("git status --short",),
67
+ ),
68
+ "release-managed": ProfileDefinition(
69
+ id="release-managed",
70
+ title="Release-managed project",
71
+ description="Repository with changelog, release planning, release validation, and citation/archive metadata.",
72
+ required_files=("CHANGELOG.md",),
73
+ recommended_commands=("agentic-kit release-plan", "agentic-kit release-check"),
74
+ ),
75
+ "governance-wrapper": ProfileDefinition(
76
+ id="governance-wrapper",
77
+ title="Governance wrapper",
78
+ description="Strict human-AI wrapper project with output contracts, validation, repair boundaries, and auditability.",
79
+ required_files=("AGENTS.md", "docs/architecture/ARCHITECTURE_CONTRACT.md", "docs/TEST_GATES.md"),
80
+ recommended_commands=("agentic-kit check-docs", "agentic-kit doctor", "python -m pytest -q"),
81
+ ),
82
+ }
83
+
84
+
85
+ POLICY_PACK_DEFINITIONS: dict[str, PolicyPackDefinition] = {
86
+ "starter": PolicyPackDefinition(
87
+ id="starter",
88
+ title="Starter",
89
+ description="Low-friction project start with visible state files and basic gates.",
90
+ strictness="low",
91
+ recommended_for=("new repositories", "small projects"),
92
+ ),
93
+ "prototype": PolicyPackDefinition(
94
+ id="prototype",
95
+ title="Prototype",
96
+ description="Fast exploration with explicit non-production status and lightweight checks.",
97
+ strictness="low",
98
+ recommended_for=("experiments", "vibe-coding exploration"),
99
+ ),
100
+ "solo-maintainer": PolicyPackDefinition(
101
+ id="solo-maintainer",
102
+ title="Solo maintainer",
103
+ description="Single-maintainer workflow with explicit handoff, reviewability, and local evidence gates.",
104
+ strictness="medium",
105
+ recommended_for=("single-user development", "personal tools"),
106
+ ),
107
+ "agentic-development": PolicyPackDefinition(
108
+ id="agentic-development",
109
+ title="Agentic development",
110
+ description="Human-AI development workflow with architecture review, documentation coverage, and doctor checks.",
111
+ strictness="medium-high",
112
+ recommended_for=("coding agents", "AI-assisted development"),
113
+ ),
114
+ "release-managed": PolicyPackDefinition(
115
+ id="release-managed",
116
+ title="Release managed",
117
+ description="Release-state validation for versions, tags, changelog, release artifacts, and citation metadata.",
118
+ strictness="high",
119
+ recommended_for=("published packages", "GitHub releases"),
120
+ ),
121
+ "documentation-governed": PolicyPackDefinition(
122
+ id="documentation-governed",
123
+ title="Documentation governed",
124
+ description="Documentation coverage and drift checks for documentation-heavy or governance-heavy repositories.",
125
+ strictness="medium-high",
126
+ recommended_for=("docs repositories", "architecture-governed repositories"),
127
+ ),
128
+ "output-contracts": PolicyPackDefinition(
129
+ id="output-contracts",
130
+ title="Output contracts",
131
+ description="Strict response/output governance with schemas, validators, bounded repair, and evidence-oriented failure handling.",
132
+ strictness="high",
133
+ recommended_for=("LLM wrappers", "governance-heavy assistants", "auditable output pipelines"),
134
+ ),
135
+ }
136
+
137
+
138
+ def default_profiles(project_type: str, *, github_actions: bool) -> tuple[str, ...]:
139
+ profiles = ["generic-git-repo", "markdown-docs"]
140
+ if project_type in {"python-cli", "python-lib", "governance-wrapper"}:
141
+ profiles.append(project_type)
142
+ if github_actions:
143
+ profiles.append("git-github")
144
+ return tuple(dict.fromkeys(profiles))
145
+
146
+
147
+ def recommended_policy_packs(
148
+ project_type: str,
149
+ *,
150
+ github_actions: bool,
151
+ logging_evidence: bool,
152
+ ) -> tuple[str, ...]:
153
+ packs = ["starter", "solo-maintainer"]
154
+ if logging_evidence or github_actions:
155
+ packs.append("agentic-development")
156
+ if project_type == "generic":
157
+ packs.append("documentation-governed")
158
+ if project_type == "governance-wrapper":
159
+ packs.append("documentation-governed")
160
+ packs.append("output-contracts")
161
+ return tuple(dict.fromkeys(packs))
162
+
163
+
164
+ def validate_ids(kind: str, selected: tuple[str, ...], known: dict[str, object]) -> list[str]:
165
+ return [f"unknown {kind}: {item}" for item in selected if item not in known]
166
+
167
+
168
+ def build_contract_data(
169
+ *,
170
+ name: str,
171
+ description: str,
172
+ project_type: str,
173
+ profiles: tuple[str, ...],
174
+ policy_packs: tuple[str, ...],
175
+ ) -> dict[str, Any]:
176
+ return {
177
+ "version": CONTRACT_VERSION,
178
+ "project": {
179
+ "name": name,
180
+ "description": description,
181
+ "type": project_type,
182
+ },
183
+ "profiles": list(profiles),
184
+ "policy_packs": list(policy_packs),
185
+ "governance": {
186
+ "architecture_contract_required": True,
187
+ "documentation_coverage_required": True,
188
+ "doctor_before_merge": True,
189
+ "human_owns_merge_release_and_publication": True,
190
+ },
191
+ }
192
+
193
+
194
+ def render_contract_yaml(data: dict[str, Any]) -> str:
195
+ return yaml.safe_dump(data, sort_keys=False, allow_unicode=True)
196
+
197
+
198
+ def load_project_contract(project_root: Path) -> dict[str, Any] | None:
199
+ path = project_root / CONTRACT_PATH
200
+ if not path.exists():
201
+ return None
202
+ data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
203
+ if not isinstance(data, dict):
204
+ raise ValueError(f"{CONTRACT_PATH} must contain a YAML mapping")
205
+ return data
206
+
207
+
208
+ def validate_project_contract(data: dict[str, Any]) -> list[str]:
209
+ errors: list[str] = []
210
+ if data.get("version") != CONTRACT_VERSION:
211
+ errors.append(f"unsupported contract version: {data.get('version')!r}")
212
+
213
+ project = data.get("project")
214
+ if not isinstance(project, dict):
215
+ errors.append("project must be a mapping")
216
+ else:
217
+ for field in ("name", "description", "type"):
218
+ if not str(project.get(field, "")).strip():
219
+ errors.append(f"project.{field} is required")
220
+
221
+ profiles = _string_list(data.get("profiles"), "profiles", errors)
222
+ policy_packs = _string_list(data.get("policy_packs"), "policy_packs", errors)
223
+ errors.extend(validate_ids("profile", profiles, PROFILE_DEFINITIONS))
224
+ errors.extend(validate_ids("policy pack", policy_packs, POLICY_PACK_DEFINITIONS))
225
+
226
+ governance = data.get("governance")
227
+ if not isinstance(governance, dict):
228
+ errors.append("governance must be a mapping")
229
+
230
+ return errors
231
+
232
+
233
+ def contract_summary(data: dict[str, Any]) -> str:
234
+ project = data.get("project", {}) if isinstance(data.get("project"), dict) else {}
235
+ profiles = ", ".join(_string_list(data.get("profiles"), "profiles", [])) or "none"
236
+ packs = ", ".join(_string_list(data.get("policy_packs"), "policy_packs", [])) or "none"
237
+ name = project.get("name", "unknown")
238
+ return f"{name}; profiles: {profiles}; policy packs: {packs}"
239
+
240
+
241
+ def _string_list(value: object, field_name: str, errors: list[str]) -> tuple[str, ...]:
242
+ if not isinstance(value, list):
243
+ errors.append(f"{field_name} must be a list")
244
+ return ()
245
+ result: list[str] = []
246
+ for item in value:
247
+ if not isinstance(item, str) or not item.strip():
248
+ errors.append(f"{field_name} entries must be non-empty strings")
249
+ else:
250
+ result.append(item.strip())
251
+ return tuple(result)
@@ -0,0 +1,83 @@
1
+ """Deterministic workflow control states.
2
+
3
+ This module separates command execution results from target-state outcomes.
4
+ A workflow step may be successful because the desired target state already
5
+ exists, even if a low-level command would otherwise fail or be unnecessary.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from enum import Enum
12
+
13
+
14
+ class ControlStatus(str, Enum):
15
+ """Machine-readable status values for deterministic workflow steps."""
16
+
17
+ PASS = "PASS"
18
+ DONE = "DONE"
19
+ NOOP = "NOOP"
20
+ ALREADY_ON_MAIN = "ALREADY_ON_MAIN"
21
+ ALREADY_MERGED = "ALREADY_MERGED"
22
+ ALREADY_RELEASED = "ALREADY_RELEASED"
23
+ DOI_VERIFIED = "DOI_VERIFIED"
24
+ SUPERSEDED = "SUPERSEDED"
25
+ PENDING = "PENDING"
26
+ WAIT = "WAIT"
27
+ FAIL = "FAIL"
28
+ NEEDS_HUMAN_REVIEW = "NEEDS_HUMAN_REVIEW"
29
+
30
+
31
+ SUCCESS_STATUSES = frozenset({
32
+ ControlStatus.PASS,
33
+ ControlStatus.DONE,
34
+ ControlStatus.NOOP,
35
+ ControlStatus.ALREADY_ON_MAIN,
36
+ ControlStatus.ALREADY_MERGED,
37
+ ControlStatus.ALREADY_RELEASED,
38
+ ControlStatus.DOI_VERIFIED,
39
+ ControlStatus.SUPERSEDED,
40
+ })
41
+
42
+ RETRYABLE_STATUSES = frozenset({
43
+ ControlStatus.PENDING,
44
+ ControlStatus.WAIT,
45
+ })
46
+
47
+ FAILURE_STATUSES = frozenset({
48
+ ControlStatus.FAIL,
49
+ ControlStatus.NEEDS_HUMAN_REVIEW,
50
+ })
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class StepResult:
55
+ """Result of a workflow step after target-state classification."""
56
+
57
+ status: ControlStatus
58
+ message: str
59
+ detail: str = ""
60
+
61
+ @property
62
+ def is_success(self) -> bool:
63
+ return self.status in SUCCESS_STATUSES
64
+
65
+ @property
66
+ def is_retryable(self) -> bool:
67
+ return self.status in RETRYABLE_STATUSES
68
+
69
+ @property
70
+ def is_failure(self) -> bool:
71
+ return self.status in FAILURE_STATUSES
72
+
73
+ @property
74
+ def should_continue(self) -> bool:
75
+ return self.is_success
76
+
77
+
78
+ def normalize_status(value: str | ControlStatus) -> ControlStatus:
79
+ """Return a ControlStatus from a user/tool supplied value."""
80
+ if isinstance(value, ControlStatus):
81
+ return value
82
+ normalized = value.strip().upper().replace("-", "_")
83
+ return ControlStatus(normalized)