sourcecode 2.4.0__py3-none-any.whl → 2.5.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.
@@ -0,0 +1,248 @@
1
+ """GRAPH-analysis pack step handlers (Fase 11).
2
+
3
+ Three intents (EXECUTION_PATHS_THROUGH, TRANSACTIONS_REACHING, SUBSYSTEM_COUPLING)
4
+ expressed purely as compositions of these projection steps. NO new analyzer: every step
5
+ reuses an engine already wired into the shared `KnowledgeContext`.
6
+
7
+ * Execution paths → `spring_impact.run_impact_chain` (the SAME Spring-aware call-chain
8
+ engine the `impact-chain` command uses — multi-hop caller reachability with TX/SEC
9
+ enrichment). Distinct from BLAST_RADIUS, which uses the structural repo_ir engine.
10
+ * Transactions reaching → the caller reachability from run_impact_chain ∩ the
11
+ `SpringSemanticModel.tx_index` boundaries.
12
+ * Subsystem coupling → the module graph summary (hubs / cycles / orphans, from
13
+ `GraphAnalyzer`) + the repo_ir subsystem partition.
14
+
15
+ The two symbol-scoped intents reuse the impact pack's `ResolveTarget` head (cross-pack step
16
+ reuse). No step opens source or parses.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ from typing import TYPE_CHECKING
21
+
22
+ from sourcecode.retrieval.query import (
23
+ CollectGraphEvidence,
24
+ ResolveExecutionPaths,
25
+ ResolveSubsystemCoupling,
26
+ ResolveTransactionsReaching,
27
+ Step,
28
+ )
29
+ from sourcecode.retrieval.result import (
30
+ Explanation,
31
+ KnowledgeEntity,
32
+ Observation,
33
+ Provenance,
34
+ RetrievalEvidence,
35
+ )
36
+ from sourcecode.retrieval.steps import (
37
+ SLOT_CONFIDENCE,
38
+ SLOT_ENTITIES,
39
+ SLOT_EVIDENCE,
40
+ SLOT_EXPLANATION,
41
+ SLOT_OBSERVATIONS,
42
+ SLOT_PROVENANCE,
43
+ )
44
+ from sourcecode.retrieval.steps_impact import SLOT_TARGET
45
+
46
+ if TYPE_CHECKING:
47
+ from sourcecode.retrieval.runtime import StepContext, StepRegistry
48
+
49
+ SLOT_SERVICES = "graph_services"
50
+ _MODULE_PREFIX = "module:"
51
+
52
+
53
+ # ── execution paths (spring_impact.run_impact_chain) ─────────────────────────────
54
+ def resolve_execution_paths(step: Step, ctx: "StepContext") -> None:
55
+ """The multi-hop caller reachability through the target symbol, from run_impact_chain.
56
+ Reuses the impact pack's ResolveTarget for the symbol."""
57
+ assert isinstance(step, ResolveExecutionPaths)
58
+ result = _impact_chain(ctx)
59
+ entities = [
60
+ KnowledgeEntity(identity=fqn, kind="execution_path", score=0.0, attributes={"reach": "direct"})
61
+ for fqn in result.direct_callers
62
+ ] + [
63
+ KnowledgeEntity(identity=fqn, kind="execution_path", score=0.0, attributes={"reach": "indirect"})
64
+ for fqn in result.indirect_callers
65
+ ]
66
+ ctx.put(SLOT_ENTITIES, entities)
67
+ ctx.put(
68
+ SLOT_OBSERVATIONS,
69
+ [
70
+ Observation(name="direct_callers", value=len(result.direct_callers), detail="run_impact_chain"),
71
+ Observation(name="indirect_callers", value=len(result.indirect_callers), detail="run_impact_chain"),
72
+ Observation(name="endpoints_affected", value=len(result.endpoints_affected), detail="run_impact_chain"),
73
+ Observation(name="risk_level", value=result.risk_level, detail="run_impact_chain"),
74
+ Observation(name="risk_score", value=result.risk_score, detail="run_impact_chain"),
75
+ ],
76
+ )
77
+ ctx.put(
78
+ SLOT_EXPLANATION,
79
+ Explanation(
80
+ winner=result.symbol,
81
+ score=float(result.risk_score or 0.0),
82
+ confidence=result.confidence,
83
+ top_contributions=(),
84
+ runner_up=None,
85
+ engine_explanation=result.explanation or "", # verbatim engine text
86
+ ),
87
+ )
88
+ ctx.put(SLOT_CONFIDENCE, result.confidence)
89
+ _add_service(ctx, "run_impact_chain")
90
+
91
+
92
+ # ── transactions reaching (impact chain callers ∩ tx_index) ──────────────────────
93
+ def resolve_transactions_reaching(step: Step, ctx: "StepContext") -> None:
94
+ """The @Transactional boundaries whose enclosing class can reach the target through the
95
+ call chain. Reuses run_impact_chain (reachability) + the tx boundary index."""
96
+ assert isinstance(step, ResolveTransactionsReaching)
97
+ result = _impact_chain(ctx)
98
+ caller_classes = {_enclosing_class(c) for c in result.direct_callers} | {
99
+ _enclosing_class(c) for c in result.indirect_callers
100
+ }
101
+ boundaries = [
102
+ b for b in ctx.knowledge.spring_model.tx_index.all_declared
103
+ if _enclosing_class(b.symbol) in caller_classes
104
+ ]
105
+ ctx.put(
106
+ SLOT_ENTITIES,
107
+ [
108
+ KnowledgeEntity(
109
+ identity=b.symbol,
110
+ kind="transaction_reaching",
111
+ score=0.0,
112
+ attributes={
113
+ "propagation": b.propagation,
114
+ "read_only": b.read_only,
115
+ "confidence": b.confidence,
116
+ },
117
+ )
118
+ for b in boundaries
119
+ ],
120
+ )
121
+ ctx.put(
122
+ SLOT_EVIDENCE,
123
+ [
124
+ RetrievalEvidence(
125
+ signal="transactional",
126
+ detail=f"@Transactional({b.propagation}) reaches {result.symbol}",
127
+ weight=1.0,
128
+ strength=1.0,
129
+ mass=1.0,
130
+ contribution=1.0,
131
+ provider="SpringSemanticModel + run_impact_chain",
132
+ source_file=b.source_file or None,
133
+ )
134
+ for b in boundaries
135
+ ],
136
+ )
137
+ ctx.put(
138
+ SLOT_OBSERVATIONS,
139
+ [
140
+ Observation(name="reaching_transactions", value=len(boundaries), detail="tx ∩ callers"),
141
+ Observation(name="caller_classes", value=len(caller_classes), detail="run_impact_chain"),
142
+ ],
143
+ )
144
+ ctx.put(SLOT_CONFIDENCE, "high" if boundaries else "low")
145
+ _add_service(ctx, "run_impact_chain")
146
+ _add_service(ctx, "SpringSemanticModel")
147
+
148
+
149
+ # ── subsystem coupling (module graph summary + subsystems) ───────────────────────
150
+ def resolve_subsystem_coupling(step: Step, ctx: "StepContext") -> None:
151
+ """Coupling structure of the repository: the module graph's hub / cycle / orphan
152
+ summary + the subsystem partition. Reuses the built module graph and repo_ir."""
153
+ assert isinstance(step, ResolveSubsystemCoupling)
154
+ mg = ctx.knowledge.module_graph
155
+ if mg is None:
156
+ summary = None
157
+ hubs: list = []
158
+ else:
159
+ summary = mg.summary
160
+ hubs = list(getattr(summary, "hubs", []) or [])
161
+ entities = [
162
+ KnowledgeEntity(
163
+ identity=_node_path(h), kind="coupling_hub", score=0.0, attributes={"role": "hub"}
164
+ )
165
+ for h in hubs
166
+ ]
167
+ ctx.put(SLOT_ENTITIES, entities)
168
+ subsystems = ctx.knowledge.repo_ir.get("subsystems", []) or []
169
+ obs = [Observation(name="subsystems", value=len(subsystems), detail="repo_ir partition")]
170
+ if summary is not None:
171
+ obs += [
172
+ Observation(name="hubs", value=len(hubs), detail="module graph hubs"),
173
+ Observation(name="import_cycles", value=getattr(summary, "cycle_count", 0), detail="module graph"),
174
+ Observation(name="orphans", value=len(getattr(summary, "orphans", []) or []), detail="module graph"),
175
+ Observation(name="nodes", value=getattr(summary, "node_count", 0), detail="module graph"),
176
+ Observation(name="edges", value=getattr(summary, "edge_count", 0), detail="module graph"),
177
+ ]
178
+ ctx.put(SLOT_OBSERVATIONS, obs)
179
+ ctx.put(SLOT_CONFIDENCE, "high" if summary is not None else "low")
180
+ _add_service(ctx, "GraphAnalyzer(module_graph)")
181
+ _add_service(ctx, "build_repo_ir(subsystems)")
182
+
183
+
184
+ # ── terminal evidence (generic across the pack) ──────────────────────────────────
185
+ def collect_graph_evidence(step: Step, ctx: "StepContext") -> None:
186
+ """Assemble confidence, provenance, and (unless a resolve step already set a richer
187
+ one) a default explanation. GENERIC across the graph pack."""
188
+ assert isinstance(step, CollectGraphEvidence)
189
+ confidence = ctx.get(SLOT_CONFIDENCE, "medium")
190
+ if not ctx.has(SLOT_EXPLANATION):
191
+ ctx.put(
192
+ SLOT_EXPLANATION,
193
+ Explanation(
194
+ winner=None,
195
+ score=0.0,
196
+ confidence=confidence,
197
+ top_contributions=(),
198
+ runner_up=None,
199
+ engine_explanation="",
200
+ ),
201
+ )
202
+ ctx.put(
203
+ SLOT_PROVENANCE,
204
+ Provenance(
205
+ services=tuple(ctx.get(SLOT_SERVICES, [])),
206
+ signals_used=("call_chain", "tx_index", "module_graph", "subsystems"),
207
+ signals_missing=(),
208
+ knowledge_source="spring_impact + SpringSemanticModel + module graph",
209
+ ),
210
+ )
211
+
212
+
213
+ # ── helpers ──────────────────────────────────────────────────────────────────────
214
+ def _impact_chain(ctx: "StepContext"):
215
+ from sourcecode.spring_impact import run_impact_chain
216
+
217
+ target = ctx.require(SLOT_TARGET)
218
+ return run_impact_chain(
219
+ ctx.knowledge.context_graph.cir,
220
+ target,
221
+ root=ctx.knowledge.root,
222
+ model=ctx.knowledge.spring_model,
223
+ )
224
+
225
+
226
+ def _enclosing_class(symbol: str) -> str:
227
+ return symbol.split("#", 1)[0]
228
+
229
+
230
+ def _node_path(node_id) -> str:
231
+ if not node_id:
232
+ return ""
233
+ return node_id[len(_MODULE_PREFIX):] if node_id.startswith(_MODULE_PREFIX) else node_id
234
+
235
+
236
+ def _add_service(ctx: "StepContext", name: str) -> None:
237
+ services = list(ctx.get(SLOT_SERVICES, []))
238
+ if name not in services:
239
+ services.append(name)
240
+ ctx.put(SLOT_SERVICES, services)
241
+
242
+
243
+ def register_graph_steps(registry: "StepRegistry") -> None:
244
+ """Register the GRAPH-analysis handlers (the sanctioned extension seam)."""
245
+ registry.register(ResolveExecutionPaths.kind, resolve_execution_paths)
246
+ registry.register(ResolveTransactionsReaching.kind, resolve_transactions_reaching)
247
+ registry.register(ResolveSubsystemCoupling.kind, resolve_subsystem_coupling)
248
+ registry.register(CollectGraphEvidence.kind, collect_graph_evidence)
@@ -0,0 +1,293 @@
1
+ """IMPACT-pack step handlers (Fase 5).
2
+
3
+ Five intents (BLAST_RADIUS, AFFECTED_ENDPOINTS, AFFECTED_INTEGRATIONS,
4
+ TRANSACTION_BOUNDARIES, CROSS_MODULE_IMPACT) expressed purely as compositions of these
5
+ reusable steps. Maximal reuse: every intent shares `ResolveTarget → ComputeBlastRadius →
6
+ … → CollectImpactEvidence → BuildResult`; only the single projection step differs.
7
+
8
+ All logic REUSES existing engines and recomputes nothing new:
9
+ * `ComputeBlastRadius` calls `repository_ir.compute_blast_radius` over the acquired repo IR
10
+ — the SAME engine the legacy `impact` command uses, so results are identical.
11
+ * Four of the five projections read that ONE blast dict (callers / endpoints / tx
12
+ boundaries / cross-module are all already computed by the engine).
13
+ * `ResolveIntegrations` additionally reuses `SemanticIntegrationEngine`, scoped to the
14
+ blast cone.
15
+
16
+ No step opens source or parses: the repo IR / ContextGraph are acquired once by the shared
17
+ `KnowledgeContext` (the acquisition boundary), and steps only read them.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ from typing import TYPE_CHECKING
22
+
23
+ from sourcecode.retrieval.errors import StepContractError
24
+ from sourcecode.retrieval.query import (
25
+ CollectImpactEvidence,
26
+ ComputeBlastRadius,
27
+ RankByRisk,
28
+ ResolveDependents,
29
+ ResolveEndpoints,
30
+ ResolveIntegrations,
31
+ ResolveModules,
32
+ ResolveTarget,
33
+ ResolveTransactions,
34
+ Step,
35
+ )
36
+ from sourcecode.retrieval.result import (
37
+ Explanation,
38
+ KnowledgeEntity,
39
+ Observation,
40
+ Provenance,
41
+ RetrievalEvidence,
42
+ )
43
+ from sourcecode.retrieval.steps import (
44
+ SLOT_CONFIDENCE,
45
+ SLOT_ENTITIES,
46
+ SLOT_EVIDENCE,
47
+ SLOT_EXPLANATION,
48
+ SLOT_OBSERVATIONS,
49
+ SLOT_PROVENANCE,
50
+ )
51
+
52
+ if TYPE_CHECKING:
53
+ from sourcecode.retrieval.runtime import StepContext, StepRegistry
54
+
55
+ # impact-specific slots
56
+ SLOT_TARGET = "impact_target"
57
+ SLOT_BLAST = "blast_radius"
58
+ SLOT_SERVICES = "impact_services"
59
+
60
+ _TARGET_ANCHOR_KINDS = ("symbol", "file")
61
+
62
+
63
+ # ── shared knowledge-producing steps ─────────────────────────────────────────────
64
+ def resolve_target(step: Step, ctx: "StepContext") -> None:
65
+ """Pick the impact target from the request's symbol/file anchor. The token is passed
66
+ to the blast engine, which does its own (legacy-identical) resolution."""
67
+ assert isinstance(step, ResolveTarget)
68
+ anchor = next((a for a in ctx.anchors if a.anchor.kind in _TARGET_ANCHOR_KINDS), None)
69
+ if anchor is None:
70
+ raise StepContractError("impact intents require a 'symbol' or 'file' anchor")
71
+ target = anchor.identities[0] if anchor.identities else anchor.anchor.raw
72
+ ctx.put(SLOT_TARGET, target)
73
+
74
+
75
+ def compute_blast_radius(step: Step, ctx: "StepContext") -> None:
76
+ """Run `repository_ir.compute_blast_radius` once over the acquired repo IR. Every
77
+ projection step reads the resulting blast dict."""
78
+ assert isinstance(step, ComputeBlastRadius)
79
+ from sourcecode.repository_ir import compute_blast_radius as _compute_blast_radius
80
+
81
+ target = ctx.require(SLOT_TARGET)
82
+ blast = _compute_blast_radius(ctx.knowledge.repo_ir, target, max_depth=step.max_depth)
83
+ ctx.put(SLOT_BLAST, blast)
84
+ _add_service(ctx, "compute_blast_radius")
85
+
86
+
87
+ # ── projection steps (each reads the ONE blast dict) ─────────────────────────────
88
+ def resolve_dependents(step: Step, ctx: "StepContext") -> None:
89
+ blast = ctx.require(SLOT_BLAST)
90
+ entities = [
91
+ KnowledgeEntity(identity=fqn, kind="dependent", score=0.0, attributes={"reach": "direct"})
92
+ for fqn in blast.get("direct_callers", [])
93
+ ] + [
94
+ KnowledgeEntity(identity=fqn, kind="dependent", score=0.0, attributes={"reach": "indirect"})
95
+ for fqn in blast.get("indirect_callers", [])
96
+ ]
97
+ ctx.put(SLOT_ENTITIES, entities)
98
+
99
+
100
+ def resolve_endpoints(step: Step, ctx: "StepContext") -> None:
101
+ blast = ctx.require(SLOT_BLAST)
102
+ ctx.put(
103
+ SLOT_ENTITIES,
104
+ [
105
+ KnowledgeEntity(
106
+ identity=ep.get("handler") or f"{ep.get('method', '')} {ep.get('path', '')}".strip(),
107
+ kind="endpoint",
108
+ score=0.0,
109
+ attributes={"method": ep.get("method", ""), "path": ep.get("path", "")},
110
+ )
111
+ for ep in blast.get("endpoints_affected", [])
112
+ ],
113
+ )
114
+
115
+
116
+ def resolve_transactions(step: Step, ctx: "StepContext") -> None:
117
+ blast = ctx.require(SLOT_BLAST)
118
+ ctx.put(
119
+ SLOT_ENTITIES,
120
+ [
121
+ KnowledgeEntity(identity=fqn, kind="transaction_boundary", score=0.0)
122
+ for fqn in blast.get("transactional_boundaries_touched", [])
123
+ ],
124
+ )
125
+
126
+
127
+ def resolve_modules(step: Step, ctx: "StepContext") -> None:
128
+ blast = ctx.require(SLOT_BLAST)
129
+ ctx.put(
130
+ SLOT_ENTITIES,
131
+ [
132
+ KnowledgeEntity(
133
+ identity=m.get("module", ""),
134
+ kind="module",
135
+ score=0.0,
136
+ attributes={k: v for k, v in m.items() if k != "module"},
137
+ )
138
+ for m in blast.get("cross_module_impact", [])
139
+ ],
140
+ )
141
+
142
+
143
+ def resolve_integrations(step: Step, ctx: "StepContext") -> None:
144
+ """Reuse `SemanticIntegrationEngine`; keep only integrations declared by a type inside
145
+ the blast cone (matched target ∪ direct ∪ indirect callers)."""
146
+ assert isinstance(step, ResolveIntegrations)
147
+ from sourcecode.semantic_integration_engine import SemanticIntegrationEngine
148
+
149
+ blast = ctx.require(SLOT_BLAST)
150
+ cone = (
151
+ set(blast.get("matched_fqns", []))
152
+ | set(blast.get("direct_callers", []))
153
+ | set(blast.get("indirect_callers", []))
154
+ )
155
+ integrations = SemanticIntegrationEngine(ctx.knowledge.context_graph).detect()
156
+ _add_service(ctx, "SemanticIntegrationEngine")
157
+ ctx.put(
158
+ SLOT_ENTITIES,
159
+ [
160
+ KnowledgeEntity(
161
+ identity=f"{i.kind}:{i.target or i.client}",
162
+ kind="integration",
163
+ score=0.0,
164
+ attributes={"integration_kind": i.kind, "client": i.client, "target": i.target},
165
+ )
166
+ for i in integrations
167
+ if i.client in cone
168
+ ],
169
+ )
170
+ # integration evidence carries the declaring site (relpath:line) via the engine
171
+ ctx.put(
172
+ SLOT_EVIDENCE,
173
+ [
174
+ _integration_evidence(i)
175
+ for i in integrations
176
+ if i.client in cone
177
+ ],
178
+ )
179
+
180
+
181
+ # ── ranking + terminal-evidence steps ────────────────────────────────────────────
182
+ def rank_by_risk(step: Step, ctx: "StepContext") -> None:
183
+ """Order impacted entities using the engine's OWN direct/indirect classification — no
184
+ new score is computed. Deterministic: direct before indirect, then identity."""
185
+ assert isinstance(step, RankByRisk)
186
+ entities = ctx.get(SLOT_ENTITIES, [])
187
+ order = {"direct": 0, "indirect": 1}
188
+ ctx.put(
189
+ SLOT_ENTITIES,
190
+ sorted(entities, key=lambda e: (order.get(e.attributes.get("reach"), 2), e.identity)),
191
+ )
192
+
193
+
194
+ def collect_impact_evidence(step: Step, ctx: "StepContext") -> None:
195
+ """GENERIC across all impact intents: set confidence, explanation (engine text
196
+ passthrough — no synthesized prose), provenance, and risk observations from the blast
197
+ dict. Reuses the engine's own risk/confidence; computes nothing new."""
198
+ assert isinstance(step, CollectImpactEvidence)
199
+ blast = ctx.require(SLOT_BLAST)
200
+ confidence = blast.get("confidence_level", "low")
201
+ ctx.put(SLOT_CONFIDENCE, confidence)
202
+ # B3: evidence parity — projections other than ResolveIntegrations produce entities but
203
+ # no evidence rows (the blast engine yields FQNs, not source spans). Emit one grounded-
204
+ # as-far-as-possible evidence row per entity so `evidence` mirrors `entities` and every
205
+ # hit is auditable. No span is fabricated: source_file stays None where the engine gives
206
+ # none (the same honest convention the archetype signals use).
207
+ if not ctx.has(SLOT_EVIDENCE):
208
+ ctx.put(
209
+ SLOT_EVIDENCE,
210
+ [
211
+ RetrievalEvidence(
212
+ signal=e.kind,
213
+ detail=e.identity,
214
+ weight=1.0,
215
+ strength=1.0,
216
+ mass=1.0,
217
+ contribution=1.0,
218
+ provider="compute_blast_radius",
219
+ source_file=None,
220
+ source_line=None,
221
+ )
222
+ for e in ctx.get(SLOT_ENTITIES, [])
223
+ ],
224
+ )
225
+ ctx.put(
226
+ SLOT_OBSERVATIONS,
227
+ [
228
+ Observation(name="risk_score", value=blast.get("risk_score", 0.0), detail="compute_blast_radius"),
229
+ Observation(name="risk_level", value=blast.get("risk_level", "unknown"), detail="compute_blast_radius"),
230
+ Observation(name="confidence_score", value=blast.get("confidence_score", 0.0), detail="compute_blast_radius"),
231
+ Observation(name="resolution", value=blast.get("resolution", ""), detail="target resolution"),
232
+ ],
233
+ )
234
+ ctx.put(
235
+ SLOT_EXPLANATION,
236
+ Explanation(
237
+ winner=blast.get("target"),
238
+ score=float(blast.get("risk_score", 0.0)),
239
+ confidence=confidence,
240
+ top_contributions=(),
241
+ runner_up=None,
242
+ engine_explanation=blast.get("explanation", ""), # verbatim engine text
243
+ ),
244
+ )
245
+ ctx.put(
246
+ SLOT_PROVENANCE,
247
+ Provenance(
248
+ services=tuple(ctx.get(SLOT_SERVICES, [])),
249
+ signals_used=("reverse_graph", "route_surface", "subsystems"),
250
+ signals_missing=(),
251
+ knowledge_source="repo IR (build_repo_ir)",
252
+ ),
253
+ )
254
+
255
+
256
+ # ── helpers ──────────────────────────────────────────────────────────────────────
257
+ def _add_service(ctx: "StepContext", name: str) -> None:
258
+ services = list(ctx.get(SLOT_SERVICES, []))
259
+ if name not in services:
260
+ services.append(name)
261
+ ctx.put(SLOT_SERVICES, services)
262
+
263
+
264
+ def _integration_evidence(i) -> RetrievalEvidence:
265
+ file = line = None
266
+ if i.evidence and ":" in i.evidence:
267
+ f, _, ln = i.evidence.rpartition(":")
268
+ file = f or None
269
+ line = int(ln) if ln.isdigit() else None
270
+ return RetrievalEvidence(
271
+ signal=f"integration_{i.kind}",
272
+ detail=f"{i.client} → {i.target or i.kind} ({i.basis})",
273
+ weight=1.0,
274
+ strength=1.0,
275
+ mass=1.0,
276
+ contribution=1.0,
277
+ provider="SemanticIntegrationEngine",
278
+ source_file=file,
279
+ source_line=line,
280
+ )
281
+
282
+
283
+ def register_impact_steps(registry: "StepRegistry") -> None:
284
+ """Register the IMPACT-pack handlers into a registry (the sanctioned extension seam)."""
285
+ registry.register(ResolveTarget.kind, resolve_target)
286
+ registry.register(ComputeBlastRadius.kind, compute_blast_radius)
287
+ registry.register(ResolveDependents.kind, resolve_dependents)
288
+ registry.register(ResolveEndpoints.kind, resolve_endpoints)
289
+ registry.register(ResolveIntegrations.kind, resolve_integrations)
290
+ registry.register(ResolveTransactions.kind, resolve_transactions)
291
+ registry.register(ResolveModules.kind, resolve_modules)
292
+ registry.register(RankByRisk.kind, rank_by_risk)
293
+ registry.register(CollectImpactEvidence.kind, collect_impact_evidence)