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.
- agentic_project_kit/__init__.py +1 -0
- agentic_project_kit/absolute_path_portability_audit.py +241 -0
- agentic_project_kit/access_level_policy.py +14 -0
- agentic_project_kit/access_levels.py +22 -0
- agentic_project_kit/action_registry.py +195 -0
- agentic_project_kit/action_specs.py +286 -0
- agentic_project_kit/agent_command_runner.py +516 -0
- agentic_project_kit/audit_test_matrix.py +36 -0
- agentic_project_kit/audit_test_matrix_cli.py +43 -0
- agentic_project_kit/chat_bootloader.py +257 -0
- agentic_project_kit/chat_entrypoint_contract.py +297 -0
- agentic_project_kit/checks.py +511 -0
- agentic_project_kit/ci_readiness.py +211 -0
- agentic_project_kit/cli.py +147 -0
- agentic_project_kit/cli_commands/__init__.py +1 -0
- agentic_project_kit/cli_commands/absolute_path_audit.py +25 -0
- agentic_project_kit/cli_commands/actions.py +21 -0
- agentic_project_kit/cli_commands/artifact_gc.py +240 -0
- agentic_project_kit/cli_commands/boot.py +55 -0
- agentic_project_kit/cli_commands/chat.py +34 -0
- agentic_project_kit/cli_commands/checks.py +305 -0
- agentic_project_kit/cli_commands/cockpit.py +103 -0
- agentic_project_kit/cli_commands/command_taxonomy.py +25 -0
- agentic_project_kit/cli_commands/commands.py +129 -0
- agentic_project_kit/cli_commands/dev.py +23 -0
- agentic_project_kit/cli_commands/direction.py +92 -0
- agentic_project_kit/cli_commands/doc_currency_audit.py +25 -0
- agentic_project_kit/cli_commands/doc_registry.py +126 -0
- agentic_project_kit/cli_commands/docs.py +223 -0
- agentic_project_kit/cli_commands/dpa.py +1338 -0
- agentic_project_kit/cli_commands/evidence.py +251 -0
- agentic_project_kit/cli_commands/github.py +34 -0
- agentic_project_kit/cli_commands/governance.py +14 -0
- agentic_project_kit/cli_commands/gui.py +23 -0
- agentic_project_kit/cli_commands/gui_readiness_gate.py +27 -0
- agentic_project_kit/cli_commands/handoff.py +183 -0
- agentic_project_kit/cli_commands/human_workflows.py +430 -0
- agentic_project_kit/cli_commands/init.py +132 -0
- agentic_project_kit/cli_commands/instruction.py +75 -0
- agentic_project_kit/cli_commands/mutation_lock_audit.py +24 -0
- agentic_project_kit/cli_commands/ns_legacy_audit.py +25 -0
- agentic_project_kit/cli_commands/pass_already_done.py +66 -0
- agentic_project_kit/cli_commands/patch_failure_discipline_audit.py +26 -0
- agentic_project_kit/cli_commands/patch_preflight.py +37 -0
- agentic_project_kit/cli_commands/path_literal_audit.py +39 -0
- agentic_project_kit/cli_commands/patterns.py +40 -0
- agentic_project_kit/cli_commands/planning_docs_audit.py +25 -0
- agentic_project_kit/cli_commands/pr.py +133 -0
- agentic_project_kit/cli_commands/pr_hygiene.py +27 -0
- agentic_project_kit/cli_commands/profiles.py +26 -0
- agentic_project_kit/cli_commands/program_redundancy_audit.py +25 -0
- agentic_project_kit/cli_commands/project_direction.py +25 -0
- agentic_project_kit/cli_commands/release.py +392 -0
- agentic_project_kit/cli_commands/release_publish_orchestration.py +36 -0
- agentic_project_kit/cli_commands/remote_branch_hygiene.py +152 -0
- agentic_project_kit/cli_commands/remote_next.py +57 -0
- agentic_project_kit/cli_commands/removed_ns_commands.py +65 -0
- agentic_project_kit/cli_commands/rule_registry.py +129 -0
- agentic_project_kit/cli_commands/rules.py +222 -0
- agentic_project_kit/cli_commands/scaffold.py +46 -0
- agentic_project_kit/cli_commands/slice.py +21 -0
- agentic_project_kit/cli_commands/standard_gates_audit_suite.py +27 -0
- agentic_project_kit/cli_commands/state.py +51 -0
- agentic_project_kit/cli_commands/status_current_state_audit.py +30 -0
- agentic_project_kit/cli_commands/todo.py +53 -0
- agentic_project_kit/cli_commands/transfer.py +19 -0
- agentic_project_kit/cli_commands/transfer_context_flow.py +398 -0
- agentic_project_kit/cli_commands/transfer_context_helpers.py +398 -0
- agentic_project_kit/cli_commands/transfer_diagnostics.py +767 -0
- agentic_project_kit/cli_commands/transfer_evidence_flow.py +368 -0
- agentic_project_kit/cli_commands/transfer_handoff_flow.py +594 -0
- agentic_project_kit/cli_commands/transfer_order_contract.py +74 -0
- agentic_project_kit/cli_commands/transfer_post_merge_complete.py +442 -0
- agentic_project_kit/cli_commands/transfer_post_merge_settle.py +171 -0
- agentic_project_kit/cli_commands/transfer_pr_closeout_complete.py +124 -0
- agentic_project_kit/cli_commands/transfer_pr_create_flow.py +743 -0
- agentic_project_kit/cli_commands/transfer_pr_merge_flow.py +547 -0
- agentic_project_kit/cli_commands/transfer_repo_after_pr.py +160 -0
- agentic_project_kit/cli_commands/transfer_repo_pre_pr.py +664 -0
- agentic_project_kit/cli_commands/transfer_runner_flow.py +131 -0
- agentic_project_kit/cli_commands/transfer_runtime_initial.py +194 -0
- agentic_project_kit/cli_commands/transfer_shared.py +274 -0
- agentic_project_kit/cli_commands/validation.py +200 -0
- agentic_project_kit/cli_commands/version.py +27 -0
- agentic_project_kit/cli_commands/work_orders.py +219 -0
- agentic_project_kit/cli_commands/workflow.py +471 -0
- agentic_project_kit/cli_commands/workflow_guard.py +26 -0
- agentic_project_kit/cli_commands/workspace.py +252 -0
- agentic_project_kit/cockpit.py +608 -0
- agentic_project_kit/cockpit_readiness.py +67 -0
- agentic_project_kit/command_authority_audit.py +192 -0
- agentic_project_kit/command_inbox_check.py +103 -0
- agentic_project_kit/command_manifest.py +591 -0
- agentic_project_kit/command_queue_contract.py +161 -0
- agentic_project_kit/command_selector.py +245 -0
- agentic_project_kit/command_taxonomy.py +264 -0
- agentic_project_kit/commit_guard.py +67 -0
- agentic_project_kit/communication_artifact_gc.py +495 -0
- agentic_project_kit/communication_rule_context.py +437 -0
- agentic_project_kit/communication_state.py +115 -0
- agentic_project_kit/composed_wrapper_invariants.py +97 -0
- agentic_project_kit/composite_short_circuit.py +140 -0
- agentic_project_kit/contract.py +251 -0
- agentic_project_kit/control_state.py +83 -0
- agentic_project_kit/doc_currency_audit.py +536 -0
- agentic_project_kit/doc_lifecycle.py +1179 -0
- agentic_project_kit/doc_lifecycle_signals.py +446 -0
- agentic_project_kit/doc_lifecycle_sweep.py +639 -0
- agentic_project_kit/doc_mesh.py +491 -0
- agentic_project_kit/doctor.py +379 -0
- agentic_project_kit/documentation_registry.py +1122 -0
- agentic_project_kit/documentation_system_audit.py +310 -0
- agentic_project_kit/dpa_current_handoff_lifecycle.py +549 -0
- agentic_project_kit/dpa_dp2_decision_readiness.py +362 -0
- agentic_project_kit/dpa_dp3_dp4_adjudication.py +604 -0
- agentic_project_kit/dpa_dp5_block_new_gate.py +294 -0
- agentic_project_kit/dpa_dp5_stage_adoption.py +615 -0
- agentic_project_kit/dpa_dp5_strict_gate.py +187 -0
- agentic_project_kit/dpa_final_closeout.py +627 -0
- agentic_project_kit/dpa_fixture_evidence.py +680 -0
- agentic_project_kit/dpa_maintainer_record_check.py +711 -0
- agentic_project_kit/dpa_post_dp2_scope_assessment.py +734 -0
- agentic_project_kit/dpa_probe_002_readiness.py +509 -0
- agentic_project_kit/dpa_probe_003_readiness.py +417 -0
- agentic_project_kit/dpa_probe_004_readiness.py +437 -0
- agentic_project_kit/dpa_readiness.py +505 -0
- agentic_project_kit/dpa_readonly_probe_execution.py +500 -0
- agentic_project_kit/dpa_registry_contracts.py +312 -0
- agentic_project_kit/dpa_renderer_readiness.py +407 -0
- agentic_project_kit/dpa_repo_adoption_assessment.py +574 -0
- agentic_project_kit/dpa_stable_readiness.py +743 -0
- agentic_project_kit/dpa_successor_projection.py +168 -0
- agentic_project_kit/dpa_workspace_init_projection.py +93 -0
- agentic_project_kit/dpa_wrt_ch001_evidence.py +327 -0
- agentic_project_kit/entrypoint_slice_runner.py +153 -0
- agentic_project_kit/evidence_clean.py +152 -0
- agentic_project_kit/evidence_commit_paths.py +214 -0
- agentic_project_kit/evidence_finalize_log.py +204 -0
- agentic_project_kit/evidence_guard.py +114 -0
- agentic_project_kit/evidence_inspector.py +507 -0
- agentic_project_kit/evidence_state_contract.py +99 -0
- agentic_project_kit/execution_mode_state.py +148 -0
- agentic_project_kit/final_summary_contract.py +85 -0
- agentic_project_kit/finalize_guard.py +139 -0
- agentic_project_kit/gatekeeper_core.py +232 -0
- agentic_project_kit/github.py +25 -0
- agentic_project_kit/governance.py +59 -0
- agentic_project_kit/gui_action_execution.py +105 -0
- agentic_project_kit/gui_action_renderer.py +81 -0
- agentic_project_kit/gui_action_run_intent.py +98 -0
- agentic_project_kit/gui_action_views.py +195 -0
- agentic_project_kit/gui_activity_log.py +76 -0
- agentic_project_kit/gui_button_catalog.py +605 -0
- agentic_project_kit/gui_cockpit.py +365 -0
- agentic_project_kit/gui_cockpit_actions.py +695 -0
- agentic_project_kit/gui_cockpit_common.py +172 -0
- agentic_project_kit/gui_cockpit_header.py +501 -0
- agentic_project_kit/gui_cockpit_sidebar.py +387 -0
- agentic_project_kit/gui_cockpit_task.py +748 -0
- agentic_project_kit/gui_command_projection.py +355 -0
- agentic_project_kit/gui_communication_modes.py +130 -0
- agentic_project_kit/gui_dry_run.py +219 -0
- agentic_project_kit/gui_gatekeeper_status.py +259 -0
- agentic_project_kit/gui_layout_plan.py +148 -0
- agentic_project_kit/gui_open_folder.py +18 -0
- agentic_project_kit/gui_output_status_panel.py +61 -0
- agentic_project_kit/gui_panel_state.py +47 -0
- agentic_project_kit/gui_presenter.py +62 -0
- agentic_project_kit/gui_readiness_gate.py +206 -0
- agentic_project_kit/gui_release_flow.py +59 -0
- agentic_project_kit/gui_task_editor.py +1077 -0
- agentic_project_kit/gui_tk_widgets.py +177 -0
- agentic_project_kit/gui_tkinter_renderer.py +87 -0
- agentic_project_kit/gui_tkinter_shell.py +1176 -0
- agentic_project_kit/gui_transfer_contract.py +28 -0
- agentic_project_kit/gui_viewmodel.py +698 -0
- agentic_project_kit/gui_window_guard.py +46 -0
- agentic_project_kit/handoff_freshness.py +390 -0
- agentic_project_kit/handoff_prompt.py +180 -0
- agentic_project_kit/handoff_state.py +254 -0
- agentic_project_kit/instruction_lint.py +455 -0
- agentic_project_kit/llm_context_carriers.py +66 -0
- agentic_project_kit/llm_execution_context.py +285 -0
- agentic_project_kit/local_command_stack.py +133 -0
- agentic_project_kit/local_feature_gate.py +82 -0
- agentic_project_kit/local_garbage_collector.py +269 -0
- agentic_project_kit/models.py +18 -0
- agentic_project_kit/mutation_lock_audit.py +421 -0
- agentic_project_kit/next_turn_evidence.py +253 -0
- agentic_project_kit/next_turn_merge_if_green.py +459 -0
- agentic_project_kit/next_turn_pr_status.py +255 -0
- agentic_project_kit/next_turn_result.py +183 -0
- agentic_project_kit/next_turn_runner.py +159 -0
- agentic_project_kit/next_turn_slot.py +107 -0
- agentic_project_kit/next_turn_status.py +274 -0
- agentic_project_kit/ns_legacy_reference_audit.py +258 -0
- agentic_project_kit/ns_slice_runner.py +16 -0
- agentic_project_kit/operational_handoff_projection.py +124 -0
- agentic_project_kit/output_contract.py +80 -0
- agentic_project_kit/output_repair.py +61 -0
- agentic_project_kit/pass_already_done.py +216 -0
- agentic_project_kit/patch_artifact_preflight.py +147 -0
- agentic_project_kit/patch_cycle_workflow.py +440 -0
- agentic_project_kit/patch_failure_discipline_audit.py +223 -0
- agentic_project_kit/patch_preflight.py +215 -0
- agentic_project_kit/path_literal_audit.py +612 -0
- agentic_project_kit/patterns.py +99 -0
- agentic_project_kit/planning_docs_consolidation_audit.py +313 -0
- agentic_project_kit/portability_shell_gate.py +40 -0
- agentic_project_kit/post_merge_handoff_refresh.py +56 -0
- agentic_project_kit/post_release.py +252 -0
- agentic_project_kit/post_release_closeout.py +616 -0
- agentic_project_kit/pr_cleanup.py +172 -0
- agentic_project_kit/pr_closeout.py +50 -0
- agentic_project_kit/pr_closeout_complete.py +344 -0
- agentic_project_kit/pr_create_or_skip.py +71 -0
- agentic_project_kit/pr_hygiene.py +210 -0
- agentic_project_kit/program_redundancy_audit.py +251 -0
- agentic_project_kit/project_direction.py +840 -0
- agentic_project_kit/protected_change_planner.py +194 -0
- agentic_project_kit/release.py +401 -0
- agentic_project_kit/release_doi_safety.py +24 -0
- agentic_project_kit/release_gate_core.py +174 -0
- agentic_project_kit/release_metadata_authority_gate.py +328 -0
- agentic_project_kit/release_metadata_prep.py +43 -0
- agentic_project_kit/release_notes.py +823 -0
- agentic_project_kit/release_prep_core.py +204 -0
- agentic_project_kit/release_prepare.py +369 -0
- agentic_project_kit/release_process_guardrails.py +247 -0
- agentic_project_kit/release_publish_core.py +114 -0
- agentic_project_kit/release_publish_orchestration.py +469 -0
- agentic_project_kit/release_state.py +552 -0
- agentic_project_kit/release_verify_core.py +175 -0
- agentic_project_kit/remote_branch_hygiene.py +223 -0
- agentic_project_kit/remote_next.py +89 -0
- agentic_project_kit/remote_next_closeout.py +101 -0
- agentic_project_kit/remote_next_order_contract.py +391 -0
- agentic_project_kit/removed_ns_commands.py +458 -0
- agentic_project_kit/removed_source_audit.py +225 -0
- agentic_project_kit/rendering.py +13 -0
- agentic_project_kit/repair_report.py +79 -0
- agentic_project_kit/repo_identity.py +59 -0
- agentic_project_kit/result_report_classifier.py +57 -0
- agentic_project_kit/rule_ack.py +105 -0
- agentic_project_kit/rule_preservation.py +58 -0
- agentic_project_kit/rule_refresh.py +174 -0
- agentic_project_kit/rule_registry_registration.py +322 -0
- agentic_project_kit/rule_registry_report.py +206 -0
- agentic_project_kit/rule_registry_validator.py +704 -0
- agentic_project_kit/rule_snapshot.py +98 -0
- agentic_project_kit/rule_source_validator.py +133 -0
- agentic_project_kit/run_summary.py +68 -0
- agentic_project_kit/run_summary_renderer.py +238 -0
- agentic_project_kit/runtime_validator.py +68 -0
- agentic_project_kit/safe_file_replace.py +35 -0
- agentic_project_kit/safe_patch.py +47 -0
- agentic_project_kit/safe_push.py +218 -0
- agentic_project_kit/safe_remove_diagnostic.py +51 -0
- agentic_project_kit/scaffold.py +69 -0
- agentic_project_kit/site_claims.py +444 -0
- agentic_project_kit/site_generator.py +1140 -0
- agentic_project_kit/slice_gate.py +225 -0
- agentic_project_kit/slice_runner.py +75 -0
- agentic_project_kit/standard_gates_audit_suite.py +167 -0
- agentic_project_kit/state_freshness.py +135 -0
- agentic_project_kit/status_current_state_audit.py +762 -0
- agentic_project_kit/successor_handoff_package.py +1763 -0
- agentic_project_kit/templates.py +292 -0
- agentic_project_kit/terminal_block_guard.py +166 -0
- agentic_project_kit/terminal_logging.py +275 -0
- agentic_project_kit/todo.py +97 -0
- agentic_project_kit/transfer_closeout.py +136 -0
- agentic_project_kit/transfer_continue.py +215 -0
- agentic_project_kit/transfer_local_runner.py +101 -0
- agentic_project_kit/transfer_operation_monitor.py +236 -0
- agentic_project_kit/transfer_post_merge_lifecycle.py +356 -0
- agentic_project_kit/transfer_post_merge_settle.py +383 -0
- agentic_project_kit/transfer_pr_actions.py +164 -0
- agentic_project_kit/transfer_remote_next.py +959 -0
- agentic_project_kit/transfer_repo_actions.py +2253 -0
- agentic_project_kit/transfer_runner.py +347 -0
- agentic_project_kit/transfer_safety_context.py +291 -0
- agentic_project_kit/transfer_state.py +483 -0
- agentic_project_kit/transfer_uplink.py +471 -0
- agentic_project_kit/transfer_workflow_next.py +148 -0
- agentic_project_kit/typed_work_order_evidence.py +58 -0
- agentic_project_kit/typed_work_order_queue.py +166 -0
- agentic_project_kit/typed_work_order_runner.py +168 -0
- agentic_project_kit/work_cycle.py +317 -0
- agentic_project_kit/work_discard_changes.py +129 -0
- agentic_project_kit/work_order_patch.py +199 -0
- agentic_project_kit/work_order_runner.py +120 -0
- agentic_project_kit/work_order_uploader.py +220 -0
- agentic_project_kit/work_order_validator.py +185 -0
- agentic_project_kit/work_orders.py +246 -0
- agentic_project_kit/workflow_guard.py +262 -0
- agentic_project_kit/workflow_summary_runner.py +124 -0
- agentic_project_kit/workspace.py +665 -0
- agentic_project_kit/workspace_adopt.py +471 -0
- agentic_project_kit/workspace_dpa_intake.py +406 -0
- agentic_project_kit/workspace_init.py +487 -0
- agentic_project_kit/workspace_lock.py +129 -0
- agentic_project_kit/workspace_remove.py +381 -0
- agentic_project_kit/workspace_upgrade.py +324 -0
- agentic_project_kit/wrapper_live_status.py +104 -0
- agentic_project_kit-1.0.1.dist-info/METADATA +724 -0
- agentic_project_kit-1.0.1.dist-info/RECORD +310 -0
- agentic_project_kit-1.0.1.dist-info/WHEEL +4 -0
- agentic_project_kit-1.0.1.dist-info/entry_points.txt +3 -0
- 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
|