@saptools/service-flow 0.1.68 → 0.1.70

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 (124) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +11 -5
  3. package/TECHNICAL-NOTE.md +13 -0
  4. package/dist/{chunk-AEM4JY22.js → chunk-GSLFY6J2.js} +6986 -5503
  5. package/dist/chunk-GSLFY6J2.js.map +1 -0
  6. package/dist/cli.js +334 -122
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.ts +7 -1
  9. package/dist/index.js +1 -1
  10. package/package.json +1 -1
  11. package/src/cli/{002-doctor-lifecycle.ts → doctor-lifecycle.ts} +1 -1
  12. package/src/cli/{001-doctor-projection.ts → doctor-projection.ts} +1 -1
  13. package/src/cli/doctor.ts +5 -5
  14. package/src/cli/index-summary.ts +22 -0
  15. package/src/cli.ts +155 -91
  16. package/src/db/{009-binding-fact-semantics.ts → binding-fact-semantics.ts} +6 -1
  17. package/src/db/binding-helper-provenance.ts +17 -0
  18. package/src/db/{012-binding-reference-proof.ts → binding-reference-proof.ts} +4 -0
  19. package/src/db/{000-call-fact-repository.ts → call-fact-repository.ts} +49 -23
  20. package/src/db/{003-current-fact-semantics.ts → current-fact-semantics.ts} +5 -4
  21. package/src/db/{001-fact-lifecycle.ts → fact-lifecycle.ts} +3 -3
  22. package/src/db/index-publication-failure.ts +91 -0
  23. package/src/db/{007-package-fact-semantics.ts → package-fact-semantics.ts} +6 -6
  24. package/src/db/{010-package-symbol-surface-semantics.ts → package-symbol-surface-semantics.ts} +2 -2
  25. package/src/db/{004-package-target-invalidation.ts → package-target-invalidation.ts} +11 -1
  26. package/src/db/{008-relative-fact-semantics.ts → relative-fact-semantics.ts} +9 -6
  27. package/src/db/{006-relative-symbol-resolution.ts → relative-symbol-resolution.ts} +28 -7
  28. package/src/db/repositories.ts +24 -7
  29. package/src/db/{011-symbol-call-semantics.ts → symbol-call-semantics.ts} +1 -1
  30. package/src/index.ts +3 -3
  31. package/src/indexer/cds-extension-resolver.ts +27 -3
  32. package/src/indexer/repository-indexer.ts +70 -9
  33. package/src/indexer/workspace-indexer.ts +142 -30
  34. package/src/linker/call-edge-insertion.ts +568 -0
  35. package/src/linker/{002-call-evidence.ts → call-evidence.ts} +2 -2
  36. package/src/linker/cross-repo-linker.ts +6 -169
  37. package/src/linker/dynamic-edge-resolver.ts +1 -1
  38. package/src/linker/{004-event-subscription-handler-linker.ts → event-subscription-handler-linker.ts} +32 -11
  39. package/src/linker/event-template-link.ts +72 -0
  40. package/src/linker/external-http-target.ts +1 -1
  41. package/src/linker/helper-package-linker.ts +1 -1
  42. package/src/linker/{000-implementation-candidates.ts → implementation-candidates.ts} +1 -1
  43. package/src/linker/{001-implementation-evidence-projection.ts → implementation-evidence-projection.ts} +1 -1
  44. package/src/linker/odata-path-normalizer.ts +1 -1
  45. package/src/linker/{005-odata-path-structure.ts → odata-path-structure.ts} +1 -1
  46. package/src/linker/{003-package-import-symbol-resolver.ts → package-import-symbol-resolver.ts} +3 -3
  47. package/src/linker/service-resolver.ts +2 -2
  48. package/src/output/{001-compact-json-output.ts → compact-json-output.ts} +1 -1
  49. package/src/parsers/{006-binding-identity.ts → binding-identity.ts} +9 -4
  50. package/src/parsers/{011-binding-lexical-scope.ts → binding-lexical-scope.ts} +1 -1
  51. package/src/parsers/{021-binding-visibility.ts → binding-visibility.ts} +18 -2
  52. package/src/parsers/{000-direct-query-execution.ts → direct-query-execution.ts} +11 -0
  53. package/src/parsers/{005-event-subscription-facts.ts → event-subscription-facts.ts} +2 -2
  54. package/src/parsers/{016-local-symbol-reference.ts → local-symbol-reference.ts} +2 -2
  55. package/src/parsers/operation-path-analysis.ts +1 -1
  56. package/src/parsers/outbound-call-classifier.ts +692 -0
  57. package/src/parsers/outbound-call-parser.ts +146 -509
  58. package/src/parsers/outbound-expression-analysis.ts +700 -0
  59. package/src/parsers/{018-package-commonjs-syntax.ts → package-commonjs-syntax.ts} +1 -1
  60. package/src/parsers/{012-package-fact-contract.ts → package-fact-contract.ts} +3 -3
  61. package/src/parsers/{010-package-public-surface-analysis.ts → package-public-surface-analysis.ts} +2 -2
  62. package/src/parsers/{003-package-public-surface.ts → package-public-surface.ts} +3 -3
  63. package/src/parsers/{008-package-surface-publication.ts → package-surface-publication.ts} +1 -1
  64. package/src/parsers/{001-query-entity-resolution.ts → query-entity-resolution.ts} +1 -1
  65. package/src/parsers/{015-service-binding-collector.ts → service-binding-collector.ts} +3 -3
  66. package/src/parsers/{014-service-binding-helper-flow.ts → service-binding-helper-flow.ts} +72 -6
  67. package/src/parsers/service-binding-parser-helpers.ts +1 -1
  68. package/src/parsers/service-binding-parser.ts +2 -2
  69. package/src/parsers/{007-source-fact-reconciliation.ts → source-fact-reconciliation.ts} +3 -3
  70. package/src/parsers/{020-stable-local-value.ts → stable-local-value.ts} +2 -2
  71. package/src/parsers/{009-symbol-call-facts.ts → symbol-call-facts.ts} +3 -3
  72. package/src/parsers/{017-symbol-derived-contexts.ts → symbol-derived-contexts.ts} +2 -2
  73. package/src/parsers/symbol-parser.ts +43 -11
  74. package/src/trace/{014-compact-contract.ts → compact-contract.ts} +6 -0
  75. package/src/trace/{021-compact-decision-normalization.ts → compact-decision-normalization.ts} +77 -11
  76. package/src/trace/{020-compact-field-projection.ts → compact-field-projection.ts} +77 -4
  77. package/src/trace/{024-compact-observation-decision.ts → compact-observation-decision.ts} +10 -5
  78. package/src/trace/{016-compact-projector.ts → compact-projector.ts} +6 -7
  79. package/src/trace/{018-compact-trace.ts → compact-trace.ts} +3 -3
  80. package/src/trace/{006-contextual-projection.ts → contextual-projection.ts} +1 -1
  81. package/src/trace/{008-contextual-runtime-state.ts → contextual-runtime-state.ts} +1 -1
  82. package/src/trace/{001-dynamic-identity.ts → dynamic-identity.ts} +2 -2
  83. package/src/trace/{003-dynamic-references.ts → dynamic-references.ts} +2 -2
  84. package/src/trace/dynamic-targets.ts +6 -6
  85. package/src/trace/event-runtime-resolution.ts +151 -0
  86. package/src/trace/{011-event-subscriber-traversal.ts → event-subscriber-traversal.ts} +101 -9
  87. package/src/trace/evidence.ts +2 -2
  88. package/src/trace/implementation-hints.ts +10 -7
  89. package/src/trace/{005-implementation-selection.ts → implementation-selection.ts} +2 -2
  90. package/src/trace/{007-implementation-start-diagnostic.ts → implementation-start-diagnostic.ts} +1 -0
  91. package/src/trace/local-call-expansion.ts +37 -0
  92. package/src/trace/{009-selected-handler-provenance.ts → selected-handler-provenance.ts} +1 -1
  93. package/src/trace/selectors.ts +2 -1
  94. package/src/trace/{017-trace-context.ts → trace-context.ts} +2 -2
  95. package/src/trace/{015-trace-edge-recorder.ts → trace-edge-recorder.ts} +62 -5
  96. package/src/trace/{019-trace-edge-semantics.ts → trace-edge-semantics.ts} +9 -13
  97. package/src/trace/trace-engine.ts +10 -10
  98. package/src/trace/{022-trace-fact-preflight.ts → trace-fact-preflight.ts} +2 -2
  99. package/src/trace/{025-trace-implementation-scope.ts → trace-implementation-scope.ts} +1 -1
  100. package/src/trace/{028-trace-operation-execution.ts → trace-operation-execution.ts} +10 -10
  101. package/src/trace/{013-trace-root-scopes.ts → trace-root-scopes.ts} +5 -6
  102. package/src/trace/{027-trace-scope-execution.ts → trace-scope-execution.ts} +44 -44
  103. package/src/trace/{029-trace-start-implementation.ts → trace-start-implementation.ts} +7 -7
  104. package/src/trace/{026-trace-start-scope.ts → trace-start-scope.ts} +6 -5
  105. package/src/trace/{010-traversal-scope.ts → traversal-scope.ts} +1 -1
  106. package/src/types.ts +2 -1
  107. package/src/version.ts +1 -1
  108. package/dist/chunk-AEM4JY22.js.map +0 -1
  109. /package/src/cli/{000-clean.ts → clean.ts} +0 -0
  110. /package/src/cli/{003-doctor-package-resolution.ts → doctor-package-resolution.ts} +0 -0
  111. /package/src/db/{002-fact-json-inventory.ts → fact-json-inventory.ts} +0 -0
  112. /package/src/db/{005-schema-structure.ts → schema-structure.ts} +0 -0
  113. /package/src/output/{000-stdout-policy.ts → stdout-policy.ts} +0 -0
  114. /package/src/parsers/{019-binding-assignment-targets.ts → binding-assignment-targets.ts} +0 -0
  115. /package/src/parsers/{013-executable-body-eligibility.ts → executable-body-eligibility.ts} +0 -0
  116. /package/src/parsers/{004-fact-identity.ts → fact-identity.ts} +0 -0
  117. /package/src/parsers/{002-symbol-import-bindings.ts → symbol-import-bindings.ts} +0 -0
  118. /package/src/trace/{004-dynamic-candidate-sources.ts → dynamic-candidate-sources.ts} +0 -0
  119. /package/src/trace/{000-dynamic-target-types.ts → dynamic-target-types.ts} +0 -0
  120. /package/src/trace/{023-nested-event-scopes.ts → nested-event-scopes.ts} +0 -0
  121. /package/src/trace/{002-trace-diagnostics.ts → trace-diagnostics.ts} +0 -0
  122. /package/src/trace/{012-trace-graph-lookups.ts → trace-graph-lookups.ts} +0 -0
  123. /package/src/utils/{000-bounded-projection.ts → bounded-projection.ts} +0 -0
  124. /package/src/utils/{001-placeholders.ts → placeholders.ts} +0 -0
@@ -1,5 +1,6 @@
1
1
  import type { ImplementationHint } from '../types.js';
2
- import { projectBounded } from '../utils/000-bounded-projection.js';
2
+ import { projectBounded } from '../utils/bounded-projection.js';
3
+ import { compareBinary } from './traversal-scope.js';
3
4
 
4
5
  interface Candidate {
5
6
  accepted?: boolean;
@@ -106,6 +107,7 @@ export function implementationHintSuggestions(rawEvidence: Record<string, unknow
106
107
 
107
108
  export function implementationHintSuggestionProjection(
108
109
  rawEvidence: Record<string, unknown>,
110
+ limit?: number,
109
111
  ): ImplementationHintSuggestionProjection {
110
112
  const evidence = asEvidence(rawEvidence);
111
113
  const accepted = (evidence.candidates ?? []).filter((candidate) => candidate.accepted);
@@ -119,7 +121,7 @@ export function implementationHintSuggestionProjection(
119
121
  }
120
122
  const repos = selectableRepositories(accepted);
121
123
  const repositoryProjection = projectBounded(
122
- repos, (left, right) => left.localeCompare(right),
124
+ repos, compareBinary, limit,
123
125
  );
124
126
  const suggestions = accepted
125
127
  .flatMap((candidate) => {
@@ -142,7 +144,7 @@ export function implementationHintSuggestionProjection(
142
144
  cli: `--implementation-hint ${hintString(hint)}`,
143
145
  }];
144
146
  });
145
- const projection = projectBounded(suggestions, compareSuggestion);
147
+ const projection = projectBounded(suggestions, compareSuggestion, limit);
146
148
  return {
147
149
  suggestions: projection.items,
148
150
  suggestionCount: projection.totalCount,
@@ -170,15 +172,16 @@ function projectedSuggestions(value: unknown): ImplementationHintSuggestionProje
170
172
  }
171
173
 
172
174
  function compareHints(left: ImplementationHint, right: ImplementationHint): number {
173
- return hintString(left).localeCompare(hintString(right));
175
+ return compareBinary(hintString(left), hintString(right));
174
176
  }
175
177
 
176
178
  function compareSuggestion(
177
179
  left: Record<string, unknown>,
178
180
  right: Record<string, unknown>,
179
181
  ): number {
180
- return String(left.cli ?? '').localeCompare(String(right.cli ?? ''))
181
- || String(left.implementationRepo ?? '').localeCompare(
182
+ return compareBinary(String(left.cli ?? ''), String(right.cli ?? ''))
183
+ || compareBinary(
184
+ String(left.implementationRepo ?? ''),
182
185
  String(right.implementationRepo ?? ''),
183
186
  );
184
187
  }
@@ -202,7 +205,7 @@ function selectableRepositories(candidates: Candidate[]): string[] {
202
205
  const repos = new Set(candidates.flatMap((candidate) => candidate.handlerPackage?.name ? [candidate.handlerPackage.name] : []));
203
206
  return [...repos]
204
207
  .filter((repo) => candidates.filter((candidate) => candidateMatchesRepo(candidate, repo)).length === 1)
205
- .sort();
208
+ .sort(compareBinary);
206
209
  }
207
210
 
208
211
  function assignHintField(hint: Partial<ImplementationHint>, key: string, value: string): void {
@@ -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,
@@ -15,6 +15,7 @@ export function implementationStartDiagnostic(
15
15
  message: `Indexed operation matched but implementation edge is ${String(
16
16
  edge.status ?? 'unresolved',
17
17
  )}`,
18
+ selectorKind: 'operation',
18
19
  resolutionStage: 'implementation',
19
20
  resolutionStatus: edge.status === 'ambiguous'
20
21
  ? 'ambiguous_implementation'
@@ -0,0 +1,37 @@
1
+ import type { Db } from '../db/connection.js';
2
+ import {
3
+ type ContextBinding,
4
+ } from './contextual-runtime-state.js';
5
+ import {
6
+ type TraversalScheduleDecision,
7
+ type TraversalScopeScheduler,
8
+ type TraversalScopeState,
9
+ } from './traversal-scope.js';
10
+ import { contextForSymbolCall } from './trace-context.js';
11
+
12
+ export interface LocalCallExpansion {
13
+ repoId: number;
14
+ files: Set<string>;
15
+ symbols: Set<number>;
16
+ context: Map<string, ContextBinding>;
17
+ scheduling: TraversalScheduleDecision;
18
+ }
19
+
20
+ export function planLocalCallExpansion(
21
+ db: Db,
22
+ scheduler: TraversalScopeScheduler,
23
+ workspaceId: number | undefined,
24
+ parent: TraversalScopeState,
25
+ row: Record<string, unknown>,
26
+ bindings: Map<string, ContextBinding>,
27
+ symbolId: number,
28
+ ): LocalCallExpansion {
29
+ const symbols = new Set([symbolId]);
30
+ const files = new Set([String(row.calleeFile)]);
31
+ const repoId = Number(row.calleeRepoId);
32
+ const context = contextForSymbolCall(db, row, bindings);
33
+ const scheduling = scheduler.schedule({
34
+ workspaceId, repoId, files, symbolIds: symbols, context,
35
+ }, parent);
36
+ return { repoId, files, symbols, context, scheduling };
37
+ }
@@ -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>;
@@ -550,6 +550,7 @@ export function ambiguousStartDiagnostic(
550
550
  severity: 'warning',
551
551
  code: 'trace_start_ambiguous',
552
552
  message,
553
+ selectorKind: 'operation',
553
554
  normalizedSelectorValue: requested,
554
555
  resolutionStage: 'operation',
555
556
  resolutionStatus: 'ambiguous_operation',
@@ -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,7 +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
+ import { implementationHintSuggestionProjection } from
12
+ './implementation-hints.js';
13
+ import {
14
+ isSafeCompactReferenceName,
15
+ projectCompactReferenceGroup,
16
+ } from './compact-decision-normalization.js';
11
17
 
12
18
  export interface TraceEdgeSemantics {
13
19
  source: CompactSemanticEndpoint;
@@ -111,8 +117,7 @@ export function semanticGraphTarget(
111
117
  ): CompactSemanticEndpoint {
112
118
  if (row.to_kind === 'event') return {
113
119
  kind: 'event', workspaceId,
114
- eventName: typeof call.event_name_expr === 'string'
115
- ? call.event_name_expr : row.to_id,
120
+ eventName: row.to_id,
116
121
  };
117
122
  const id = positiveNumber(row.to_id);
118
123
  if (row.to_kind === 'operation')
@@ -198,7 +203,7 @@ export function compactDecisionFromEvidence(
198
203
  const authoritativeMissingCount = finiteNumber(
199
204
  dynamic.missingVariableCount ?? evidence.missingVariableCount,
200
205
  );
201
- return {
206
+ const decision: CompactDecisionInput = {
202
207
  effectiveResolutionStatus: stringValue(effective.status),
203
208
  effectiveTarget: targetSummary(effective),
204
209
  persistedResolutionStatus: stringValue(persisted.status),
@@ -221,7 +226,7 @@ export function compactDecisionFromEvidence(
221
226
  implementationGuided: booleanValue(implementation.guided),
222
227
  implementationContextual: booleanValue(
223
228
  evidence.contextualImplementationSelected),
224
- reasonCode: stringValue(evidence.reasonCode ?? evidence.cycleReason),
229
+ reasonCode: compactEvidenceReasonCode(evidence),
225
230
  eventMatchStrategy: stringValue(evidence.matchStrategy),
226
231
  dispatchCertainty: stringValue(evidence.dispatchCertainty),
227
232
  associationStatus: stringValue(evidence.associationStatus),
@@ -233,6 +238,7 @@ export function compactDecisionFromEvidence(
233
238
  bodyExpansion: stringValue(evidence.bodyExpansion),
234
239
  ...overrides,
235
240
  };
241
+ return decisionWithTiedCandidates(evidence, decision);
236
242
  }
237
243
 
238
244
  export function compactRefs(
@@ -334,3 +340,54 @@ function firstNumber(...values: unknown[]): number | undefined {
334
340
  }
335
341
  return undefined;
336
342
  }
343
+
344
+ export function compactEvidenceReasonCode(
345
+ evidence: Record<string, unknown>,
346
+ ): string | undefined {
347
+ return stringValue(evidence.reasonCode ?? evidence.cycleReason)
348
+ ?? parserWarningReason(evidence.parserWarning);
349
+ }
350
+
351
+ function parserWarningReason(value: unknown): string | undefined {
352
+ const warning = recordValue(value);
353
+ const code = stringValue(warning.code);
354
+ if (code && code !== 'parser_warning') return code;
355
+ return stringValue(warning.message);
356
+ }
357
+
358
+ function tiedCandidateRepositoryGroup(
359
+ evidence: Record<string, unknown>,
360
+ decision: CompactDecisionInput,
361
+ ): CompactDecisionInput['tiedCandidateRepos'] {
362
+ if (decision.effectiveResolutionStatus !== 'ambiguous'
363
+ || (decision.candidateCount ?? 0) <= 1) return undefined;
364
+ const persisted = Array.isArray(evidence.implementationHintSuggestions)
365
+ ? evidence.implementationHintSuggestions.filter(
366
+ (value): value is Record<string, unknown> =>
367
+ Boolean(value && typeof value === 'object' && !Array.isArray(value)),
368
+ )
369
+ : [];
370
+ const projection = persisted.length > 0 ? {
371
+ suggestions: persisted,
372
+ suggestionCount: finiteNumber(
373
+ evidence.implementationHintSuggestionCount,
374
+ ) ?? persisted.length,
375
+ } : implementationHintSuggestionProjection(
376
+ evidence, Number.MAX_SAFE_INTEGER,
377
+ );
378
+ const repos = projection.suggestions.flatMap((suggestion) =>
379
+ typeof suggestion.implementationRepo === 'string'
380
+ ? [suggestion.implementationRepo] : []);
381
+ const group = projectCompactReferenceGroup(
382
+ repos, projection.suggestionCount, isSafeCompactReferenceName,
383
+ );
384
+ return group && group.total > 1 ? group : undefined;
385
+ }
386
+
387
+ function decisionWithTiedCandidates(
388
+ evidence: Record<string, unknown>,
389
+ decision: CompactDecisionInput,
390
+ ): CompactDecisionInput {
391
+ const tiedCandidateRepos = tiedCandidateRepositoryGroup(evidence, decision);
392
+ return tiedCandidateRepos ? { ...decision, tiedCandidateRepos } : decision;
393
+ }
@@ -1,7 +1,8 @@
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
+ compactEvidenceReasonCode,
5
6
  compactEventStatus,
6
7
  compactGraphStatus,
7
8
  compactRefs,
@@ -16,8 +17,8 @@ import {
16
17
  type SemanticCallRow,
17
18
  type SemanticTargetRow,
18
19
  type TraceEdgeRecorder,
19
- } from './015-trace-edge-recorder.js';
20
- import type { PlannedEventSubscriberTransition } from './011-event-subscriber-traversal.js';
20
+ } from './trace-edge-recorder.js';
21
+ import type { PlannedEventSubscriberTransition } from './event-subscriber-traversal.js';
21
22
  import type { DynamicCandidateBranch } from './dynamic-branches.js';
22
23
 
23
24
  interface ImplementationObservation {
@@ -157,11 +158,11 @@ export function recordOutboundObservation(
157
158
  source, target,
158
159
  status: compactGraphStatus(input.row, input.evidence,
159
160
  input.unresolvedReason, input.dynamicMode),
160
- decision: compactDecisionFromEvidence(input.evidence, {
161
- reasonCode: input.unresolvedReason
162
- ? safeReasonCode(input.evidence.reasonCode, 'outbound_target_unresolved')
163
- : undefined,
164
- }),
161
+ decision: compactDecisionFromEvidence(input.evidence,
162
+ input.unresolvedReason ? {
163
+ reasonCode: compactEvidenceReasonCode(input.evidence)
164
+ ?? 'outbound_target_unresolved',
165
+ } : {}),
165
166
  refs: compactRefs({
166
167
  graphEdgeId: positiveId(input.evidence.persistedGraphEdgeId),
167
168
  outboundCallId: input.call.id,
@@ -295,11 +296,6 @@ function eventSite(
295
296
  endOffset: plan.transition.callSiteEndOffset });
296
297
  }
297
298
 
298
- function safeReasonCode(value: unknown, fallback: string): string {
299
- return typeof value === 'string' && /^[a-z][a-z0-9_.-]{0,79}$/.test(value)
300
- ? value : fallback;
301
- }
302
-
303
299
  function positiveId(value: unknown): number | string | undefined {
304
300
  if (typeof value === 'number') return Number.isFinite(value) && value > 0
305
301
  ? value : undefined;
@@ -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;
@@ -253,12 +253,11 @@ function hasExactDispatch(db: Db, calls: RootCallRow[]): boolean {
253
253
  AND subscriber.generation=emitted.generation
254
254
  AND subscriber.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
255
255
  AND subscriber.from_kind='event'
256
- AND subscriber.from_id COLLATE BINARY=? COLLATE BINARY)
256
+ AND subscriber.from_id COLLATE BINARY=emitted.to_id COLLATE BINARY)
257
257
  LIMIT 1`);
258
258
  return calls.some((call) => call.callType === 'async_emit'
259
- && typeof call.eventName === 'string'
260
259
  && Boolean(match.get(call.workspaceId, call.graphGeneration,
261
- String(call.id), call.eventName)));
260
+ String(call.id))));
262
261
  }
263
262
 
264
263
  function workspaceAmbiguityDiagnostic(db: Db): Record<string, unknown> {
@@ -12,36 +12,32 @@ 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 {
21
- planEventSubscriberTransitions,
22
- type PlannedEventSubscriberTransition,
23
- } from './011-event-subscriber-traversal.js';
19
+ } from './traversal-scope.js';
20
+ import type { PlannedEventSubscriberTransition } from './event-subscriber-traversal.js';
24
21
  import {
25
22
  graphForCalls,
26
23
  operationNode,
27
24
  symbolNode,
28
25
  type TraceGraphEdgeRow,
29
- } from './012-trace-graph-lookups.js';
26
+ } from './trace-graph-lookups.js';
30
27
  import {
31
28
  enqueueCausalScope,
32
29
  nextPendingRoot,
33
30
  type PendingTraceRootScope,
34
31
  type TraceQueueScope,
35
- } from './013-trace-root-scopes.js';
36
- import type { CompactSemanticEndpoint } from './014-compact-contract.js';
37
- import { TraceEdgeRecorder } from './015-trace-edge-recorder.js';
32
+ } from './trace-root-scopes.js';
33
+ import type { CompactSemanticEndpoint } from './compact-contract.js';
34
+ import { TraceEdgeRecorder } from './trace-edge-recorder.js';
38
35
  import {
39
- contextForSymbolCall,
40
36
  knownBindingsForCalls,
41
37
  knownBindingsForScope,
42
38
  parseTraceEvidence,
43
39
  receiverFromTraceEvidence,
44
- } from './017-trace-context.js';
40
+ } from './trace-context.js';
45
41
  import {
46
42
  recordCycleObservation,
47
43
  recordDynamicBranchObservation,
@@ -49,10 +45,12 @@ import {
49
45
  recordEventCycleObservation,
50
46
  recordLocalCallObservation,
51
47
  recordOutboundObservation,
52
- } from './019-trace-edge-semantics.js';
53
- import { outboundScopeSymbolIds } from './023-nested-event-scopes.js';
54
- import type { ImplementationHintOptions } from './025-trace-implementation-scope.js';
55
- import { processOperationTarget } from './028-trace-operation-execution.js';
48
+ } from './trace-edge-semantics.js';
49
+ import { outboundScopeSymbolIds } from './nested-event-scopes.js';
50
+ import type { ImplementationHintOptions } from './trace-implementation-scope.js';
51
+ import { processOperationTarget } from './trace-operation-execution.js';
52
+ import { runtimeEventResolution, runtimeEventSubscriberPlans } from './event-runtime-resolution.js';
53
+ import { planLocalCallExpansion } from './local-call-expansion.js';
56
54
 
57
55
  export interface CallRow extends Record<string, unknown> {
58
56
  id: number;
@@ -226,20 +224,10 @@ function processLocalCall(
226
224
  bindings: Map<string, ContextBinding>,
227
225
  row: Record<string, unknown>,
228
226
  ): void {
229
- if (!row.callee_symbol_id) return;
230
- const symbolId = Number(row.callee_symbol_id);
231
- const symbols = new Set([symbolId]);
232
- const files = new Set([String(row.calleeFile)]);
233
- const repoId = Number(row.calleeRepoId);
234
- const context = contextForSymbolCall(runtime.db, row, bindings);
235
- const scheduling = runtime.scheduler.schedule({
236
- workspaceId: runtime.workspaceId,
237
- repoId,
238
- files,
239
- symbolIds: symbols,
240
- context,
241
- }, current.state);
242
- const node = symbolNode(runtime.db, symbolId);
227
+ const symbolId = row.callee_symbol_id
228
+ ? Number(row.callee_symbol_id) : undefined;
229
+ const node = symbolId === undefined
230
+ ? undefined : symbolNode(runtime.db, symbolId);
243
231
  if (node) runtime.nodes.set(String(node.id), node);
244
232
  const evidence = localCallEvidence(row, node);
245
233
  const unresolvedReason = localCallUnresolvedReason(row);
@@ -247,6 +235,12 @@ function processLocalCall(
247
235
  const target = recordLocalCallObservation(runtime.recorder, edge, {
248
236
  symbolCall: row, evidence, unresolvedReason,
249
237
  });
238
+ if (symbolId === undefined || row.status !== 'resolved' || !node) return;
239
+ const { repoId, files, symbols, context, scheduling } =
240
+ planLocalCallExpansion(
241
+ runtime.db, runtime.scheduler, runtime.workspaceId, current.state,
242
+ row, bindings, symbolId,
243
+ );
250
244
  if (scheduling.kind === 'cycle')
251
245
  recordLocalCycle(runtime, current, row, target, scheduling.state,
252
246
  repoId, files, symbols);
@@ -291,7 +285,8 @@ function localTraceEdge(
291
285
  type: 'local_symbol_call',
292
286
  from: String(row.callee_expression),
293
287
  to: node?.label
294
- ? String(node.label) : `symbol:${String(row.callee_symbol_id)}`,
288
+ ? String(node.label)
289
+ : `${String(row.status)}:${String(row.callee_expression)}`,
295
290
  evidence,
296
291
  confidence: Number(row.confidence ?? 0.8),
297
292
  unresolvedReason,
@@ -390,11 +385,12 @@ function recordEffectiveOutbound(
390
385
  ): EffectiveOutbound {
391
386
  const persisted = parseTraceEvidence(row.evidence_json);
392
387
  const raw = baseTraceEvidence(row, call, persisted, contextual.evidence);
393
- const effective = runtimeResolution(runtime.db, row, raw, {
394
- vars: runtime.options.vars,
395
- dynamicMode: runtime.options.dynamicMode ?? 'strict',
396
- maxDynamicCandidates: runtime.options.maxDynamicCandidates,
397
- }, call.workspaceId, contextual.state);
388
+ const effective = runtimeEventResolution(row, raw, runtime.options.vars)
389
+ ?? runtimeResolution(runtime.db, row, raw, {
390
+ vars: runtime.options.vars,
391
+ dynamicMode: runtime.options.dynamicMode ?? 'strict',
392
+ maxDynamicCandidates: runtime.options.maxDynamicCandidates,
393
+ }, call.workspaceId, contextual.state);
398
394
  const target = `${effective.row.to_kind}:${effective.row.to_id}`;
399
395
  const operation = effective.row.to_kind === 'operation'
400
396
  ? operationNode(runtime.db, effective.row.to_id) : undefined;
@@ -461,15 +457,19 @@ function processEventTransitions(
461
457
  ): void {
462
458
  if (!runtime.options.includeAsync || call.call_type !== 'async_emit'
463
459
  || effective.row.edge_type !== 'HANDLER_EMITS_EVENT'
464
- || typeof call.event_name_expr !== 'string') return;
465
- const plans = planEventSubscriberTransitions(runtime.db, {
466
- workspaceId: runtime.workspaceId ?? call.workspaceId,
467
- graphGeneration: call.graphGeneration,
468
- eventName: call.event_name_expr,
469
- }, runtime.scheduler, current.state, current.depth, runtime.maxDepth);
470
- for (const plan of plans)
460
+ || effective.row.to_kind !== 'event') return;
461
+ const planned = runtimeEventSubscriberPlans(
462
+ runtime.db, {
463
+ workspaceId: runtime.workspaceId ?? call.workspaceId,
464
+ graphGeneration: call.graphGeneration,
465
+ eventName: effective.row.to_id,
466
+ vars: runtime.options.vars ?? {},
467
+ }, runtime.scheduler, current.state, current.depth, runtime.maxDepth,
468
+ );
469
+ if (planned.diagnostic) runtime.diagnostics.push(planned.diagnostic);
470
+ for (const plan of planned.plans)
471
471
  recordEventTransition(runtime, current, plan,
472
- effective.semanticWorkspaceId, plans.length);
472
+ effective.semanticWorkspaceId, planned.plans.length);
473
473
  }
474
474
 
475
475
  function recordEventTransition(
@@ -1,22 +1,22 @@
1
1
  import type { Db } from '../db/connection.js';
2
2
  import type { TraceEdge } from '../types.js';
3
- import { hintedImplementationSelection } from './005-implementation-selection.js';
3
+ import { hintedImplementationSelection } from './implementation-selection.js';
4
4
  import {
5
5
  handlerMethodNode,
6
6
  withSelectedHandlerProvenance,
7
7
  type SelectedHandlerEvidence,
8
- } from './009-selected-handler-provenance.js';
8
+ } from './selected-handler-provenance.js';
9
9
  import {
10
10
  operationNode,
11
11
  type TraceGraphEdgeRow,
12
- } from './012-trace-graph-lookups.js';
13
- import { parseTraceEvidence } from './017-trace-context.js';
14
- import { recordImplementationObservation } from './019-trace-edge-semantics.js';
12
+ } from './trace-graph-lookups.js';
13
+ import { parseTraceEvidence } from './trace-context.js';
14
+ import { recordImplementationObservation } from './trace-edge-semantics.js';
15
15
  import {
16
16
  handlerScope,
17
17
  implementationScope,
18
- } from './025-trace-implementation-scope.js';
19
- import type { TraceExecutionRuntime } from './027-trace-scope-execution.js';
18
+ } from './trace-implementation-scope.js';
19
+ import type { TraceExecutionRuntime } from './trace-scope-execution.js';
20
20
 
21
21
  interface StartImplementationSelection {
22
22
  edge: TraceGraphEdgeRow;