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,168 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ DPA_SUCCESSOR_PROJECTION_WRITER_ID = "WRT-CH-006"
6
+ DPA_SUCCESSOR_PROJECTION_TARGET_SCOPE = "GENERATED_SUCCESSOR_HANDOFF_PACKAGE_AND_PROMPT_PROJECTIONS"
7
+ DPA_SUCCESSOR_PROJECTION_CONTRACT_ID = "DPA-GENERATED-SUCCESSOR-HANDOFF-PROJECTION-v1"
8
+ DPA_SUCCESSOR_PROJECTION_GENERATED_OUTPUT_CLASS = "command_generated_successor_handoff_projection"
9
+ DPA_SUCCESSOR_PROJECTION_GENERATOR_COMMAND = (
10
+ "agentic-kit transfer prepare-successor-handoff --render-prompt"
11
+ )
12
+ DPA_SUCCESSOR_PROJECTION_CURRENT_COMMAND = "agentic-kit transfer chat-switch-complete --render-prompt"
13
+ DPA_SUCCESSOR_PROJECTION_SOURCE_PATHS = (
14
+ "src/agentic_project_kit/successor_handoff_package.py",
15
+ "src/agentic_project_kit/cli_commands/transfer_handoff_flow.py",
16
+ )
17
+
18
+
19
+ def render_successor_projection_dpa_contract(
20
+ *,
21
+ generated_projection_paths: tuple[str, ...] | list[str],
22
+ default_direct_write_paths: tuple[str, ...] | list[str],
23
+ dedicated_update_only_paths: tuple[str, ...] | list[str],
24
+ initial_create_if_missing_paths: tuple[str, ...] | list[str] = (),
25
+ source_paths: tuple[str, ...] = DPA_SUCCESSOR_PROJECTION_SOURCE_PATHS,
26
+ ) -> dict[str, Any]:
27
+ return {
28
+ "schema_version": 1,
29
+ "kind": "dpa_generated_successor_handoff_projection_contract",
30
+ "contract_id": DPA_SUCCESSOR_PROJECTION_CONTRACT_ID,
31
+ "writer_id": DPA_SUCCESSOR_PROJECTION_WRITER_ID,
32
+ "target_scope": DPA_SUCCESSOR_PROJECTION_TARGET_SCOPE,
33
+ "generated_output_class": DPA_SUCCESSOR_PROJECTION_GENERATED_OUTPUT_CLASS,
34
+ "source_paths": list(source_paths),
35
+ "generator_command": DPA_SUCCESSOR_PROJECTION_GENERATOR_COMMAND,
36
+ "current_generator_command": DPA_SUCCESSOR_PROJECTION_CURRENT_COMMAND,
37
+ "generated_projection_paths": list(generated_projection_paths),
38
+ "default_direct_write_paths": list(default_direct_write_paths),
39
+ "dedicated_update_only_paths": list(dedicated_update_only_paths),
40
+ "initial_create_if_missing_paths": list(initial_create_if_missing_paths),
41
+ "source_command_contract_required": True,
42
+ "manual_durable_target_patches_allowed": False,
43
+ "current_handoff_target_byte_writer": False,
44
+ "exact_byte_rollback_claims_renderer_reproducibility": False,
45
+ "acceptance_state_invalidated_without_renderer_reproducibility": True,
46
+ "generated_outputs_manually_patched": False,
47
+ "production_mutation_claimed": False,
48
+ "kit_conformance_claimed": False,
49
+ "full_probe_pass_claimed": False,
50
+ }
51
+
52
+
53
+ def validate_successor_projection_dpa_contract(data: Any) -> list[str]:
54
+ if not isinstance(data, dict):
55
+ return ["dpa successor projection contract must be a mapping"]
56
+
57
+ errors: list[str] = []
58
+ if data.get("schema_version") != 1:
59
+ errors.append("schema_version must be 1")
60
+ if data.get("kind") != "dpa_generated_successor_handoff_projection_contract":
61
+ errors.append("kind must be dpa_generated_successor_handoff_projection_contract")
62
+ if data.get("contract_id") != DPA_SUCCESSOR_PROJECTION_CONTRACT_ID:
63
+ errors.append(f"contract_id must be {DPA_SUCCESSOR_PROJECTION_CONTRACT_ID}")
64
+ if data.get("writer_id") != DPA_SUCCESSOR_PROJECTION_WRITER_ID:
65
+ errors.append("writer_id must be WRT-CH-006")
66
+ if data.get("target_scope") != DPA_SUCCESSOR_PROJECTION_TARGET_SCOPE:
67
+ errors.append("target_scope must classify generated successor handoff projections")
68
+ if data.get("generated_output_class") != DPA_SUCCESSOR_PROJECTION_GENERATED_OUTPUT_CLASS:
69
+ errors.append("generated_output_class must classify command-generated successor projections")
70
+ if data.get("generator_command") != DPA_SUCCESSOR_PROJECTION_GENERATOR_COMMAND:
71
+ errors.append("generator_command must name the compatibility projection generator")
72
+ if data.get("current_generator_command") != DPA_SUCCESSOR_PROJECTION_CURRENT_COMMAND:
73
+ errors.append("current_generator_command must name the current projection generator")
74
+
75
+ for field in (
76
+ "source_command_contract_required",
77
+ "acceptance_state_invalidated_without_renderer_reproducibility",
78
+ ):
79
+ if data.get(field) is not True:
80
+ errors.append(f"{field} must be true")
81
+ for field in (
82
+ "manual_durable_target_patches_allowed",
83
+ "current_handoff_target_byte_writer",
84
+ "exact_byte_rollback_claims_renderer_reproducibility",
85
+ "generated_outputs_manually_patched",
86
+ "production_mutation_claimed",
87
+ "kit_conformance_claimed",
88
+ "full_probe_pass_claimed",
89
+ ):
90
+ if data.get(field) is not False:
91
+ errors.append(f"{field} must be false")
92
+
93
+ generated_paths = _string_list(data.get("generated_projection_paths"))
94
+ direct_paths = _string_list(data.get("default_direct_write_paths"))
95
+ dedicated_paths = _string_list(data.get("dedicated_update_only_paths"))
96
+ initial_create_paths = _string_list(data.get("initial_create_if_missing_paths"))
97
+ source_paths = _string_list(data.get("source_paths"))
98
+ if generated_paths is None:
99
+ errors.append("generated_projection_paths must be a string list")
100
+ generated_paths = []
101
+ if direct_paths is None:
102
+ errors.append("default_direct_write_paths must be a string list")
103
+ direct_paths = []
104
+ if dedicated_paths is None:
105
+ errors.append("dedicated_update_only_paths must be a string list")
106
+ dedicated_paths = []
107
+ if initial_create_paths is None:
108
+ errors.append("initial_create_if_missing_paths must be a string list")
109
+ initial_create_paths = []
110
+ if source_paths is None:
111
+ errors.append("source_paths must be a string list")
112
+ source_paths = []
113
+
114
+ if not generated_paths:
115
+ errors.append("generated_projection_paths must not be empty")
116
+ if not direct_paths:
117
+ errors.append("default_direct_write_paths must not be empty")
118
+ for label, paths in (
119
+ ("generated_projection_paths", generated_paths),
120
+ ("default_direct_write_paths", direct_paths),
121
+ ("dedicated_update_only_paths", dedicated_paths),
122
+ ("initial_create_if_missing_paths", initial_create_paths),
123
+ ("source_paths", source_paths),
124
+ ):
125
+ for path in paths:
126
+ if _is_unsafe_relative_path(path):
127
+ errors.append(f"{label} contains unsafe path: {path}")
128
+
129
+ generated_set = set(generated_paths)
130
+ direct_outside_generated = sorted(set(direct_paths) - generated_set)
131
+ dedicated_outside_generated = sorted(set(dedicated_paths) - generated_set)
132
+ initial_create_outside_generated = sorted(set(initial_create_paths) - generated_set)
133
+ if direct_outside_generated:
134
+ errors.append(
135
+ "default_direct_write_paths must be generated paths: "
136
+ + ", ".join(direct_outside_generated)
137
+ )
138
+ if dedicated_outside_generated:
139
+ errors.append(
140
+ "dedicated_update_only_paths must be generated paths: "
141
+ + ", ".join(dedicated_outside_generated)
142
+ )
143
+ if initial_create_outside_generated:
144
+ errors.append(
145
+ "initial_create_if_missing_paths must be generated paths: "
146
+ + ", ".join(initial_create_outside_generated)
147
+ )
148
+ if "src/agentic_project_kit/successor_handoff_package.py" not in source_paths:
149
+ errors.append("source_paths must include successor_handoff_package.py")
150
+ return errors
151
+
152
+
153
+ def _string_list(value: Any) -> list[str] | None:
154
+ if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
155
+ return None
156
+ return value
157
+
158
+
159
+ def _is_unsafe_relative_path(path: str) -> bool:
160
+ return (
161
+ not path
162
+ or path.startswith("/")
163
+ or path == "."
164
+ or path == ".."
165
+ or path.startswith("../")
166
+ or "/../" in path
167
+ or path.endswith("/..")
168
+ )
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ DPA_WORKSPACE_INIT_WRITER_ID = "WRT-CH-005"
7
+ DPA_WORKSPACE_INIT_TARGET_SCOPE = "EXTERNAL_WORKSPACE_INITIALIZATION_TEMPLATE"
8
+ DPA_WORKSPACE_INIT_CONTRACT_ID = "DPA-WORKSPACE-INIT-HANDOFF-TEMPLATE-v1"
9
+ DPA_WORKSPACE_INIT_MANIFEST_PATH = ".agentic/dpa/workspace_init_projection.json"
10
+ DPA_WORKSPACE_INIT_SOURCE_PATHS = (
11
+ "src/agentic_project_kit/templates.py",
12
+ "src/agentic_project_kit/workspace_init.py",
13
+ )
14
+ DPA_WORKSPACE_INIT_HANDOFF_TEMPLATE_PATH = "docs/handoff/CURRENT_HANDOFF.md"
15
+
16
+
17
+ def render_workspace_init_projection_manifest(
18
+ *,
19
+ project_name: str,
20
+ project_type: str,
21
+ profile: str | None,
22
+ profiles: tuple[str, ...] = (),
23
+ generated_target_paths: tuple[str, ...],
24
+ emits_current_handoff_template: bool,
25
+ ) -> str:
26
+ manifest = {
27
+ "schema_version": 1,
28
+ "kind": "dpa_workspace_init_projection_manifest",
29
+ "writer_id": DPA_WORKSPACE_INIT_WRITER_ID,
30
+ "target_scope": DPA_WORKSPACE_INIT_TARGET_SCOPE,
31
+ "contract_id": DPA_WORKSPACE_INIT_CONTRACT_ID,
32
+ "generated_output_class": "external_workspace_initialization_output",
33
+ "generated_target_root_only": True,
34
+ "emits_current_handoff_template": emits_current_handoff_template,
35
+ "self_hosting_current_handoff": False,
36
+ "kit_live_acceptance_state": False,
37
+ "full_probe_pass_claimed": False,
38
+ "kit_conformance_claimed": False,
39
+ "production_mutation_claimed": False,
40
+ "generated_outputs_manually_patched": False,
41
+ "source_paths": list(DPA_WORKSPACE_INIT_SOURCE_PATHS),
42
+ "generated_target_paths": list(generated_target_paths),
43
+ "project": {
44
+ "name": project_name,
45
+ "type": project_type,
46
+ "profile": profile or "",
47
+ "profiles": list(profiles),
48
+ },
49
+ }
50
+ return json.dumps(manifest, indent=2, sort_keys=True) + "\n"
51
+
52
+
53
+ def validate_workspace_init_projection_manifest(data: dict[str, Any]) -> tuple[str, ...]:
54
+ errors: list[str] = []
55
+ if data.get("schema_version") != 1:
56
+ errors.append("schema_version must be 1")
57
+ if data.get("kind") != "dpa_workspace_init_projection_manifest":
58
+ errors.append("kind must be dpa_workspace_init_projection_manifest")
59
+ if data.get("writer_id") != DPA_WORKSPACE_INIT_WRITER_ID:
60
+ errors.append("writer_id must be WRT-CH-005")
61
+ if data.get("target_scope") != DPA_WORKSPACE_INIT_TARGET_SCOPE:
62
+ errors.append("target_scope must classify external workspace initialization")
63
+ if data.get("contract_id") != DPA_WORKSPACE_INIT_CONTRACT_ID:
64
+ errors.append("contract_id must match the workspace-init handoff template contract")
65
+ for claim in (
66
+ "self_hosting_current_handoff",
67
+ "kit_live_acceptance_state",
68
+ "full_probe_pass_claimed",
69
+ "kit_conformance_claimed",
70
+ "production_mutation_claimed",
71
+ "generated_outputs_manually_patched",
72
+ ):
73
+ if data.get(claim) is not False:
74
+ errors.append(f"{claim} must be false")
75
+ if data.get("generated_target_root_only") is not True:
76
+ errors.append("generated_target_root_only must be true")
77
+ paths = data.get("generated_target_paths")
78
+ if not isinstance(paths, list) or not paths:
79
+ errors.append("generated_target_paths must be a non-empty list")
80
+ elif any(_is_unsafe_relative_path(str(path)) for path in paths):
81
+ errors.append("generated_target_paths must stay inside the generated target root")
82
+ if data.get("emits_current_handoff_template") is True and (
83
+ not isinstance(paths, list) or DPA_WORKSPACE_INIT_HANDOFF_TEMPLATE_PATH not in paths
84
+ ):
85
+ errors.append("handoff-template manifests must include docs/handoff/CURRENT_HANDOFF.md")
86
+ source_paths = data.get("source_paths")
87
+ if source_paths != list(DPA_WORKSPACE_INIT_SOURCE_PATHS):
88
+ errors.append("source_paths must identify the Kit workspace-init template sources")
89
+ return tuple(errors)
90
+
91
+
92
+ def _is_unsafe_relative_path(path: str) -> bool:
93
+ return path.startswith("/") or path == ".." or path.startswith("../") or "/../" in path
@@ -0,0 +1,327 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import json
5
+ from pathlib import Path
6
+ import subprocess
7
+ from typing import Any
8
+
9
+ from agentic_project_kit.workspace import Workspace, load_workspace
10
+
11
+ EVIDENCE_OUTPUT_ROOT_PARTS = ("evidence", "dpa", "probes")
12
+ GH_PR_FIELDS = (
13
+ "number,state,title,baseRefName,headRefName,headRefOid,mergeCommit,files,commits,statusCheckRollup"
14
+ )
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class WrtCh001Finding:
19
+ code: str
20
+ message: str
21
+ path: str = "github-pr"
22
+
23
+ def as_dict(self) -> dict[str, str]:
24
+ return {"code": self.code, "message": self.message, "path": self.path}
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class WrtCh001EvidenceResult:
29
+ source_pr: int
30
+ admin_pr: int
31
+ data: dict[str, Any]
32
+ findings: tuple[WrtCh001Finding, ...]
33
+
34
+ @property
35
+ def structural_ok(self) -> bool:
36
+ return not self.findings
37
+
38
+ @property
39
+ def result_status(self) -> str:
40
+ if not self.structural_ok:
41
+ return "STRUCTURAL_BLOCK"
42
+ return "OBSERVED_ADMIN_REFRESH_NOT_DISPOSABLE_FIXTURE"
43
+
44
+ @property
45
+ def full_wrt_ch001_fixture_satisfied(self) -> bool:
46
+ return False
47
+
48
+ def as_dict(self) -> dict[str, Any]:
49
+ return {
50
+ "schema_version": 1,
51
+ "kind": "dpa_wrt_ch001_admin_refresh_observation",
52
+ "result_status": self.result_status,
53
+ "source_pr": self.source_pr,
54
+ "admin_pr": self.admin_pr,
55
+ "structural_ok": self.structural_ok,
56
+ "full_wrt_ch001_fixture_satisfied": self.full_wrt_ch001_fixture_satisfied,
57
+ "finding_count": len(self.findings),
58
+ "findings": [finding.as_dict() for finding in self.findings],
59
+ **self.data,
60
+ }
61
+
62
+
63
+ def fetch_admin_refresh_pr_data(admin_pr: int, *, root: Path | str = ".") -> dict[str, Any]:
64
+ completed = subprocess.run(
65
+ ["gh", "pr", "view", str(admin_pr), "--json", GH_PR_FIELDS],
66
+ cwd=Path(root),
67
+ check=True,
68
+ text=True,
69
+ stdout=subprocess.PIPE,
70
+ stderr=subprocess.PIPE,
71
+ )
72
+ data = json.loads(completed.stdout)
73
+ if not isinstance(data, dict):
74
+ raise ValueError("gh pr view did not return a JSON object")
75
+ return data
76
+
77
+
78
+ def load_pr_data(path: Path | str) -> dict[str, Any]:
79
+ data = json.loads(Path(path).read_text(encoding="utf-8"))
80
+ if not isinstance(data, dict):
81
+ raise ValueError("PR evidence input must contain a JSON object")
82
+ return data
83
+
84
+
85
+ def evaluate_wrt_ch001_admin_refresh_observation(
86
+ root: Path | str,
87
+ *,
88
+ source_pr: int,
89
+ admin_pr: int,
90
+ pr_data: dict[str, Any],
91
+ ) -> WrtCh001EvidenceResult:
92
+ base = Path(root).resolve()
93
+ ws = load_workspace(base, suppress_legacy_profile_warning=True)
94
+ findings: list[WrtCh001Finding] = []
95
+
96
+ expected_head_ref = f"{ws.admin_refresh_branch_prefix()}{source_pr}-handoff-refresh"
97
+ expected_title = f"Refresh handoff state after PR{source_pr}"
98
+ expected_files = _expected_admin_refresh_paths(ws, source_pr)
99
+ observed_files = _observed_file_paths(pr_data)
100
+
101
+ _require(pr_data.get("number") == admin_pr, findings, "admin-pr-number-mismatch", f"Expected PR #{admin_pr}.")
102
+ _require(pr_data.get("state") == "MERGED", findings, "admin-pr-not-merged", "Admin refresh PR must be merged.")
103
+ _require(
104
+ pr_data.get("baseRefName") == "main",
105
+ findings,
106
+ "admin-pr-base-not-main",
107
+ "Admin refresh PR base must be main.",
108
+ )
109
+ _require(
110
+ pr_data.get("headRefName") == expected_head_ref,
111
+ findings,
112
+ "admin-pr-head-ref-mismatch",
113
+ f"Admin refresh PR head must be {expected_head_ref}.",
114
+ )
115
+ _require(
116
+ pr_data.get("title") == expected_title,
117
+ findings,
118
+ "admin-pr-title-mismatch",
119
+ f"Admin refresh PR title must be {expected_title}.",
120
+ )
121
+
122
+ merge_commit = pr_data.get("mergeCommit")
123
+ merge_oid = merge_commit.get("oid") if isinstance(merge_commit, dict) else None
124
+ _require(isinstance(merge_oid, str) and len(merge_oid) == 40, findings, "merge-commit-missing", "Admin refresh PR must have a merge commit oid.")
125
+
126
+ missing_files = sorted(expected_files - observed_files)
127
+ unexpected_files = sorted(observed_files - expected_files)
128
+ for item in missing_files:
129
+ findings.append(
130
+ WrtCh001Finding(
131
+ code="admin-refresh-file-missing",
132
+ message=f"Expected admin refresh file is missing from PR files: {item}",
133
+ path=item,
134
+ )
135
+ )
136
+ for item in unexpected_files:
137
+ findings.append(
138
+ WrtCh001Finding(
139
+ code="admin-refresh-file-unexpected",
140
+ message=f"Unexpected file in admin refresh PR: {item}",
141
+ path=item,
142
+ )
143
+ )
144
+
145
+ for item in sorted(expected_files):
146
+ if not (base / item).exists():
147
+ findings.append(
148
+ WrtCh001Finding(
149
+ code="admin-refresh-file-missing-locally",
150
+ message=f"Expected merged admin refresh file is missing locally: {item}",
151
+ path=item,
152
+ )
153
+ )
154
+
155
+ check_records = _check_records(pr_data)
156
+ if not check_records:
157
+ findings.append(
158
+ WrtCh001Finding(
159
+ code="admin-refresh-checks-missing",
160
+ message="Admin refresh PR must have at least one status check record.",
161
+ )
162
+ )
163
+ for check in check_records:
164
+ if check["status"] != "COMPLETED" or check["conclusion"] != "SUCCESS":
165
+ findings.append(
166
+ WrtCh001Finding(
167
+ code="admin-refresh-check-not-successful",
168
+ message=f"Admin refresh check is not successful: {check['name']}",
169
+ )
170
+ )
171
+
172
+ commits = pr_data.get("commits")
173
+ commit_headlines = [
174
+ str(item.get("messageHeadline"))
175
+ for item in commits
176
+ if isinstance(item, dict) and isinstance(item.get("messageHeadline"), str)
177
+ ] if isinstance(commits, list) else []
178
+ _require(
179
+ commit_headlines == [expected_title],
180
+ findings,
181
+ "admin-refresh-commit-shape-mismatch",
182
+ f"Admin refresh PR must contain one commit titled {expected_title}.",
183
+ )
184
+
185
+ data = {
186
+ "validation_ref": merge_oid or "UNKNOWN",
187
+ "admin_refresh_head_ref": pr_data.get("headRefName"),
188
+ "admin_refresh_head_oid": pr_data.get("headRefOid"),
189
+ "observed_files": sorted(observed_files),
190
+ "expected_files": sorted(expected_files),
191
+ "status_checks": check_records,
192
+ "commit_headlines": commit_headlines,
193
+ "claims": {
194
+ "admin_refresh_observed": True,
195
+ "disposable_fixture_claimed": False,
196
+ "full_probe_002_claimed": False,
197
+ "dp2_authorized": False,
198
+ "production_mutation_performed_by_this_command": False,
199
+ "generated_outputs_manually_patched": False,
200
+ },
201
+ "limitations": [
202
+ "This observes the merged Kit administrative handoff refresh PR.",
203
+ "It is not a disposable WRT-CH-001 fixture execution.",
204
+ "It does not satisfy full PROBE-002 evidence or authorize DP2.",
205
+ ],
206
+ "raw_pr": pr_data,
207
+ }
208
+ return WrtCh001EvidenceResult(
209
+ source_pr=source_pr,
210
+ admin_pr=admin_pr,
211
+ data=data,
212
+ findings=tuple(findings),
213
+ )
214
+
215
+
216
+ def render_wrt_ch001_evidence(result: WrtCh001EvidenceResult) -> str:
217
+ payload = result.as_dict()
218
+ lines = [
219
+ "DPA_WRT_CH001_ADMIN_REFRESH_OBSERVATION",
220
+ f"STATUS={payload['result_status']}",
221
+ f"SOURCE_PR={payload['source_pr']}",
222
+ f"ADMIN_PR={payload['admin_pr']}",
223
+ f"VALIDATION_REF={payload['validation_ref']}",
224
+ f"FULL_WRT_CH001_FIXTURE_SATISFIED={str(payload['full_wrt_ch001_fixture_satisfied']).lower()}",
225
+ f"FINDINGS={payload['finding_count']}",
226
+ ]
227
+ for finding in payload["findings"]:
228
+ lines.append(f"FINDING={finding['code']}|path={finding['path']}|{finding['message']}")
229
+ return "\n".join(lines) + "\n"
230
+
231
+
232
+ def write_wrt_ch001_evidence_json(
233
+ result: WrtCh001EvidenceResult,
234
+ root: Path | str,
235
+ output: Path | str,
236
+ *,
237
+ execute: bool,
238
+ ) -> dict[str, Any]:
239
+ base = Path(root).resolve()
240
+ output_path = _resolve_under_root(base, output)
241
+ relative = output_path.relative_to(base)
242
+ evidence_root = _evidence_output_root(base)
243
+ if evidence_root not in (output_path, *output_path.parents):
244
+ return {
245
+ "result_status": "BLOCK",
246
+ "reason": "output_outside_dpa_probe_evidence_root",
247
+ "output_path": relative.as_posix(),
248
+ "written": False,
249
+ }
250
+ rendered = json.dumps(result.as_dict(), indent=2, sort_keys=True) + "\n"
251
+ changed = True
252
+ if output_path.exists():
253
+ changed = output_path.read_text(encoding="utf-8") != rendered
254
+ if execute:
255
+ output_path.parent.mkdir(parents=True, exist_ok=True)
256
+ output_path.write_text(rendered, encoding="utf-8")
257
+ return {
258
+ "result_status": "PASS",
259
+ "output_path": relative.as_posix(),
260
+ "changed": changed,
261
+ "written": bool(execute),
262
+ }
263
+
264
+
265
+ def _expected_admin_refresh_paths(ws: Workspace, source_pr: int) -> set[str]:
266
+ paths = {
267
+ ws.handoff_state_path(),
268
+ ws.operational_handoff_state_path(),
269
+ ws.status_path(),
270
+ ws.handoff_file("CURRENT_HANDOFF.md"),
271
+ ws.handoff_file("NEXT_CHAT_BOOTSTRAP.md"),
272
+ ws.handoff_file("START_NEW_CHAT_PROMPT.md"),
273
+ ws.package_file("execution_contract.json"),
274
+ ws.package_file("source_manifest.json"),
275
+ ws.package_file("successor_context.yaml"),
276
+ ws.package_file("successor_prompt.md"),
277
+ ws.package_file("validation_report.json"),
278
+ ws.post_pr_successor_chat_handoff_path(source_pr),
279
+ }
280
+ return {ws.path_text(item) for item in paths}
281
+
282
+
283
+ def _observed_file_paths(pr_data: dict[str, Any]) -> set[str]:
284
+ files = pr_data.get("files")
285
+ if not isinstance(files, list):
286
+ return set()
287
+ return {
288
+ str(item["path"])
289
+ for item in files
290
+ if isinstance(item, dict) and isinstance(item.get("path"), str)
291
+ }
292
+
293
+
294
+ def _check_records(pr_data: dict[str, Any]) -> list[dict[str, str]]:
295
+ checks = pr_data.get("statusCheckRollup")
296
+ if not isinstance(checks, list):
297
+ return []
298
+ records: list[dict[str, str]] = []
299
+ for item in checks:
300
+ if not isinstance(item, dict):
301
+ continue
302
+ records.append(
303
+ {
304
+ "name": str(item.get("name") or ""),
305
+ "status": str(item.get("status") or ""),
306
+ "conclusion": str(item.get("conclusion") or ""),
307
+ "workflowName": str(item.get("workflowName") or ""),
308
+ }
309
+ )
310
+ return records
311
+
312
+
313
+ def _require(condition: bool, findings: list[WrtCh001Finding], code: str, message: str) -> None:
314
+ if not condition:
315
+ findings.append(WrtCh001Finding(code=code, message=message))
316
+
317
+
318
+ def _resolve_under_root(root: Path, path: Path | str) -> Path:
319
+ candidate = Path(path)
320
+ if candidate.is_absolute():
321
+ return candidate
322
+ return root / candidate
323
+
324
+
325
+ def _evidence_output_root(root: Path) -> Path:
326
+ ws = load_workspace(root, suppress_legacy_profile_warning=True)
327
+ return ws.architecture_file(Path(*EVIDENCE_OUTPUT_ROOT_PARTS)).resolve()