vigil-codeintel 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. vigil_codeintel-0.1.0.dist-info/METADATA +780 -0
  2. vigil_codeintel-0.1.0.dist-info/RECORD +131 -0
  3. vigil_codeintel-0.1.0.dist-info/WHEEL +5 -0
  4. vigil_codeintel-0.1.0.dist-info/entry_points.txt +3 -0
  5. vigil_codeintel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. vigil_codeintel-0.1.0.dist-info/top_level.txt +3 -0
  7. vigil_forensic/__init__.py +224 -0
  8. vigil_forensic/_git_utils.py +178 -0
  9. vigil_forensic/_shared.py +510 -0
  10. vigil_forensic/_stubs.py +156 -0
  11. vigil_forensic/gate_checks/__init__.py +1 -0
  12. vigil_forensic/gate_checks/_ast_helpers.py +629 -0
  13. vigil_forensic/gate_checks/_deployment_detector.py +573 -0
  14. vigil_forensic/gate_checks/atomic_write_checks.py +1143 -0
  15. vigil_forensic/gate_checks/authority_checks.py +95 -0
  16. vigil_forensic/gate_checks/boundary_breach_checks.py +202 -0
  17. vigil_forensic/gate_checks/broad_except_checks.py +301 -0
  18. vigil_forensic/gate_checks/broad_except_hidden_sentinel_checks.py +365 -0
  19. vigil_forensic/gate_checks/common.py +253 -0
  20. vigil_forensic/gate_checks/config_safety_checks.py +704 -0
  21. vigil_forensic/gate_checks/config_ssot_checks.py +78 -0
  22. vigil_forensic/gate_checks/conflict_checks.py +193 -0
  23. vigil_forensic/gate_checks/context_fallback_checks.py +697 -0
  24. vigil_forensic/gate_checks/context_health_checks.py +289 -0
  25. vigil_forensic/gate_checks/contract_shape_drift_checks.py +459 -0
  26. vigil_forensic/gate_checks/dirty_baseline_check.py +274 -0
  27. vigil_forensic/gate_checks/duplication_checks.py +387 -0
  28. vigil_forensic/gate_checks/embedded_string_checks.py +123 -0
  29. vigil_forensic/gate_checks/empty_output_checks.py +87 -0
  30. vigil_forensic/gate_checks/encoding_checks.py +847 -0
  31. vigil_forensic/gate_checks/export_completeness_checks.py +156 -0
  32. vigil_forensic/gate_checks/fallback_checks.py +41 -0
  33. vigil_forensic/gate_checks/file_proliferation_checks.py +171 -0
  34. vigil_forensic/gate_checks/fix_without_test_checks.py +69 -0
  35. vigil_forensic/gate_checks/forensic_cluster_runners/__init__.py +9 -0
  36. vigil_forensic/gate_checks/forensic_cluster_runners/_helpers.py +71 -0
  37. vigil_forensic/gate_checks/forensic_cluster_runners/advanced_checks.py +322 -0
  38. vigil_forensic/gate_checks/forensic_cluster_runners/core.py +273 -0
  39. vigil_forensic/gate_checks/forensic_cluster_runners/integrity_checks.py +203 -0
  40. vigil_forensic/gate_checks/forensic_cluster_runners/quality_checks.py +666 -0
  41. vigil_forensic/gate_checks/forensic_clusters/__init__.py +193 -0
  42. vigil_forensic/gate_checks/forensic_clusters/allowlist.py +426 -0
  43. vigil_forensic/gate_checks/forensic_clusters/allowlist_writer.py +302 -0
  44. vigil_forensic/gate_checks/forensic_clusters/api_protocol.py +231 -0
  45. vigil_forensic/gate_checks/forensic_clusters/async_quality.py +1156 -0
  46. vigil_forensic/gate_checks/forensic_clusters/code_style.py +808 -0
  47. vigil_forensic/gate_checks/forensic_clusters/core.py +319 -0
  48. vigil_forensic/gate_checks/forensic_clusters/data_quality.py +763 -0
  49. vigil_forensic/gate_checks/forensic_clusters/dead_code.py +480 -0
  50. vigil_forensic/gate_checks/forensic_clusters/edit_mutation.py +842 -0
  51. vigil_forensic/gate_checks/forensic_clusters/exception_boundary.py +240 -0
  52. vigil_forensic/gate_checks/forensic_clusters/legacy_debt.py +556 -0
  53. vigil_forensic/gate_checks/forensic_clusters/static_analysis.py +834 -0
  54. vigil_forensic/gate_checks/forensic_clusters/structural_quality.py +298 -0
  55. vigil_forensic/gate_checks/god_object_zones_checks.py +173 -0
  56. vigil_forensic/gate_checks/hallucination_checks.py +566 -0
  57. vigil_forensic/gate_checks/hunter_artifact_completeness_check.py +139 -0
  58. vigil_forensic/gate_checks/implementation_overfit_checks.py +380 -0
  59. vigil_forensic/gate_checks/import_integrity_checks.py +233 -0
  60. vigil_forensic/gate_checks/imports_in_function_checks.py +283 -0
  61. vigil_forensic/gate_checks/ml_checks.py +318 -0
  62. vigil_forensic/gate_checks/performance_checks.py +106 -0
  63. vigil_forensic/gate_checks/project_specific_runner.py +691 -0
  64. vigil_forensic/gate_checks/provider_capability_checks.py +73 -0
  65. vigil_forensic/gate_checks/refactor_completeness_checks.py +274 -0
  66. vigil_forensic/gate_checks/reliability_checks.py +389 -0
  67. vigil_forensic/gate_checks/reporting_checks.py +55 -0
  68. vigil_forensic/gate_checks/runtime_behavior_checks.py +220 -0
  69. vigil_forensic/gate_checks/security_injection_checks.py +332 -0
  70. vigil_forensic/gate_checks/semantic_intent_checks.py +139 -0
  71. vigil_forensic/gate_checks/size_complexity_checks.py +336 -0
  72. vigil_forensic/gate_checks/stuck_feature_flag_checks.py +354 -0
  73. vigil_forensic/gate_checks/syntax_validity_checks.py +217 -0
  74. vigil_forensic/gate_checks/temporal_freshness_checks.py +79 -0
  75. vigil_forensic/gate_checks/test_quality_checks.py +946 -0
  76. vigil_forensic/gate_checks/testing_checks.py +149 -0
  77. vigil_forensic/gate_checks/toctou_checks.py +367 -0
  78. vigil_forensic/gate_checks/type_checking_checks.py +316 -0
  79. vigil_forensic/gate_models.py +392 -0
  80. vigil_forensic/gate_packs/__init__.py +1 -0
  81. vigil_forensic/gate_packs/universal.py +179 -0
  82. vigil_forensic/gate_profile.json +31 -0
  83. vigil_forensic/gate_registry.py +21 -0
  84. vigil_forensic/language_profiles.py +219 -0
  85. vigil_forensic/meta_findings.py +207 -0
  86. vigil_forensic/self_audit.py +725 -0
  87. vigil_forensic/source_analysis.py +175 -0
  88. vigil_mapper/__init__.py +103 -0
  89. vigil_mapper/_ast_helpers_minimal.py +229 -0
  90. vigil_mapper/_extract_imports_impl.py +123 -0
  91. vigil_mapper/_file_count_guard.py +129 -0
  92. vigil_mapper/_git_utils.py +178 -0
  93. vigil_mapper/_runtime_ast.py +438 -0
  94. vigil_mapper/_runtime_dispatch.py +137 -0
  95. vigil_mapper/_seed_helpers.py +82 -0
  96. vigil_mapper/authority_builder.py +1102 -0
  97. vigil_mapper/cli_entry.py +731 -0
  98. vigil_mapper/conflict_builder.py +818 -0
  99. vigil_mapper/data_contract_builder.py +446 -0
  100. vigil_mapper/findings_builder.py +716 -0
  101. vigil_mapper/fingerprint.py +53 -0
  102. vigil_mapper/hotspot_builder.py +539 -0
  103. vigil_mapper/map_common.py +449 -0
  104. vigil_mapper/map_errors.py +55 -0
  105. vigil_mapper/map_models.py +431 -0
  106. vigil_mapper/map_models_ext.py +206 -0
  107. vigil_mapper/map_models_findings.py +130 -0
  108. vigil_mapper/map_storage.py +455 -0
  109. vigil_mapper/parse_cache.py +795 -0
  110. vigil_mapper/refactor_boundary_builder.py +266 -0
  111. vigil_mapper/runtime_builder.py +527 -0
  112. vigil_mapper/runtime_tracer.py +243 -0
  113. vigil_mapper/runtime_tracer_entry.py +199 -0
  114. vigil_mapper/semantic_diff.py +71 -0
  115. vigil_mapper/source_adapters/__init__.py +109 -0
  116. vigil_mapper/source_adapters/_base.py +264 -0
  117. vigil_mapper/source_adapters/_ir.py +156 -0
  118. vigil_mapper/source_adapters/_lexer.py +309 -0
  119. vigil_mapper/source_adapters/_patterns.py +212 -0
  120. vigil_mapper/source_adapters/_treesitter.py +182 -0
  121. vigil_mapper/source_adapters/go.py +553 -0
  122. vigil_mapper/source_adapters/java.py +541 -0
  123. vigil_mapper/source_adapters/javascript.py +626 -0
  124. vigil_mapper/source_adapters/python.py +325 -0
  125. vigil_mapper/source_adapters/typescript.py +749 -0
  126. vigil_mapper/structural_builder.py +586 -0
  127. vigil_mcp/__init__.py +1 -0
  128. vigil_mcp/_jobs.py +587 -0
  129. vigil_mcp/_paths.py +93 -0
  130. vigil_mcp/forensic_server.py +419 -0
  131. vigil_mcp/map_server.py +452 -0
@@ -0,0 +1,431 @@
1
+ """Data models for the map builder subsystem -- Maps 1-4 + envelope + container.
2
+
3
+ Seven frozen dataclasses (one per map) + MapMetadata envelope + RepoMaps container.
4
+ Each dataclass implements to_dict() / from_dict() for JSON round-trip.
5
+
6
+ Maps 5-7 (ConflictEntry, HotspotEntry, RefactorBoundary) are in map_models_ext.py.
7
+
8
+ Python 3.11 -> slots=True, kw_only=True supported.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import sys
13
+ import logging
14
+ from dataclasses import dataclass, field
15
+ from typing import Any, Literal
16
+
17
+ __all__ = [
18
+ "BuildMeta",
19
+ "MapMetadata",
20
+ "StructuralEntry",
21
+ "RuntimeNode",
22
+ "DataContractEntry",
23
+ "AuthorityDomain",
24
+ "ConflictEntry",
25
+ "HotspotEntry",
26
+ "RefactorBoundary",
27
+ "Finding",
28
+ "RepoMaps",
29
+ ]
30
+
31
+ _log = logging.getLogger(__name__)
32
+
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # BuildMeta — per-map build provenance (schema v2.0.0)
36
+ # ---------------------------------------------------------------------------
37
+
38
+ @dataclass
39
+ class BuildMeta:
40
+ """Build provenance metadata for one map payload (schema v2.0.0).
41
+
42
+ NOT frozen -- constructed incrementally in cli_entry._build_build_meta().
43
+ """
44
+ analysis_mode: str # "python_ast" | "regex_signals" | "seed_only" | "derived" | "unsupported"
45
+ status: str # "ok" | "partial" | "unsupported"
46
+ reason: str # human-readable; empty string if status="ok"
47
+ confidence_avg: float
48
+ coverage: dict # {"files_scanned_by_lang": {...}, "files_supported_by_lang": {...}, "coverage_ratio": float}
49
+ producer: str # "vigil_mapper.<module>"
50
+ built_at: str # ISO8601 UTC Z-suffix
51
+ duration_s: float
52
+ # Phase 5.2: representative sample of files with no coverage for this map.
53
+ # 5-10 entries; empty list when all scanned files are supported (coverage_ratio=1.0)
54
+ # or when the caller does not populate the field (backward compat).
55
+ unsupported_files_sample: list = field(default_factory=list)
56
+
57
+ def to_dict(self) -> dict:
58
+ # NOTE: "built_at" and "build_duration_s" are in semantic_diff._IGNORED_FIELDS
59
+ # and will be stripped during I2 semantic comparisons, keeping builds deterministic.
60
+ # The key is "build_duration_s" (not "duration_s") so it matches _IGNORED_FIELDS.
61
+ return {
62
+ "analysis_mode": self.analysis_mode,
63
+ "build_duration_s": self.duration_s,
64
+ "built_at": self.built_at,
65
+ "confidence_avg": self.confidence_avg,
66
+ "coverage": self.coverage,
67
+ "producer": self.producer,
68
+ "reason": self.reason,
69
+ "status": self.status,
70
+ "unsupported_files_sample": list(self.unsupported_files_sample),
71
+ }
72
+
73
+ @classmethod
74
+ def from_dict(cls, d: dict) -> "BuildMeta":
75
+ """Deserialise from a payload dict.
76
+
77
+ Backward-compat: if ``d`` is missing any required field (old v1.0.0
78
+ payload without build_meta), returns a sentinel with sensible defaults.
79
+ "build_duration_s" is the canonical key in the payload; "duration_s"
80
+ is accepted as a fallback for any pre-canonical payloads.
81
+ "unsupported_files_sample" defaults to [] for pre-Phase-5 payloads.
82
+ """
83
+ return cls(
84
+ analysis_mode=str(d.get("analysis_mode", "python_ast")),
85
+ status=str(d.get("status", "ok")),
86
+ reason=str(d.get("reason", "")),
87
+ confidence_avg=float(d.get("confidence_avg", 1.0)),
88
+ coverage=dict(d.get("coverage", {
89
+ "files_scanned_by_lang": {},
90
+ "files_supported_by_lang": {},
91
+ "coverage_ratio": 0.0,
92
+ })),
93
+ producer=str(d.get("producer", "vigil_mapper.unknown")),
94
+ built_at=str(d.get("built_at", "")),
95
+ # Accept both "build_duration_s" (canonical) and legacy "duration_s".
96
+ duration_s=float(
97
+ d.get("build_duration_s", d.get("duration_s", 0.0))
98
+ ),
99
+ unsupported_files_sample=list(d.get("unsupported_files_sample", [])),
100
+ )
101
+
102
+
103
+ # Re-export Maps 5-7 from the extension module so callers can import all
104
+ # models from a single location: `from .map_models import ConflictEntry`.
105
+ from .map_models_ext import ConflictEntry, HotspotEntry, RefactorBoundary # noqa: E402
106
+
107
+ # Re-export Map 8 (Finding) from findings module
108
+ from .map_models_findings import Finding # noqa: E402
109
+
110
+ # Python 3.10+ supports slots + kw_only in @dataclass
111
+ _DATACLASS_KWARGS: dict[str, Any] = {"frozen": True}
112
+ if sys.version_info >= (3, 10):
113
+ _DATACLASS_KWARGS["slots"] = True
114
+ _DATACLASS_KWARGS["kw_only"] = True
115
+
116
+
117
+ # ---------------------------------------------------------------------------
118
+ # MapMetadata envelope (embedded in every entry)
119
+ # ---------------------------------------------------------------------------
120
+
121
+ @dataclass(**_DATACLASS_KWARGS)
122
+ class MapMetadata:
123
+ """Common metadata envelope for all map entries."""
124
+ source: str
125
+ evidence: tuple[str, ...]
126
+ confidence: float
127
+ freshness: str # ISO8601 UTC Z-suffix
128
+ status: Literal["observed", "inferred", "validated", "canonical", "deprecated"]
129
+
130
+ def to_dict(self) -> dict:
131
+ return {
132
+ "source": self.source,
133
+ "evidence": list(self.evidence),
134
+ "confidence": self.confidence,
135
+ "freshness": self.freshness,
136
+ "status": self.status,
137
+ }
138
+
139
+ @classmethod
140
+ def from_dict(cls, d: dict) -> "MapMetadata":
141
+ return cls(
142
+ source=str(d["source"]),
143
+ evidence=tuple(d.get("evidence", [])),
144
+ confidence=float(d["confidence"]),
145
+ freshness=str(d["freshness"]),
146
+ status=d["status"], # type: ignore[arg-type]
147
+ )
148
+
149
+
150
+ # ---------------------------------------------------------------------------
151
+ # Map 1: Structural Entry
152
+ # ---------------------------------------------------------------------------
153
+
154
+ @dataclass(**_DATACLASS_KWARGS)
155
+ class StructuralEntry:
156
+ """One entry in the structural map -- represents a single Python file."""
157
+ file: str
158
+ language: str
159
+ size_lines: int
160
+ imports_out: tuple[str, ...]
161
+ imports_in: tuple[str, ...]
162
+ symbols_defined: tuple[str, ...]
163
+ symbols_used_external: tuple[str, ...]
164
+ cycles: tuple[str, ...]
165
+ tags: tuple[str, ...]
166
+ # Metadata fields (flattened for storage)
167
+ source: str
168
+ evidence: tuple[str, ...]
169
+ confidence: float
170
+ freshness: str
171
+ status: str
172
+
173
+ def to_dict(self) -> dict:
174
+ return {
175
+ "file": self.file,
176
+ "language": self.language,
177
+ "size_lines": self.size_lines,
178
+ "imports_out": list(self.imports_out),
179
+ "imports_in": list(self.imports_in),
180
+ "symbols_defined": list(self.symbols_defined),
181
+ "symbols_used_external": list(self.symbols_used_external),
182
+ "cycles": list(self.cycles),
183
+ "tags": list(self.tags),
184
+ "source": self.source,
185
+ "evidence": list(self.evidence),
186
+ "confidence": self.confidence,
187
+ "freshness": self.freshness,
188
+ "status": self.status,
189
+ }
190
+
191
+ @classmethod
192
+ def from_dict(cls, d: dict) -> "StructuralEntry":
193
+ return cls(
194
+ file=str(d["file"]),
195
+ language=str(d.get("language", "python")),
196
+ size_lines=int(d["size_lines"]),
197
+ imports_out=tuple(d.get("imports_out", [])),
198
+ imports_in=tuple(d.get("imports_in", [])),
199
+ symbols_defined=tuple(d.get("symbols_defined", [])),
200
+ symbols_used_external=tuple(d.get("symbols_used_external", [])),
201
+ cycles=tuple(d.get("cycles", [])),
202
+ tags=tuple(d.get("tags", [])),
203
+ source=str(d.get("source", "static_scan")),
204
+ evidence=tuple(d.get("evidence", [])),
205
+ confidence=float(d.get("confidence", 0.9)),
206
+ freshness=str(d.get("freshness", "")),
207
+ status=str(d.get("status", "observed")),
208
+ )
209
+
210
+
211
+ # ---------------------------------------------------------------------------
212
+ # Map 2: Runtime Node
213
+ # ---------------------------------------------------------------------------
214
+
215
+ @dataclass(**_DATACLASS_KWARGS)
216
+ class RuntimeNode:
217
+ """One node in the runtime map -- represents a runtime entry point or service."""
218
+ node: str
219
+ defined_in: str
220
+ kind: str
221
+ calls: tuple[str, ...]
222
+ side_effects: tuple[str, ...]
223
+ depends_on_env: tuple[str, ...]
224
+ order_constraints: tuple[str, ...]
225
+ hidden_runtime_dependencies: tuple[str, ...]
226
+ tags: tuple[str, ...]
227
+ # Metadata
228
+ source: str
229
+ evidence: tuple[str, ...]
230
+ confidence: float
231
+ freshness: str
232
+ status: str
233
+
234
+ def to_dict(self) -> dict:
235
+ return {
236
+ "node": self.node,
237
+ "defined_in": self.defined_in,
238
+ "kind": self.kind,
239
+ "calls": list(self.calls),
240
+ "side_effects": list(self.side_effects),
241
+ "depends_on_env": list(self.depends_on_env),
242
+ "order_constraints": list(self.order_constraints),
243
+ "hidden_runtime_dependencies": list(self.hidden_runtime_dependencies),
244
+ "tags": list(self.tags),
245
+ "source": self.source,
246
+ "evidence": list(self.evidence),
247
+ "confidence": self.confidence,
248
+ "freshness": self.freshness,
249
+ "status": self.status,
250
+ }
251
+
252
+ @classmethod
253
+ def from_dict(cls, d: dict) -> "RuntimeNode":
254
+ return cls(
255
+ node=str(d["node"]),
256
+ defined_in=str(d["defined_in"]),
257
+ kind=str(d.get("kind", "unknown")),
258
+ calls=tuple(d.get("calls", [])),
259
+ side_effects=tuple(d.get("side_effects", [])),
260
+ depends_on_env=tuple(d.get("depends_on_env", [])),
261
+ order_constraints=tuple(d.get("order_constraints", [])),
262
+ hidden_runtime_dependencies=tuple(d.get("hidden_runtime_dependencies", [])),
263
+ tags=tuple(d.get("tags", [])),
264
+ source=str(d.get("source", "static_scan")),
265
+ evidence=tuple(d.get("evidence", [])),
266
+ confidence=float(d.get("confidence", 0.8)),
267
+ freshness=str(d.get("freshness", "")),
268
+ status=str(d.get("status", "inferred")),
269
+ )
270
+
271
+
272
+ # ---------------------------------------------------------------------------
273
+ # Map 3: Data Contract Entry
274
+ # ---------------------------------------------------------------------------
275
+
276
+ @dataclass(**_DATACLASS_KWARGS)
277
+ class DataContractEntry:
278
+ """One entity in the data contract map."""
279
+ entity: str
280
+ canonical_schema: str
281
+ variants: tuple[str, ...] # JSON-serialised variant dicts as strings
282
+ transformations: tuple[str, ...] # JSON-serialised transformation dicts as strings
283
+ writers: tuple[str, ...]
284
+ readers: tuple[str, ...]
285
+ drift_flags: tuple[str, ...]
286
+ # Metadata
287
+ source: str
288
+ evidence: tuple[str, ...]
289
+ confidence: float
290
+ freshness: str
291
+ status: str
292
+
293
+ def to_dict(self) -> dict:
294
+ import json as _json
295
+ return {
296
+ "entity": self.entity,
297
+ "canonical_schema": self.canonical_schema,
298
+ "variants": [_json.loads(v) if isinstance(v, str) else v for v in self.variants],
299
+ "transformations": [_json.loads(t) if isinstance(t, str) else t for t in self.transformations],
300
+ "writers": list(self.writers),
301
+ "readers": list(self.readers),
302
+ "drift_flags": list(self.drift_flags),
303
+ "source": self.source,
304
+ "evidence": list(self.evidence),
305
+ "confidence": self.confidence,
306
+ "freshness": self.freshness,
307
+ "status": self.status,
308
+ }
309
+
310
+ @classmethod
311
+ def from_dict(cls, d: dict) -> "DataContractEntry":
312
+ import json as _json
313
+ def _serialize(items: list) -> tuple[str, ...]:
314
+ return tuple(
315
+ _json.dumps(item, sort_keys=True) if isinstance(item, dict) else str(item)
316
+ for item in items
317
+ )
318
+ return cls(
319
+ entity=str(d["entity"]),
320
+ canonical_schema=str(d.get("canonical_schema", "")),
321
+ variants=_serialize(d.get("variants", [])),
322
+ transformations=_serialize(d.get("transformations", [])),
323
+ writers=tuple(d.get("writers", [])),
324
+ readers=tuple(d.get("readers", [])),
325
+ drift_flags=tuple(d.get("drift_flags", [])),
326
+ source=str(d.get("source", "static_scan")),
327
+ evidence=tuple(d.get("evidence", [])),
328
+ confidence=float(d.get("confidence", 0.9)),
329
+ freshness=str(d.get("freshness", "")),
330
+ status=str(d.get("status", "observed")),
331
+ )
332
+
333
+
334
+ # ---------------------------------------------------------------------------
335
+ # Map 4: Authority Domain
336
+ # ---------------------------------------------------------------------------
337
+
338
+ @dataclass(**_DATACLASS_KWARGS)
339
+ class AuthorityDomain:
340
+ """One domain in the authority map."""
341
+ authority_domain: str
342
+ canonical_owner: str
343
+ allowed_writers: tuple[str, ...]
344
+ derived_readers: tuple[str, ...]
345
+ cache_layers: tuple[str, ...]
346
+ freshness_sla: str
347
+ invalidation_rule: str
348
+ drift_policy: str
349
+ writers_detected: tuple[str, ...] # JSON-serialised dicts as strings
350
+ last_drift_events: tuple[str, ...]
351
+ # Metadata
352
+ source: str
353
+ evidence: tuple[str, ...]
354
+ confidence: float
355
+ freshness: str
356
+ status: str
357
+ # Per-domain target file patterns for auto-discovery filtering.
358
+ # Glob patterns (fnmatch style) that a write call's resolved target must
359
+ # match for the writer to be attributed to this domain.
360
+ # Empty tuple → no auto-discovery (only seed-based allowed_writers remain).
361
+ target_file_patterns: tuple[str, ...] = ()
362
+
363
+ def to_dict(self) -> dict:
364
+ import json as _json
365
+ return {
366
+ "authority_domain": self.authority_domain,
367
+ "canonical_owner": self.canonical_owner,
368
+ "allowed_writers": list(self.allowed_writers),
369
+ "derived_readers": list(self.derived_readers),
370
+ "cache_layers": list(self.cache_layers),
371
+ "freshness_sla": self.freshness_sla,
372
+ "invalidation_rule": self.invalidation_rule,
373
+ "drift_policy": self.drift_policy,
374
+ "writers_detected": [
375
+ _json.loads(w) if isinstance(w, str) else w for w in self.writers_detected
376
+ ],
377
+ "last_drift_events": list(self.last_drift_events),
378
+ "target_file_patterns": list(self.target_file_patterns),
379
+ "source": self.source,
380
+ "evidence": list(self.evidence),
381
+ "confidence": self.confidence,
382
+ "freshness": self.freshness,
383
+ "status": self.status,
384
+ }
385
+
386
+ @classmethod
387
+ def from_dict(cls, d: dict) -> "AuthorityDomain":
388
+ import json as _json
389
+ def _serialize(items: list) -> tuple[str, ...]:
390
+ return tuple(
391
+ _json.dumps(item, sort_keys=True) if isinstance(item, dict) else str(item)
392
+ for item in items
393
+ )
394
+ return cls(
395
+ authority_domain=str(d["authority_domain"]),
396
+ canonical_owner=str(d.get("canonical_owner", "")),
397
+ allowed_writers=tuple(d.get("allowed_writers", [])),
398
+ derived_readers=tuple(d.get("derived_readers", [])),
399
+ cache_layers=tuple(d.get("cache_layers", [])),
400
+ freshness_sla=str(d.get("freshness_sla", "immediate")),
401
+ invalidation_rule=str(d.get("invalidation_rule", "")),
402
+ drift_policy=str(d.get("drift_policy", "fail_close")),
403
+ writers_detected=_serialize(d.get("writers_detected", [])),
404
+ last_drift_events=tuple(d.get("last_drift_events", [])),
405
+ # Backward-compat: missing field → empty tuple (no auto-discovery)
406
+ target_file_patterns=tuple(d.get("target_file_patterns", [])),
407
+ source=str(d.get("source", "static_scan")),
408
+ evidence=tuple(d.get("evidence", [])),
409
+ confidence=float(d.get("confidence", 0.85)),
410
+ freshness=str(d.get("freshness", "")),
411
+ status=str(d.get("status", "observed")),
412
+ )
413
+
414
+
415
+ # ---------------------------------------------------------------------------
416
+ # RepoMaps container (imports ext models lazily to avoid circular imports)
417
+ # ---------------------------------------------------------------------------
418
+
419
+ @dataclass(**_DATACLASS_KWARGS)
420
+ class RepoMaps:
421
+ """Container for all 8 maps loaded from disk."""
422
+ structural: tuple = field(default_factory=tuple) # tuple[StructuralEntry, ...]
423
+ runtime: tuple = field(default_factory=tuple) # tuple[RuntimeNode, ...]
424
+ data_contract: tuple = field(default_factory=tuple) # tuple[DataContractEntry, ...]
425
+ authority: tuple = field(default_factory=tuple) # tuple[AuthorityDomain, ...]
426
+ conflict: tuple = field(default_factory=tuple) # tuple[ConflictEntry, ...]
427
+ hotspot: tuple = field(default_factory=tuple) # tuple[HotspotEntry, ...]
428
+ refactor_boundary: tuple = field(default_factory=tuple) # tuple[RefactorBoundary, ...]
429
+ findings: tuple = field(default_factory=tuple) # tuple[Finding, ...] — Map 8
430
+ missing: bool = False
431
+ schema_version: str = "2.0.0"
@@ -0,0 +1,206 @@
1
+ """Data models for the map builder subsystem -- Maps 5-7.
2
+
3
+ Continuation of map_models.py:
4
+ ConflictEntry -- Map 5: Conflict Map
5
+ HotspotEntry -- Map 6: Hotspot Map
6
+ RefactorBoundary -- Map 7: Refactor Boundary Map
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ import logging
12
+ from dataclasses import dataclass
13
+ from typing import Any
14
+
15
+ __all__ = [
16
+ "ConflictEntry",
17
+ "HotspotEntry",
18
+ "RefactorBoundary",
19
+ ]
20
+
21
+ _log = logging.getLogger(__name__)
22
+
23
+ _DATACLASS_KWARGS: dict[str, Any] = {"frozen": True}
24
+ if sys.version_info >= (3, 10):
25
+ _DATACLASS_KWARGS["slots"] = True
26
+ _DATACLASS_KWARGS["kw_only"] = True
27
+
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Map 5: Conflict Entry
31
+ # ---------------------------------------------------------------------------
32
+
33
+ @dataclass(**_DATACLASS_KWARGS)
34
+ class ConflictEntry:
35
+ """One conflict entry in the conflict map."""
36
+ conflict_id: str
37
+ domain: str
38
+ subject: str
39
+ sources: tuple[str, ...] # JSON-serialised source dicts as strings
40
+ severity: str # low / medium / high / critical
41
+ conflict_status: str # open / in_progress / resolved
42
+ action: str
43
+ # Metadata
44
+ source: str
45
+ evidence: tuple[str, ...]
46
+ confidence: float
47
+ freshness: str
48
+ status: str
49
+
50
+ def to_dict(self) -> dict:
51
+ import json as _json
52
+ return {
53
+ "conflict_id": self.conflict_id,
54
+ "domain": self.domain,
55
+ "subject": self.subject,
56
+ "sources": [
57
+ _json.loads(s) if isinstance(s, str) else s for s in self.sources
58
+ ],
59
+ "severity": self.severity,
60
+ "conflict_status": self.conflict_status,
61
+ "action": self.action,
62
+ "source": self.source,
63
+ "evidence": list(self.evidence),
64
+ "confidence": self.confidence,
65
+ "freshness": self.freshness,
66
+ "status": self.status,
67
+ }
68
+
69
+ @classmethod
70
+ def from_dict(cls, d: dict) -> "ConflictEntry":
71
+ import json as _json
72
+ def _serialize(items: list) -> tuple[str, ...]:
73
+ return tuple(
74
+ _json.dumps(item, sort_keys=True) if isinstance(item, dict) else str(item)
75
+ for item in items
76
+ )
77
+ return cls(
78
+ conflict_id=str(d["conflict_id"]),
79
+ domain=str(d.get("domain", "")),
80
+ subject=str(d.get("subject", "")),
81
+ sources=_serialize(d.get("sources", [])),
82
+ severity=str(d.get("severity", "medium")),
83
+ conflict_status=str(d.get("conflict_status", "open")),
84
+ action=str(d.get("action", "investigate")),
85
+ source=str(d.get("source", "inter_map_comparison")),
86
+ evidence=tuple(d.get("evidence", [])),
87
+ confidence=float(d.get("confidence", 0.9)),
88
+ freshness=str(d.get("freshness", "")),
89
+ status=str(d.get("status", "validated")),
90
+ )
91
+
92
+
93
+ # ---------------------------------------------------------------------------
94
+ # Map 6: Hotspot Entry
95
+ # ---------------------------------------------------------------------------
96
+
97
+ @dataclass(**_DATACLASS_KWARGS)
98
+ class HotspotEntry:
99
+ """One entry in the hotspot map."""
100
+ target: str
101
+ hotspot_score: int
102
+ reasons: tuple[str, ...]
103
+ recommended_mode: str
104
+ # Metadata
105
+ source: str
106
+ evidence: tuple[str, ...]
107
+ confidence: float
108
+ freshness: str
109
+ status: str
110
+
111
+ def to_dict(self) -> dict:
112
+ return {
113
+ "target": self.target,
114
+ "hotspot_score": self.hotspot_score,
115
+ "reasons": list(self.reasons),
116
+ "recommended_mode": self.recommended_mode,
117
+ "source": self.source,
118
+ "evidence": list(self.evidence),
119
+ "confidence": self.confidence,
120
+ "freshness": self.freshness,
121
+ "status": self.status,
122
+ }
123
+
124
+ @classmethod
125
+ def from_dict(cls, d: dict) -> "HotspotEntry":
126
+ return cls(
127
+ target=str(d["target"]),
128
+ hotspot_score=int(d.get("hotspot_score", 0)),
129
+ reasons=tuple(d.get("reasons", [])),
130
+ recommended_mode=str(d.get("recommended_mode", "safe_refactor")),
131
+ source=str(d.get("source", "automated_scoring")),
132
+ evidence=tuple(d.get("evidence", [])),
133
+ confidence=float(d.get("confidence", 0.88)),
134
+ freshness=str(d.get("freshness", "")),
135
+ status=str(d.get("status", "observed")),
136
+ )
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Map 7: Refactor Boundary
141
+ # ---------------------------------------------------------------------------
142
+
143
+ @dataclass(**_DATACLASS_KWARGS)
144
+ class RefactorBoundary:
145
+ """One refactor boundary entry."""
146
+ boundary_id: str
147
+ goal: str
148
+ phase: str
149
+ allowed_files: tuple[str, ...]
150
+ watch_files: tuple[str, ...]
151
+ forbidden_files: tuple[str, ...]
152
+ entrypoints: tuple[str, ...]
153
+ must_hold_invariants: tuple[str, ...]
154
+ # Metadata
155
+ source: str
156
+ evidence: tuple[str, ...]
157
+ confidence: float
158
+ freshness: str
159
+ status: str
160
+ # FOC extension (Wave 1.6, schema 1.1 — additive, backwards compat preserved).
161
+ # Defaults match pre-extension behaviour: any boundary loaded from an old
162
+ # payload (no FOC keys) is treated as freely patchable by FOC.
163
+ safe_auto_patch: bool = True
164
+ suggest_only: bool = False
165
+ forbidden_for_foc: bool = False
166
+
167
+ def to_dict(self) -> dict:
168
+ return {
169
+ "boundary_id": self.boundary_id,
170
+ "goal": self.goal,
171
+ "phase": self.phase,
172
+ "allowed_files": list(self.allowed_files),
173
+ "watch_files": list(self.watch_files),
174
+ "forbidden_files": list(self.forbidden_files),
175
+ "entrypoints": list(self.entrypoints),
176
+ "must_hold_invariants": list(self.must_hold_invariants),
177
+ "source": self.source,
178
+ "evidence": list(self.evidence),
179
+ "confidence": self.confidence,
180
+ "freshness": self.freshness,
181
+ "status": self.status,
182
+ "safe_auto_patch": self.safe_auto_patch,
183
+ "suggest_only": self.suggest_only,
184
+ "forbidden_for_foc": self.forbidden_for_foc,
185
+ }
186
+
187
+ @classmethod
188
+ def from_dict(cls, d: dict) -> "RefactorBoundary":
189
+ return cls(
190
+ boundary_id=str(d["boundary_id"]),
191
+ goal=str(d.get("goal", "")),
192
+ phase=str(d.get("phase", "")),
193
+ allowed_files=tuple(d.get("allowed_files", [])),
194
+ watch_files=tuple(d.get("watch_files", [])),
195
+ forbidden_files=tuple(d.get("forbidden_files", [])),
196
+ entrypoints=tuple(d.get("entrypoints", [])),
197
+ must_hold_invariants=tuple(d.get("must_hold_invariants", [])),
198
+ source=str(d.get("source", "manual_planning")),
199
+ evidence=tuple(d.get("evidence", [])),
200
+ confidence=float(d.get("confidence", 1.0)),
201
+ freshness=str(d.get("freshness", "")),
202
+ status=str(d.get("status", "canonical")),
203
+ safe_auto_patch=bool(d.get("safe_auto_patch", True)),
204
+ suggest_only=bool(d.get("suggest_only", False)),
205
+ forbidden_for_foc=bool(d.get("forbidden_for_foc", False)),
206
+ )