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,612 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass
4
+ from datetime import date
5
+ from pathlib import Path
6
+ from types import MappingProxyType
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class PathLiteralPattern:
11
+ name: str
12
+ literal: str
13
+
14
+
15
+ PATH_LITERAL_PATTERNS = (
16
+ PathLiteralPattern("quoted_docs", '"' + 'docs/'),
17
+ PathLiteralPattern("path_docs", 'Path(' + '"' + 'docs'),
18
+ PathLiteralPattern("quoted_tmp", '"' + 'tmp/'),
19
+ PathLiteralPattern("path_tmp", 'Path(' + '"' + 'tmp'),
20
+ )
21
+
22
+ REPO_IDENTITY_PATTERNS = (
23
+ PathLiteralPattern("repo_slug_prefix", "vfi64/"),
24
+ PathLiteralPattern("github_url", "github.com/"),
25
+ )
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class PathLiteralClassification:
30
+ kind: str
31
+ counts_as_active_path_literal: bool
32
+ rationale: str
33
+
34
+ def as_dict(self) -> dict[str, object]:
35
+ return asdict(self)
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class RepoIdentityLiteralClassification:
40
+ kind: str
41
+ counts_as_active_identity_literal: bool
42
+ rationale: str
43
+
44
+ def as_dict(self) -> dict[str, object]:
45
+ return asdict(self)
46
+
47
+
48
+ DECLARED_PATH_LITERAL_CLASSIFICATIONS = MappingProxyType(
49
+ {
50
+ "src/agentic_project_kit/workspace.py": PathLiteralClassification(
51
+ kind="resolver_source",
52
+ counts_as_active_path_literal=False,
53
+ rationale="workspace.py is the declared single source for legacy and namespace path defaults",
54
+ ),
55
+ "src/agentic_project_kit/templates.py": PathLiteralClassification(
56
+ kind="template_data",
57
+ counts_as_active_path_literal=False,
58
+ rationale="generated project template contents are data, not kit runtime path access",
59
+ ),
60
+ }
61
+ )
62
+
63
+ DECLARED_REPO_IDENTITY_EXCEPTIONS = MappingProxyType(
64
+ {
65
+ "src/agentic_project_kit/gui_cockpit_actions.py": RepoIdentityLiteralClassification(
66
+ kind="reference",
67
+ counts_as_active_identity_literal=False,
68
+ rationale="GUI help text points users to the kit source repository; it is not runtime workspace identity",
69
+ ),
70
+ "src/agentic_project_kit/gui_task_editor.py": RepoIdentityLiteralClassification(
71
+ kind="template",
72
+ counts_as_active_identity_literal=False,
73
+ rationale="GUI initial-prompt template is fixed kit bootstrap copy until P6 parameterizes selected workspaces",
74
+ ),
75
+ "src/agentic_project_kit/templates.py": RepoIdentityLiteralClassification(
76
+ kind="template",
77
+ counts_as_active_identity_literal=False,
78
+ rationale="generated project template contents are data and package metadata, not runtime repository identity",
79
+ ),
80
+ }
81
+ )
82
+
83
+
84
+ @dataclass(frozen=True)
85
+ class PathLiteralModuleCount:
86
+ path: str
87
+ total: int
88
+ patterns: dict[str, int]
89
+ classification: PathLiteralClassification
90
+
91
+ def as_dict(self) -> dict[str, object]:
92
+ data = asdict(self)
93
+ data["classification"] = self.classification.as_dict()
94
+ return data
95
+
96
+
97
+ @dataclass(frozen=True)
98
+ class RepoIdentityLiteralModuleCount:
99
+ path: str
100
+ total: int
101
+ patterns: dict[str, int]
102
+ classification: RepoIdentityLiteralClassification
103
+
104
+ def as_dict(self) -> dict[str, object]:
105
+ data = asdict(self)
106
+ data["classification"] = self.classification.as_dict()
107
+ return data
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ class PathLiteralAuditResult:
112
+ root: str
113
+ patterns: tuple[PathLiteralPattern, ...]
114
+ modules: tuple[PathLiteralModuleCount, ...]
115
+ repo_identity_patterns: tuple[PathLiteralPattern, ...]
116
+ repo_identity_modules: tuple[RepoIdentityLiteralModuleCount, ...]
117
+
118
+ @property
119
+ def status(self) -> str:
120
+ return "REPORT"
121
+
122
+ @property
123
+ def returncode(self) -> int:
124
+ return 0
125
+
126
+ @property
127
+ def affected_module_count(self) -> int:
128
+ return len(self.modules)
129
+
130
+ @property
131
+ def literal_count(self) -> int:
132
+ return sum(module.total for module in self.modules)
133
+
134
+ @property
135
+ def active_path_modules(self) -> tuple[PathLiteralModuleCount, ...]:
136
+ return tuple(
137
+ module
138
+ for module in self.modules
139
+ if module.classification.counts_as_active_path_literal
140
+ )
141
+
142
+ @property
143
+ def active_path_module_count(self) -> int:
144
+ return len(self.active_path_modules)
145
+
146
+ @property
147
+ def active_path_literal_count(self) -> int:
148
+ return sum(module.total for module in self.active_path_modules)
149
+
150
+ @property
151
+ def declared_exception_modules(self) -> tuple[PathLiteralModuleCount, ...]:
152
+ return tuple(
153
+ module
154
+ for module in self.modules
155
+ if module.classification.kind in {"resolver_source", "template_data"}
156
+ )
157
+
158
+ @property
159
+ def repo_identity_literal_count(self) -> int:
160
+ return sum(module.total for module in self.repo_identity_modules)
161
+
162
+ @property
163
+ def active_repo_identity_modules(self) -> tuple[RepoIdentityLiteralModuleCount, ...]:
164
+ return tuple(
165
+ module
166
+ for module in self.repo_identity_modules
167
+ if module.classification.counts_as_active_identity_literal
168
+ )
169
+
170
+ @property
171
+ def active_repo_identity_module_count(self) -> int:
172
+ return len(self.active_repo_identity_modules)
173
+
174
+ @property
175
+ def active_repo_identity_literal_count(self) -> int:
176
+ return sum(module.total for module in self.active_repo_identity_modules)
177
+
178
+ @property
179
+ def declared_identity_exception_modules(self) -> tuple[RepoIdentityLiteralModuleCount, ...]:
180
+ return tuple(
181
+ module
182
+ for module in self.repo_identity_modules
183
+ if module.path in DECLARED_REPO_IDENTITY_EXCEPTIONS
184
+ )
185
+
186
+ @property
187
+ def classification_summary(self) -> dict[str, dict[str, object]]:
188
+ summary: dict[str, dict[str, object]] = {}
189
+ for module in self.modules:
190
+ classification = module.classification
191
+ entry = summary.setdefault(
192
+ classification.kind,
193
+ {
194
+ "module_count": 0,
195
+ "literal_count": 0,
196
+ "counts_as_active_path_literal": classification.counts_as_active_path_literal,
197
+ },
198
+ )
199
+ entry["module_count"] = int(entry["module_count"]) + 1
200
+ entry["literal_count"] = int(entry["literal_count"]) + module.total
201
+ return dict(sorted(summary.items()))
202
+
203
+ @property
204
+ def repo_identity_classification_summary(self) -> dict[str, dict[str, object]]:
205
+ summary: dict[str, dict[str, object]] = {}
206
+ for module in self.repo_identity_modules:
207
+ classification = module.classification
208
+ entry = summary.setdefault(
209
+ classification.kind,
210
+ {
211
+ "module_count": 0,
212
+ "literal_count": 0,
213
+ "counts_as_active_identity_literal": classification.counts_as_active_identity_literal,
214
+ },
215
+ )
216
+ entry["module_count"] = int(entry["module_count"]) + 1
217
+ entry["literal_count"] = int(entry["literal_count"]) + module.total
218
+ return dict(sorted(summary.items()))
219
+
220
+ def as_dict(self) -> dict[str, object]:
221
+ return {
222
+ "schema_version": 1,
223
+ "kind": "path_literal_audit",
224
+ "root": self.root,
225
+ "status": self.status,
226
+ "affected_module_count": self.affected_module_count,
227
+ "literal_count": self.literal_count,
228
+ "active_path_module_count": self.active_path_module_count,
229
+ "active_path_literal_count": self.active_path_literal_count,
230
+ "declared_exception_module_count": len(self.declared_exception_modules),
231
+ "classification_summary": self.classification_summary,
232
+ "patterns": [asdict(pattern) for pattern in self.patterns],
233
+ "modules": [module.as_dict() for module in self.modules],
234
+ "active_path_modules": [module.as_dict() for module in self.active_path_modules],
235
+ "declared_exception_modules": [
236
+ module.as_dict() for module in self.declared_exception_modules
237
+ ],
238
+ "repo_identity_literal_count": self.repo_identity_literal_count,
239
+ "active_repo_identity_module_count": self.active_repo_identity_module_count,
240
+ "active_repo_identity_literal_count": self.active_repo_identity_literal_count,
241
+ "declared_identity_exception_module_count": len(
242
+ self.declared_identity_exception_modules
243
+ ),
244
+ "repo_identity_classification_summary": self.repo_identity_classification_summary,
245
+ "repo_identity_patterns": [asdict(pattern) for pattern in self.repo_identity_patterns],
246
+ "repo_identity_modules": [
247
+ module.as_dict() for module in self.repo_identity_modules
248
+ ],
249
+ "active_repo_identity_modules": [
250
+ module.as_dict() for module in self.active_repo_identity_modules
251
+ ],
252
+ "declared_identity_exception_modules": [
253
+ module.as_dict() for module in self.declared_identity_exception_modules
254
+ ],
255
+ }
256
+
257
+
258
+ @dataclass(frozen=True)
259
+ class PathLiteralActiveClassEnforcementResult:
260
+ audit: PathLiteralAuditResult
261
+
262
+ @property
263
+ def status(self) -> str:
264
+ return "PASS" if self.blocker_count == 0 else "FAIL"
265
+
266
+ @property
267
+ def returncode(self) -> int:
268
+ return 0 if self.status == "PASS" else 1
269
+
270
+ @property
271
+ def blocker_count(self) -> int:
272
+ return self.audit.active_path_module_count + self.audit.active_repo_identity_module_count
273
+
274
+ @property
275
+ def active_path_literal_count(self) -> int:
276
+ return self.audit.active_path_literal_count
277
+
278
+ @property
279
+ def active_identity_literal_count(self) -> int:
280
+ return self.audit.active_repo_identity_literal_count
281
+
282
+ @property
283
+ def non_blocking_path_literal_count(self) -> int:
284
+ return self.audit.literal_count - self.audit.active_path_literal_count
285
+
286
+ @property
287
+ def non_blocking_identity_literal_count(self) -> int:
288
+ return self.audit.repo_identity_literal_count - self.audit.active_repo_identity_literal_count
289
+
290
+ def as_dict(self) -> dict[str, object]:
291
+ return {
292
+ "schema_version": 1,
293
+ "kind": "path_literal_active_class_enforcement",
294
+ "status": self.status,
295
+ "returncode": self.returncode,
296
+ "blocker_count": self.blocker_count,
297
+ "active_path_literal_count": self.active_path_literal_count,
298
+ "active_identity_literal_count": self.active_identity_literal_count,
299
+ "non_blocking_path_literal_count": self.non_blocking_path_literal_count,
300
+ "non_blocking_identity_literal_count": self.non_blocking_identity_literal_count,
301
+ "active_path_modules": [
302
+ module.as_dict() for module in self.audit.active_path_modules
303
+ ],
304
+ "active_identity_modules": [
305
+ module.as_dict() for module in self.audit.active_repo_identity_modules
306
+ ],
307
+ "audit": self.audit.as_dict(),
308
+ }
309
+
310
+
311
+ def audit_path_literals(
312
+ root: Path = Path("."),
313
+ *,
314
+ patterns: tuple[PathLiteralPattern, ...] = PATH_LITERAL_PATTERNS,
315
+ repo_identity_patterns: tuple[PathLiteralPattern, ...] = REPO_IDENTITY_PATTERNS,
316
+ ) -> PathLiteralAuditResult:
317
+ root = root.resolve()
318
+ modules: list[PathLiteralModuleCount] = []
319
+ repo_identity_modules: list[RepoIdentityLiteralModuleCount] = []
320
+ src_root = root / "src"
321
+ if src_root.exists():
322
+ for path in sorted(src_root.rglob("*.py")):
323
+ if not path.is_file() or path.is_symlink():
324
+ continue
325
+ relative_path = path.relative_to(root).as_posix()
326
+ counts = _count_path_literals(path, patterns)
327
+ total = sum(counts.values())
328
+ if total:
329
+ modules.append(
330
+ PathLiteralModuleCount(
331
+ path=relative_path,
332
+ total=total,
333
+ patterns=counts,
334
+ classification=_classify_path_literal_module(relative_path, counts),
335
+ )
336
+ )
337
+ if relative_path != "src/agentic_project_kit/path_literal_audit.py":
338
+ repo_identity_counts = _count_path_literals(path, repo_identity_patterns)
339
+ repo_identity_total = sum(repo_identity_counts.values())
340
+ if repo_identity_total:
341
+ repo_identity_modules.append(
342
+ RepoIdentityLiteralModuleCount(
343
+ path=relative_path,
344
+ total=repo_identity_total,
345
+ patterns=repo_identity_counts,
346
+ classification=_classify_repo_identity_literal_module(
347
+ relative_path,
348
+ repo_identity_counts,
349
+ ),
350
+ )
351
+ )
352
+
353
+ modules.sort(key=lambda module: (-module.total, module.path))
354
+ repo_identity_modules.sort(key=lambda module: (-module.total, module.path))
355
+ return PathLiteralAuditResult(
356
+ root=root.as_posix(),
357
+ patterns=patterns,
358
+ modules=tuple(modules),
359
+ repo_identity_patterns=repo_identity_patterns,
360
+ repo_identity_modules=tuple(repo_identity_modules),
361
+ )
362
+
363
+
364
+ def enforce_active_literal_classes(
365
+ result: PathLiteralAuditResult,
366
+ ) -> PathLiteralActiveClassEnforcementResult:
367
+ return PathLiteralActiveClassEnforcementResult(audit=result)
368
+
369
+
370
+ def render_path_literal_audit(result: PathLiteralAuditResult) -> str:
371
+ lines = [
372
+ "PATH_LITERAL_AUDIT",
373
+ f"STATUS={result.status}",
374
+ f"AFFECTED_MODULES={result.affected_module_count}",
375
+ f"LITERAL_COUNT={result.literal_count}",
376
+ f"ACTIVE_PATH_MODULES={result.active_path_module_count}",
377
+ f"ACTIVE_PATH_LITERAL_COUNT={result.active_path_literal_count}",
378
+ f"DECLARED_EXCEPTION_MODULES={len(result.declared_exception_modules)}",
379
+ f"REPO_IDENTITY_MODULES={len(result.repo_identity_modules)}",
380
+ f"REPO_IDENTITY_LITERAL_COUNT={result.repo_identity_literal_count}",
381
+ f"ACTIVE_IDENTITY_MODULES={result.active_repo_identity_module_count}",
382
+ f"ACTIVE_IDENTITY_LITERAL_COUNT={result.active_repo_identity_literal_count}",
383
+ f"DECLARED_IDENTITY_EXCEPTION_MODULES={len(result.declared_identity_exception_modules)}",
384
+ ]
385
+ if result.classification_summary:
386
+ lines.append("CLASSIFICATION_SUMMARY:")
387
+ for kind, summary in result.classification_summary.items():
388
+ disposition = "active" if summary["counts_as_active_path_literal"] else "non-active"
389
+ lines.append(
390
+ f"- {kind}: modules={summary['module_count']} literals={summary['literal_count']} "
391
+ f"disposition={disposition}"
392
+ )
393
+ for module in result.modules:
394
+ pattern_summary = "|".join(
395
+ f"{pattern.name}={module.patterns.get(pattern.name, 0)}"
396
+ for pattern in result.patterns
397
+ )
398
+ disposition = "active" if module.classification.counts_as_active_path_literal else "non-active"
399
+ lines.append(
400
+ f"MODULE={module.total}|{module.path}|classification={module.classification.kind}|"
401
+ f"disposition={disposition}|{pattern_summary}"
402
+ )
403
+ if result.declared_exception_modules:
404
+ lines.append("DECLARED_EXCEPTIONS:")
405
+ for module in result.declared_exception_modules:
406
+ lines.append(
407
+ f"- {module.path}: {module.classification.kind} — {module.classification.rationale}"
408
+ )
409
+ if result.repo_identity_modules:
410
+ lines.append("REPO_IDENTITY_LITERALS:")
411
+ for module in result.repo_identity_modules:
412
+ pattern_summary = "|".join(
413
+ f"{pattern.name}={module.patterns.get(pattern.name, 0)}"
414
+ for pattern in result.repo_identity_patterns
415
+ )
416
+ disposition = (
417
+ "active" if module.classification.counts_as_active_identity_literal else "non-active"
418
+ )
419
+ lines.append(
420
+ f"REPO_IDENTITY={module.total}|{module.path}|classification={module.classification.kind}|"
421
+ f"disposition={disposition}|{pattern_summary}"
422
+ )
423
+ if result.declared_identity_exception_modules:
424
+ lines.append("DECLARED_IDENTITY_EXCEPTIONS:")
425
+ for module in result.declared_identity_exception_modules:
426
+ lines.append(
427
+ f"- {module.path}: {module.classification.kind} — {module.classification.rationale}"
428
+ )
429
+ return "\n".join(lines) + "\n"
430
+
431
+
432
+ def render_path_literal_active_class_enforcement(
433
+ result: PathLiteralActiveClassEnforcementResult,
434
+ ) -> str:
435
+ lines = [
436
+ "PATH_LITERAL_ACTIVE_CLASS_ENFORCEMENT",
437
+ f"STATUS={result.status}",
438
+ f"BLOCKER_COUNT={result.blocker_count}",
439
+ f"ACTIVE_PATH_LITERAL_COUNT={result.active_path_literal_count}",
440
+ f"ACTIVE_IDENTITY_LITERAL_COUNT={result.active_identity_literal_count}",
441
+ f"NON_BLOCKING_PATH_LITERAL_COUNT={result.non_blocking_path_literal_count}",
442
+ f"NON_BLOCKING_IDENTITY_LITERAL_COUNT={result.non_blocking_identity_literal_count}",
443
+ ]
444
+ for module in result.audit.active_path_modules:
445
+ lines.append(
446
+ f"BLOCKER=active_path_literal|{module.path}|literals={module.total}|"
447
+ f"classification={module.classification.kind}"
448
+ )
449
+ for module in result.audit.active_repo_identity_modules:
450
+ lines.append(
451
+ f"BLOCKER=active_identity_literal|{module.path}|literals={module.total}|"
452
+ f"classification={module.classification.kind}"
453
+ )
454
+ if result.audit.modules:
455
+ lines.append("PATH_LITERAL_CLASSIFICATION_SUMMARY:")
456
+ for kind, summary in result.audit.classification_summary.items():
457
+ disposition = "active" if summary["counts_as_active_path_literal"] else "non-active"
458
+ lines.append(
459
+ f"- {kind}: modules={summary['module_count']} literals={summary['literal_count']} "
460
+ f"disposition={disposition}"
461
+ )
462
+ if result.audit.repo_identity_modules:
463
+ lines.append("REPO_IDENTITY_CLASSIFICATION_SUMMARY:")
464
+ for kind, summary in result.audit.repo_identity_classification_summary.items():
465
+ disposition = (
466
+ "active" if summary["counts_as_active_identity_literal"] else "non-active"
467
+ )
468
+ lines.append(
469
+ f"- {kind}: modules={summary['module_count']} literals={summary['literal_count']} "
470
+ f"disposition={disposition}"
471
+ )
472
+ return "\n".join(lines) + "\n"
473
+
474
+
475
+ def render_path_literal_evidence_report(
476
+ result: PathLiteralAuditResult,
477
+ *,
478
+ command: str = "agentic-kit audit-path-literals",
479
+ generated_on: date | None = None,
480
+ ) -> str:
481
+ generated = generated_on or date.today()
482
+ pattern_headers = [pattern.name for pattern in result.patterns]
483
+ lines = [
484
+ "# Path Literal Audit Evidence",
485
+ "",
486
+ f"Generated: {generated.isoformat()}",
487
+ f"Command: `{command}`",
488
+ "Mode: REPORT-ONLY; this evidence does not gate the standard suite.",
489
+ "",
490
+ f"Affected modules: {result.affected_module_count}",
491
+ f"Literal count: {result.literal_count}",
492
+ f"Active path modules: {result.active_path_module_count}",
493
+ f"Active path literal count: {result.active_path_literal_count}",
494
+ f"Declared exception modules: {len(result.declared_exception_modules)}",
495
+ "",
496
+ "## Path Literal Classifications",
497
+ "",
498
+ "| module | classification | disposition | total | " + " | ".join(pattern_headers) + " |",
499
+ "|---|---|---|---:|" + "|".join("---:" for _ in pattern_headers) + "|",
500
+ ]
501
+ for module in result.modules:
502
+ counts = [str(module.patterns.get(name, 0)) for name in pattern_headers]
503
+ disposition = "active" if module.classification.counts_as_active_path_literal else "non-active"
504
+ lines.append(
505
+ f"| {module.path} | {module.classification.kind} | {disposition} | {module.total} | "
506
+ + " | ".join(counts)
507
+ + " |"
508
+ )
509
+ lines.extend(["", "## Declared Exceptions", ""])
510
+ if result.declared_exception_modules:
511
+ for module in result.declared_exception_modules:
512
+ lines.append(
513
+ f"- `{module.path}`: `{module.classification.kind}` — {module.classification.rationale}"
514
+ )
515
+ else:
516
+ lines.append("- none")
517
+ lines.extend(
518
+ [
519
+ "",
520
+ "## Repo Identity Literals",
521
+ "",
522
+ "Repo identity literals are listed for follow-up visibility and are not migrated by P4b.",
523
+ "",
524
+ f"Repo identity literal count: {result.repo_identity_literal_count}",
525
+ f"Active repo identity modules: {result.active_repo_identity_module_count}",
526
+ f"Active repo identity literal count: {result.active_repo_identity_literal_count}",
527
+ "",
528
+ ]
529
+ )
530
+ repo_pattern_headers = [pattern.name for pattern in result.repo_identity_patterns]
531
+ lines.append(
532
+ "| module | classification | disposition | total | "
533
+ + " | ".join(repo_pattern_headers)
534
+ + " |"
535
+ )
536
+ lines.append("|---|---|---|---:|" + "|".join("---:" for _ in repo_pattern_headers) + "|")
537
+ for module in result.repo_identity_modules:
538
+ counts = [str(module.patterns.get(name, 0)) for name in repo_pattern_headers]
539
+ disposition = (
540
+ "active" if module.classification.counts_as_active_identity_literal else "non-active"
541
+ )
542
+ lines.append(
543
+ f"| {module.path} | {module.classification.kind} | {disposition} | {module.total} | "
544
+ + " | ".join(counts)
545
+ + " |"
546
+ )
547
+ if not result.repo_identity_modules:
548
+ lines.append(
549
+ "| none | none | non-active | 0 | "
550
+ + " | ".join("0" for _ in repo_pattern_headers)
551
+ + " |"
552
+ )
553
+ lines.extend(["", "## Declared Identity Exceptions", ""])
554
+ if result.declared_identity_exception_modules:
555
+ for module in result.declared_identity_exception_modules:
556
+ lines.append(
557
+ f"- `{module.path}`: `{module.classification.kind}` — {module.classification.rationale}"
558
+ )
559
+ else:
560
+ lines.append("- none")
561
+ return "\n".join(lines) + "\n"
562
+
563
+
564
+ def _classify_path_literal_module(
565
+ relative_path: str,
566
+ counts: dict[str, int],
567
+ ) -> PathLiteralClassification:
568
+ declared = DECLARED_PATH_LITERAL_CLASSIFICATIONS.get(relative_path)
569
+ if declared is not None:
570
+ return declared
571
+ if counts.get("path_docs", 0) or counts.get("path_tmp", 0):
572
+ return PathLiteralClassification(
573
+ kind="active_path_literal",
574
+ counts_as_active_path_literal=True,
575
+ rationale="module still constructs docs/tmp paths directly instead of using the workspace resolver",
576
+ )
577
+ return PathLiteralClassification(
578
+ kind="reference_or_message",
579
+ counts_as_active_path_literal=False,
580
+ rationale="quoted docs/tmp text is report, policy, prompt, or diagnostic vocabulary rather than direct Path construction",
581
+ )
582
+
583
+
584
+ def _classify_repo_identity_literal_module(
585
+ relative_path: str,
586
+ counts: dict[str, int],
587
+ ) -> RepoIdentityLiteralClassification:
588
+ declared = DECLARED_REPO_IDENTITY_EXCEPTIONS.get(relative_path)
589
+ if declared is not None:
590
+ return declared
591
+ if relative_path.endswith("templates.py") or "template" in relative_path:
592
+ return RepoIdentityLiteralClassification(
593
+ kind="template",
594
+ counts_as_active_identity_literal=False,
595
+ rationale="repository identity literal appears in generated template data",
596
+ )
597
+ return RepoIdentityLiteralClassification(
598
+ kind="active",
599
+ counts_as_active_identity_literal=True,
600
+ rationale="repository identity literal is not declared as reference or template data",
601
+ )
602
+
603
+
604
+ def _count_path_literals(
605
+ path: Path,
606
+ patterns: tuple[PathLiteralPattern, ...],
607
+ ) -> dict[str, int]:
608
+ text = path.read_text(encoding="utf-8")
609
+ return {
610
+ pattern.name: text.count(pattern.literal)
611
+ for pattern in patterns
612
+ }