codegraph-engine 3.0.0__2-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.
- codegraph/__init__.py +37 -0
- codegraph/agent.py +26 -0
- codegraph/agent_brain.py +2212 -0
- codegraph/agent_capabilities.py +4058 -0
- codegraph/agent_rules.py +531 -0
- codegraph/api_drift.py +455 -0
- codegraph/architecture.py +371 -0
- codegraph/async_queue/__init__.py +50 -0
- codegraph/async_queue/detectors/__init__.py +48 -0
- codegraph/async_queue/detectors/celery.py +160 -0
- codegraph/async_queue/detectors/kafka.py +168 -0
- codegraph/async_queue/detectors/rabbitmq.py +116 -0
- codegraph/async_queue/detectors/redis.py +130 -0
- codegraph/async_queue/detectors/sqs.py +135 -0
- codegraph/async_queue/models.py +161 -0
- codegraph/async_queue/otel_models.py +74 -0
- codegraph/async_queue/otel_parser.py +337 -0
- codegraph/async_queue/reconciler.py +427 -0
- codegraph/async_queue/scanner.py +291 -0
- codegraph/async_queue/traversal.py +447 -0
- codegraph/audit.py +106 -0
- codegraph/binding_resolver.py +616 -0
- codegraph/branch_comparison.py +197 -0
- codegraph/cache.py +95 -0
- codegraph/change_impact.py +547 -0
- codegraph/cli.py +2436 -0
- codegraph/cli_output.py +205 -0
- codegraph/config.py +45 -0
- codegraph/constraints.py +238 -0
- codegraph/context.py +2182 -0
- codegraph/context_freshness.py +351 -0
- codegraph/database/__init__.py +72 -0
- codegraph/database/extractor.py +3651 -0
- codegraph/database/interrogation.py +1494 -0
- codegraph/database/models.py +310 -0
- codegraph/database/schema_drift.py +862 -0
- codegraph/epistemic.py +200 -0
- codegraph/errors.py +282 -0
- codegraph/evidence/__init__.py +15 -0
- codegraph/evidence/citations.py +418 -0
- codegraph/evidence_contract.py +486 -0
- codegraph/frameworks.py +960 -0
- codegraph/frameworks_nestjs.py +502 -0
- codegraph/frameworks_nextjs.py +337 -0
- codegraph/frameworks_react.py +457 -0
- codegraph/freshness.py +335 -0
- codegraph/frontend_html.py +109 -0
- codegraph/git.py +362 -0
- codegraph/git_state.py +545 -0
- codegraph/graph/__init__.py +46 -0
- codegraph/graph/models.py +56 -0
- codegraph/graph/traversal.py +1993 -0
- codegraph/indexing/__init__.py +24 -0
- codegraph/indexing/classifier.py +564 -0
- codegraph/indexing/indexer.py +2578 -0
- codegraph/indexing/models.py +427 -0
- codegraph/indexing/parser.py +3404 -0
- codegraph/indexing/pool.py +136 -0
- codegraph/indexing/scanner.py +211 -0
- codegraph/indexing/telemetry.py +326 -0
- codegraph/indexing/templates.py +418 -0
- codegraph/indexing/test_framework.py +116 -0
- codegraph/installer.py +1702 -0
- codegraph/interrogation.py +2355 -0
- codegraph/llm/__init__.py +3 -0
- codegraph/llm/base.py +15 -0
- codegraph/llm/context.py +20 -0
- codegraph/mcp/__init__.py +3 -0
- codegraph/mcp/server.py +1476 -0
- codegraph/mcp_diagnostics.py +882 -0
- codegraph/memory/__init__.py +3 -0
- codegraph/memory/store.py +46 -0
- codegraph/models.py +310 -0
- codegraph/monorepo.py +925 -0
- codegraph/observability.py +262 -0
- codegraph/optimizer.py +1014 -0
- codegraph/planner.py +417 -0
- codegraph/process_lifecycle.py +1153 -0
- codegraph/py.typed +1 -0
- codegraph/query_expansion.py +199 -0
- codegraph/ranking.py +408 -0
- codegraph/refactor/__init__.py +36 -0
- codegraph/refactor/models.py +114 -0
- codegraph/refactor/renamer.py +844 -0
- codegraph/refactor/transactions.py +414 -0
- codegraph/resolver.py +3968 -0
- codegraph/resources/__init__.py +46 -0
- codegraph/resources/cache.py +117 -0
- codegraph/resources/coalescer.py +83 -0
- codegraph/resources/debouncer.py +98 -0
- codegraph/resources/governor.py +219 -0
- codegraph/resources/monitor.py +102 -0
- codegraph/resources/policy.py +123 -0
- codegraph/retrieval_policy.py +320 -0
- codegraph/roblox.py +2174 -0
- codegraph/route_composer.py +363 -0
- codegraph/route_topology.py +172 -0
- codegraph/runtime/__init__.py +51 -0
- codegraph/runtime/collector.py +155 -0
- codegraph/runtime/db_watcher.py +191 -0
- codegraph/runtime/ingestor.py +692 -0
- codegraph/runtime/models.py +130 -0
- codegraph/runtime/port_inspector.py +304 -0
- codegraph/runtime/reconciliation.py +492 -0
- codegraph/runtime/runner.py +353 -0
- codegraph/runtime/trace_linker.py +175 -0
- codegraph/search/__init__.py +25 -0
- codegraph/search/hybrid.py +846 -0
- codegraph/search/semantic.py +28 -0
- codegraph/security/__init__.py +51 -0
- codegraph/security/paths.py +189 -0
- codegraph/security/redaction.py +633 -0
- codegraph/semantic_conflicts.py +270 -0
- codegraph/semantic_decorators.py +372 -0
- codegraph/structural_diff.py +542 -0
- codegraph/styles.py +144 -0
- codegraph/symbol_history.py +277 -0
- codegraph/target_resolver.py +348 -0
- codegraph/task.py +637 -0
- codegraph/tool_selection_eval.py +1158 -0
- codegraph/tsconfig.py +236 -0
- codegraph/ui/__init__.py +6 -0
- codegraph/ui/server.py +755 -0
- codegraph/watcher.py +323 -0
- codegraph_engine-3.0.0.dist-info/METADATA +868 -0
- codegraph_engine-3.0.0.dist-info/RECORD +130 -0
- codegraph_engine-3.0.0.dist-info/WHEEL +6 -0
- codegraph_engine-3.0.0.dist-info/entry_points.txt +2 -0
- codegraph_engine-3.0.0.dist-info/licenses/LICENSE +21 -0
- codegraph_engine-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Context Freshness Validation Engine.
|
|
2
|
+
|
|
3
|
+
Validates whether a previously compiled ContextPacket remains valid against
|
|
4
|
+
the current repository state or whether it has become STALE or PARTIALLY_STALE.
|
|
5
|
+
|
|
6
|
+
CRITICAL INVARIANT:
|
|
7
|
+
Do NOT invalidate context merely because an unrelated file changed!
|
|
8
|
+
A context containing files A/B must remain VALID when an unrelated file Z changes.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import sqlite3
|
|
14
|
+
import subprocess
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from enum import StrEnum
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from codegraph.git_state import GitStateReport, get_git_state
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ContextFreshnessStatus(StrEnum):
|
|
24
|
+
VALID = "VALID" # None of the files/symbols in the context were modified
|
|
25
|
+
PARTIALLY_STALE = "PARTIALLY_STALE" # Secondary files/tests in context changed, primary target unaffected
|
|
26
|
+
STALE = "STALE" # Primary targets or core symbols in context were modified or removed
|
|
27
|
+
UNKNOWN = "UNKNOWN" # Cannot verify (e.g. no git history or commit not found)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ContextFreshnessReport:
|
|
32
|
+
status: ContextFreshnessStatus
|
|
33
|
+
base_commit: str | None
|
|
34
|
+
current_head: str | None
|
|
35
|
+
repository_dirty: bool
|
|
36
|
+
context_files: list[str]
|
|
37
|
+
changed_relevant_files: list[str] = field(default_factory=list)
|
|
38
|
+
unrelated_changed_files: list[str] = field(default_factory=list)
|
|
39
|
+
invalidated_symbols: list[str] = field(default_factory=list)
|
|
40
|
+
reason: str = ""
|
|
41
|
+
recommended_action: str = "NONE" # "NONE" | "RECOMPILE_CONTEXT" | "REFRESH_PERIPHERAL"
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def is_valid(self) -> bool:
|
|
45
|
+
return self.status == ContextFreshnessStatus.VALID
|
|
46
|
+
|
|
47
|
+
def as_dict(self) -> dict[str, Any]:
|
|
48
|
+
return {
|
|
49
|
+
"status": self.status.value,
|
|
50
|
+
"base_commit": self.base_commit,
|
|
51
|
+
"current_head": self.current_head,
|
|
52
|
+
"repository_dirty": self.repository_dirty,
|
|
53
|
+
"context_files": self.context_files,
|
|
54
|
+
"changed_relevant_files": self.changed_relevant_files,
|
|
55
|
+
"unrelated_changed_files": self.unrelated_changed_files,
|
|
56
|
+
"invalidated_symbols": self.invalidated_symbols,
|
|
57
|
+
"reason": self.reason,
|
|
58
|
+
"recommended_action": self.recommended_action,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _run_git(repository: Path, *args: str, timeout: int = 10) -> str | None:
|
|
63
|
+
for arg in args:
|
|
64
|
+
if any(c in arg for c in (";", "|", "&", "`", "$", "\n", "\r")):
|
|
65
|
+
return None
|
|
66
|
+
try:
|
|
67
|
+
res = subprocess.run(
|
|
68
|
+
["git", *args],
|
|
69
|
+
cwd=str(repository),
|
|
70
|
+
capture_output=True,
|
|
71
|
+
text=True,
|
|
72
|
+
encoding="utf-8",
|
|
73
|
+
errors="replace",
|
|
74
|
+
timeout=timeout,
|
|
75
|
+
)
|
|
76
|
+
return res.stdout if res.returncode == 0 else None
|
|
77
|
+
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
_REF_PATTERN = re.compile(r"^[A-Za-z0-9_.~^/-]{1,100}$")
|
|
82
|
+
|
|
83
|
+
_COMMIT_DIFF_FILES_CACHE: dict[tuple[str, str, str], tuple[str, ...]] = {}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _get_cached_commit_diff_files(repository: Path, base: str, head: str) -> tuple[str, ...] | None:
|
|
87
|
+
"""Cache immutable diff between two commits to avoid repeated subprocess invocations."""
|
|
88
|
+
cache_key = (str(repository), base, head)
|
|
89
|
+
if cache_key in _COMMIT_DIFF_FILES_CACHE:
|
|
90
|
+
return _COMMIT_DIFF_FILES_CACHE[cache_key]
|
|
91
|
+
diff_raw = _run_git(repository, "diff", "--name-only", base, head)
|
|
92
|
+
if diff_raw is None:
|
|
93
|
+
return None
|
|
94
|
+
files = tuple(line.strip() for line in diff_raw.splitlines() if line.strip())
|
|
95
|
+
if len(_COMMIT_DIFF_FILES_CACHE) > 500:
|
|
96
|
+
_COMMIT_DIFF_FILES_CACHE.clear()
|
|
97
|
+
_COMMIT_DIFF_FILES_CACHE[cache_key] = files
|
|
98
|
+
return files
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def check_context_freshness(
|
|
102
|
+
context_packet: dict[str, Any] | Any,
|
|
103
|
+
repository: Path,
|
|
104
|
+
con: sqlite3.Connection | None = None,
|
|
105
|
+
state: GitStateReport | None = None,
|
|
106
|
+
) -> ContextFreshnessReport:
|
|
107
|
+
"""Evaluate whether previously generated ContextPacket is still valid against repository state.
|
|
108
|
+
|
|
109
|
+
Guarantees:
|
|
110
|
+
- Context remains VALID when changes occur only in unrelated files.
|
|
111
|
+
- Becomes PARTIALLY_STALE if secondary files (e.g. related tests) changed.
|
|
112
|
+
- Becomes STALE if primary target symbols or core files changed.
|
|
113
|
+
"""
|
|
114
|
+
# 1. Normalize packet data
|
|
115
|
+
packet_dict: dict[str, Any]
|
|
116
|
+
if hasattr(context_packet, "model_dump"):
|
|
117
|
+
packet_dict = context_packet.model_dump()
|
|
118
|
+
elif hasattr(context_packet, "as_dict"):
|
|
119
|
+
packet_dict = context_packet.as_dict()
|
|
120
|
+
elif isinstance(context_packet, dict):
|
|
121
|
+
packet_dict = context_packet
|
|
122
|
+
else:
|
|
123
|
+
return ContextFreshnessReport(
|
|
124
|
+
status=ContextFreshnessStatus.UNKNOWN,
|
|
125
|
+
base_commit=None,
|
|
126
|
+
current_head=None,
|
|
127
|
+
repository_dirty=False,
|
|
128
|
+
context_files=[],
|
|
129
|
+
reason="Invalid context packet data type.",
|
|
130
|
+
recommended_action="RECOMPILE_CONTEXT",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# 2. Extract base commit from packet
|
|
134
|
+
meta = packet_dict.get("metadata", {}) if isinstance(packet_dict.get("metadata"), dict) else {}
|
|
135
|
+
base_commit = (
|
|
136
|
+
packet_dict.get("current_commit")
|
|
137
|
+
or packet_dict.get("indexed_commit")
|
|
138
|
+
or meta.get("indexed_commit")
|
|
139
|
+
or meta.get("current_commit")
|
|
140
|
+
)
|
|
141
|
+
if isinstance(base_commit, str):
|
|
142
|
+
base_commit = base_commit.strip()
|
|
143
|
+
|
|
144
|
+
# 3. Collect all files and symbols mentioned in context
|
|
145
|
+
context_files: set[str] = set()
|
|
146
|
+
core_files: set[str] = set()
|
|
147
|
+
peripheral_files: set[str] = set()
|
|
148
|
+
context_symbols: set[str] = set()
|
|
149
|
+
primary_targets: set[str] = set()
|
|
150
|
+
|
|
151
|
+
# Files section
|
|
152
|
+
for f_obj in packet_dict.get("files", []):
|
|
153
|
+
f_path: str | None = None
|
|
154
|
+
if isinstance(f_obj, str):
|
|
155
|
+
f_path = f_obj
|
|
156
|
+
elif isinstance(f_obj, dict):
|
|
157
|
+
val = f_obj.get("file") or f_obj.get("path")
|
|
158
|
+
f_path = str(val) if val else None
|
|
159
|
+
else:
|
|
160
|
+
val = getattr(f_obj, "file", getattr(f_obj, "path", None))
|
|
161
|
+
f_path = str(val) if val else None
|
|
162
|
+
if f_path:
|
|
163
|
+
context_files.add(f_path)
|
|
164
|
+
core_files.add(f_path)
|
|
165
|
+
|
|
166
|
+
# Symbols section
|
|
167
|
+
for s_obj in packet_dict.get("symbols", []):
|
|
168
|
+
s_file = s_obj.get("file") if isinstance(s_obj, dict) else getattr(s_obj, "file", None)
|
|
169
|
+
s_name = s_obj.get("symbol") if isinstance(s_obj, dict) else getattr(s_obj, "symbol", None)
|
|
170
|
+
if s_file:
|
|
171
|
+
context_files.add(str(s_file))
|
|
172
|
+
core_files.add(str(s_file))
|
|
173
|
+
if s_name:
|
|
174
|
+
context_symbols.add(str(s_name))
|
|
175
|
+
|
|
176
|
+
# Evidence section
|
|
177
|
+
for e_obj in packet_dict.get("evidence", []):
|
|
178
|
+
e_file = e_obj.get("file") if isinstance(e_obj, dict) else getattr(e_obj, "file", None)
|
|
179
|
+
if e_file:
|
|
180
|
+
context_files.add(str(e_file))
|
|
181
|
+
|
|
182
|
+
# Tests section (treated as peripheral)
|
|
183
|
+
for t_obj in packet_dict.get("tests", []):
|
|
184
|
+
t_file = t_obj.get("file") if isinstance(t_obj, dict) else getattr(t_obj, "file", None)
|
|
185
|
+
if t_file:
|
|
186
|
+
context_files.add(str(t_file))
|
|
187
|
+
if str(t_file) not in core_files:
|
|
188
|
+
peripheral_files.add(str(t_file))
|
|
189
|
+
|
|
190
|
+
# Target info
|
|
191
|
+
target_obj = packet_dict.get("target")
|
|
192
|
+
if isinstance(target_obj, dict):
|
|
193
|
+
t_file = target_obj.get("file")
|
|
194
|
+
if t_file:
|
|
195
|
+
context_files.add(str(t_file))
|
|
196
|
+
core_files.add(str(t_file))
|
|
197
|
+
t_sym = target_obj.get("symbol")
|
|
198
|
+
if t_sym:
|
|
199
|
+
primary_targets.add(str(t_sym))
|
|
200
|
+
|
|
201
|
+
# 4. Check Git state
|
|
202
|
+
git_state = state if state is not None else get_git_state(repository, con=con)
|
|
203
|
+
if not git_state.is_git:
|
|
204
|
+
return ContextFreshnessReport(
|
|
205
|
+
status=ContextFreshnessStatus.UNKNOWN,
|
|
206
|
+
base_commit=base_commit,
|
|
207
|
+
current_head=None,
|
|
208
|
+
repository_dirty=False,
|
|
209
|
+
context_files=sorted(context_files),
|
|
210
|
+
reason="Not a git repository.",
|
|
211
|
+
recommended_action="NONE",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
current_head = git_state.current_head
|
|
215
|
+
state = git_state
|
|
216
|
+
|
|
217
|
+
# 5. Determine which files changed since base_commit (including working tree)
|
|
218
|
+
all_changed_files: set[str] = set()
|
|
219
|
+
|
|
220
|
+
is_at_head = (
|
|
221
|
+
not base_commit
|
|
222
|
+
or base_commit == "HEAD"
|
|
223
|
+
or (current_head and (base_commit == current_head or current_head.startswith(base_commit)))
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if is_at_head:
|
|
227
|
+
# Fast path: context was generated at current HEAD.
|
|
228
|
+
# Only working tree uncommitted changes can invalidate it.
|
|
229
|
+
wt = state.working_tree
|
|
230
|
+
if not wt.is_dirty:
|
|
231
|
+
# Absolute zero changes anywhere in repository!
|
|
232
|
+
return ContextFreshnessReport(
|
|
233
|
+
status=ContextFreshnessStatus.VALID,
|
|
234
|
+
base_commit=base_commit,
|
|
235
|
+
current_head=current_head,
|
|
236
|
+
repository_dirty=False,
|
|
237
|
+
context_files=sorted(context_files),
|
|
238
|
+
changed_relevant_files=[],
|
|
239
|
+
unrelated_changed_files=[],
|
|
240
|
+
invalidated_symbols=[],
|
|
241
|
+
reason="Context is completely fresh. Repository state is unchanged.",
|
|
242
|
+
recommended_action="NONE",
|
|
243
|
+
)
|
|
244
|
+
all_changed_files.update(wt.modified_files)
|
|
245
|
+
all_changed_files.update(wt.staged_files)
|
|
246
|
+
all_changed_files.update(wt.deleted_files)
|
|
247
|
+
all_changed_files.update(wt.untracked_files)
|
|
248
|
+
elif base_commit and _REF_PATTERN.match(base_commit):
|
|
249
|
+
# Diff base_commit against current HEAD using cached immutable diff, then add working tree
|
|
250
|
+
head_ref = current_head or "HEAD"
|
|
251
|
+
commit_diff = _get_cached_commit_diff_files(repository, base_commit, head_ref)
|
|
252
|
+
if commit_diff is not None:
|
|
253
|
+
all_changed_files.update(commit_diff)
|
|
254
|
+
wt = state.working_tree
|
|
255
|
+
all_changed_files.update(wt.modified_files)
|
|
256
|
+
all_changed_files.update(wt.staged_files)
|
|
257
|
+
all_changed_files.update(wt.deleted_files)
|
|
258
|
+
all_changed_files.update(wt.untracked_files)
|
|
259
|
+
else:
|
|
260
|
+
# Fallback direct diff
|
|
261
|
+
diff_raw = _run_git(repository, "diff", "--name-only", base_commit)
|
|
262
|
+
if diff_raw is not None:
|
|
263
|
+
for line in diff_raw.splitlines():
|
|
264
|
+
if line.strip():
|
|
265
|
+
all_changed_files.add(line.strip())
|
|
266
|
+
all_changed_files.update(state.working_tree.untracked_files)
|
|
267
|
+
else:
|
|
268
|
+
return ContextFreshnessReport(
|
|
269
|
+
status=ContextFreshnessStatus.UNKNOWN,
|
|
270
|
+
base_commit=base_commit,
|
|
271
|
+
current_head=current_head,
|
|
272
|
+
repository_dirty=state.working_tree.is_dirty,
|
|
273
|
+
context_files=sorted(context_files),
|
|
274
|
+
reason=f"Base commit {base_commit} not reachable in git repository.",
|
|
275
|
+
recommended_action="RECOMPILE_CONTEXT",
|
|
276
|
+
)
|
|
277
|
+
else:
|
|
278
|
+
wt = state.working_tree
|
|
279
|
+
all_changed_files.update(wt.modified_files)
|
|
280
|
+
all_changed_files.update(wt.staged_files)
|
|
281
|
+
all_changed_files.update(wt.deleted_files)
|
|
282
|
+
all_changed_files.update(wt.untracked_files)
|
|
283
|
+
|
|
284
|
+
# 6. Intersect changed files with context files
|
|
285
|
+
changed_relevant = sorted(all_changed_files.intersection(context_files))
|
|
286
|
+
unrelated_changed = sorted(all_changed_files - context_files)
|
|
287
|
+
|
|
288
|
+
# 7. Evaluate Freshness
|
|
289
|
+
if not changed_relevant:
|
|
290
|
+
# None of the files in context were modified!
|
|
291
|
+
if unrelated_changed:
|
|
292
|
+
reason = f"Context remains VALID. {len(unrelated_changed)} unrelated file(s) changed in repository, but none belong to this context packet."
|
|
293
|
+
else:
|
|
294
|
+
reason = "Context is completely fresh. Repository state is unchanged."
|
|
295
|
+
return ContextFreshnessReport(
|
|
296
|
+
status=ContextFreshnessStatus.VALID,
|
|
297
|
+
base_commit=base_commit,
|
|
298
|
+
current_head=current_head,
|
|
299
|
+
repository_dirty=state.working_tree.is_dirty,
|
|
300
|
+
context_files=sorted(context_files),
|
|
301
|
+
changed_relevant_files=[],
|
|
302
|
+
unrelated_changed_files=unrelated_changed,
|
|
303
|
+
invalidated_symbols=[],
|
|
304
|
+
reason=reason,
|
|
305
|
+
recommended_action="NONE",
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Some relevant files did change! Check if they are core or peripheral
|
|
309
|
+
invalidated_symbols: list[str] = []
|
|
310
|
+
core_hit = False
|
|
311
|
+
|
|
312
|
+
for crf in changed_relevant:
|
|
313
|
+
if crf in core_files or crf not in peripheral_files:
|
|
314
|
+
core_hit = True
|
|
315
|
+
|
|
316
|
+
# Find which symbols were in this file
|
|
317
|
+
for s_obj in packet_dict.get("symbols", []):
|
|
318
|
+
s_file = s_obj.get("file") if isinstance(s_obj, dict) else getattr(s_obj, "file", None)
|
|
319
|
+
s_name = s_obj.get("symbol") if isinstance(s_obj, dict) else getattr(s_obj, "symbol", None)
|
|
320
|
+
if s_file == crf and s_name:
|
|
321
|
+
invalidated_symbols.append(str(s_name))
|
|
322
|
+
|
|
323
|
+
# Check if primary targets were hit
|
|
324
|
+
primary_hit = any(t in invalidated_symbols for t in primary_targets)
|
|
325
|
+
|
|
326
|
+
if core_hit or primary_hit:
|
|
327
|
+
return ContextFreshnessReport(
|
|
328
|
+
status=ContextFreshnessStatus.STALE,
|
|
329
|
+
base_commit=base_commit,
|
|
330
|
+
current_head=current_head,
|
|
331
|
+
repository_dirty=state.working_tree.is_dirty,
|
|
332
|
+
context_files=sorted(context_files),
|
|
333
|
+
changed_relevant_files=changed_relevant,
|
|
334
|
+
unrelated_changed_files=unrelated_changed,
|
|
335
|
+
invalidated_symbols=sorted(set(invalidated_symbols)),
|
|
336
|
+
reason=f"Context is STALE. Core file(s) ({', '.join(changed_relevant[:3])}) were modified or deleted.",
|
|
337
|
+
recommended_action="RECOMPILE_CONTEXT",
|
|
338
|
+
)
|
|
339
|
+
else:
|
|
340
|
+
return ContextFreshnessReport(
|
|
341
|
+
status=ContextFreshnessStatus.PARTIALLY_STALE,
|
|
342
|
+
base_commit=base_commit,
|
|
343
|
+
current_head=current_head,
|
|
344
|
+
repository_dirty=state.working_tree.is_dirty,
|
|
345
|
+
context_files=sorted(context_files),
|
|
346
|
+
changed_relevant_files=changed_relevant,
|
|
347
|
+
unrelated_changed_files=unrelated_changed,
|
|
348
|
+
invalidated_symbols=sorted(set(invalidated_symbols)),
|
|
349
|
+
reason=f"Context is PARTIALLY_STALE. Secondary file(s) ({', '.join(changed_relevant[:3])}) were modified, but primary targets remain valid.",
|
|
350
|
+
recommended_action="REFRESH_PERIPHERAL",
|
|
351
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Database Deep Intelligence package for CodeGraph."""
|
|
2
|
+
from .extractor import (
|
|
3
|
+
DatabaseExtractionResult,
|
|
4
|
+
detect_file_dialects,
|
|
5
|
+
extract_database_from_file,
|
|
6
|
+
has_potential_database_activity,
|
|
7
|
+
has_potential_orm_models,
|
|
8
|
+
parse_sql_statement,
|
|
9
|
+
)
|
|
10
|
+
from .interrogation import (
|
|
11
|
+
find_db_callers,
|
|
12
|
+
find_db_columns,
|
|
13
|
+
find_db_models,
|
|
14
|
+
find_db_queries,
|
|
15
|
+
find_db_readers,
|
|
16
|
+
find_db_relationships,
|
|
17
|
+
find_db_tables,
|
|
18
|
+
find_db_writers,
|
|
19
|
+
get_db_impact,
|
|
20
|
+
get_db_schema,
|
|
21
|
+
get_db_table,
|
|
22
|
+
get_route_db_lineage,
|
|
23
|
+
)
|
|
24
|
+
from .models import (
|
|
25
|
+
DatabaseEntity,
|
|
26
|
+
DatabaseEntityKind,
|
|
27
|
+
DatabaseQueryFact,
|
|
28
|
+
MigrationFact,
|
|
29
|
+
build_db_canonical_id,
|
|
30
|
+
normalize_dialect,
|
|
31
|
+
normalize_schema_name,
|
|
32
|
+
)
|
|
33
|
+
from .schema_drift import (
|
|
34
|
+
DriftSeverity,
|
|
35
|
+
DriftType,
|
|
36
|
+
SchemaDriftIssue,
|
|
37
|
+
SchemaFieldSpec,
|
|
38
|
+
detect_schema_drift,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"DatabaseEntity",
|
|
43
|
+
"DatabaseEntityKind",
|
|
44
|
+
"DatabaseExtractionResult",
|
|
45
|
+
"DatabaseQueryFact",
|
|
46
|
+
"DriftSeverity",
|
|
47
|
+
"DriftType",
|
|
48
|
+
"MigrationFact",
|
|
49
|
+
"SchemaDriftIssue",
|
|
50
|
+
"SchemaFieldSpec",
|
|
51
|
+
"build_db_canonical_id",
|
|
52
|
+
"detect_file_dialects",
|
|
53
|
+
"detect_schema_drift",
|
|
54
|
+
"extract_database_from_file",
|
|
55
|
+
"find_db_callers",
|
|
56
|
+
"find_db_columns",
|
|
57
|
+
"find_db_models",
|
|
58
|
+
"find_db_queries",
|
|
59
|
+
"find_db_readers",
|
|
60
|
+
"find_db_relationships",
|
|
61
|
+
"find_db_tables",
|
|
62
|
+
"find_db_writers",
|
|
63
|
+
"get_db_impact",
|
|
64
|
+
"get_db_schema",
|
|
65
|
+
"get_db_table",
|
|
66
|
+
"get_route_db_lineage",
|
|
67
|
+
"has_potential_database_activity",
|
|
68
|
+
"has_potential_orm_models",
|
|
69
|
+
"normalize_dialect",
|
|
70
|
+
"normalize_schema_name",
|
|
71
|
+
"parse_sql_statement",
|
|
72
|
+
]
|