sphinx-mkdocs-migrate 0.0.1.dev0__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.
- sphinx_mkdocs_migrate/__init__.py +8 -0
- sphinx_mkdocs_migrate/analyzer/__init__.py +17 -0
- sphinx_mkdocs_migrate/analyzer/ci.py +223 -0
- sphinx_mkdocs_migrate/analyzer/dependencies.py +134 -0
- sphinx_mkdocs_migrate/analyzer/markdown.py +148 -0
- sphinx_mkdocs_migrate/analyzer/mkdocs.py +263 -0
- sphinx_mkdocs_migrate/analyzer/models.py +348 -0
- sphinx_mkdocs_migrate/analyzer/navigation.py +108 -0
- sphinx_mkdocs_migrate/analyzer/project.py +511 -0
- sphinx_mkdocs_migrate/cli.py +507 -0
- sphinx_mkdocs_migrate/parsing/doc_ir.py +533 -0
- sphinx_mkdocs_migrate/parsing/flow_extractor.py +457 -0
- sphinx_mkdocs_migrate/parsing/html_flow_parser.py +349 -0
- sphinx_mkdocs_migrate/parsing/markdown.py +22 -0
- sphinx_mkdocs_migrate/parsing/markdown_ir.py +49 -0
- sphinx_mkdocs_migrate/parsing/markdown_it_adapter.py +496 -0
- sphinx_mkdocs_migrate/parsing/requirements.py +155 -0
- sphinx_mkdocs_migrate/planner/accountability.py +111 -0
- sphinx_mkdocs_migrate/planner/ci.py +142 -0
- sphinx_mkdocs_migrate/planner/conf_builder.py +183 -0
- sphinx_mkdocs_migrate/planner/models.py +379 -0
- sphinx_mkdocs_migrate/planner/planner.py +1867 -0
- sphinx_mkdocs_migrate/planner/policy.py +474 -0
- sphinx_mkdocs_migrate/planner/theme_constants.py +70 -0
- sphinx_mkdocs_migrate/planner/toctree.py +158 -0
- sphinx_mkdocs_migrate/py.typed +1 -0
- sphinx_mkdocs_migrate/rules/catalog.py +154 -0
- sphinx_mkdocs_migrate/rules/engine.py +94 -0
- sphinx_mkdocs_migrate/rules/models.py +176 -0
- sphinx_mkdocs_migrate/transformer/engine.py +897 -0
- sphinx_mkdocs_migrate/transformer/models.py +59 -0
- sphinx_mkdocs_migrate/transformer/myst_transformer.py +393 -0
- sphinx_mkdocs_migrate/validator/models.py +40 -0
- sphinx_mkdocs_migrate/validator/verifier.py +377 -0
- sphinx_mkdocs_migrate-0.0.1.dev0.dist-info/METADATA +199 -0
- sphinx_mkdocs_migrate-0.0.1.dev0.dist-info/RECORD +39 -0
- sphinx_mkdocs_migrate-0.0.1.dev0.dist-info/WHEEL +4 -0
- sphinx_mkdocs_migrate-0.0.1.dev0.dist-info/entry_points.txt +2 -0
- sphinx_mkdocs_migrate-0.0.1.dev0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""Data models for deterministic repository-wide MigrationPlan and aggregated inventories."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import List, Dict, Any, Optional
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
from ..analyzer.models import (
|
|
10
|
+
Classification,
|
|
11
|
+
ConfigAnalysis,
|
|
12
|
+
DependencyAnalysis,
|
|
13
|
+
CIAnalysis,
|
|
14
|
+
NavigationAnalysis,
|
|
15
|
+
VersionEnvironment,
|
|
16
|
+
)
|
|
17
|
+
from ..rules.models import MigrationAction
|
|
18
|
+
from .ci import CIWorkflowPlan
|
|
19
|
+
from .. import __version__
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RequirementProvenance(str, Enum):
|
|
23
|
+
TARGET_BASELINE = "TARGET_BASELINE" # Core runtime requirement (e.g. Sphinx, MyST)
|
|
24
|
+
DOCUMENT_CONSTRUCT = (
|
|
25
|
+
"DOCUMENT_CONSTRUCT" # Triggered by parsed document nodes (e.g. tabs)
|
|
26
|
+
)
|
|
27
|
+
THEME_POLICY = "THEME_POLICY" # Derived from source theme mapping rule
|
|
28
|
+
EXTENSION_POLICY = (
|
|
29
|
+
"EXTENSION_POLICY" # Derived from MkDocs plugin/markdown_extension mapping
|
|
30
|
+
)
|
|
31
|
+
FEATURE_POLICY = "FEATURE_POLICY" # Derived from MkDocs theme feature mapping
|
|
32
|
+
GENERATED_PIPELINE = "GENERATED_PIPELINE" # Derived from build-time documentation generation pipelines (e.g. gen-files, mkdocstrings)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GeneratedDocumentProposal(BaseModel):
|
|
36
|
+
"""Proposal for synthesizing generated documentation files (e.g., API reference stubs from gen-files/mkdocstrings)."""
|
|
37
|
+
|
|
38
|
+
target_path: str # e.g. "docs/reference/pythonjsonlogger/jsonlogger.md"
|
|
39
|
+
title: str # e.g. "jsonlogger"
|
|
40
|
+
content: str # MyST / autodoc content
|
|
41
|
+
generator_plugin: str # e.g. "gen-files"
|
|
42
|
+
generator_script: Optional[str] = None # e.g. "scripts/gen_ref_nav.py"
|
|
43
|
+
provenance: RequirementProvenance = RequirementProvenance.GENERATED_PIPELINE
|
|
44
|
+
rationale: str
|
|
45
|
+
flow_actions: List[DocumentFlowAction] = Field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RequirementItem(BaseModel):
|
|
49
|
+
"""Traceable dependency requirement (extension or package) with provenance."""
|
|
50
|
+
|
|
51
|
+
name: str # e.g. "sphinx_design", "sphinx-design>=0.5.0"
|
|
52
|
+
kind: str # "extension" or "package"
|
|
53
|
+
provenance: RequirementProvenance
|
|
54
|
+
rationale: str
|
|
55
|
+
sources: List[str] = Field(
|
|
56
|
+
default_factory=list
|
|
57
|
+
) # e.g. ["docs/tabs.md:12", "target_baseline"]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class PlanActionSummary(BaseModel):
|
|
61
|
+
"""Aggregate count and categorized inventory of construct actions."""
|
|
62
|
+
|
|
63
|
+
total_actions: int = 0
|
|
64
|
+
transform_count: int = 0
|
|
65
|
+
preserve_count: int = 0
|
|
66
|
+
manual_count: int = 0
|
|
67
|
+
unsupported_count: int = 0
|
|
68
|
+
construct_counts: Dict[str, int] = Field(default_factory=dict)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ThemeMigrationProposal(BaseModel):
|
|
72
|
+
"""Explicit theme migration mapping with source theme, classification, and rationale."""
|
|
73
|
+
|
|
74
|
+
source_theme: str
|
|
75
|
+
target_theme: Optional[str] = None
|
|
76
|
+
target_package: Optional[str] = None
|
|
77
|
+
classification: Classification = Classification.TRANSFORM
|
|
78
|
+
rationale: str
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ConfigMigrationProposal(BaseModel):
|
|
82
|
+
"""Proposed Sphinx configuration updates derived strictly from source evidence."""
|
|
83
|
+
|
|
84
|
+
project_name: str
|
|
85
|
+
theme: ThemeMigrationProposal
|
|
86
|
+
extensions_to_add: List[str] = Field(default_factory=list)
|
|
87
|
+
myst_enable_extensions: List[str] = Field(default_factory=list)
|
|
88
|
+
custom_options: Dict[str, Any] = Field(default_factory=dict)
|
|
89
|
+
rendered_content: Optional[str] = None
|
|
90
|
+
rationale: Dict[str, str] = Field(default_factory=dict)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ManualReviewItem(BaseModel):
|
|
94
|
+
"""Traceable manual migration action item."""
|
|
95
|
+
|
|
96
|
+
item_id: str
|
|
97
|
+
source_file: str
|
|
98
|
+
line_number: int
|
|
99
|
+
construct_type: str
|
|
100
|
+
instruction: str
|
|
101
|
+
rationale: str
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class MigrationPlanMetadata(BaseModel):
|
|
105
|
+
"""Metadata about the generation session (separated to guarantee plan determinism)."""
|
|
106
|
+
|
|
107
|
+
generated_at: str
|
|
108
|
+
planner_version: str = Field(
|
|
109
|
+
default="1.0.0",
|
|
110
|
+
description="Specification format version of the MigrationPlan schema",
|
|
111
|
+
)
|
|
112
|
+
generator_version: str = Field(
|
|
113
|
+
default_factory=lambda: __version__,
|
|
114
|
+
description="Release version of sphinx-mkdocs-migrate that synthesized this plan",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def schema_version(self) -> str:
|
|
119
|
+
"""Alias for planner_version clarifying it as the plan format schema specification."""
|
|
120
|
+
return self.planner_version
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class ApiDirectiveKind(str, Enum):
|
|
124
|
+
AUTOMODULE = "AUTOMODULE"
|
|
125
|
+
AUTOCLASS = "AUTOCLASS"
|
|
126
|
+
AUTOFUNCTION = "AUTOFUNCTION"
|
|
127
|
+
AUTOMETHOD = "AUTOMETHOD"
|
|
128
|
+
AUTOATTRIBUTE = "AUTOATTRIBUTE"
|
|
129
|
+
AUTOEXCEPTION = "AUTOEXCEPTION"
|
|
130
|
+
AUTOSUMMARY = "AUTOSUMMARY"
|
|
131
|
+
GENERATED_API_PAGE = "GENERATED_API_PAGE"
|
|
132
|
+
MANUAL = "MANUAL"
|
|
133
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ApiGenerationStrategy(BaseModel):
|
|
137
|
+
"""Explicit strategy for realizing an ApiDocumentationRequest in Sphinx."""
|
|
138
|
+
|
|
139
|
+
object_path: str
|
|
140
|
+
directive_kind: ApiDirectiveKind
|
|
141
|
+
options: Dict[str, Any] = Field(default_factory=dict)
|
|
142
|
+
summary_entries: List[str] = Field(default_factory=list)
|
|
143
|
+
target_toctree: Optional[str] = None
|
|
144
|
+
members: Optional[List[str]] = None
|
|
145
|
+
inherited_members: Optional[str] = None
|
|
146
|
+
show_inheritance: bool = True
|
|
147
|
+
module_first: bool = False
|
|
148
|
+
source_construct_id: Optional[str] = None
|
|
149
|
+
rationale: str = ""
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class DocumentFlowAction(BaseModel):
|
|
153
|
+
"""Ordered semantic flow action planned for a target document."""
|
|
154
|
+
|
|
155
|
+
action_id: str
|
|
156
|
+
order_index: int
|
|
157
|
+
source_construct_id: Optional[str] = None
|
|
158
|
+
element_type: (
|
|
159
|
+
str # HEADING, PARAGRAPH, TABLE, CODE_BLOCK, API_REQUEST, AUTOSUMMARY, etc.
|
|
160
|
+
)
|
|
161
|
+
strategy: str # TRANSFORM, PRESERVE, AUTODOC, AUTOSUMMARY, MANUAL, etc.
|
|
162
|
+
content_summary: str
|
|
163
|
+
target_directive: Optional[str] = None
|
|
164
|
+
rationale: str
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class ArtifactProvenance(BaseModel):
|
|
168
|
+
"""Traceable provenance and verification expectation for a planned artifact."""
|
|
169
|
+
|
|
170
|
+
source_construct_ids: List[str] = Field(default_factory=list)
|
|
171
|
+
source_files: List[str] = Field(default_factory=list)
|
|
172
|
+
generated_from_pipeline: Optional[str] = None
|
|
173
|
+
required_extensions: List[str] = Field(default_factory=list)
|
|
174
|
+
required_packages: List[str] = Field(default_factory=list)
|
|
175
|
+
verification_expectation: Optional[str] = None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class DocumentationArtifact(BaseModel):
|
|
179
|
+
"""Planned document artifact with ordered semantic flow and traceable provenance."""
|
|
180
|
+
|
|
181
|
+
artifact_id: str
|
|
182
|
+
target_path: str
|
|
183
|
+
source_file: Optional[str] = None
|
|
184
|
+
title: str
|
|
185
|
+
artifact_kind: str # markdown_doc, api_reference, generated_stub, toctree_index
|
|
186
|
+
provenance: RequirementProvenance
|
|
187
|
+
rationale: str
|
|
188
|
+
flow_actions: List[DocumentFlowAction] = Field(default_factory=list)
|
|
189
|
+
artifact_provenance: Optional[ArtifactProvenance] = None
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class NavigationPlan(BaseModel):
|
|
193
|
+
"""Synthesized navigation hierarchy and Sphinx toctree layout."""
|
|
194
|
+
|
|
195
|
+
root_toctrees: List[str] = Field(default_factory=list)
|
|
196
|
+
sub_toctrees: Dict[str, List[str]] = Field(default_factory=dict)
|
|
197
|
+
hidden_routes: List[str] = Field(default_factory=list)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class GeneratedPipelinePlan(BaseModel):
|
|
201
|
+
"""Traceable transformation plan for build-time generated document pipelines."""
|
|
202
|
+
|
|
203
|
+
pipeline_id: str
|
|
204
|
+
source_plugin: str
|
|
205
|
+
generator_script: Optional[str] = None
|
|
206
|
+
target_strategy: str # AUTODOC_AUTOSUMMARY_STUBS, MANUAL, PRESERVE_SCRIPTS
|
|
207
|
+
target_artifacts: List[str] = Field(default_factory=list)
|
|
208
|
+
rationale: str
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class ReferenceKind(str, Enum):
|
|
212
|
+
INTERNAL_REFERENCE = "INTERNAL_REFERENCE"
|
|
213
|
+
API_REFERENCE = "API_REFERENCE"
|
|
214
|
+
EXTERNAL_INVENTORY_REFERENCE = "EXTERNAL_INVENTORY_REFERENCE"
|
|
215
|
+
UNRESOLVED_REFERENCE = "UNRESOLVED_REFERENCE"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class CrossReferenceAction(BaseModel):
|
|
219
|
+
"""Traceable link transformation from MkDocs syntax/paths to Sphinx targets."""
|
|
220
|
+
|
|
221
|
+
source_construct_id: str
|
|
222
|
+
source_file: str
|
|
223
|
+
source_target: str
|
|
224
|
+
transformed_target: str
|
|
225
|
+
reference_kind: ReferenceKind = ReferenceKind.INTERNAL_REFERENCE
|
|
226
|
+
is_doc_ref: bool = True
|
|
227
|
+
target_inventory: Optional[str] = None # e.g. "https://docs.python.org/3"
|
|
228
|
+
rationale: str
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class ExternalInventoryConfig(BaseModel):
|
|
232
|
+
"""Configuration mapping for sphinx.ext.intersphinx external inventories."""
|
|
233
|
+
|
|
234
|
+
inventory_id: str # e.g. "python"
|
|
235
|
+
url: str # e.g. "https://docs.python.org/3"
|
|
236
|
+
objects_inv: str # e.g. "https://docs.python.org/3/objects.inv"
|
|
237
|
+
provenance: RequirementProvenance = RequirementProvenance.EXTENSION_POLICY
|
|
238
|
+
rationale: str
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class VersioningDeploymentPlan(BaseModel):
|
|
242
|
+
"""Explicit accountability separating build-time document generation from deployment/versioning (e.g. mike)."""
|
|
243
|
+
|
|
244
|
+
source_tool: str # e.g. "mike"
|
|
245
|
+
canonical_version: Optional[str] = None
|
|
246
|
+
target_strategy: str # e.g. "SPHINX_VERSIONING_DEPLOYMENT_WORKFLOW"
|
|
247
|
+
status: str = "ACCOUNTED"
|
|
248
|
+
rationale: str
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class CapabilityDisposition(str, Enum):
|
|
252
|
+
PRESERVE = "PRESERVE"
|
|
253
|
+
TRANSFORM = "TRANSFORM"
|
|
254
|
+
MANUAL = "MANUAL"
|
|
255
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
256
|
+
ACCOUNTED_NO_DIRECT_EQUIVALENT = "ACCOUNTED_NO_DIRECT_EQUIVALENT"
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class ImplementationStrategy(str, Enum):
|
|
260
|
+
NATIVE_SPHINX = "NATIVE_SPHINX"
|
|
261
|
+
SPHINX_EXTENSION = "SPHINX_EXTENSION"
|
|
262
|
+
AUTODOC = "AUTODOC"
|
|
263
|
+
AUTOSUMMARY = "AUTOSUMMARY"
|
|
264
|
+
AUTODOC_AUTOSUMMARY_STUBS = "AUTODOC_AUTOSUMMARY_STUBS"
|
|
265
|
+
GENERATED_PIPELINE = "GENERATED_PIPELINE"
|
|
266
|
+
TOCTREE = "TOCTREE"
|
|
267
|
+
INTERSPHINX = "INTERSPHINX"
|
|
268
|
+
DEPLOYMENT_WORKFLOW = "DEPLOYMENT_WORKFLOW"
|
|
269
|
+
MYST_DIRECTIVE = "MYST_DIRECTIVE"
|
|
270
|
+
NONE = "NONE"
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class VerificationStatus(str, Enum):
|
|
274
|
+
VERIFIED = "VERIFIED"
|
|
275
|
+
NOT_YET_VERIFIED = "NOT_YET_VERIFIED"
|
|
276
|
+
PENDING_HTML_COMPARISON = "PENDING_HTML_COMPARISON"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class SystemCapabilityAccountability(BaseModel):
|
|
280
|
+
"""Complete accountability item ensuring no source capability or plugin silently disappears."""
|
|
281
|
+
|
|
282
|
+
capability_name: str
|
|
283
|
+
source_category: (
|
|
284
|
+
str # plugin, markdown_extension, theme_feature, external_inventory
|
|
285
|
+
)
|
|
286
|
+
disposition: CapabilityDisposition
|
|
287
|
+
implementation_strategy: ImplementationStrategy
|
|
288
|
+
verification_status: VerificationStatus = VerificationStatus.NOT_YET_VERIFIED
|
|
289
|
+
target_equivalent: Optional[str] = None
|
|
290
|
+
rationale: str
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class AssetAction(BaseModel):
|
|
294
|
+
"""Traceable asset copying/referencing action."""
|
|
295
|
+
|
|
296
|
+
source_construct_id: Optional[str] = None
|
|
297
|
+
source_path: str
|
|
298
|
+
target_path: str
|
|
299
|
+
asset_kind: str
|
|
300
|
+
rationale: str
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class DocumentationPlan(BaseModel):
|
|
304
|
+
"""Structured documentation synthesis contract derived from the DocumentationSiteGraph."""
|
|
305
|
+
|
|
306
|
+
schema_version: str = "1.0.0"
|
|
307
|
+
pages: List[DocumentationArtifact] = Field(default_factory=list)
|
|
308
|
+
api_strategies: List[ApiGenerationStrategy] = Field(default_factory=list)
|
|
309
|
+
navigation: NavigationPlan = Field(default_factory=NavigationPlan)
|
|
310
|
+
generated_pipelines: List[GeneratedPipelinePlan] = Field(default_factory=list)
|
|
311
|
+
cross_references: List[CrossReferenceAction] = Field(default_factory=list)
|
|
312
|
+
external_inventories: List[ExternalInventoryConfig] = Field(default_factory=list)
|
|
313
|
+
versioning_deployment: Optional[VersioningDeploymentPlan] = None
|
|
314
|
+
capability_accountability: List[SystemCapabilityAccountability] = Field(
|
|
315
|
+
default_factory=list
|
|
316
|
+
)
|
|
317
|
+
asset_actions: List[AssetAction] = Field(default_factory=list)
|
|
318
|
+
required_extensions: List[str] = Field(default_factory=list)
|
|
319
|
+
required_packages: List[str] = Field(default_factory=list)
|
|
320
|
+
manual_items: List[ManualReviewItem] = Field(default_factory=list)
|
|
321
|
+
unsupported_items: List[MigrationAction] = Field(default_factory=list)
|
|
322
|
+
|
|
323
|
+
# Backward compatibility properties
|
|
324
|
+
artifacts: List[DocumentationArtifact] = Field(default_factory=list)
|
|
325
|
+
api_generation_strategy: Dict[str, str] = Field(default_factory=dict)
|
|
326
|
+
toctree_hierarchies: Dict[str, List[str]] = Field(default_factory=dict)
|
|
327
|
+
cross_reference_mappings: Dict[str, str] = Field(default_factory=dict)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class MigrationPlan(BaseModel):
|
|
331
|
+
"""Comprehensive, deterministic repository-wide migration plan without mutating disk."""
|
|
332
|
+
|
|
333
|
+
project_root: str
|
|
334
|
+
metadata: MigrationPlanMetadata
|
|
335
|
+
|
|
336
|
+
# Subsystem Inspection Evidence
|
|
337
|
+
source_mkdocs_config: Optional[ConfigAnalysis] = None
|
|
338
|
+
version_environment: Optional[VersionEnvironment] = None
|
|
339
|
+
navigation_analysis: Optional[NavigationAnalysis] = None
|
|
340
|
+
dependency_analysis: Optional[DependencyAnalysis] = None
|
|
341
|
+
ci_analysis: Optional[CIAnalysis] = None
|
|
342
|
+
|
|
343
|
+
# Documentation Plan IR
|
|
344
|
+
documentation_plan: Optional[DocumentationPlan] = None
|
|
345
|
+
|
|
346
|
+
# Traceable Actions
|
|
347
|
+
document_actions: List[MigrationAction] = Field(default_factory=list)
|
|
348
|
+
generated_documents: List[GeneratedDocumentProposal] = Field(default_factory=list)
|
|
349
|
+
|
|
350
|
+
# Aggregated & Deduplicated Repositories Requirements with Provenance
|
|
351
|
+
summary: PlanActionSummary = Field(default_factory=PlanActionSummary)
|
|
352
|
+
requirements: List[RequirementItem] = Field(default_factory=list)
|
|
353
|
+
packages_to_remove: List[str] = Field(default_factory=list)
|
|
354
|
+
|
|
355
|
+
# Concrete Subsystem Proposals
|
|
356
|
+
proposed_sphinx_config: Optional[ConfigMigrationProposal] = None
|
|
357
|
+
ci_plan: Optional[CIWorkflowPlan] = None
|
|
358
|
+
manual_action_items: List[ManualReviewItem] = Field(default_factory=list)
|
|
359
|
+
unsupported_constructs: List[MigrationAction] = Field(default_factory=list)
|
|
360
|
+
obsolete_files: List[str] = Field(default_factory=list)
|
|
361
|
+
|
|
362
|
+
def canonical_dict(self) -> Dict[str, Any]:
|
|
363
|
+
"""Returns the canonical deterministic content dictionary excluding ephemeral metadata."""
|
|
364
|
+
data = self.model_dump()
|
|
365
|
+
data.pop("metadata", None)
|
|
366
|
+
return data
|
|
367
|
+
|
|
368
|
+
def canonical_hash(self) -> str:
|
|
369
|
+
"""Returns a stable SHA256 hash of the canonical plan content."""
|
|
370
|
+
canonical_json = json.dumps(self.canonical_dict(), sort_keys=True, default=str)
|
|
371
|
+
return hashlib.sha256(canonical_json.encode("utf-8")).hexdigest()
|
|
372
|
+
|
|
373
|
+
def get_required_extensions(self) -> List[str]:
|
|
374
|
+
return sorted(
|
|
375
|
+
list({r.name for r in self.requirements if r.kind == "extension"})
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def get_required_packages(self) -> List[str]:
|
|
379
|
+
return sorted(list({r.name for r in self.requirements if r.kind == "package"}))
|