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,252 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Sequence
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
import urllib.parse
|
|
11
|
+
import urllib.request
|
|
12
|
+
|
|
13
|
+
from agentic_project_kit.documentation_registry import build_documentation_registry_summary
|
|
14
|
+
from agentic_project_kit.release import CommandResult, read_project_version, run_command
|
|
15
|
+
|
|
16
|
+
ZENODO_HTTP_TIMEOUT_SECONDS = 5
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PostReleaseStatus(str, Enum):
|
|
20
|
+
PASS = "PASS"
|
|
21
|
+
FAIL = "FAIL"
|
|
22
|
+
WARN = "WARN"
|
|
23
|
+
WAITING = "WAITING"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class PostReleaseCheckResult:
|
|
28
|
+
name: str
|
|
29
|
+
status: PostReleaseStatus
|
|
30
|
+
detail: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class PostReleaseReport:
|
|
35
|
+
version: str
|
|
36
|
+
checks: tuple[PostReleaseCheckResult, ...]
|
|
37
|
+
registry_summary: dict[str, Any] | None = None
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def ok(self) -> bool:
|
|
41
|
+
return all(check.status != PostReleaseStatus.FAIL for check in self.checks)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
HttpGetter = Callable[[str], tuple[int, str]]
|
|
45
|
+
CommandRunner = Callable[[Path, Sequence[str]], CommandResult]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def build_post_release_report(
|
|
49
|
+
project_root: Path,
|
|
50
|
+
version: str | None = None,
|
|
51
|
+
command_runner: CommandRunner | None = None,
|
|
52
|
+
http_getter: HttpGetter | None = None,
|
|
53
|
+
) -> PostReleaseReport:
|
|
54
|
+
resolved_version = version or read_project_version(project_root)
|
|
55
|
+
resolved_command_runner = command_runner or run_command
|
|
56
|
+
resolved_http_getter = http_getter or urlopen_text
|
|
57
|
+
|
|
58
|
+
github_release = check_github_release_exists(project_root, resolved_version, resolved_command_runner)
|
|
59
|
+
concept_doi = read_citation_doi(project_root)
|
|
60
|
+
concept_doi_check = check_concept_doi(concept_doi)
|
|
61
|
+
zenodo_check = check_zenodo_version_record(resolved_version, concept_doi, resolved_http_getter)
|
|
62
|
+
|
|
63
|
+
return PostReleaseReport(
|
|
64
|
+
version=resolved_version,
|
|
65
|
+
checks=(github_release, concept_doi_check, zenodo_check),
|
|
66
|
+
registry_summary=_load_registry_summary(project_root),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def check_github_release_exists(
|
|
71
|
+
project_root: Path,
|
|
72
|
+
version: str,
|
|
73
|
+
command_runner: CommandRunner | None = None,
|
|
74
|
+
) -> PostReleaseCheckResult:
|
|
75
|
+
resolved_command_runner = command_runner or run_command
|
|
76
|
+
tag = f"v{version}"
|
|
77
|
+
result = resolved_command_runner(project_root, ["gh", "release", "view", tag])
|
|
78
|
+
output = "\n".join(part for part in (result.stdout.strip(), result.stderr.strip()) if part)
|
|
79
|
+
if result.returncode == 0:
|
|
80
|
+
return PostReleaseCheckResult("GitHub release", PostReleaseStatus.PASS, f"GitHub release exists: {tag}")
|
|
81
|
+
if result.returncode == 127 or _looks_like_unavailable_github_cli(output):
|
|
82
|
+
return PostReleaseCheckResult("GitHub release", PostReleaseStatus.WARN, output or "gh release view failed")
|
|
83
|
+
if _looks_like_missing_github_release(output):
|
|
84
|
+
return PostReleaseCheckResult("GitHub release", PostReleaseStatus.FAIL, f"GitHub release is absent: {tag}")
|
|
85
|
+
return PostReleaseCheckResult("GitHub release", PostReleaseStatus.WARN, output or "gh release view failed")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def check_concept_doi(concept_doi: str | None) -> PostReleaseCheckResult:
|
|
89
|
+
if not concept_doi:
|
|
90
|
+
return PostReleaseCheckResult(
|
|
91
|
+
"Zenodo concept DOI",
|
|
92
|
+
PostReleaseStatus.WARN,
|
|
93
|
+
"no DOI found in CITATION.cff; Zenodo lookup skipped",
|
|
94
|
+
)
|
|
95
|
+
if not concept_doi.startswith("10.5281/zenodo."):
|
|
96
|
+
return PostReleaseCheckResult(
|
|
97
|
+
"Zenodo concept DOI",
|
|
98
|
+
PostReleaseStatus.WARN,
|
|
99
|
+
f"DOI does not look like a Zenodo DOI: {concept_doi}",
|
|
100
|
+
)
|
|
101
|
+
return PostReleaseCheckResult("Zenodo concept DOI", PostReleaseStatus.PASS, f"found DOI: {concept_doi}")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def check_zenodo_version_record(
|
|
105
|
+
version: str,
|
|
106
|
+
concept_doi: str | None,
|
|
107
|
+
http_getter: HttpGetter | None = None,
|
|
108
|
+
) -> PostReleaseCheckResult:
|
|
109
|
+
if not concept_doi:
|
|
110
|
+
return PostReleaseCheckResult(
|
|
111
|
+
"Zenodo version DOI",
|
|
112
|
+
PostReleaseStatus.WARN,
|
|
113
|
+
"Zenodo lookup skipped because no concept DOI was found",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
resolved_http_getter = http_getter or urlopen_text
|
|
117
|
+
url = build_zenodo_records_url(concept_doi)
|
|
118
|
+
try:
|
|
119
|
+
status_code, body = resolved_http_getter(url)
|
|
120
|
+
except OSError as exc:
|
|
121
|
+
return PostReleaseCheckResult("Zenodo version DOI", PostReleaseStatus.WARN, f"Zenodo lookup failed: {exc}")
|
|
122
|
+
|
|
123
|
+
if status_code != 200:
|
|
124
|
+
return PostReleaseCheckResult(
|
|
125
|
+
"Zenodo version DOI",
|
|
126
|
+
PostReleaseStatus.WARN,
|
|
127
|
+
f"Zenodo lookup returned HTTP {status_code}",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
payload = json.loads(body)
|
|
132
|
+
except json.JSONDecodeError as exc:
|
|
133
|
+
return PostReleaseCheckResult("Zenodo version DOI", PostReleaseStatus.WARN, f"Zenodo JSON parse failed: {exc}")
|
|
134
|
+
|
|
135
|
+
version_doi = find_version_doi(payload, version)
|
|
136
|
+
if version_doi:
|
|
137
|
+
return PostReleaseCheckResult(
|
|
138
|
+
"Zenodo version DOI",
|
|
139
|
+
PostReleaseStatus.PASS,
|
|
140
|
+
f"verified version DOI for v{version}: {version_doi}",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return PostReleaseCheckResult(
|
|
144
|
+
"Zenodo version DOI",
|
|
145
|
+
PostReleaseStatus.WAITING,
|
|
146
|
+
f"no verified Zenodo record found yet for v{version}; leave README/CITATION unchanged",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def build_zenodo_records_url(concept_doi: str) -> str:
|
|
151
|
+
query = f'conceptdoi:"{concept_doi}"'
|
|
152
|
+
return "https://zenodo.org/api/records?" + urllib.parse.urlencode(
|
|
153
|
+
{"q": query, "all_versions": "true", "sort": "mostrecent"}
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def find_version_doi(payload: object, version: str) -> str | None:
|
|
158
|
+
hits = _extract_hits(payload)
|
|
159
|
+
wanted = {version, f"v{version}"}
|
|
160
|
+
for record in hits:
|
|
161
|
+
if not isinstance(record, dict):
|
|
162
|
+
continue
|
|
163
|
+
metadata = record.get("metadata")
|
|
164
|
+
if not isinstance(metadata, dict):
|
|
165
|
+
metadata = {}
|
|
166
|
+
record_version = str(metadata.get("version", "")).strip()
|
|
167
|
+
title = str(metadata.get("title", ""))
|
|
168
|
+
doi = str(record.get("doi") or metadata.get("doi") or "").strip()
|
|
169
|
+
if not doi:
|
|
170
|
+
continue
|
|
171
|
+
if record_version in wanted or _title_mentions_version(title, version):
|
|
172
|
+
return doi
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def read_citation_doi(project_root: Path) -> str | None:
|
|
177
|
+
citation = project_root / "CITATION.cff"
|
|
178
|
+
if not citation.exists():
|
|
179
|
+
return None
|
|
180
|
+
match = re.search(r"^doi:\s*['\"]?([^'\"\s]+)", citation.read_text(encoding="utf-8"), re.MULTILINE)
|
|
181
|
+
if not match:
|
|
182
|
+
return None
|
|
183
|
+
return match.group(1).strip()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def render_post_release_report(report: PostReleaseReport) -> str:
|
|
187
|
+
lines = [f"Post-release check for target v{report.version}", ""]
|
|
188
|
+
for check in report.checks:
|
|
189
|
+
lines.append(f"[{check.status.value}] {check.name}: {check.detail}")
|
|
190
|
+
if report.registry_summary is not None:
|
|
191
|
+
lines.extend(("", *_render_registry_summary_lines(report.registry_summary)))
|
|
192
|
+
lines.append("")
|
|
193
|
+
lines.append("Overall: PASS" if report.ok else "Overall: FAIL")
|
|
194
|
+
return "\n".join(lines) + "\n"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def urlopen_text(url: str) -> tuple[int, str]:
|
|
198
|
+
request = urllib.request.Request(url, headers={"User-Agent": "agentic-project-kit"})
|
|
199
|
+
with urllib.request.urlopen(request, timeout=ZENODO_HTTP_TIMEOUT_SECONDS) as response: # noqa: S310
|
|
200
|
+
status = getattr(response, "status", 200)
|
|
201
|
+
body = response.read().decode("utf-8")
|
|
202
|
+
return status, body
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _extract_hits(payload: object) -> list[object]:
|
|
206
|
+
if not isinstance(payload, dict):
|
|
207
|
+
return []
|
|
208
|
+
hits = payload.get("hits")
|
|
209
|
+
if isinstance(hits, dict):
|
|
210
|
+
records = hits.get("hits")
|
|
211
|
+
if isinstance(records, list):
|
|
212
|
+
return records
|
|
213
|
+
if isinstance(hits, list):
|
|
214
|
+
return hits
|
|
215
|
+
return []
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _title_mentions_version(title: str, version: str) -> bool:
|
|
219
|
+
normalized = title.lower()
|
|
220
|
+
return f"v{version}" in normalized or f" {version}" in normalized
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _looks_like_unavailable_github_cli(output: str) -> bool:
|
|
224
|
+
normalized = output.lower()
|
|
225
|
+
return any(fragment in normalized for fragment in ("could not run gh", "gh unavailable"))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _looks_like_missing_github_release(output: str) -> bool:
|
|
229
|
+
normalized = output.lower()
|
|
230
|
+
return any(fragment in normalized for fragment in ("release not found", "http 404"))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _load_registry_summary(project_root: Path) -> dict[str, Any] | None:
|
|
234
|
+
try:
|
|
235
|
+
return build_documentation_registry_summary(project_root)
|
|
236
|
+
except (OSError, ValueError):
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _render_registry_summary_lines(summary: dict[str, Any]) -> list[str]:
|
|
241
|
+
lines = [
|
|
242
|
+
"Documentation registry:",
|
|
243
|
+
f"- registry: {summary['registry_path']}",
|
|
244
|
+
f"- version: {summary['version']}",
|
|
245
|
+
f"- documents: {summary['document_count']}",
|
|
246
|
+
f"- broad_migration_allowed: {summary['broad_migration_allowed']}",
|
|
247
|
+
]
|
|
248
|
+
class_counts = summary.get("class_counts", {})
|
|
249
|
+
if isinstance(class_counts, dict):
|
|
250
|
+
for class_name, count in class_counts.items():
|
|
251
|
+
lines.append(f"- class:{class_name}: {count}")
|
|
252
|
+
return lines
|