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