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,107 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ FIXED_SLOT_YAML = Path(".agentic/commands/inbox/next-turn.yaml")
8
+ FIXED_SLOT_SCRIPT = Path(".agentic/commands/inbox/next-turn.py")
9
+
10
+ @dataclass(frozen=True)
11
+ class FixedSlotStatus:
12
+ state: str
13
+ yaml_exists: bool
14
+ script_exists: bool
15
+ overwrite_allowed: bool
16
+ reason: str
17
+ yaml_path: str
18
+ script_path: str
19
+
20
+ def fixed_slot_status(root: Path | str = ".") -> FixedSlotStatus:
21
+ root_path = Path(root)
22
+ yaml_path = root_path / FIXED_SLOT_YAML
23
+ script_path = root_path / FIXED_SLOT_SCRIPT
24
+ yaml_exists = yaml_path.exists()
25
+ script_exists = script_path.exists()
26
+ if yaml_exists and script_exists:
27
+ return FixedSlotStatus("ready", True, True, False, "fixed-slot work order exists", str(FIXED_SLOT_YAML), str(FIXED_SLOT_SCRIPT))
28
+ if yaml_exists or script_exists:
29
+ return FixedSlotStatus("partial", yaml_exists, script_exists, False, "fixed-slot work order is incomplete; use --force only after inspection", str(FIXED_SLOT_YAML), str(FIXED_SLOT_SCRIPT))
30
+ return FixedSlotStatus("empty", False, False, True, "no fixed-slot work order exists", str(FIXED_SLOT_YAML), str(FIXED_SLOT_SCRIPT))
31
+
32
+ def render_status(status: FixedSlotStatus) -> str:
33
+ return "\n".join([
34
+ "NEXT_TURN_FIXED_SLOT_STATUS",
35
+ f"state={status.state}",
36
+ f"yaml_exists={str(status.yaml_exists).lower()}",
37
+ f"script_exists={str(status.script_exists).lower()}",
38
+ f"overwrite_allowed={str(status.overwrite_allowed).lower()}",
39
+ f"yaml_path={status.yaml_path}",
40
+ f"script_path={status.script_path}",
41
+ f"reason={status.reason}",
42
+ "### RESULT: PASS ###",
43
+ ])
44
+
45
+ def default_yaml(command_id: str = "next-turn") -> str:
46
+ return "\n".join([
47
+ "schema_version: 1",
48
+ f"command_id: {command_id}",
49
+ "kind: fixed-slot-work-order",
50
+ "slot: next-turn",
51
+ "entrypoint: .agentic/commands/inbox/next-turn.py",
52
+ "safety_class: bounded",
53
+ "status: pending",
54
+ "evidence:",
55
+ " local_terminal_log: ${AGENTIC_KIT_NEXT_TURN_LOG:-tmp/next-turn-latest.log}",
56
+ " remote_terminal_log: docs/reports/terminal/next-turn-latest.log",
57
+ " command_report: docs/reports/command_runs/next-turn-latest.json",
58
+ "",
59
+ ])
60
+
61
+ def default_script() -> str:
62
+ return "\n".join([
63
+ "#!/usr/bin/env python3",
64
+ "from __future__ import annotations",
65
+ "",
66
+ "def main() -> int:",
67
+ " print(\"NEXT_TURN_FIXED_SLOT_PLACEHOLDER\")",
68
+ " print(\"status=pending_runner\")",
69
+ " print(\"### RESULT: PASS ###\")",
70
+ " return 0",
71
+ "",
72
+ "if __name__ == \"__main__\":",
73
+ " raise SystemExit(main())",
74
+ "",
75
+ ])
76
+
77
+ def write_fixed_slot(root: Path | str = ".", *, force: bool = False, command_id: str = "next-turn") -> FixedSlotStatus:
78
+ root_path = Path(root)
79
+ status = fixed_slot_status(root_path)
80
+ if status.state != "empty" and not force:
81
+ raise RuntimeError(f"fixed slot is not empty: {status.state}")
82
+ yaml_path = root_path / FIXED_SLOT_YAML
83
+ script_path = root_path / FIXED_SLOT_SCRIPT
84
+ yaml_path.parent.mkdir(parents=True, exist_ok=True)
85
+ yaml_path.write_text(default_yaml(command_id), encoding="utf-8")
86
+ script_path.write_text(default_script(), encoding="utf-8")
87
+ script_path.chmod(0o755)
88
+ return fixed_slot_status(root_path)
89
+
90
+ def main() -> int:
91
+ parser = argparse.ArgumentParser(prog="next-turn-slot")
92
+ sub = parser.add_subparsers(dest="command", required=True)
93
+ sub.add_parser("status")
94
+ write_parser = sub.add_parser("write")
95
+ write_parser.add_argument("--force", action="store_true")
96
+ write_parser.add_argument("--command-id", default="next-turn")
97
+ args = parser.parse_args()
98
+ if args.command == "status":
99
+ print(render_status(fixed_slot_status()))
100
+ return 0
101
+ if args.command == "write":
102
+ print(render_status(write_fixed_slot(force=args.force, command_id=args.command_id)))
103
+ return 0
104
+ raise AssertionError(args.command)
105
+
106
+ if __name__ == "__main__":
107
+ raise SystemExit(main())
@@ -0,0 +1,274 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import re
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Iterable
9
+
10
+ from agentic_project_kit.evidence_inspector import EvidenceVerdict
11
+ from agentic_project_kit.evidence_inspector import inspect_evidence
12
+ from agentic_project_kit.next_turn_slot import FIXED_SLOT_SCRIPT
13
+ from agentic_project_kit.next_turn_slot import FIXED_SLOT_YAML
14
+
15
+ VALID_STATES = {"empty", "prepared", "running", "completed", "failed", "blocked", "recovery_needed"}
16
+
17
+ EVIDENCE_LOOKUP_ORDER = (
18
+ "docs/reports/command_runs/next-turn-latest.json",
19
+ "docs/reports/command_runs/next-turn-latest.md",
20
+ "docs/reports/terminal/next-turn-latest.log",
21
+ ".agentic/commands/executed.jsonl",
22
+ )
23
+
24
+ @dataclass(frozen=True)
25
+ class NextTurnStatus:
26
+ state: str
27
+ yaml_exists: bool
28
+ script_exists: bool
29
+ overwrite_allowed: bool
30
+ reason: str
31
+
32
+ def _extract_state(yaml_text: str) -> str | None:
33
+ match = re.search(r"(?m)^state:\s*([A-Za-z0-9_-]+)\s*$", yaml_text)
34
+ return match.group(1) if match else None
35
+
36
+ def detect_next_turn_status(root: Path | str = ".") -> NextTurnStatus:
37
+ root_path = Path(root)
38
+ yaml_path = root_path / FIXED_SLOT_YAML
39
+ script_path = root_path / FIXED_SLOT_SCRIPT
40
+ yaml_exists = yaml_path.exists()
41
+ script_exists = script_path.exists()
42
+ if not yaml_exists and not script_exists:
43
+ return NextTurnStatus("empty", False, False, True, "no fixed-slot files exist")
44
+ if yaml_exists != script_exists:
45
+ return NextTurnStatus("blocked", yaml_exists, script_exists, False, "partial fixed-slot state")
46
+ try:
47
+ declared = _extract_state(yaml_path.read_text(encoding="utf-8"))
48
+ except UnicodeDecodeError:
49
+ return NextTurnStatus("blocked", True, True, False, "next-turn yaml is not valid utf-8")
50
+ if declared is None:
51
+ return NextTurnStatus("blocked", True, True, False, "next-turn yaml has no state field")
52
+ if declared not in VALID_STATES:
53
+ return NextTurnStatus("blocked", True, True, False, f"unknown state: {declared}")
54
+ overwrite_allowed = declared in {"completed", "failed"}
55
+ reason = "completed or failed slot may be replaced" if overwrite_allowed else f"{declared} slot must not be overwritten"
56
+ return NextTurnStatus(declared, True, True, overwrite_allowed, reason)
57
+
58
+ def _bool(value: bool) -> str:
59
+ return "true" if value else "false"
60
+
61
+ def render_status(status: NextTurnStatus) -> str:
62
+ lines = [
63
+ "NEXT_TURN_STATUS",
64
+ f"state={status.state}",
65
+ f"yaml_exists={_bool(status.yaml_exists)}",
66
+ f"script_exists={_bool(status.script_exists)}",
67
+ f"overwrite_allowed={_bool(status.overwrite_allowed)}",
68
+ f"reason={status.reason}",
69
+ "evidence_lookup_order:",
70
+ ]
71
+ lines.extend(f"- {path}" for path in EVIDENCE_LOOKUP_ORDER)
72
+ lines.append("### RESULT: PASS ###")
73
+ return "\n".join(lines)
74
+
75
+ def _read_preview(path: Path) -> str:
76
+ text = path.read_text(encoding="utf-8", errors="replace")
77
+ if path.suffix == ".json":
78
+ try:
79
+ return json.dumps(json.loads(text), indent=2, sort_keys=True)
80
+ except json.JSONDecodeError:
81
+ return text[:4000]
82
+ if path.name == "executed.jsonl":
83
+ lines = [line for line in text.splitlines() if line.strip()]
84
+ return lines[-1] if lines else ""
85
+ return text[:4000]
86
+
87
+
88
+ def _classify_executed_jsonl(preview: str) -> tuple[str, str, str, str]:
89
+ if not preview:
90
+ return (
91
+ "FOUND_UNUSABLE",
92
+ "MISSING_EVIDENCE_UPLOAD_FIRST",
93
+ "paste-output",
94
+ "executed ledger exists but contains no usable entry; inspect terminal evidence before requesting pasted output",
95
+ )
96
+ try:
97
+ payload = json.loads(preview)
98
+ except json.JSONDecodeError:
99
+ return (
100
+ "FOUND_UNUSABLE",
101
+ "AMBIGUOUS_SUMMARY_REVIEW_REQUIRED",
102
+ "paste-output",
103
+ "executed ledger latest entry is unreadable; inspect terminal evidence before requesting pasted output",
104
+ )
105
+
106
+ outcome = str(payload.get("outcome") or "")
107
+ exit_code = payload.get("exit_code")
108
+
109
+ if outcome == "PASS_EXECUTED" or exit_code == 0:
110
+ return (
111
+ "FOUND_PASS",
112
+ "PASS_CONTINUE",
113
+ "d",
114
+ "continue from executed command ledger",
115
+ )
116
+ if outcome.startswith("FAIL") or isinstance(exit_code, int) and exit_code != 0:
117
+ return (
118
+ "FOUND_FAIL",
119
+ "FAIL_DIAGNOSE",
120
+ "f",
121
+ "diagnose from executed command ledger and inspect terminal evidence if needed",
122
+ )
123
+ return (
124
+ "FOUND_AMBIGUOUS",
125
+ "AMBIGUOUS_SUMMARY_REVIEW_REQUIRED",
126
+ "paste-output",
127
+ "executed ledger latest entry is ambiguous; inspect terminal evidence before requesting pasted output",
128
+ )
129
+
130
+ @dataclass(frozen=True)
131
+ class LastResult:
132
+ path: Path | None
133
+ checked_paths: tuple[str, ...]
134
+ preview: str
135
+ status: str
136
+ evidence_verdict: str
137
+ recommended_chat_reply: str
138
+ recovery: str
139
+
140
+
141
+ def _classify_json_result(path: Path, preview: str) -> tuple[str, str, str, str]:
142
+ try:
143
+ payload = json.loads(path.read_text(encoding="utf-8"))
144
+ except json.JSONDecodeError:
145
+ return (
146
+ "FOUND_UNUSABLE",
147
+ "AMBIGUOUS_SUMMARY_REVIEW_REQUIRED",
148
+ "paste-output",
149
+ "JSON result is unreadable; inspect terminal log before requesting pasted output.",
150
+ )
151
+
152
+ overall = str(payload.get("overall_result") or payload.get("work_result") or payload.get("outcome") or "")
153
+ remote = str(payload.get("remote_evidence") or "")
154
+ reply = str(payload.get("next_chat_reply") or "")
155
+
156
+ if reply:
157
+ recommended = reply
158
+ elif overall in {"PASS", "PASS_ALREADY_DONE", "NOOP", "PASS_EXECUTED"}:
159
+ recommended = "d"
160
+ elif remote in {"PASS", "PARTIAL"}:
161
+ recommended = "f"
162
+ else:
163
+ recommended = "paste-output"
164
+
165
+ if overall in {"PASS", "PASS_ALREADY_DONE", "NOOP", "PASS_EXECUTED"}:
166
+ verdict = "PASS_CONTINUE"
167
+ status = "FOUND_PASS"
168
+ recovery = "continue from structured command result"
169
+ elif overall in {"FAIL", "HARD_FAIL", "FAIL_EXECUTED", "FAIL_NO_FIXED_SLOT"}:
170
+ verdict = "FAIL_DIAGNOSE"
171
+ status = "FOUND_FAIL"
172
+ recovery = "diagnose from structured command result and then inspect terminal evidence if needed"
173
+ else:
174
+ verdict = "AMBIGUOUS_SUMMARY_REVIEW_REQUIRED"
175
+ status = "FOUND_AMBIGUOUS"
176
+ recovery = "inspect terminal evidence before requesting pasted output"
177
+
178
+ if recommended == "paste-output" and "docs/reports/terminal/next-turn-latest.log" in preview:
179
+ recovery = "inspect the referenced terminal log before requesting pasted output"
180
+
181
+ return status, verdict, recommended, recovery
182
+
183
+
184
+ def _classify_terminal_result(path: Path, root: Path) -> tuple[str, str, str, str]:
185
+ inspection = inspect_evidence(path, root=root, require_summary=False)
186
+ verdict = inspection.verdict.value
187
+ if inspection.verdict == EvidenceVerdict.PASS_CONTINUE:
188
+ return "FOUND_PASS", verdict, "d", "continue from inspected terminal evidence"
189
+ if inspection.verdict == EvidenceVerdict.FAIL_DIAGNOSE:
190
+ return "FOUND_FAIL", verdict, "f", "diagnose from inspected terminal evidence"
191
+ if inspection.verdict == EvidenceVerdict.MISSING_EVIDENCE_UPLOAD_FIRST:
192
+ return "FOUND_UNUSABLE", verdict, "paste-output", "upload or locate evidence before requesting pasted output"
193
+ return "FOUND_AMBIGUOUS", verdict, "paste-output", "review ambiguous terminal evidence before requesting pasted output"
194
+
195
+
196
+ def find_last_result(root: Path | str = ".") -> LastResult:
197
+ root_path = Path(root)
198
+ checked: list[str] = []
199
+ for relative in EVIDENCE_LOOKUP_ORDER:
200
+ checked.append(relative)
201
+ candidate = root_path / relative
202
+ if not candidate.exists():
203
+ continue
204
+ preview = _read_preview(candidate)
205
+ if candidate.suffix == ".json":
206
+ status, verdict, reply, recovery = _classify_json_result(candidate, preview)
207
+ elif candidate.name == "executed.jsonl":
208
+ status, verdict, reply, recovery = _classify_executed_jsonl(preview)
209
+ elif candidate.suffix in {".log", ".md"}:
210
+ status, verdict, reply, recovery = _classify_terminal_result(candidate, root_path)
211
+ else:
212
+ status, verdict, reply, recovery = (
213
+ "FOUND_AMBIGUOUS",
214
+ "AMBIGUOUS_SUMMARY_REVIEW_REQUIRED",
215
+ "paste-output",
216
+ "review result source before requesting pasted output",
217
+ )
218
+ return LastResult(candidate, tuple(checked), preview, status, verdict, reply, recovery)
219
+ return LastResult(
220
+ None,
221
+ tuple(checked),
222
+ "",
223
+ "NO_RESULT_FOUND",
224
+ "MISSING_EVIDENCE_UPLOAD_FIRST",
225
+ "paste-output",
226
+ "run ./ns next-turn --status before requesting pasted terminal output",
227
+ )
228
+
229
+
230
+ def render_last_result(root: Path | str = ".") -> str:
231
+ result = find_last_result(root)
232
+ lines: list[str] = ["NEXT_TURN_LAST_RESULT"]
233
+ if result.path is None:
234
+ lines.append(f"status={result.status}")
235
+ lines.append(f"evidence_verdict={result.evidence_verdict}")
236
+ lines.append(f"recommended_chat_reply={result.recommended_chat_reply}")
237
+ lines.append("checked_paths:")
238
+ lines.extend(f"- {path}" for path in result.checked_paths)
239
+ lines.append(f"recovery={result.recovery}")
240
+ lines.append("### RESULT: PASS ###")
241
+ return "\n".join(lines)
242
+ try:
243
+ relative = result.path.relative_to(Path(root))
244
+ except ValueError:
245
+ relative = result.path
246
+ lines.append(f"status={result.status}")
247
+ lines.append(f"path={relative}")
248
+ lines.append(f"evidence_verdict={result.evidence_verdict}")
249
+ lines.append(f"recommended_chat_reply={result.recommended_chat_reply}")
250
+ lines.append(f"recovery={result.recovery}")
251
+ lines.append("checked_paths:")
252
+ lines.extend(f"- {path}" for path in result.checked_paths)
253
+ lines.append("preview:")
254
+ lines.append(result.preview)
255
+ lines.append("### RESULT: PASS ###")
256
+ return "\n".join(lines)
257
+
258
+ def main(argv: Iterable[str] | None = None) -> int:
259
+ parser = argparse.ArgumentParser(prog="next-turn-status")
260
+ subparsers = parser.add_subparsers(dest="command", required=True)
261
+ next_turn = subparsers.add_parser("next-turn")
262
+ next_turn.add_argument("--status", action="store_true")
263
+ subparsers.add_parser("last-result")
264
+ args = parser.parse_args(list(argv) if argv is not None else None)
265
+ if args.command == "next-turn":
266
+ print(render_status(detect_next_turn_status()))
267
+ return 0
268
+ if args.command == "last-result":
269
+ print(render_last_result())
270
+ return 0
271
+ raise AssertionError(args.command)
272
+
273
+ if __name__ == "__main__":
274
+ raise SystemExit(main())
@@ -0,0 +1,258 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ import re
6
+ from typing import Iterable
7
+
8
+
9
+ LEGACY_PATTERNS: tuple[re.Pattern[str], ...] = (
10
+ re.compile(r"\./ns\b"),
11
+ re.compile(r"\bns-menu\b"),
12
+ re.compile(r"\bns_release_[A-Za-z0-9_]*"),
13
+ re.compile(r"\btools/ns_[A-Za-z0-9_./-]*"),
14
+ re.compile(r"\bNS RELEASE\b"),
15
+ )
16
+
17
+ TEXT_SUFFIXES = {
18
+ ".md",
19
+ ".rst",
20
+ ".txt",
21
+ ".py",
22
+ ".toml",
23
+ ".yaml",
24
+ ".yml",
25
+ ".json",
26
+ ".cff",
27
+ }
28
+
29
+ SKIP_DIR_PARTS = {
30
+ ".git",
31
+ ".venv",
32
+ "__pycache__",
33
+ ".pytest_cache",
34
+ ".ruff_cache",
35
+ "node_modules",
36
+ "tmp",
37
+ }
38
+
39
+ SKIP_PATH_PREFIXES = (
40
+ "docs/reports/",
41
+ ".agentic/transfer/outbox/",
42
+ )
43
+
44
+ CURRENT_INSTRUCTION_PATTERNS: tuple[re.Pattern[str], ...] = (
45
+ re.compile(r"\b(run|execute|use|start|call|invoke|rerun|copy|paste|command|befehl|ausführen|starte)\b", re.I),
46
+ re.compile(r"^\s*(?:[-*]\s*)?(?:cd\s+\S+\s*&&\s*)?\./ns\b", re.I),
47
+ re.compile(r"^\s*(?:[-*]\s*)?tools/ns_", re.I),
48
+ )
49
+
50
+ HISTORICAL_MARKERS: tuple[re.Pattern[str], ...] = (
51
+ re.compile(r"\b(historical|legacy|obsolete|removed|deprecated|archiv|archived|historisch|veraltet|entfernt)\b", re.I),
52
+ re.compile(r"\b(before|formerly|previously|früher|ehemalig)\b", re.I),
53
+ )
54
+
55
+ SAFE_CONTEXT_PATH_PARTS = (
56
+ "tests",
57
+ "docs/reports",
58
+ "docs/releases",
59
+ "docs/archive",
60
+ "CHANGELOG.md",
61
+ )
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class LegacyReference:
66
+ path: str
67
+ line: int
68
+ text: str
69
+ classification: str
70
+ reason: str
71
+
72
+ def as_dict(self) -> dict[str, object]:
73
+ return {
74
+ "path": self.path,
75
+ "line": self.line,
76
+ "text": self.text,
77
+ "classification": self.classification,
78
+ "reason": self.reason,
79
+ }
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class NsLegacyReferenceAuditResult:
84
+ status: str
85
+ returncode: int
86
+ references: tuple[LegacyReference, ...]
87
+ blockers: tuple[LegacyReference, ...]
88
+
89
+ @property
90
+ def ok(self) -> bool:
91
+ return self.returncode == 0
92
+
93
+ def as_dict(self) -> dict[str, object]:
94
+ return {
95
+ "status": self.status,
96
+ "returncode": self.returncode,
97
+ "references": [reference.as_dict() for reference in self.references],
98
+ "blockers": [reference.as_dict() for reference in self.blockers],
99
+ "ok": self.ok,
100
+ }
101
+
102
+
103
+ def _is_text_file(path: Path) -> bool:
104
+ if path.suffix in TEXT_SUFFIXES:
105
+ return True
106
+ if path.name in {"CHANGELOG", "README", "LICENSE"}:
107
+ return True
108
+ return False
109
+
110
+
111
+ def iter_candidate_files(project_root: Path) -> Iterable[Path]:
112
+ for path in sorted(project_root.rglob("*")):
113
+ if not path.is_file():
114
+ continue
115
+ relative = path.relative_to(project_root).as_posix()
116
+ relative_parts = path.relative_to(project_root).parts
117
+ if any(part in SKIP_DIR_PARTS for part in relative_parts):
118
+ continue
119
+ if any(relative.startswith(prefix) for prefix in SKIP_PATH_PREFIXES):
120
+ continue
121
+ if not _is_text_file(path):
122
+ continue
123
+ yield path
124
+
125
+
126
+ def _has_legacy_reference(line: str) -> bool:
127
+ return any(pattern.search(line) for pattern in LEGACY_PATTERNS)
128
+
129
+
130
+ def _safe_context_by_path(relative: str) -> bool:
131
+ return relative.startswith(SAFE_CONTEXT_PATH_PARTS) or any(
132
+ part and relative.startswith(part + "/") for part in SAFE_CONTEXT_PATH_PARTS
133
+ )
134
+
135
+
136
+ def _legacy_documentation_context(relative_path: str) -> bool:
137
+ return (
138
+ relative_path.startswith("docs/workflow/")
139
+ or relative_path.startswith("docs/governance/")
140
+ or relative_path.startswith("docs/strategy/")
141
+ or relative_path.startswith("docs/architecture/")
142
+ or relative_path.startswith("docs/planning/")
143
+ or relative_path == "docs/DOCUMENTATION_COVERAGE.yaml"
144
+ or relative_path == "docs/TEST_GATES.md"
145
+ or relative_path == "docs/WORKFLOW_OUTPUT_CYCLE.md"
146
+ )
147
+
148
+
149
+ def _compatibility_implementation_context(relative_path: str) -> bool:
150
+ return relative_path in {
151
+ "src/agentic_project_kit/ns_legacy_reference_audit.py",
152
+ "src/agentic_project_kit/planning_docs_consolidation_audit.py",
153
+ "src/agentic_project_kit/release_gate_core.py",
154
+ "src/agentic_project_kit/release_verify_core.py",
155
+ "src/agentic_project_kit/removed_ns_commands.py",
156
+ "src/agentic_project_kit/work_order_validator.py",
157
+ "src/agentic_project_kit/gui_dry_run.py",
158
+ "src/agentic_project_kit/chat_bootloader.py",
159
+ "src/agentic_project_kit/doc_currency_audit.py",
160
+ "src/agentic_project_kit/entrypoint_slice_runner.py",
161
+ "src/agentic_project_kit/handoff_prompt.py",
162
+ "src/agentic_project_kit/release_prepare.py",
163
+ "src/agentic_project_kit/run_summary_renderer.py",
164
+ "src/agentic_project_kit/terminal_block_guard.py",
165
+ "tools/NEXT_TRANSFER_TASK.py",
166
+ }
167
+
168
+
169
+ def classify_legacy_reference(relative_path: str, line: str) -> tuple[str, str]:
170
+ stripped = line.strip()
171
+ if _compatibility_implementation_context(relative_path):
172
+ return "compatibility_implementation", "legacy tokens are implementation/classifier compatibility patterns"
173
+ if relative_path.startswith("tests/"):
174
+ return "test_fixture", "test fixture/reference"
175
+ if _legacy_documentation_context(relative_path):
176
+ return "legacy_documentation_context", (
177
+ "planning/workflow/governance documentation; use follow-up documentation cleanup "
178
+ "rather than treating as current supported command surface"
179
+ )
180
+ if any(pattern.search(stripped) for pattern in HISTORICAL_MARKERS):
181
+ return "historical", "line contains explicit historical/legacy marker"
182
+ if relative_path.startswith("docs/reports/"):
183
+ return "generated_report_or_evidence", "report/evidence context"
184
+ if relative_path.startswith("docs/releases/") or relative_path == "CHANGELOG.md":
185
+ return "release_history", "release history context"
186
+ if _safe_context_by_path(relative_path) and not any(
187
+ pattern.search(stripped) for pattern in CURRENT_INSTRUCTION_PATTERNS
188
+ ):
189
+ return "historical", "safe documentation context without imperative wording"
190
+ if any(pattern.search(stripped) for pattern in CURRENT_INSTRUCTION_PATTERNS):
191
+ return "current_instruction_blocker", "looks like a current instruction or executable route"
192
+ return "needs_review", "legacy reference is not explicitly historical or a test fixture"
193
+
194
+
195
+ def audit_ns_legacy_references(project_root: Path | str = ".") -> NsLegacyReferenceAuditResult:
196
+ root = Path(project_root).resolve()
197
+ references: list[LegacyReference] = []
198
+
199
+ for path in iter_candidate_files(root):
200
+ relative = path.relative_to(root).as_posix()
201
+ try:
202
+ lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
203
+ except OSError:
204
+ continue
205
+ for index, line in enumerate(lines, start=1):
206
+ if not _has_legacy_reference(line):
207
+ continue
208
+ classification, reason = classify_legacy_reference(relative, line)
209
+ references.append(
210
+ LegacyReference(
211
+ path=relative,
212
+ line=index,
213
+ text=line.strip(),
214
+ classification=classification,
215
+ reason=reason,
216
+ )
217
+ )
218
+
219
+ blockers = tuple(
220
+ reference
221
+ for reference in references
222
+ if reference.classification in {"current_instruction_blocker", "needs_review"}
223
+ )
224
+ return NsLegacyReferenceAuditResult(
225
+ status="PASS" if not blockers else "BLOCK",
226
+ returncode=0 if not blockers else 1,
227
+ references=tuple(references),
228
+ blockers=blockers,
229
+ )
230
+
231
+
232
+ def render_ns_legacy_reference_audit(result: NsLegacyReferenceAuditResult) -> str:
233
+ lines = [
234
+ "NS_LEGACY_REFERENCE_AUDIT",
235
+ f"STATUS={result.status}",
236
+ f"RETURNCODE={result.returncode}",
237
+ f"REFERENCE_COUNT={len(result.references)}",
238
+ f"BLOCKER_COUNT={len(result.blockers)}",
239
+ ]
240
+ for reference in result.references:
241
+ lines.append(
242
+ "REFERENCE="
243
+ f"{reference.classification}|{reference.path}:{reference.line}|{reference.text}"
244
+ )
245
+ for blocker in result.blockers:
246
+ lines.append(f"BLOCKER={blocker.path}:{blocker.line}|{blocker.reason}|{blocker.text}")
247
+ lines.append("FINAL_SIGNAL=d" if result.ok else "FINAL_SIGNAL=f")
248
+ return "\n".join(lines) + "\n"
249
+
250
+
251
+ def main() -> int:
252
+ result = audit_ns_legacy_references(Path("."))
253
+ print(render_ns_legacy_reference_audit(result), end="")
254
+ return result.returncode
255
+
256
+
257
+ if __name__ == "__main__":
258
+ raise SystemExit(main())
@@ -0,0 +1,16 @@
1
+ """Backward-compatible import shim for the neutral entrypoint slice runner.
2
+
3
+ The supported implementation lives in
4
+ :mod:`agentic_project_kit.entrypoint_slice_runner`.
5
+
6
+ This shim remains temporarily to keep historical imports stable while active
7
+ runtime paths move away from the ns-named module.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from agentic_project_kit.entrypoint_slice_runner import * # noqa: F403
13
+ from agentic_project_kit.entrypoint_slice_runner import main
14
+
15
+ if __name__ == "__main__":
16
+ raise SystemExit(main())