@saptools/service-flow 0.1.69 → 0.1.72

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 (144) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +9 -5
  3. package/TECHNICAL-NOTE.md +17 -0
  4. package/dist/{chunk-3N3B5KHV.js → chunk-Z6D433R5.js} +8766 -6546
  5. package/dist/chunk-Z6D433R5.js.map +1 -0
  6. package/dist/cli.js +703 -160
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.ts +53 -14
  9. package/dist/index.js +2 -23
  10. package/dist/index.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/cli/doctor-event-quality.ts +371 -0
  13. package/src/cli/{002-doctor-lifecycle.ts → doctor-lifecycle.ts} +1 -1
  14. package/src/cli/{001-doctor-projection.ts → doctor-projection.ts} +1 -1
  15. package/src/cli/doctor.ts +9 -11
  16. package/src/cli.ts +6 -6
  17. package/src/db/{009-binding-fact-semantics.ts → binding-fact-semantics.ts} +2 -2
  18. package/src/db/{000-call-fact-repository.ts → call-fact-repository.ts} +12 -9
  19. package/src/db/{003-current-fact-semantics.ts → current-fact-semantics.ts} +8 -8
  20. package/src/db/event-fact-semantics.ts +347 -0
  21. package/src/db/event-surface-invalidation.ts +38 -0
  22. package/src/db/{002-fact-json-inventory.ts → fact-json-inventory.ts} +16 -0
  23. package/src/db/{001-fact-lifecycle.ts → fact-lifecycle.ts} +3 -3
  24. package/src/db/migrations.ts +12 -1
  25. package/src/db/{007-package-fact-semantics.ts → package-fact-semantics.ts} +6 -6
  26. package/src/db/{010-package-symbol-surface-semantics.ts → package-symbol-surface-semantics.ts} +2 -2
  27. package/src/db/{004-package-target-invalidation.ts → package-target-invalidation.ts} +80 -1
  28. package/src/db/{008-relative-fact-semantics.ts → relative-fact-semantics.ts} +3 -3
  29. package/src/db/{006-relative-symbol-resolution.ts → relative-symbol-resolution.ts} +1 -1
  30. package/src/db/repositories.ts +31 -5
  31. package/src/db/{005-schema-structure.ts → schema-structure.ts} +41 -1
  32. package/src/db/schema.ts +6 -2
  33. package/src/db/{011-symbol-call-semantics.ts → symbol-call-semantics.ts} +1 -1
  34. package/src/index.ts +3 -3
  35. package/src/indexer/repository-indexer.ts +50 -11
  36. package/src/indexer/workspace-indexer.ts +3 -3
  37. package/src/linker/{007-call-edge-insertion.ts → call-edge-insertion.ts} +2 -2
  38. package/src/linker/{002-call-evidence.ts → call-evidence.ts} +2 -2
  39. package/src/linker/cross-repo-linker.ts +30 -8
  40. package/src/linker/dynamic-edge-resolver.ts +1 -1
  41. package/src/linker/event-environment-link.ts +211 -0
  42. package/src/linker/event-shape-candidate-linker.ts +161 -0
  43. package/src/linker/{004-event-subscription-handler-linker.ts → event-subscription-handler-linker.ts} +124 -20
  44. package/src/linker/{006-event-template-link.ts → event-template-link.ts} +40 -6
  45. package/src/linker/external-http-target.ts +1 -1
  46. package/src/linker/helper-package-linker.ts +1 -1
  47. package/src/linker/{000-implementation-candidates.ts → implementation-candidates.ts} +1 -1
  48. package/src/linker/{001-implementation-evidence-projection.ts → implementation-evidence-projection.ts} +1 -1
  49. package/src/linker/odata-path-normalizer.ts +1 -1
  50. package/src/linker/{005-odata-path-structure.ts → odata-path-structure.ts} +1 -1
  51. package/src/linker/package-event-constant-resolver.ts +298 -0
  52. package/src/linker/{003-package-import-symbol-resolver.ts → package-import-symbol-resolver.ts} +3 -3
  53. package/src/linker/service-resolver.ts +2 -2
  54. package/src/output/{001-compact-json-output.ts → compact-json-output.ts} +1 -1
  55. package/src/output/table-output.ts +13 -1
  56. package/src/parsers/{006-binding-identity.ts → binding-identity.ts} +3 -3
  57. package/src/parsers/{011-binding-lexical-scope.ts → binding-lexical-scope.ts} +1 -1
  58. package/src/parsers/{021-binding-visibility.ts → binding-visibility.ts} +1 -1
  59. package/src/parsers/decorator-parser.ts +9 -53
  60. package/src/parsers/environment-declarations.ts +327 -0
  61. package/src/parsers/event-call-analysis.ts +242 -0
  62. package/src/parsers/event-environment-reference.ts +231 -0
  63. package/src/parsers/event-loop-registration.ts +132 -0
  64. package/src/parsers/event-name-import-resolution.ts +243 -0
  65. package/src/parsers/event-receiver-analysis.ts +404 -0
  66. package/src/parsers/{005-event-subscription-facts.ts → event-subscription-facts.ts} +6 -2
  67. package/src/parsers/generated-constants-parser.ts +80 -14
  68. package/src/parsers/{016-local-symbol-reference.ts → local-symbol-reference.ts} +2 -2
  69. package/src/parsers/operation-path-analysis.ts +1 -1
  70. package/src/parsers/{023-outbound-call-classifier.ts → outbound-call-classifier.ts} +30 -127
  71. package/src/parsers/outbound-call-parser.ts +15 -3
  72. package/src/parsers/{022-outbound-expression-analysis.ts → outbound-expression-analysis.ts} +4 -4
  73. package/src/parsers/{018-package-commonjs-syntax.ts → package-commonjs-syntax.ts} +1 -1
  74. package/src/parsers/{012-package-fact-contract.ts → package-fact-contract.ts} +3 -3
  75. package/src/parsers/{010-package-public-surface-analysis.ts → package-public-surface-analysis.ts} +2 -2
  76. package/src/parsers/{003-package-public-surface.ts → package-public-surface.ts} +3 -3
  77. package/src/parsers/{008-package-surface-publication.ts → package-surface-publication.ts} +1 -1
  78. package/src/parsers/{001-query-entity-resolution.ts → query-entity-resolution.ts} +1 -1
  79. package/src/parsers/{015-service-binding-collector.ts → service-binding-collector.ts} +3 -3
  80. package/src/parsers/{014-service-binding-helper-flow.ts → service-binding-helper-flow.ts} +2 -2
  81. package/src/parsers/service-binding-parser-helpers.ts +1 -1
  82. package/src/parsers/service-binding-parser.ts +2 -2
  83. package/src/parsers/{007-source-fact-reconciliation.ts → source-fact-reconciliation.ts} +3 -3
  84. package/src/parsers/{020-stable-local-value.ts → stable-local-value.ts} +32 -11
  85. package/src/parsers/string-constant-lookups.ts +358 -0
  86. package/src/parsers/{009-symbol-call-facts.ts → symbol-call-facts.ts} +3 -3
  87. package/src/parsers/{017-symbol-derived-contexts.ts → symbol-derived-contexts.ts} +2 -2
  88. package/src/parsers/symbol-parser.ts +6 -6
  89. package/src/trace/{021-compact-decision-normalization.ts → compact-decision-normalization.ts} +2 -2
  90. package/src/trace/{020-compact-field-projection.ts → compact-field-projection.ts} +3 -3
  91. package/src/trace/{024-compact-observation-decision.ts → compact-observation-decision.ts} +3 -3
  92. package/src/trace/{016-compact-projector.ts → compact-projector.ts} +4 -4
  93. package/src/trace/{018-compact-trace.ts → compact-trace.ts} +3 -3
  94. package/src/trace/{006-contextual-projection.ts → contextual-projection.ts} +1 -1
  95. package/src/trace/{008-contextual-runtime-state.ts → contextual-runtime-state.ts} +1 -1
  96. package/src/trace/{001-dynamic-identity.ts → dynamic-identity.ts} +2 -2
  97. package/src/trace/{003-dynamic-references.ts → dynamic-references.ts} +2 -2
  98. package/src/trace/dynamic-targets.ts +6 -6
  99. package/src/trace/{030-event-runtime-resolution.ts → event-runtime-resolution.ts} +26 -5
  100. package/src/trace/event-shape-candidate-trace.ts +172 -0
  101. package/src/trace/{011-event-subscriber-traversal.ts → event-subscriber-traversal.ts} +20 -8
  102. package/src/trace/evidence.ts +9 -2
  103. package/src/trace/implementation-hints.ts +2 -2
  104. package/src/trace/{005-implementation-selection.ts → implementation-selection.ts} +2 -2
  105. package/src/trace/{031-local-call-expansion.ts → local-call-expansion.ts} +3 -3
  106. package/src/trace/{009-selected-handler-provenance.ts → selected-handler-provenance.ts} +1 -1
  107. package/src/trace/selectors.ts +1 -1
  108. package/src/trace/{017-trace-context.ts → trace-context.ts} +2 -2
  109. package/src/trace/{015-trace-edge-recorder.ts → trace-edge-recorder.ts} +2 -2
  110. package/src/trace/{019-trace-edge-semantics.ts → trace-edge-semantics.ts} +3 -3
  111. package/src/trace/trace-engine.ts +10 -10
  112. package/src/trace/{022-trace-fact-preflight.ts → trace-fact-preflight.ts} +2 -2
  113. package/src/trace/{025-trace-implementation-scope.ts → trace-implementation-scope.ts} +1 -1
  114. package/src/trace/{028-trace-operation-execution.ts → trace-operation-execution.ts} +10 -10
  115. package/src/trace/{013-trace-root-scopes.ts → trace-root-scopes.ts} +3 -3
  116. package/src/trace/{027-trace-scope-execution.ts → trace-scope-execution.ts} +35 -43
  117. package/src/trace/{029-trace-start-implementation.ts → trace-start-implementation.ts} +7 -7
  118. package/src/trace/{026-trace-start-scope.ts → trace-start-scope.ts} +5 -5
  119. package/src/trace/{010-traversal-scope.ts → traversal-scope.ts} +1 -1
  120. package/src/types.ts +17 -1
  121. package/src/utils/event-skeleton.ts +207 -0
  122. package/src/version.ts +1 -1
  123. package/dist/chunk-3N3B5KHV.js.map +0 -1
  124. /package/src/cli/{000-clean.ts → clean.ts} +0 -0
  125. /package/src/cli/{003-doctor-package-resolution.ts → doctor-package-resolution.ts} +0 -0
  126. /package/src/cli/{001-index-summary.ts → index-summary.ts} +0 -0
  127. /package/src/db/{014-binding-helper-provenance.ts → binding-helper-provenance.ts} +0 -0
  128. /package/src/db/{012-binding-reference-proof.ts → binding-reference-proof.ts} +0 -0
  129. /package/src/db/{013-index-publication-failure.ts → index-publication-failure.ts} +0 -0
  130. /package/src/output/{000-stdout-policy.ts → stdout-policy.ts} +0 -0
  131. /package/src/parsers/{019-binding-assignment-targets.ts → binding-assignment-targets.ts} +0 -0
  132. /package/src/parsers/{000-direct-query-execution.ts → direct-query-execution.ts} +0 -0
  133. /package/src/parsers/{013-executable-body-eligibility.ts → executable-body-eligibility.ts} +0 -0
  134. /package/src/parsers/{004-fact-identity.ts → fact-identity.ts} +0 -0
  135. /package/src/parsers/{002-symbol-import-bindings.ts → symbol-import-bindings.ts} +0 -0
  136. /package/src/trace/{014-compact-contract.ts → compact-contract.ts} +0 -0
  137. /package/src/trace/{004-dynamic-candidate-sources.ts → dynamic-candidate-sources.ts} +0 -0
  138. /package/src/trace/{000-dynamic-target-types.ts → dynamic-target-types.ts} +0 -0
  139. /package/src/trace/{007-implementation-start-diagnostic.ts → implementation-start-diagnostic.ts} +0 -0
  140. /package/src/trace/{023-nested-event-scopes.ts → nested-event-scopes.ts} +0 -0
  141. /package/src/trace/{002-trace-diagnostics.ts → trace-diagnostics.ts} +0 -0
  142. /package/src/trace/{012-trace-graph-lookups.ts → trace-graph-lookups.ts} +0 -0
  143. /package/src/utils/{000-bounded-projection.ts → bounded-projection.ts} +0 -0
  144. /package/src/utils/{001-placeholders.ts → placeholders.ts} +0 -0
@@ -0,0 +1,172 @@
1
+ import type { Db } from '../db/connection.js';
2
+ import type { TraceOptions } from '../types.js';
3
+ import {
4
+ operationNode,
5
+ symbolNode,
6
+ type TraceGraphEdgeRow,
7
+ } from './trace-graph-lookups.js';
8
+ import type { TraceGraphRow } from './evidence.js';
9
+ import {
10
+ eventMissingVariableNames,
11
+ eventTemplateVariables,
12
+ parseEventSkeletonFact,
13
+ } from '../utils/event-skeleton.js';
14
+
15
+ const defaultEventShapeCandidateCap = 5;
16
+ const maximumEventShapeCandidateCap = 50;
17
+
18
+ function candidateCap(options: TraceOptions): number {
19
+ const value = options.maxDynamicCandidates
20
+ ?? defaultEventShapeCandidateCap;
21
+ if (!Number.isSafeInteger(value) || value < 1)
22
+ return defaultEventShapeCandidateCap;
23
+ return Math.min(value, maximumEventShapeCandidateCap);
24
+ }
25
+
26
+ function withCandidateCounts(
27
+ row: TraceGraphEdgeRow,
28
+ total: number,
29
+ shown: number,
30
+ ): TraceGraphEdgeRow {
31
+ let evidence: Record<string, unknown> = {};
32
+ try {
33
+ const parsed: unknown = JSON.parse(row.evidence_json);
34
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
35
+ evidence = parsed as Record<string, unknown>;
36
+ } catch {
37
+ evidence = {};
38
+ }
39
+ return {
40
+ ...row,
41
+ evidence_json: JSON.stringify({
42
+ ...evidence,
43
+ eventShapeCandidateCount: total,
44
+ shownEventShapeCandidateCount: shown,
45
+ omittedEventShapeCandidateCount: Math.max(0, total - shown),
46
+ }),
47
+ };
48
+ }
49
+
50
+ export function visibleEventShapeRows(
51
+ rows: readonly TraceGraphEdgeRow[],
52
+ options: TraceOptions,
53
+ ): TraceGraphEdgeRow[] {
54
+ const regular = rows.filter((row) =>
55
+ row.edge_type !== 'EVENT_SHAPE_CANDIDATE_SUBSCRIBER');
56
+ if ((options.dynamicMode ?? 'strict') !== 'candidates') return regular;
57
+ const candidates = rows.filter((row) =>
58
+ row.edge_type === 'EVENT_SHAPE_CANDIDATE_SUBSCRIBER');
59
+ const shown = candidates.slice(0, candidateCap(options));
60
+ return [
61
+ ...regular,
62
+ ...shown.map((row) =>
63
+ withCandidateCounts(row, candidates.length, shown.length)),
64
+ ];
65
+ }
66
+
67
+ export function outboundTraceEdgeType(
68
+ call: { call_type: string },
69
+ row: { edge_type: string; to_kind: string },
70
+ ): string {
71
+ if (row.edge_type === 'EVENT_SHAPE_CANDIDATE_SUBSCRIBER')
72
+ return 'event_shape_candidate_subscriber';
73
+ if (row.to_kind === 'operation'
74
+ && row.edge_type === 'REMOTE_CALL_RESOLVES_TO_OPERATION')
75
+ return 'remote_action';
76
+ if (row.to_kind === 'operation'
77
+ && row.edge_type === 'LOCAL_CALL_RESOLVES_TO_OPERATION')
78
+ return 'local_service_call';
79
+ return call.call_type;
80
+ }
81
+
82
+ export function outboundTraceTargetNode(
83
+ db: Db,
84
+ id: string,
85
+ row: TraceGraphRow,
86
+ ): Record<string, unknown> {
87
+ const operation = row.to_kind === 'operation'
88
+ ? operationNode(db, row.to_id) : undefined;
89
+ const candidate = row.edge_type === 'EVENT_SHAPE_CANDIDATE_SUBSCRIBER'
90
+ ? symbolNode(db, Number(row.to_id)) : undefined;
91
+ return operation ?? candidate ?? {
92
+ id,
93
+ kind: row.to_kind,
94
+ label: row.to_kind === 'db_entity'
95
+ ? `Entity: ${row.to_id || 'unknown'}` : row.to_id,
96
+ };
97
+ }
98
+
99
+ function stringArray(value: unknown): string[] {
100
+ return Array.isArray(value)
101
+ ? value.filter((item): item is string => typeof item === 'string') : [];
102
+ }
103
+
104
+ function subscriberSkeletons(
105
+ db: Db,
106
+ workspaceId: number,
107
+ publishCallId: number,
108
+ ): unknown[] {
109
+ return db.prepare(`SELECT subscription.event_skeleton_json skeleton
110
+ FROM outbound_calls publication
111
+ JOIN repositories publication_repo ON publication_repo.id=publication.repo_id
112
+ JOIN outbound_calls subscription
113
+ ON subscription.call_type='async_subscribe'
114
+ AND subscription.event_skeleton_signature
115
+ =publication.event_skeleton_signature
116
+ JOIN repositories subscription_repo
117
+ ON subscription_repo.id=subscription.repo_id
118
+ AND subscription_repo.workspace_id=publication_repo.workspace_id
119
+ WHERE publication.id=? AND publication_repo.workspace_id=?
120
+ AND publication.event_skeleton_signature IS NOT NULL
121
+ ORDER BY subscription_repo.name COLLATE BINARY,
122
+ subscription.repo_id,subscription.source_file COLLATE BINARY,
123
+ subscription.call_site_start_offset,subscription.id`).all(
124
+ publishCallId, workspaceId,
125
+ ).map((row) => row.skeleton);
126
+ }
127
+
128
+ export function eventShapeMissingVariableEvidence(
129
+ db: Db,
130
+ workspaceId: number,
131
+ publishCallId: number,
132
+ evidence: Record<string, unknown>,
133
+ variables: Record<string, string> | undefined,
134
+ ): Record<string, unknown> {
135
+ const current = stringArray(evidence.missingRuntimeVariables);
136
+ if (current.length === 0) return evidence;
137
+ const names = new Set(current);
138
+ let matchingSubscriptions = 0;
139
+ for (const value of subscriberSkeletons(db, workspaceId, publishCallId)) {
140
+ const skeleton = parseEventSkeletonFact(value);
141
+ if (!skeleton?.candidateEligible) continue;
142
+ matchingSubscriptions += 1;
143
+ const expanded = eventTemplateVariables(skeleton, variables ?? {});
144
+ const missing = skeleton.sourceKeys.filter((key) =>
145
+ !Object.hasOwn(expanded, key));
146
+ for (const name of eventMissingVariableNames(skeleton, missing))
147
+ names.add(name);
148
+ }
149
+ const missing = [...names].sort((left, right) =>
150
+ left < right ? -1 : left > right ? 1 : 0);
151
+ return {
152
+ ...evidence,
153
+ missingRuntimeVariables: missing,
154
+ missingVariableCount: missing.length,
155
+ eventShapeMatchingSubscriptionCount: matchingSubscriptions,
156
+ };
157
+ }
158
+
159
+ export function eventShapeRuntimeEvidence(
160
+ db: Db,
161
+ workspaceId: number,
162
+ callId: number,
163
+ callType: string,
164
+ evidence: Record<string, unknown>,
165
+ variables: Record<string, string> | undefined,
166
+ ): Record<string, unknown> {
167
+ return callType === 'async_emit'
168
+ ? eventShapeMissingVariableEvidence(
169
+ db, workspaceId, callId, evidence, variables,
170
+ )
171
+ : evidence;
172
+ }
@@ -7,7 +7,11 @@ import {
7
7
  compareBinary,
8
8
  type TraversalScopeScheduler,
9
9
  type TraversalScopeState,
10
- } from './010-traversal-scope.js';
10
+ } from './traversal-scope.js';
11
+ import {
12
+ eventTemplateVariables,
13
+ parseEventSkeletonFact,
14
+ } from '../utils/event-skeleton.js';
11
15
 
12
16
  export type EventSubscriberTransitionStatus =
13
17
  | 'resolved'
@@ -96,7 +100,8 @@ export function planEventSubscriberTransitions(
96
100
  ): PlannedEventSubscriberTransition[] {
97
101
  return loadEventSubscriberTransitions(db, query).map((transition) => {
98
102
  const handler = transition.handler;
99
- if (!handler) return plannedTransition(transition, 'not_resolved');
103
+ if (transition.status !== 'resolved' || !handler)
104
+ return plannedTransition(transition, 'not_resolved');
100
105
  if (depth >= maxDepth)
101
106
  return plannedTransition(transition, 'depth_limited');
102
107
  const state = scheduler.schedule({
@@ -245,13 +250,15 @@ function eventRowForQuery(
245
250
  row: Record<string, unknown>,
246
251
  query: EventSubscriberTransitionQuery,
247
252
  ): Record<string, unknown> | undefined {
253
+ const exact = exactPersistedEventRow(row, query);
254
+ if (exact) return exact;
248
255
  const variables = query.vars;
249
- if (variables === undefined) return exactPersistedEventRow(row, query);
256
+ if (variables === undefined) return undefined;
250
257
  const evidence = parseEvidence(row.evidenceJson);
251
258
  const resolution = isRecord(evidence.eventTemplateResolution)
252
259
  ? evidence.eventTemplateResolution : {};
253
260
  if (typeof resolution.original !== 'string')
254
- return exactPersistedEventRow(row, query);
261
+ return undefined;
255
262
  return runtimeMatchedEventRow(
256
263
  row, query, evidence, resolution.original, variables,
257
264
  );
@@ -272,7 +279,10 @@ function runtimeMatchedEventRow(
272
279
  template: string,
273
280
  variables: Record<string, string>,
274
281
  ): Record<string, unknown> | undefined {
275
- const substitution = substituteVariables(template, variables);
282
+ const skeleton = parseEventSkeletonFact(evidence.eventSkeleton);
283
+ const substitution = substituteVariables(
284
+ template, eventTemplateVariables(skeleton, variables),
285
+ );
276
286
  if (substitution.missing.length > 0
277
287
  || substitution.effective !== query.eventName) return undefined;
278
288
  const resolvedAssociation = evidence.associationStatus === 'resolved';
@@ -295,8 +305,7 @@ export function eventSubscriberMissingVariables(
295
305
  db: Db,
296
306
  query: EventSubscriberTransitionQuery,
297
307
  ): string[] {
298
- if (query.vars === undefined) return [];
299
- const variables = query.vars;
308
+ const variables = query.vars ?? {};
300
309
  const rows = db.prepare(`SELECT ge.from_id eventName,
301
310
  ge.evidence_json evidenceJson FROM graph_edges ge
302
311
  WHERE ge.workspace_id=? AND ge.generation=?
@@ -311,7 +320,10 @@ export function eventSubscriberMissingVariables(
311
320
  const template = typeof resolution.original === 'string'
312
321
  ? resolution.original : undefined;
313
322
  if (!template) return [];
314
- const substitution = substituteVariables(template, variables);
323
+ const skeleton = parseEventSkeletonFact(evidence.eventSkeleton);
324
+ const substitution = substituteVariables(
325
+ template, eventTemplateVariables(skeleton, variables),
326
+ );
315
327
  if (substitution.missing.length === 0
316
328
  || matchRuntimeTemplate(
317
329
  substitution.effective, query.eventName,
@@ -4,12 +4,12 @@ import { normalizeODataOperationInvocationPath } from '../linker/odata-path-norm
4
4
  import { resolveOperation, type OperationTarget } from '../linker/service-resolver.js';
5
5
  import type { DynamicMode } from '../types.js';
6
6
  import { analyzeDynamicTargetCandidates, type DynamicTargetAnalysis, type DynamicTargetCandidate } from './dynamic-targets.js';
7
- import { boundCandidateLikeEvidence } from '../utils/000-bounded-projection.js';
7
+ import { boundCandidateLikeEvidence } from '../utils/bounded-projection.js';
8
8
  import {
9
9
  dynamicMissingReason,
10
10
  isStructuralContextualBlocker,
11
11
  type ContextualRuntimeState,
12
- } from './008-contextual-runtime-state.js';
12
+ } from './contextual-runtime-state.js';
13
13
 
14
14
  export interface TraceGraphRow extends Record<string, unknown> {
15
15
  id: number;
@@ -262,6 +262,8 @@ function runtimeDiagnosticTotals(
262
262
  const effective = parseObject(edge.evidence.effectiveResolution);
263
263
  if (!['dynamic', 'unresolved', 'ambiguous'].includes(String(effective.status ?? '')))
264
264
  continue;
265
+ for (const key of stringArray(edge.evidence.missingRuntimeVariables))
266
+ missing.add(key);
265
267
  const substitutions = edge.evidence.runtimeSubstitutions;
266
268
  if (!substitutions || typeof substitutions !== 'object' || Array.isArray(substitutions)) continue;
267
269
  for (const value of Object.values(substitutions as Record<string, RuntimeSubstitution>))
@@ -648,6 +650,11 @@ function stringValue(value: unknown): string | undefined {
648
650
  return typeof value === 'string' ? value : undefined;
649
651
  }
650
652
 
653
+ function stringArray(value: unknown): string[] {
654
+ return Array.isArray(value)
655
+ ? value.filter((item): item is string => typeof item === 'string') : [];
656
+ }
657
+
651
658
  function numeric(value: unknown): number {
652
659
  return typeof value === 'number' && Number.isFinite(value) ? value : 0;
653
660
  }
@@ -1,6 +1,6 @@
1
1
  import type { ImplementationHint } from '../types.js';
2
- import { projectBounded } from '../utils/000-bounded-projection.js';
3
- import { compareBinary } from './010-traversal-scope.js';
2
+ import { projectBounded } from '../utils/bounded-projection.js';
3
+ import { compareBinary } from './traversal-scope.js';
4
4
 
5
5
  interface Candidate {
6
6
  accepted?: boolean;
@@ -1,7 +1,7 @@
1
1
  import type { Db } from '../db/connection.js';
2
- import { canonicalImplementationEvidence } from '../linker/000-implementation-candidates.js';
2
+ import { canonicalImplementationEvidence } from '../linker/implementation-candidates.js';
3
3
  import type { ImplementationHint } from '../types.js';
4
- import { projectBounded } from '../utils/000-bounded-projection.js';
4
+ import { projectBounded } from '../utils/bounded-projection.js';
5
5
  import {
6
6
  selectImplementation,
7
7
  type ImplementationSelection,
@@ -1,13 +1,13 @@
1
1
  import type { Db } from '../db/connection.js';
2
2
  import {
3
3
  type ContextBinding,
4
- } from './008-contextual-runtime-state.js';
4
+ } from './contextual-runtime-state.js';
5
5
  import {
6
6
  type TraversalScheduleDecision,
7
7
  type TraversalScopeScheduler,
8
8
  type TraversalScopeState,
9
- } from './010-traversal-scope.js';
10
- import { contextForSymbolCall } from './017-trace-context.js';
9
+ } from './traversal-scope.js';
10
+ import { contextForSymbolCall } from './trace-context.js';
11
11
 
12
12
  export interface LocalCallExpansion {
13
13
  repoId: number;
@@ -3,7 +3,7 @@ import {
3
3
  selectedHandlerProvenance,
4
4
  type SelectedHandlerProvenance,
5
5
  type SelectedHandlerSource,
6
- } from '../linker/001-implementation-evidence-projection.js';
6
+ } from '../linker/implementation-evidence-projection.js';
7
7
 
8
8
  export interface HandlerMethodNode extends Record<string, unknown> {
9
9
  id: string;
@@ -3,7 +3,7 @@ import type { TraceStart } from '../types.js';
3
3
  import {
4
4
  projectBounded,
5
5
  type BoundedProjection,
6
- } from '../utils/000-bounded-projection.js';
6
+ } from '../utils/bounded-projection.js';
7
7
 
8
8
  export interface SelectorSourceScope {
9
9
  files?: Set<string>;
@@ -1,7 +1,7 @@
1
1
  import type { Db } from '../db/connection.js';
2
2
  import { extractPlaceholders } from '../linker/dynamic-edge-resolver.js';
3
- import { boundCandidateLikeEvidence } from '../utils/000-bounded-projection.js';
4
- import type { ContextBinding } from './008-contextual-runtime-state.js';
3
+ import { boundCandidateLikeEvidence } from '../utils/bounded-projection.js';
4
+ import type { ContextBinding } from './contextual-runtime-state.js';
5
5
 
6
6
  export interface TraceContextCall extends Record<string, unknown> {
7
7
  service_binding_id?: number;
@@ -7,13 +7,13 @@ import type {
7
7
  CompactSourceSite,
8
8
  CompactStatus,
9
9
  CompactTraceObserver,
10
- } from './014-compact-contract.js';
10
+ } from './compact-contract.js';
11
11
  import { implementationHintSuggestionProjection } from
12
12
  './implementation-hints.js';
13
13
  import {
14
14
  isSafeCompactReferenceName,
15
15
  projectCompactReferenceGroup,
16
- } from './021-compact-decision-normalization.js';
16
+ } from './compact-decision-normalization.js';
17
17
 
18
18
  export interface TraceEdgeSemantics {
19
19
  source: CompactSemanticEndpoint;
@@ -1,5 +1,5 @@
1
1
  import type { TraceEdge } from '../types.js';
2
- import type { CompactSemanticEndpoint, CompactStatus } from './014-compact-contract.js';
2
+ import type { CompactSemanticEndpoint, CompactStatus } from './compact-contract.js';
3
3
  import {
4
4
  compactDecisionFromEvidence,
5
5
  compactEvidenceReasonCode,
@@ -17,8 +17,8 @@ import {
17
17
  type SemanticCallRow,
18
18
  type SemanticTargetRow,
19
19
  type TraceEdgeRecorder,
20
- } from './015-trace-edge-recorder.js';
21
- import type { PlannedEventSubscriberTransition } from './011-event-subscriber-traversal.js';
20
+ } from './trace-edge-recorder.js';
21
+ import type { PlannedEventSubscriberTransition } from './event-subscriber-traversal.js';
22
22
  import type { DynamicCandidateBranch } from './dynamic-branches.js';
23
23
 
24
24
  interface ImplementationObservation {
@@ -1,5 +1,5 @@
1
1
  import type { Db } from '../db/connection.js';
2
- import { schemaLifecycleDiagnostic } from '../db/001-fact-lifecycle.js';
2
+ import { schemaLifecycleDiagnostic } from '../db/fact-lifecycle.js';
3
3
  import type {
4
4
  TraceEdge,
5
5
  TraceOptions,
@@ -13,21 +13,21 @@ import {
13
13
  import {
14
14
  loadTraceDiagnostics,
15
15
  prependTraceDiagnostic,
16
- } from './002-trace-diagnostics.js';
17
- import { TraversalScopeScheduler } from './010-traversal-scope.js';
18
- import { createTraceRootPlan } from './013-trace-root-scopes.js';
19
- import type { CompactTraceObserver } from './014-compact-contract.js';
20
- import { TraceEdgeRecorder } from './015-trace-edge-recorder.js';
21
- import type { ImplementationHintOptions } from './025-trace-implementation-scope.js';
16
+ } from './trace-diagnostics.js';
17
+ import { TraversalScopeScheduler } from './traversal-scope.js';
18
+ import { createTraceRootPlan } from './trace-root-scopes.js';
19
+ import type { CompactTraceObserver } from './compact-contract.js';
20
+ import { TraceEdgeRecorder } from './trace-edge-recorder.js';
21
+ import type { ImplementationHintOptions } from './trace-implementation-scope.js';
22
22
  import {
23
23
  resolveTraceStartScope,
24
24
  type TraceStartScope,
25
- } from './026-trace-start-scope.js';
25
+ } from './trace-start-scope.js';
26
26
  import {
27
27
  executeTraceScopes,
28
28
  type TraceExecutionRuntime,
29
- } from './027-trace-scope-execution.js';
30
- import { recordTraceStartImplementation } from './029-trace-start-implementation.js';
29
+ } from './trace-scope-execution.js';
30
+ import { recordTraceStartImplementation } from './trace-start-implementation.js';
31
31
  import { selectorNotFoundDiagnostic } from './selectors.js';
32
32
 
33
33
  const compactObserverKey = Symbol('service-flow.compact-trace-observer');
@@ -1,6 +1,6 @@
1
1
  import type { Db } from '../db/connection.js';
2
- import { currentFactLifecycleDiagnostic } from '../db/001-fact-lifecycle.js';
3
- import { resolveTraversalWorkspaceId } from './010-traversal-scope.js';
2
+ import { currentFactLifecycleDiagnostic } from '../db/fact-lifecycle.js';
3
+ import { resolveTraversalWorkspaceId } from './traversal-scope.js';
4
4
 
5
5
  function workspaceScopeIsAmbiguous(db: Db): boolean {
6
6
  const rows = db.prepare(`SELECT DISTINCT workspace_id workspaceId
@@ -1,6 +1,6 @@
1
1
  import type { Db } from '../db/connection.js';
2
2
  import type { ImplementationHint } from '../types.js';
3
- import type { TraceGraphEdgeRow } from './012-trace-graph-lookups.js';
3
+ import type { TraceGraphEdgeRow } from './trace-graph-lookups.js';
4
4
 
5
5
  export interface ImplementationHintOptions {
6
6
  implementationRepo?: string;
@@ -1,34 +1,34 @@
1
1
  import type { Db } from '../db/connection.js';
2
2
  import type { TraceEdge } from '../types.js';
3
3
  import { implementationHintDiagnostic } from './implementation-hints.js';
4
- import { contextualImplementationSelection } from './005-implementation-selection.js';
5
- import type { ContextBinding } from './008-contextual-runtime-state.js';
4
+ import { contextualImplementationSelection } from './implementation-selection.js';
5
+ import type { ContextBinding } from './contextual-runtime-state.js';
6
6
  import {
7
7
  handlerMethodNode,
8
8
  withSelectedHandlerProvenance,
9
9
  type SelectedHandlerEvidence,
10
- } from './009-selected-handler-provenance.js';
11
- import type { TraversalScopeState } from './010-traversal-scope.js';
12
- import type { TraceGraphEdgeRow } from './012-trace-graph-lookups.js';
10
+ } from './selected-handler-provenance.js';
11
+ import type { TraversalScopeState } from './traversal-scope.js';
12
+ import type { TraceGraphEdgeRow } from './trace-graph-lookups.js';
13
13
  import {
14
14
  enqueueCausalScope,
15
15
  type TraceQueueScope,
16
- } from './013-trace-root-scopes.js';
17
- import { parseTraceEvidence } from './017-trace-context.js';
16
+ } from './trace-root-scopes.js';
17
+ import { parseTraceEvidence } from './trace-context.js';
18
18
  import {
19
19
  recordCycleObservation,
20
20
  recordImplementationObservation,
21
- } from './019-trace-edge-semantics.js';
21
+ } from './trace-edge-semantics.js';
22
22
  import {
23
23
  handlerFilesForOperation,
24
24
  handlerScope,
25
25
  implementationScope,
26
- } from './025-trace-implementation-scope.js';
26
+ } from './trace-implementation-scope.js';
27
27
  import type {
28
28
  CallRow,
29
29
  EffectiveOutbound,
30
30
  TraceExecutionRuntime,
31
- } from './027-trace-scope-execution.js';
31
+ } from './trace-scope-execution.js';
32
32
 
33
33
  interface OperationSelection {
34
34
  implementation: ReturnType<typeof implementationScope>;
@@ -1,11 +1,11 @@
1
1
  import type { Db } from '../db/connection.js';
2
- import { currentFactLifecycleDiagnostic } from '../db/001-fact-lifecycle.js';
3
- import type { ContextBinding } from './008-contextual-runtime-state.js';
2
+ import { currentFactLifecycleDiagnostic } from '../db/fact-lifecycle.js';
3
+ import type { ContextBinding } from './contextual-runtime-state.js';
4
4
  import {
5
5
  resolveTraversalWorkspaceId,
6
6
  type TraversalScopeScheduler,
7
7
  type TraversalScopeState,
8
- } from './010-traversal-scope.js';
8
+ } from './traversal-scope.js';
9
9
 
10
10
  export interface TraceQueueScope {
11
11
  repoId?: number;
@@ -12,32 +12,31 @@ import {
12
12
  type ContextBinding,
13
13
  type ContextualGraphRow,
14
14
  type ContextualRuntimeResolution,
15
- } from './008-contextual-runtime-state.js';
15
+ } from './contextual-runtime-state.js';
16
16
  import {
17
17
  TraversalScopeScheduler,
18
18
  type TraversalScopeState,
19
- } from './010-traversal-scope.js';
20
- import type { PlannedEventSubscriberTransition } from './011-event-subscriber-traversal.js';
19
+ } from './traversal-scope.js';
20
+ import type { PlannedEventSubscriberTransition } from './event-subscriber-traversal.js';
21
21
  import {
22
22
  graphForCalls,
23
- operationNode,
24
23
  symbolNode,
25
24
  type TraceGraphEdgeRow,
26
- } from './012-trace-graph-lookups.js';
25
+ } from './trace-graph-lookups.js';
27
26
  import {
28
27
  enqueueCausalScope,
29
28
  nextPendingRoot,
30
29
  type PendingTraceRootScope,
31
30
  type TraceQueueScope,
32
- } from './013-trace-root-scopes.js';
33
- import type { CompactSemanticEndpoint } from './014-compact-contract.js';
34
- import { TraceEdgeRecorder } from './015-trace-edge-recorder.js';
31
+ } from './trace-root-scopes.js';
32
+ import type { CompactSemanticEndpoint } from './compact-contract.js';
33
+ import { TraceEdgeRecorder } from './trace-edge-recorder.js';
35
34
  import {
36
35
  knownBindingsForCalls,
37
36
  knownBindingsForScope,
38
37
  parseTraceEvidence,
39
38
  receiverFromTraceEvidence,
40
- } from './017-trace-context.js';
39
+ } from './trace-context.js';
41
40
  import {
42
41
  recordCycleObservation,
43
42
  recordDynamicBranchObservation,
@@ -45,12 +44,18 @@ import {
45
44
  recordEventCycleObservation,
46
45
  recordLocalCallObservation,
47
46
  recordOutboundObservation,
48
- } from './019-trace-edge-semantics.js';
49
- import { outboundScopeSymbolIds } from './023-nested-event-scopes.js';
50
- import type { ImplementationHintOptions } from './025-trace-implementation-scope.js';
51
- import { processOperationTarget } from './028-trace-operation-execution.js';
52
- import { runtimeEventResolution, runtimeEventSubscriberPlans } from './030-event-runtime-resolution.js';
53
- import { planLocalCallExpansion } from './031-local-call-expansion.js';
47
+ } from './trace-edge-semantics.js';
48
+ import { outboundScopeSymbolIds } from './nested-event-scopes.js';
49
+ import type { ImplementationHintOptions } from './trace-implementation-scope.js';
50
+ import { processOperationTarget } from './trace-operation-execution.js';
51
+ import { runtimeEventResolution, runtimeEventSubscriberPlans } from './event-runtime-resolution.js';
52
+ import { planLocalCallExpansion } from './local-call-expansion.js';
53
+ import {
54
+ eventShapeRuntimeEvidence,
55
+ outboundTraceEdgeType,
56
+ outboundTraceTargetNode,
57
+ visibleEventShapeRows,
58
+ } from './event-shape-candidate-trace.js';
54
59
 
55
60
  export interface CallRow extends Record<string, unknown> {
56
61
  id: number;
@@ -90,16 +95,6 @@ export interface TraceExecutionRuntime {
90
95
  recorder: TraceEdgeRecorder;
91
96
  }
92
97
 
93
- function traceEdgeType(call: CallRow, row: TraceGraphRow): string {
94
- if (row.to_kind === 'operation'
95
- && row.edge_type === 'REMOTE_CALL_RESOLVES_TO_OPERATION')
96
- return 'remote_action';
97
- if (row.to_kind === 'operation'
98
- && row.edge_type === 'LOCAL_CALL_RESOLVES_TO_OPERATION')
99
- return 'local_service_call';
100
- return String(call.call_type);
101
- }
102
-
103
98
  function includeCall(type: string, options: TraceOptions): boolean {
104
99
  if (!options.includeDb && type === 'local_db_query') return false;
105
100
  if (!options.includeExternal && type === 'external_http') return false;
@@ -339,7 +334,9 @@ function processOutboundCall(
339
334
  const contextual = contextualRuntimeResolution(
340
335
  runtime.db, call, bindings.get(receiver), call.workspaceId, persistedRows,
341
336
  );
342
- const rows = contextual.row ? [contextual.row] : persistedRows;
337
+ const rows = contextual.row
338
+ ? [contextual.row]
339
+ : visibleEventShapeRows(persistedRows, runtime.options);
343
340
  for (const row of rows)
344
341
  processOutboundRow(runtime, current, call, { ...row }, contextual);
345
342
  }
@@ -385,16 +382,23 @@ function recordEffectiveOutbound(
385
382
  ): EffectiveOutbound {
386
383
  const persisted = parseTraceEvidence(row.evidence_json);
387
384
  const raw = baseTraceEvidence(row, call, persisted, contextual.evidence);
388
- const effective = runtimeEventResolution(row, raw, runtime.options.vars)
385
+ const resolved = runtimeEventResolution(row, raw, runtime.options.vars)
389
386
  ?? runtimeResolution(runtime.db, row, raw, {
390
387
  vars: runtime.options.vars,
391
388
  dynamicMode: runtime.options.dynamicMode ?? 'strict',
392
389
  maxDynamicCandidates: runtime.options.maxDynamicCandidates,
393
390
  }, call.workspaceId, contextual.state);
391
+ const effective = {
392
+ ...resolved,
393
+ evidence: eventShapeRuntimeEvidence(
394
+ runtime.db, call.workspaceId, call.id, call.call_type,
395
+ resolved.evidence, runtime.options.vars,
396
+ ),
397
+ };
394
398
  const target = `${effective.row.to_kind}:${effective.row.to_id}`;
395
- const operation = effective.row.to_kind === 'operation'
396
- ? operationNode(runtime.db, effective.row.to_id) : undefined;
397
- runtime.nodes.set(target, operation ?? targetNode(target, effective.row));
399
+ runtime.nodes.set(
400
+ target, outboundTraceTargetNode(runtime.db, target, effective.row),
401
+ );
398
402
  const to = edgeTarget(effective.row, effective.evidence);
399
403
  const edge = outboundTraceEdge(
400
404
  current.depth, call, effective.row, to,
@@ -418,18 +422,6 @@ function recordEffectiveOutbound(
418
422
  };
419
423
  }
420
424
 
421
- function targetNode(
422
- id: string,
423
- row: TraceGraphRow,
424
- ): Record<string, unknown> {
425
- return {
426
- id,
427
- kind: row.to_kind,
428
- label: row.to_kind === 'db_entity'
429
- ? `Entity: ${row.to_id || 'unknown'}` : row.to_id,
430
- };
431
- }
432
-
433
425
  function outboundTraceEdge(
434
426
  depth: number,
435
427
  call: CallRow,
@@ -440,7 +432,7 @@ function outboundTraceEdge(
440
432
  ): TraceEdge {
441
433
  return {
442
434
  step: depth,
443
- type: traceEdgeType(call, row),
435
+ type: outboundTraceEdgeType(call, row),
444
436
  from: `${call.repoName}:${call.source_file}:${call.source_line}`,
445
437
  to,
446
438
  evidence,