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,486 @@
|
|
|
1
|
+
"""Authoritative Evidence Contract for CodeGraph MCP (Phase 12).
|
|
2
|
+
|
|
3
|
+
Enforces a strict, explicit allowlist of relationship types, evidence classes,
|
|
4
|
+
and (relationship, evidence_class) pairs across every relationship-producing,
|
|
5
|
+
traversing, optimizing, and serializing path in CodeGraph.
|
|
6
|
+
|
|
7
|
+
Invariants:
|
|
8
|
+
- Unknown relationship types fail closed.
|
|
9
|
+
- Unknown evidence classes fail closed.
|
|
10
|
+
- Disallowed (relationship, evidence_class) combinations fail closed.
|
|
11
|
+
- Contradictory epistemic records (e.g. CALLS + POSSIBLE, CALLS + UNKNOWN,
|
|
12
|
+
CALLS + FRAMEWORK_VERIFIED, status=FACT with POSSIBLE/UNKNOWN) fail closed.
|
|
13
|
+
- Raw dictionaries must be normalized and validated into `RelationshipRecord`.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
DEFAULT_PARSER_VERSION = "3.0"
|
|
21
|
+
PARSER_VERSION = DEFAULT_PARSER_VERSION
|
|
22
|
+
|
|
23
|
+
ALLOWED_EVIDENCE_CLASSES: frozenset[str] = frozenset({
|
|
24
|
+
"AST_VERIFIED",
|
|
25
|
+
"STATIC_VERIFIED",
|
|
26
|
+
"FRAMEWORK_VERIFIED",
|
|
27
|
+
"ROJO_VERIFIED",
|
|
28
|
+
"DATAFLOW_VERIFIED",
|
|
29
|
+
"RUNTIME_OBSERVED",
|
|
30
|
+
"RUNTIME_UNOBSERVED",
|
|
31
|
+
"POSSIBLE",
|
|
32
|
+
"UNKNOWN",
|
|
33
|
+
"AMBIGUOUS",
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
ALLOWED_CONFIDENCE_LEVELS: frozenset[str] = frozenset({
|
|
37
|
+
"HIGH",
|
|
38
|
+
"MEDIUM",
|
|
39
|
+
"LOW",
|
|
40
|
+
"UNKNOWN",
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
ALLOWED_EPISTEMIC_STATUSES: frozenset[str] = frozenset({
|
|
44
|
+
"FACT",
|
|
45
|
+
"ASSUMPTION",
|
|
46
|
+
"INFERENCE",
|
|
47
|
+
"UNKNOWN",
|
|
48
|
+
"CONFLICT",
|
|
49
|
+
"POSSIBLE",
|
|
50
|
+
"AMBIGUOUS",
|
|
51
|
+
"RUNTIME_OBSERVED",
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
# Explicit ALLOWLIST mapping every canonical relationship type to its valid evidence classes.
|
|
55
|
+
ALLOWED_RELATIONSHIP_EVIDENCE_MATRIX: dict[str, frozenset[str]] = {
|
|
56
|
+
# Direct executable call edges — NEVER FRAMEWORK_VERIFIED, POSSIBLE, or UNKNOWN
|
|
57
|
+
"CALLS": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED"}),
|
|
58
|
+
"CALLED_BY": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED"}),
|
|
59
|
+
# Uncertain / candidate call edges — NEVER verified
|
|
60
|
+
"POSSIBLE_CALLS": frozenset({"POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
61
|
+
# Framework routing & composition
|
|
62
|
+
"MOUNTS": frozenset({"FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
63
|
+
"ROUTE_HANDLER": frozenset({"FRAMEWORK_VERIFIED", "RUNTIME_OBSERVED", "POSSIBLE", "UNKNOWN"}),
|
|
64
|
+
"HANDLED_BY": frozenset({"FRAMEWORK_VERIFIED", "RUNTIME_OBSERVED", "POSSIBLE", "UNKNOWN"}),
|
|
65
|
+
"ROUTES_TO": frozenset({"FRAMEWORK_VERIFIED", "RUNTIME_OBSERVED", "POSSIBLE", "UNKNOWN"}),
|
|
66
|
+
# Dependency Injection & Configuration
|
|
67
|
+
"INJECTS": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
68
|
+
"PROVIDES": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
69
|
+
"RESOLVES_DEPENDENCY": frozenset({"DATAFLOW_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
70
|
+
"CONFIGURES": frozenset({"DATAFLOW_VERIFIED", "FRAMEWORK_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
71
|
+
"DI_CYCLE": frozenset({"DATAFLOW_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
72
|
+
# Registry, Dispatch, Events, Tasks, Commands
|
|
73
|
+
"REGISTERS": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
74
|
+
"REGISTERED_HANDLER": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
75
|
+
"DISPATCHES_TO": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "POSSIBLE", "UNKNOWN"}),
|
|
76
|
+
"EVENT_LISTENER": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
77
|
+
"TASK_HANDLER": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
78
|
+
"COMMAND_HANDLER": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
79
|
+
# Monorepo / Package boundaries
|
|
80
|
+
"DEPENDS_ON_PACKAGE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
81
|
+
"PACKAGE_IMPORTS": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
82
|
+
"CROSS_PACKAGE_IMPORT": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
83
|
+
"CONTAINS_PACKAGE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED"}),
|
|
84
|
+
# Test intelligence
|
|
85
|
+
"TESTS": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
86
|
+
"TESTS_SYMBOL": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
87
|
+
"TESTS_ROUTE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
88
|
+
"TESTS_PROVIDER": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
89
|
+
"TESTS_EVENT_HANDLER": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
90
|
+
# Structural & AST relationships
|
|
91
|
+
"IMPORTS": frozenset({"AST_VERIFIED", "ROJO_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
92
|
+
"EXPORTS": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED"}),
|
|
93
|
+
"REEXPORTS": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED"}),
|
|
94
|
+
"EXTENDS": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
95
|
+
"IMPLEMENTS": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
96
|
+
"DEFINES": frozenset({"AST_VERIFIED", "STATIC_VERIFIED"}),
|
|
97
|
+
"CONTAINS": frozenset({"AST_VERIFIED", "STATIC_VERIFIED"}),
|
|
98
|
+
"RESOLVES_TO": frozenset({"DATAFLOW_VERIFIED", "ROJO_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
99
|
+
"BINDS_TO": frozenset({"DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
100
|
+
"ALIASED_TO": frozenset({"DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
101
|
+
"REFERENCES": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
102
|
+
"USES": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
103
|
+
"DEPENDS_ON": frozenset({"AST_VERIFIED", "ROJO_VERIFIED", "DATAFLOW_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
104
|
+
"UNRESOLVED_REFERENCE": frozenset({"UNKNOWN", "POSSIBLE", "AMBIGUOUS"}),
|
|
105
|
+
# Database & Persistence Intelligence (Phase 6)
|
|
106
|
+
"MAPS_TO_TABLE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
107
|
+
"MAPS_TO_COLUMN": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
108
|
+
"READS_TABLE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "RUNTIME_UNOBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
109
|
+
"WRITES_TABLE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "RUNTIME_UNOBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
110
|
+
"READS_COLUMN": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "RUNTIME_UNOBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
111
|
+
"WRITES_COLUMN": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "RUNTIME_UNOBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
112
|
+
"REFERENCES_TABLE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
113
|
+
"REFERENCES_COLUMN": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
114
|
+
"FOREIGN_KEY_TO": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
115
|
+
"HAS_PRIMARY_KEY": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED"}),
|
|
116
|
+
"HAS_INDEX": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED"}),
|
|
117
|
+
"HAS_UNIQUE_CONSTRAINT": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED"}),
|
|
118
|
+
"HAS_CHECK_CONSTRAINT": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED"}),
|
|
119
|
+
"MIGRATES_TABLE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
120
|
+
"QUERIES_DATABASE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
121
|
+
"ORM_RELATION": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
122
|
+
"POSSIBLE_TABLE": frozenset({"POSSIBLE", "AMBIGUOUS", "UNKNOWN"}),
|
|
123
|
+
"UNKNOWN_TABLE": frozenset({"UNKNOWN", "AMBIGUOUS"}),
|
|
124
|
+
# Environment variable dependency tracking (Phase 8 Security)
|
|
125
|
+
"READS_ENV": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "DATAFLOW_VERIFIED"}),
|
|
126
|
+
# Frontend, UI & Web Architecture (Pillar 4)
|
|
127
|
+
"RENDERS": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
128
|
+
"USES_HOOK": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
129
|
+
"IMPORTS_STYLE": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
130
|
+
"USES_STYLE_CLASS": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
131
|
+
"FETCHES_ROUTE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
132
|
+
"LOADS_SCRIPT": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
133
|
+
"LOADS_STYLESHEET": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
134
|
+
# NoSQL Document Database & Collections (Mongoose / MongoDB)
|
|
135
|
+
"MAPS_TO_COLLECTION": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
136
|
+
"WRITES_COLLECTION": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "RUNTIME_UNOBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
137
|
+
"READS_COLLECTION": frozenset({"AST_VERIFIED", "STATIC_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "RUNTIME_OBSERVED", "RUNTIME_UNOBSERVED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
138
|
+
# Python Full-Stack (Celery, Django Signals, Templates)
|
|
139
|
+
"DISPATCHES_TASK": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
140
|
+
"TRIGGERS_SIGNAL": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
141
|
+
"HANDLES_SIGNAL": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
142
|
+
"RENDERS_TEMPLATE": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
143
|
+
# Python AI/ML Stack (PyTorch, LangChain, Ray, LlamaIndex)
|
|
144
|
+
"DISPATCHES_FORWARD": frozenset({"AST_VERIFIED", "DATAFLOW_VERIFIED", "FRAMEWORK_VERIFIED", "POSSIBLE", "UNKNOWN", "AMBIGUOUS"}),
|
|
145
|
+
"TOOL_HANDLER": frozenset({"FRAMEWORK_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
146
|
+
"PIPELINE_STEP": frozenset({"FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "AST_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
147
|
+
# Roblox / Luau Intelligence (Phases R1-R8)
|
|
148
|
+
"REQUIRES_MODULE": frozenset({"AST_VERIFIED", "ROJO_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
149
|
+
"CLIENT_DISPATCHES_REMOTE": frozenset({"FRAMEWORK_VERIFIED", "ROJO_VERIFIED", "AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
150
|
+
"SERVER_HANDLES_REMOTE": frozenset({"FRAMEWORK_VERIFIED", "ROJO_VERIFIED", "AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
151
|
+
"GETS_SERVICE": frozenset({"FRAMEWORK_VERIFIED", "ROJO_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
152
|
+
"PROVIDES_SERVICE": frozenset({"FRAMEWORK_VERIFIED", "ROJO_VERIFIED", "AST_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
153
|
+
"READS_PERSISTENCE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
154
|
+
"WRITES_PERSISTENCE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
155
|
+
"CONFIGURES_PERSISTENCE": frozenset({"AST_VERIFIED", "FRAMEWORK_VERIFIED", "DATAFLOW_VERIFIED", "POSSIBLE", "UNKNOWN"}),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def validate_relationship_type(relationship: str) -> str:
|
|
160
|
+
"""Validate that `relationship` is in the explicit allowlist. Fails closed."""
|
|
161
|
+
if not isinstance(relationship, str) or not relationship.strip():
|
|
162
|
+
raise ValueError("Invalid relationship type: must be a non-empty string.")
|
|
163
|
+
norm = relationship.strip()
|
|
164
|
+
if norm not in ALLOWED_RELATIONSHIP_EVIDENCE_MATRIX:
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Unknown or disallowed relationship type '{relationship}'. "
|
|
167
|
+
f"Must be one of the canonical allowlist types."
|
|
168
|
+
)
|
|
169
|
+
return norm
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def validate_evidence_class(evidence_class: str) -> str:
|
|
173
|
+
"""Validate that `evidence_class` is in the explicit allowlist. Fails closed."""
|
|
174
|
+
if not isinstance(evidence_class, str) or not evidence_class.strip():
|
|
175
|
+
raise ValueError("Invalid evidence_class: must be a non-empty string.")
|
|
176
|
+
norm = evidence_class.strip()
|
|
177
|
+
if norm not in ALLOWED_EVIDENCE_CLASSES:
|
|
178
|
+
raise ValueError(
|
|
179
|
+
f"Unknown or disallowed evidence_class '{evidence_class}'. "
|
|
180
|
+
f"Allowed: {sorted(ALLOWED_EVIDENCE_CLASSES)}"
|
|
181
|
+
)
|
|
182
|
+
return norm
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def validate_relationship_evidence_pair(
|
|
186
|
+
relationship: str,
|
|
187
|
+
evidence_class: str,
|
|
188
|
+
) -> tuple[str, str]:
|
|
189
|
+
"""Validate that `(relationship, evidence_class)` is explicitly allowed. Fails closed."""
|
|
190
|
+
if not isinstance(relationship, str) or not relationship.strip():
|
|
191
|
+
raise ValueError("Invalid relationship type: must be a non-empty string.")
|
|
192
|
+
raw_rel = relationship.strip()
|
|
193
|
+
ev = validate_evidence_class(evidence_class)
|
|
194
|
+
if ev == "UNKNOWN" and raw_rel in ("CALLS", "CALLED_BY"):
|
|
195
|
+
raise ValueError(
|
|
196
|
+
"Epistemic invariant violation: UNKNOWN relationships cannot be exposed as authoritative 'CALLS'."
|
|
197
|
+
)
|
|
198
|
+
if ev == "POSSIBLE" and raw_rel in ("CALLS", "CALLED_BY"):
|
|
199
|
+
raise ValueError(
|
|
200
|
+
"Epistemic invariant violation: POSSIBLE relationships cannot be labeled as 'CALLS'; use 'POSSIBLE_CALLS'."
|
|
201
|
+
)
|
|
202
|
+
if ev == "AST_VERIFIED" and raw_rel.startswith("POSSIBLE"):
|
|
203
|
+
raise ValueError(
|
|
204
|
+
f"Epistemic invariant violation: POSSIBLE relationship '{raw_rel}' cannot be marked as AST_VERIFIED."
|
|
205
|
+
)
|
|
206
|
+
rel = validate_relationship_type(raw_rel)
|
|
207
|
+
allowed_for_rel = ALLOWED_RELATIONSHIP_EVIDENCE_MATRIX[rel]
|
|
208
|
+
if ev not in allowed_for_rel:
|
|
209
|
+
raise ValueError(
|
|
210
|
+
f"Disallowed (relationship, evidence_class) pair: ('{rel}', '{ev}'). "
|
|
211
|
+
f"Allowed evidence classes for '{rel}': {sorted(allowed_for_rel)}"
|
|
212
|
+
)
|
|
213
|
+
return rel, ev
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@dataclass(frozen=True)
|
|
217
|
+
class RelationshipRecord:
|
|
218
|
+
"""Canonical validated relationship record preventing raw-dict contract bypass."""
|
|
219
|
+
|
|
220
|
+
source: str
|
|
221
|
+
target: str
|
|
222
|
+
relationship: str
|
|
223
|
+
evidence_class: str = "AST_VERIFIED"
|
|
224
|
+
confidence: str = "HIGH"
|
|
225
|
+
file: str = ""
|
|
226
|
+
start_line: int = 1
|
|
227
|
+
end_line: int = 1
|
|
228
|
+
target_file: str | None = None
|
|
229
|
+
target_line: int | None = None
|
|
230
|
+
evidence: str = ""
|
|
231
|
+
reason: str | None = None
|
|
232
|
+
status: str = "FACT"
|
|
233
|
+
parser_version: str = PARSER_VERSION
|
|
234
|
+
index_generation: int = 0
|
|
235
|
+
freshness: str = "FRESH"
|
|
236
|
+
supporting_locations: tuple[str, ...] = field(default_factory=tuple)
|
|
237
|
+
occurrence_count: int = 1
|
|
238
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
239
|
+
|
|
240
|
+
def __post_init__(self) -> None:
|
|
241
|
+
if not isinstance(self.source, str) or not self.source.strip():
|
|
242
|
+
raise ValueError("RelationshipRecord.source must be a non-empty string.")
|
|
243
|
+
if not isinstance(self.target, str) or not self.target.strip():
|
|
244
|
+
raise ValueError("RelationshipRecord.target must be a non-empty string.")
|
|
245
|
+
|
|
246
|
+
rel, ev = validate_relationship_evidence_pair(self.relationship, self.evidence_class)
|
|
247
|
+
object.__setattr__(self, "relationship", rel)
|
|
248
|
+
object.__setattr__(self, "evidence_class", ev)
|
|
249
|
+
|
|
250
|
+
conf = str(self.confidence or "HIGH").strip().upper()
|
|
251
|
+
if conf not in ALLOWED_CONFIDENCE_LEVELS:
|
|
252
|
+
raise ValueError(f"Invalid confidence level '{self.confidence}'.")
|
|
253
|
+
object.__setattr__(self, "confidence", conf)
|
|
254
|
+
|
|
255
|
+
st = str(self.status or "FACT").strip().upper()
|
|
256
|
+
if st not in ALLOWED_EPISTEMIC_STATUSES:
|
|
257
|
+
raise ValueError(f"Invalid epistemic status '{self.status}'.")
|
|
258
|
+
object.__setattr__(self, "status", st)
|
|
259
|
+
|
|
260
|
+
# Reject contradictory epistemic combinations
|
|
261
|
+
if ev == "UNKNOWN":
|
|
262
|
+
if conf == "HIGH":
|
|
263
|
+
raise ValueError(
|
|
264
|
+
"Contradictory RelationshipRecord: evidence_class='UNKNOWN' contradicts confidence='HIGH'."
|
|
265
|
+
)
|
|
266
|
+
if st == "FACT":
|
|
267
|
+
raise ValueError(
|
|
268
|
+
"Contradictory RelationshipRecord: evidence_class='UNKNOWN' contradicts status='FACT'."
|
|
269
|
+
)
|
|
270
|
+
if ev == "POSSIBLE":
|
|
271
|
+
if st == "FACT":
|
|
272
|
+
raise ValueError(
|
|
273
|
+
"Contradictory RelationshipRecord: evidence_class='POSSIBLE' contradicts status='FACT'."
|
|
274
|
+
)
|
|
275
|
+
if conf == "HIGH":
|
|
276
|
+
raise ValueError(
|
|
277
|
+
"Contradictory RelationshipRecord: evidence_class='POSSIBLE' contradicts confidence='HIGH'."
|
|
278
|
+
)
|
|
279
|
+
if rel == "POSSIBLE_CALLS" and conf == "HIGH":
|
|
280
|
+
raise ValueError(
|
|
281
|
+
"Contradictory RelationshipRecord: relationship='POSSIBLE_CALLS' contradicts confidence='HIGH'."
|
|
282
|
+
)
|
|
283
|
+
if rel in ("CALLS", "CALLED_BY") and ev in ("AST_VERIFIED", "DATAFLOW_VERIFIED") and conf in ("LOW", "UNKNOWN"):
|
|
284
|
+
raise ValueError(
|
|
285
|
+
f"Contradictory RelationshipRecord: verified '{rel}' with evidence_class='{ev}' contradicts confidence='{conf}'."
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def as_dict(self) -> dict[str, Any]:
|
|
289
|
+
out: dict[str, Any] = {
|
|
290
|
+
"source": self.source,
|
|
291
|
+
"target": self.target,
|
|
292
|
+
"relationship": self.relationship,
|
|
293
|
+
"evidence_class": self.evidence_class,
|
|
294
|
+
"confidence": self.confidence,
|
|
295
|
+
"file": self.file,
|
|
296
|
+
"start_line": self.start_line,
|
|
297
|
+
"end_line": self.end_line,
|
|
298
|
+
"evidence": self.evidence,
|
|
299
|
+
"reason": self.reason,
|
|
300
|
+
"status": self.status,
|
|
301
|
+
"parser_version": self.parser_version,
|
|
302
|
+
"index_generation": self.index_generation,
|
|
303
|
+
"freshness": self.freshness,
|
|
304
|
+
"supporting_locations": list(self.supporting_locations),
|
|
305
|
+
"occurrence_count": self.occurrence_count,
|
|
306
|
+
}
|
|
307
|
+
if self.target_file is not None:
|
|
308
|
+
out["target_file"] = self.target_file
|
|
309
|
+
if self.target_line is not None:
|
|
310
|
+
out["target_line"] = self.target_line
|
|
311
|
+
if self.metadata:
|
|
312
|
+
out["metadata"] = dict(self.metadata)
|
|
313
|
+
return out
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def validate_graph_edge_contract(
|
|
317
|
+
source: str,
|
|
318
|
+
target: str,
|
|
319
|
+
relationship: str,
|
|
320
|
+
evidence_class: str,
|
|
321
|
+
confidence: str,
|
|
322
|
+
) -> tuple[str, str, str]:
|
|
323
|
+
"""Fast fail-closed validator for GraphEdge fields enforcing identical epistemic invariants."""
|
|
324
|
+
if not isinstance(source, str) or not source.strip():
|
|
325
|
+
raise ValueError("RelationshipRecord.source must be a non-empty string.")
|
|
326
|
+
if not isinstance(target, str) or not target.strip():
|
|
327
|
+
raise ValueError("RelationshipRecord.target must be a non-empty string.")
|
|
328
|
+
|
|
329
|
+
raw_ev_cls = evidence_class
|
|
330
|
+
raw_conf = confidence
|
|
331
|
+
if raw_ev_cls is None or str(raw_ev_cls).strip() == "":
|
|
332
|
+
from codegraph.epistemic import classify_relationship_evidence
|
|
333
|
+
|
|
334
|
+
inferred_ev, _ = classify_relationship_evidence(
|
|
335
|
+
str(relationship or "").strip(), str(raw_conf or "HIGH").strip().upper()
|
|
336
|
+
)
|
|
337
|
+
ev_cls = inferred_ev.value
|
|
338
|
+
else:
|
|
339
|
+
ev_cls = str(raw_ev_cls).strip()
|
|
340
|
+
|
|
341
|
+
rel, ev = validate_relationship_evidence_pair(relationship, ev_cls)
|
|
342
|
+
|
|
343
|
+
if raw_conf is None or str(raw_conf).strip() == "":
|
|
344
|
+
conf = "UNKNOWN" if ev == "UNKNOWN" else ("LOW" if ev == "POSSIBLE" else "HIGH")
|
|
345
|
+
else:
|
|
346
|
+
conf = str(raw_conf).strip().upper()
|
|
347
|
+
|
|
348
|
+
if conf not in ALLOWED_CONFIDENCE_LEVELS:
|
|
349
|
+
raise ValueError(f"Invalid confidence level '{confidence}'.")
|
|
350
|
+
|
|
351
|
+
if ev == "UNKNOWN" and conf == "HIGH":
|
|
352
|
+
raise ValueError(
|
|
353
|
+
"Contradictory RelationshipRecord: evidence_class='UNKNOWN' contradicts confidence='HIGH'."
|
|
354
|
+
)
|
|
355
|
+
if ev == "POSSIBLE" and conf == "HIGH":
|
|
356
|
+
raise ValueError(
|
|
357
|
+
"Contradictory RelationshipRecord: evidence_class='POSSIBLE' contradicts confidence='HIGH'."
|
|
358
|
+
)
|
|
359
|
+
if rel == "POSSIBLE_CALLS" and conf == "HIGH":
|
|
360
|
+
raise ValueError(
|
|
361
|
+
"Contradictory RelationshipRecord: relationship='POSSIBLE_CALLS' contradicts confidence='HIGH'."
|
|
362
|
+
)
|
|
363
|
+
if rel in ("CALLS", "CALLED_BY") and ev in ("AST_VERIFIED", "DATAFLOW_VERIFIED") and conf in ("LOW", "UNKNOWN"):
|
|
364
|
+
raise ValueError(
|
|
365
|
+
f"Contradictory RelationshipRecord: verified '{rel}' with evidence_class='{ev}' contradicts confidence='{conf}'."
|
|
366
|
+
)
|
|
367
|
+
return rel, ev, conf
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def validate_relationship_record(
|
|
371
|
+
record: RelationshipRecord | dict[str, Any] | Any,
|
|
372
|
+
) -> RelationshipRecord:
|
|
373
|
+
"""Validate any relationship object or dictionary and return a canonical `RelationshipRecord`.
|
|
374
|
+
|
|
375
|
+
Fails closed (`ValueError`) if required fields are missing or if the
|
|
376
|
+
`(relationship, evidence_class, confidence, status)` combination is invalid or contradictory.
|
|
377
|
+
"""
|
|
378
|
+
if isinstance(record, RelationshipRecord):
|
|
379
|
+
validate_relationship_evidence_pair(record.relationship, record.evidence_class)
|
|
380
|
+
return record
|
|
381
|
+
|
|
382
|
+
if isinstance(record, dict):
|
|
383
|
+
src = str(record.get("source") or record.get("source_symbol_id") or record.get("symbol") or "").strip()
|
|
384
|
+
tgt = str(record.get("target") or record.get("target_symbol_id") or record.get("callee") or "").strip()
|
|
385
|
+
rel = str(record.get("relationship") or "").strip()
|
|
386
|
+
if not src or not tgt or not rel:
|
|
387
|
+
raise ValueError(
|
|
388
|
+
f"Invalid relationship dictionary: 'source', 'target', and 'relationship' are required (got {record!r})."
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
raw_ev_cls = record.get("evidence_class")
|
|
392
|
+
raw_conf = record.get("confidence")
|
|
393
|
+
if raw_ev_cls is None or str(raw_ev_cls).strip() == "":
|
|
394
|
+
# Derive default evidence class only when not explicitly supplied, then validate strictly
|
|
395
|
+
from codegraph.epistemic import classify_relationship_evidence
|
|
396
|
+
|
|
397
|
+
inferred_ev, _ = classify_relationship_evidence(rel, str(raw_conf or "HIGH").strip().upper())
|
|
398
|
+
ev_cls = inferred_ev.value
|
|
399
|
+
else:
|
|
400
|
+
ev_cls = str(raw_ev_cls).strip()
|
|
401
|
+
|
|
402
|
+
if raw_conf is None or str(raw_conf).strip() == "":
|
|
403
|
+
conf = "UNKNOWN" if ev_cls == "UNKNOWN" else ("LOW" if ev_cls == "POSSIBLE" else "HIGH")
|
|
404
|
+
else:
|
|
405
|
+
conf = str(raw_conf).strip().upper()
|
|
406
|
+
|
|
407
|
+
raw_status = record.get("status")
|
|
408
|
+
if raw_status is None or str(raw_status).strip() == "":
|
|
409
|
+
if ev_cls == "UNKNOWN":
|
|
410
|
+
status = "UNKNOWN"
|
|
411
|
+
elif ev_cls == "POSSIBLE":
|
|
412
|
+
status = "INFERENCE"
|
|
413
|
+
else:
|
|
414
|
+
status = "FACT"
|
|
415
|
+
else:
|
|
416
|
+
status = str(raw_status).strip().upper()
|
|
417
|
+
|
|
418
|
+
f_path = str(record.get("file") or record.get("path") or "")
|
|
419
|
+
s_line = int(record.get("start_line") or record.get("line") or 1)
|
|
420
|
+
e_line = int(record.get("end_line") or s_line)
|
|
421
|
+
supp = record.get("supporting_locations") or ()
|
|
422
|
+
occ = int(record.get("occurrence_count") or 1)
|
|
423
|
+
|
|
424
|
+
return RelationshipRecord(
|
|
425
|
+
source=src,
|
|
426
|
+
target=tgt,
|
|
427
|
+
relationship=rel,
|
|
428
|
+
evidence_class=ev_cls,
|
|
429
|
+
confidence=conf,
|
|
430
|
+
file=f_path,
|
|
431
|
+
start_line=s_line,
|
|
432
|
+
end_line=e_line,
|
|
433
|
+
target_file=record.get("target_file"),
|
|
434
|
+
target_line=record.get("target_line"),
|
|
435
|
+
evidence=str(record.get("evidence") or ""),
|
|
436
|
+
reason=record.get("reason"),
|
|
437
|
+
status=status,
|
|
438
|
+
parser_version=str(record.get("parser_version") or PARSER_VERSION),
|
|
439
|
+
index_generation=int(record.get("index_generation") or 0),
|
|
440
|
+
freshness=str(record.get("freshness") or "FRESH"),
|
|
441
|
+
supporting_locations=tuple(str(x) for x in supp),
|
|
442
|
+
occurrence_count=occ,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
# Object with attributes (e.g., GraphEdge, Relationship Pydantic model)
|
|
446
|
+
src = str(getattr(record, "source", "") or "").strip()
|
|
447
|
+
tgt = str(getattr(record, "target", "") or "").strip()
|
|
448
|
+
rel = str(getattr(record, "relationship", "") or "").strip()
|
|
449
|
+
raw_ev_cls = getattr(record, "evidence_class", None)
|
|
450
|
+
raw_conf = getattr(record, "confidence", None)
|
|
451
|
+
if raw_ev_cls is None or str(raw_ev_cls).strip() == "":
|
|
452
|
+
from codegraph.epistemic import classify_relationship_evidence
|
|
453
|
+
|
|
454
|
+
inferred_ev, _ = classify_relationship_evidence(rel, str(raw_conf or "HIGH").strip().upper())
|
|
455
|
+
ev_cls = inferred_ev.value
|
|
456
|
+
else:
|
|
457
|
+
ev_cls = str(raw_ev_cls).strip()
|
|
458
|
+
|
|
459
|
+
if raw_conf is None or str(raw_conf).strip() == "":
|
|
460
|
+
conf = "UNKNOWN" if ev_cls == "UNKNOWN" else ("LOW" if ev_cls == "POSSIBLE" else "HIGH")
|
|
461
|
+
else:
|
|
462
|
+
conf = str(raw_conf).strip().upper()
|
|
463
|
+
|
|
464
|
+
raw_status = getattr(record, "status", None)
|
|
465
|
+
if raw_status is None or str(raw_status).strip() == "":
|
|
466
|
+
status = "UNKNOWN" if ev_cls == "UNKNOWN" else ("INFERENCE" if ev_cls == "POSSIBLE" else "FACT")
|
|
467
|
+
else:
|
|
468
|
+
status = str(raw_status).strip().upper()
|
|
469
|
+
|
|
470
|
+
return RelationshipRecord(
|
|
471
|
+
source=src,
|
|
472
|
+
target=tgt,
|
|
473
|
+
relationship=rel,
|
|
474
|
+
evidence_class=ev_cls,
|
|
475
|
+
confidence=conf,
|
|
476
|
+
file=str(getattr(record, "file", "") or ""),
|
|
477
|
+
start_line=int(getattr(record, "start_line", 1) or 1),
|
|
478
|
+
end_line=int(getattr(record, "end_line", None) or getattr(record, "start_line", 1) or 1),
|
|
479
|
+
target_file=getattr(record, "target_file", None),
|
|
480
|
+
target_line=getattr(record, "target_line", None),
|
|
481
|
+
evidence=str(getattr(record, "evidence", "") or ""),
|
|
482
|
+
reason=getattr(record, "reason", None),
|
|
483
|
+
status=status,
|
|
484
|
+
supporting_locations=tuple(getattr(record, "supporting_locations", ()) or ()),
|
|
485
|
+
occurrence_count=int(getattr(record, "occurrence_count", 1) or 1),
|
|
486
|
+
)
|