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,491 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ import json
6
+ import re
7
+ from typing import Any, Literal
8
+
9
+ from agentic_project_kit.documentation_registry import build_documentation_registry_summary
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class DocMeshDocument:
14
+ path: str
15
+ category: str
16
+ required: bool = True
17
+ historical: bool = False
18
+
19
+ def to_dict(self) -> dict[str, Any]:
20
+ return {
21
+ "path": self.path,
22
+ "category": self.category,
23
+ "required": self.required,
24
+ "historical": self.historical,
25
+ }
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class DocMeshFinding:
30
+ code: str
31
+ path: str
32
+ message: str
33
+
34
+ def to_dict(self) -> dict[str, str]:
35
+ return {
36
+ "code": self.code,
37
+ "path": self.path,
38
+ "message": self.message,
39
+ }
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class DocMeshReport:
44
+ documents: tuple[DocMeshDocument, ...]
45
+ findings: tuple[DocMeshFinding, ...]
46
+ registry_summary: dict[str, Any] | None = None
47
+
48
+ @property
49
+ def ok(self) -> bool:
50
+ return not self.findings
51
+
52
+ def to_dict(self) -> dict[str, Any]:
53
+ return {
54
+ "ok": self.ok,
55
+ "documents": [document.to_dict() for document in self.documents],
56
+ "findings": [finding.to_dict() for finding in self.findings],
57
+ "registry_summary": self.registry_summary,
58
+ }
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class DocMeshRepair:
63
+ code: str
64
+ path: str
65
+ action: str
66
+ safe: bool
67
+ mode: Literal["automatic", "manual"]
68
+ reason: str
69
+
70
+ def to_dict(self) -> dict[str, Any]:
71
+ return {
72
+ "code": self.code,
73
+ "path": self.path,
74
+ "action": self.action,
75
+ "safe": self.safe,
76
+ "mode": self.mode,
77
+ "reason": self.reason,
78
+ }
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class DocMeshRepairPlan:
83
+ ok: bool
84
+ repairs: tuple[DocMeshRepair, ...]
85
+
86
+ def to_dict(self) -> dict[str, Any]:
87
+ return {
88
+ "ok": self.ok,
89
+ "repairs": [repair.to_dict() for repair in self.repairs],
90
+ }
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class DocMeshRepairAction:
95
+ path: str
96
+ action: str
97
+ changed: bool
98
+ message: str
99
+
100
+ def to_dict(self) -> dict[str, Any]:
101
+ return {
102
+ "path": self.path,
103
+ "action": self.action,
104
+ "changed": self.changed,
105
+ "message": self.message,
106
+ }
107
+
108
+
109
+ @dataclass(frozen=True)
110
+ class DocMeshRepairResult:
111
+ changed: bool
112
+ actions: tuple[DocMeshRepairAction, ...]
113
+ skipped: tuple[DocMeshRepair, ...]
114
+
115
+ def to_dict(self) -> dict[str, Any]:
116
+ return {
117
+ "changed": self.changed,
118
+ "actions": [action.to_dict() for action in self.actions],
119
+ "skipped": [repair.to_dict() for repair in self.skipped],
120
+ }
121
+
122
+
123
+ DOC_MESH_DOCUMENTS: tuple[DocMeshDocument, ...] = (
124
+ DocMeshDocument("README.md", "current-state"),
125
+ DocMeshDocument("CHANGELOG.md", "release-history"),
126
+ DocMeshDocument("docs/releases/VERIFIED_RELEASES.md", "release-history", required=False),
127
+ DocMeshDocument("CITATION.cff", "current-state"),
128
+ DocMeshDocument("pyproject.toml", "current-state"),
129
+ DocMeshDocument("src/agentic_project_kit/__init__.py", "current-state"),
130
+ DocMeshDocument("docs/STATUS.md", "current-state"),
131
+ DocMeshDocument("docs/handoff/CURRENT_HANDOFF.md", "current-state"),
132
+ DocMeshDocument("AGENTS.md", "governance"),
133
+ DocMeshDocument("docs/TEST_GATES.md", "governance"),
134
+ DocMeshDocument("docs/DOCUMENTATION_COVERAGE.yaml", "governance"),
135
+ DocMeshDocument("sentinel.yaml", "governance"),
136
+ DocMeshDocument(".agentic/project.yaml", "governance"),
137
+ DocMeshDocument("docs/architecture/ARCHITECTURE_CONTRACT.md", "architecture"),
138
+ DocMeshDocument("docs/architecture/DOCUMENTATION_INFORMATION_ARCHITECTURE.md", "architecture"),
139
+ DocMeshDocument("docs/WORKFLOW_OUTPUT_CYCLE.md", "architecture"),
140
+ DocMeshDocument("DESIGN.md", "architecture", required=False),
141
+ DocMeshDocument(
142
+ "docs/reports/status_roadmap_summary_after_pr105_20260512.md",
143
+ "historical-plan",
144
+ historical=True,
145
+ ),
146
+ DocMeshDocument("docs/V0.3.0_OUTPUT_REPAIR_PLAN.md", "historical-plan", required=False, historical=True),
147
+ )
148
+
149
+ HISTORICAL_BANNER = "This file is historical audit evidence, not the current source of truth."
150
+
151
+
152
+ _VERSION_PATTERNS: dict[str, re.Pattern[str]] = {
153
+ "pyproject.toml": re.compile(r'^version\s*=\s*"([^"]+)"', re.MULTILINE),
154
+ "src/agentic_project_kit/__init__.py": re.compile(r'^__version__\s*=\s*"([^"]+)"', re.MULTILINE),
155
+ "docs/STATUS.md": re.compile(r"^Current version:\s*([0-9]+\.[0-9]+\.[0-9]+)", re.MULTILINE),
156
+ "docs/handoff/CURRENT_HANDOFF.md": re.compile(r"^Current version:\s*([0-9]+\.[0-9]+\.[0-9]+)", re.MULTILINE),
157
+ "CITATION.cff": re.compile(r"^version:\s*([0-9]+\.[0-9]+\.[0-9]+)", re.MULTILINE),
158
+ "README.md": re.compile(
159
+ r"(?:Version `([0-9]+\.[0-9]+\.[0-9]+)` "
160
+ r"(?:is the current release line prepared|is current\.)"
161
+ r"|Current verified release: version `([0-9]+\.[0-9]+\.[0-9]+)`)",
162
+ re.MULTILINE,
163
+ ),
164
+ }
165
+
166
+ _VERIFIED_DOI_PATTERN = re.compile(
167
+ r"(?:archived|Verified) v(?P<version>[0-9]+\.[0-9]+\.[0-9]+).*?(?P<doi>10\.5281/zenodo\.[0-9]+)",
168
+ re.IGNORECASE,
169
+ )
170
+
171
+ _STALE_CURRENT_STATE_MARKERS = (
172
+ "release candidate",
173
+ "release-preparation branch",
174
+ "must wait for post-release verification",
175
+ )
176
+
177
+ _STALE_MARKER_DOCUMENTS = frozenset(
178
+ {
179
+ "README.md",
180
+ "docs/STATUS.md",
181
+ "docs/handoff/CURRENT_HANDOFF.md",
182
+ }
183
+ )
184
+
185
+ _REPAIR_POLICY: dict[str, tuple[str, bool, Literal["automatic", "manual"], str]] = {
186
+ "historical-banner-missing": (
187
+ "insert_historical_banner",
188
+ True,
189
+ "automatic",
190
+ "Historical banner insertion is mechanical and does not alter semantic content.",
191
+ ),
192
+ "version-mismatch": (
193
+ "align_version_marker",
194
+ False,
195
+ "manual",
196
+ "Version alignment can be mechanical later, but the authoritative source must be reviewed first.",
197
+ ),
198
+ "release-doi-list-mismatch": (
199
+ "sync_release_doi_list",
200
+ False,
201
+ "manual",
202
+ "DOI list synchronization must preserve release evidence and should be reviewed before file edits.",
203
+ ),
204
+ "stale-current-state-marker": (
205
+ "manual_review_required",
206
+ False,
207
+ "manual",
208
+ "Stale wording may require contextual rewriting rather than mechanical deletion.",
209
+ ),
210
+ "missing-document": (
211
+ "manual_review_required",
212
+ False,
213
+ "manual",
214
+ "Missing documents require a maintainer decision before creation.",
215
+ ),
216
+ "missing-version": (
217
+ "manual_review_required",
218
+ False,
219
+ "manual",
220
+ "Missing version markers require a maintainer decision about the correct source of truth.",
221
+ ),
222
+ }
223
+
224
+
225
+ def build_doc_mesh_report(project_root: Path) -> DocMeshReport:
226
+ findings: list[DocMeshFinding] = []
227
+ versions: dict[str, str] = {}
228
+ contents: dict[str, str] = {}
229
+
230
+ for document in DOC_MESH_DOCUMENTS:
231
+ path = project_root / document.path
232
+ if not path.exists():
233
+ if document.required:
234
+ findings.append(DocMeshFinding("missing-document", document.path, "required mesh document is missing"))
235
+ continue
236
+
237
+ content = path.read_text(encoding="utf-8")
238
+ contents[document.path] = content
239
+ if document.historical:
240
+ findings.extend(_check_historical_document(document.path, content))
241
+ elif document.path in _STALE_MARKER_DOCUMENTS:
242
+ findings.extend(_check_current_state_document(document.path, content))
243
+
244
+ pattern = _VERSION_PATTERNS.get(document.path)
245
+ if pattern is not None:
246
+ match = pattern.search(content)
247
+ if match is None:
248
+ findings.append(DocMeshFinding("missing-version", document.path, "current-state document has no machine-readable version marker"))
249
+ else:
250
+ all_matches = list(pattern.finditer(content))
251
+ selected_match = all_matches[-1]
252
+ versions[document.path] = next(group for group in selected_match.groups() if group)
253
+
254
+ findings.extend(_check_version_consistency(versions))
255
+ findings.extend(_check_release_doi_consistency(contents))
256
+ return DocMeshReport(
257
+ documents=DOC_MESH_DOCUMENTS,
258
+ findings=tuple(findings),
259
+ registry_summary=_load_registry_summary(project_root),
260
+ )
261
+
262
+
263
+ def build_doc_mesh_repair_plan(report: DocMeshReport) -> DocMeshRepairPlan:
264
+ repairs = tuple(_repair_for_finding(finding) for finding in report.findings)
265
+ return DocMeshRepairPlan(ok=not repairs, repairs=repairs)
266
+
267
+
268
+ def apply_doc_mesh_repair_plan(project_root: Path, plan: DocMeshRepairPlan) -> DocMeshRepairResult:
269
+ actions: list[DocMeshRepairAction] = []
270
+ skipped: list[DocMeshRepair] = []
271
+
272
+ for repair in plan.repairs:
273
+ if not (repair.safe and repair.mode == "automatic" and repair.action == "insert_historical_banner"):
274
+ skipped.append(repair)
275
+ continue
276
+ actions.append(_insert_historical_banner(project_root, repair.path))
277
+
278
+ return DocMeshRepairResult(
279
+ changed=any(action.changed for action in actions),
280
+ actions=tuple(actions),
281
+ skipped=tuple(skipped),
282
+ )
283
+
284
+
285
+ def render_doc_mesh_report(report: DocMeshReport) -> str:
286
+ lines = ["Documentation mesh audit", ""]
287
+ lines.append("Documents:")
288
+ for document in report.documents:
289
+ required = "required" if document.required else "optional"
290
+ lines.append(f"- {document.path}: {document.category}, {required}")
291
+ if report.registry_summary is not None:
292
+ lines.append("")
293
+ lines.append("Documentation registry:")
294
+ lines.append(f"- registry: {report.registry_summary['registry_path']}")
295
+ lines.append(f"- version: {report.registry_summary['version']}")
296
+ lines.append(f"- documents: {report.registry_summary['document_count']}")
297
+ lines.append(
298
+ "- broad_migration_allowed: "
299
+ f"{report.registry_summary['broad_migration_allowed']}"
300
+ )
301
+ class_counts = report.registry_summary.get("class_counts", {})
302
+ if isinstance(class_counts, dict):
303
+ for class_name, count in class_counts.items():
304
+ lines.append(f"- class:{class_name}: {count}")
305
+ lines.append("")
306
+ if report.ok:
307
+ lines.append("Overall: PASS")
308
+ return "\n".join(lines)
309
+
310
+ lines.append("Findings:")
311
+ for finding in report.findings:
312
+ lines.append(f"- [{finding.code}] {finding.path}: {finding.message}")
313
+ lines.append("")
314
+ lines.append("Overall: FAIL")
315
+ return "\n".join(lines)
316
+
317
+
318
+ def render_doc_mesh_repair_plan(plan: DocMeshRepairPlan) -> str:
319
+ lines = ["Documentation mesh repair plan", ""]
320
+ if plan.ok:
321
+ lines.append("No repairs planned.")
322
+ lines.append("")
323
+ lines.append("Overall: PASS")
324
+ return "\n".join(lines)
325
+
326
+ lines.append("Repairs:")
327
+ for repair in plan.repairs:
328
+ safety = "safe" if repair.safe else "manual"
329
+ lines.append(f"- [{repair.code}] {repair.path}: {repair.action} ({safety})")
330
+ lines.append("")
331
+ lines.append("Overall: REVIEW")
332
+ return "\n".join(lines)
333
+
334
+
335
+ def render_doc_mesh_repair_result(result: DocMeshRepairResult) -> str:
336
+ lines = ["Documentation mesh repair result", ""]
337
+ if not result.actions and not result.skipped:
338
+ lines.append("No repairs were needed.")
339
+ lines.append("")
340
+ lines.append("Overall: PASS")
341
+ return "\n".join(lines)
342
+
343
+ if result.actions:
344
+ lines.append("Actions:")
345
+ for action in result.actions:
346
+ changed = "changed" if action.changed else "unchanged"
347
+ lines.append(f"- {action.path}: {action.action} ({changed})")
348
+ lines.append("")
349
+
350
+ if result.skipped:
351
+ lines.append("Skipped manual repairs:")
352
+ for repair in result.skipped:
353
+ lines.append(f"- [{repair.code}] {repair.path}: {repair.action}")
354
+ lines.append("")
355
+
356
+ lines.append("Overall: PASS")
357
+ return "\n".join(lines)
358
+
359
+
360
+ def write_doc_mesh_json_report(report: DocMeshReport, output_path: Path) -> None:
361
+ output_path.parent.mkdir(parents=True, exist_ok=True)
362
+ output_path.write_text(json.dumps(report.to_dict(), indent=2, sort_keys=True) + "\n", encoding="utf-8")
363
+
364
+
365
+ def write_doc_mesh_repair_plan(plan: DocMeshRepairPlan, output_path: Path) -> None:
366
+ output_path.parent.mkdir(parents=True, exist_ok=True)
367
+ output_path.write_text(json.dumps(plan.to_dict(), indent=2, sort_keys=True) + "\n", encoding="utf-8")
368
+
369
+
370
+ def write_doc_mesh_repair_result(result: DocMeshRepairResult, output_path: Path) -> None:
371
+ output_path.parent.mkdir(parents=True, exist_ok=True)
372
+ output_path.write_text(json.dumps(result.to_dict(), indent=2, sort_keys=True) + "\n", encoding="utf-8")
373
+
374
+
375
+ def _repair_for_finding(finding: DocMeshFinding) -> DocMeshRepair:
376
+ action, safe, mode, reason = _REPAIR_POLICY.get(
377
+ finding.code,
378
+ (
379
+ "manual_review_required",
380
+ False,
381
+ "manual",
382
+ "Unknown finding code requires manual review before any repair is attempted.",
383
+ ),
384
+ )
385
+ return DocMeshRepair(
386
+ code=finding.code,
387
+ path=finding.path,
388
+ action=action,
389
+ safe=safe,
390
+ mode=mode,
391
+ reason=reason,
392
+ )
393
+
394
+
395
+ def _insert_historical_banner(project_root: Path, relative_path: str) -> DocMeshRepairAction:
396
+ path = project_root / relative_path
397
+ if not path.exists():
398
+ return DocMeshRepairAction(relative_path, "insert_historical_banner", False, "file does not exist")
399
+
400
+ content = path.read_text(encoding="utf-8")
401
+ if HISTORICAL_BANNER in content:
402
+ return DocMeshRepairAction(relative_path, "insert_historical_banner", False, "historical banner already present")
403
+
404
+ path.write_text(f"{HISTORICAL_BANNER}\n\n{content}", encoding="utf-8")
405
+ return DocMeshRepairAction(relative_path, "insert_historical_banner", True, "historical banner inserted")
406
+
407
+
408
+ def _load_registry_summary(project_root: Path) -> dict[str, Any] | None:
409
+ try:
410
+ return build_documentation_registry_summary(project_root)
411
+ except (OSError, ValueError):
412
+ return None
413
+
414
+
415
+ def _check_historical_document(path: str, content: str) -> list[DocMeshFinding]:
416
+ findings: list[DocMeshFinding] = []
417
+ if HISTORICAL_BANNER not in content:
418
+ findings.append(
419
+ DocMeshFinding(
420
+ "historical-banner-missing",
421
+ path,
422
+ "historical or roadmap document must be marked as non-current source of truth",
423
+ )
424
+ )
425
+ return findings
426
+
427
+
428
+ def _check_current_state_document(path: str, content: str) -> list[DocMeshFinding]:
429
+ findings: list[DocMeshFinding] = []
430
+ lower = content.lower()
431
+ for marker in _STALE_CURRENT_STATE_MARKERS:
432
+ if marker in lower:
433
+ findings.append(DocMeshFinding("stale-current-state-marker", path, f"stale wording present: {marker!r}"))
434
+ return findings
435
+
436
+
437
+ def _check_version_consistency(versions: dict[str, str]) -> list[DocMeshFinding]:
438
+ if not versions:
439
+ return []
440
+
441
+ expected = versions.get("pyproject.toml") or next(iter(versions.values()))
442
+ findings: list[DocMeshFinding] = []
443
+ for path, version in sorted(versions.items()):
444
+ if version != expected:
445
+ findings.append(
446
+ DocMeshFinding(
447
+ "version-mismatch",
448
+ path,
449
+ f"version {version!r} does not match expected version {expected!r}",
450
+ )
451
+ )
452
+ return findings
453
+
454
+
455
+ def _extract_verified_dois(content: str) -> dict[str, str]:
456
+ dois: dict[str, str] = {}
457
+ for match in _VERIFIED_DOI_PATTERN.finditer(content):
458
+ dois[match.group("version")] = match.group("doi")
459
+ archive_pattern = re.compile(
460
+ r"`v(?P<version>\d+\.\d+\.\d+)`\s*/\s*`\d+\.\d+\.\d+`:\s*"
461
+ r"Zenodo version DOI `(?P<doi>10\.5281/zenodo\.\d+)`"
462
+ )
463
+ for match in archive_pattern.finditer(content):
464
+ dois[match.group("version")] = match.group("doi")
465
+ return dois
466
+
467
+ def _check_release_doi_consistency(contents: dict[str, str]) -> list[DocMeshFinding]:
468
+ findings: list[DocMeshFinding] = []
469
+ release_archive_path = "docs/releases/VERIFIED_RELEASES.md"
470
+ release_archive = contents.get(release_archive_path, "")
471
+ sources = {
472
+ "CHANGELOG.md": contents.get("CHANGELOG.md", ""),
473
+ "CITATION.cff": contents.get("CITATION.cff", ""),
474
+ "docs/STATUS.md": contents.get("docs/STATUS.md", ""),
475
+ "docs/handoff/CURRENT_HANDOFF.md": contents.get("docs/handoff/CURRENT_HANDOFF.md", ""),
476
+ }
477
+ expected: dict[str, str] = {}
478
+ for content in sources.values():
479
+ expected.update(_extract_verified_dois(content))
480
+
481
+ archive_dois = _extract_verified_dois(release_archive)
482
+ for version, doi in sorted(expected.items()):
483
+ if archive_dois.get(version) != doi:
484
+ findings.append(
485
+ DocMeshFinding(
486
+ "release-doi-list-mismatch",
487
+ release_archive_path,
488
+ f"release archive DOI list missing or mismatching v{version} -> {doi}",
489
+ )
490
+ )
491
+ return findings