@veewo/gitnexus 1.4.9 → 1.4.11-rc
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.
- package/dist/benchmark/u2-e2e/live-evidence-validator.d.ts +19 -0
- package/dist/benchmark/u2-e2e/live-evidence-validator.js +87 -0
- package/dist/benchmark/u2-e2e/live-evidence-validator.test.d.ts +1 -0
- package/dist/benchmark/u2-e2e/live-evidence-validator.test.js +33 -0
- package/dist/benchmark/u2-e2e/neonspark-full-e2e.js +23 -4
- package/dist/benchmark/u2-e2e/reload-v1-acceptance-runner.d.ts +38 -0
- package/dist/benchmark/u2-e2e/reload-v1-acceptance-runner.js +206 -0
- package/dist/benchmark/u2-e2e/reload-v1-acceptance-runner.test.d.ts +1 -0
- package/dist/benchmark/u2-e2e/reload-v1-acceptance-runner.test.js +72 -0
- package/dist/benchmark/u2-e2e/report.d.ts +1 -0
- package/dist/benchmark/u2-e2e/report.js +2 -0
- package/dist/benchmark/u2-e2e/retrieval-runner.d.ts +34 -0
- package/dist/benchmark/u2-e2e/retrieval-runner.js +95 -5
- package/dist/benchmark/u2-e2e/retrieval-runner.test.js +161 -2
- package/dist/cli/ai-context.js +32 -1
- package/dist/cli/ai-context.test.js +10 -0
- package/dist/cli/analyze-summary.d.ts +1 -0
- package/dist/cli/analyze-summary.js +21 -0
- package/dist/cli/analyze-summary.test.js +7 -1
- package/dist/cli/analyze.js +3 -10
- package/dist/cli/eval-server.js +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/setup.js +9 -0
- package/dist/cli/setup.test.js +2 -0
- package/dist/cli/tool.d.ts +2 -0
- package/dist/cli/tool.js +2 -0
- package/dist/core/ingestion/pipeline.js +24 -3
- package/dist/core/ingestion/process-processor.d.ts +6 -0
- package/dist/core/ingestion/process-processor.js +188 -7
- package/dist/core/ingestion/unity-lifecycle-config.d.ts +5 -0
- package/dist/core/ingestion/unity-lifecycle-config.js +25 -0
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.d.ts +26 -0
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.js +384 -0
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.d.ts +1 -0
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +541 -0
- package/dist/core/ingestion/unity-resource-processor.test.js +81 -0
- package/dist/core/lbug/csv-generator.js +11 -1
- package/dist/core/lbug/fallback-relationship-replay.d.ts +21 -0
- package/dist/core/lbug/fallback-relationship-replay.js +39 -0
- package/dist/core/lbug/fallback-relationship-replay.test.d.ts +1 -0
- package/dist/core/lbug/fallback-relationship-replay.test.js +25 -0
- package/dist/core/lbug/lbug-adapter.d.ts +5 -0
- package/dist/core/lbug/lbug-adapter.js +22 -23
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +9 -0
- package/dist/core/lbug/schema.test.js +1 -0
- package/dist/mcp/local/local-backend.d.ts +1 -1
- package/dist/mcp/local/local-backend.js +339 -50
- package/dist/mcp/local/local-backend.unity-merge.test.js +1 -1
- package/dist/mcp/local/process-confidence.d.ts +19 -0
- package/dist/mcp/local/process-confidence.js +29 -0
- package/dist/mcp/local/process-confidence.test.d.ts +1 -0
- package/dist/mcp/local/process-confidence.test.js +36 -0
- package/dist/mcp/local/process-evidence.d.ts +28 -0
- package/dist/mcp/local/process-evidence.js +65 -0
- package/dist/mcp/local/process-evidence.test.d.ts +1 -0
- package/dist/mcp/local/process-evidence.test.js +56 -0
- package/dist/mcp/local/runtime-chain-evidence.d.ts +7 -0
- package/dist/mcp/local/runtime-chain-evidence.js +13 -0
- package/dist/mcp/local/runtime-chain-evidence.test.d.ts +1 -0
- package/dist/mcp/local/runtime-chain-evidence.test.js +24 -0
- package/dist/mcp/local/runtime-chain-verify.d.ts +37 -0
- package/dist/mcp/local/runtime-chain-verify.js +221 -0
- package/dist/mcp/local/runtime-chain-verify.test.d.ts +1 -0
- package/dist/mcp/local/runtime-chain-verify.test.js +56 -0
- package/dist/mcp/local/unity-process-confidence-config.d.ts +1 -0
- package/dist/mcp/local/unity-process-confidence-config.js +4 -0
- package/dist/mcp/local/unity-runtime-chain-verify-config.d.ts +1 -0
- package/dist/mcp/local/unity-runtime-chain-verify-config.js +10 -0
- package/dist/mcp/local/unity-runtime-hydration.d.ts +50 -0
- package/dist/mcp/local/unity-runtime-hydration.js +323 -0
- package/dist/mcp/local/unity-runtime-hydration.test.d.ts +1 -0
- package/dist/mcp/local/unity-runtime-hydration.test.js +108 -0
- package/dist/mcp/resources.js +12 -2
- package/dist/mcp/tools.js +32 -0
- package/package.json +1 -1
- package/skills/_shared/unity-runtime-process-contract.md +38 -0
- package/skills/gitnexus-cli.md +16 -0
- package/skills/gitnexus-debugging.md +6 -0
- package/skills/gitnexus-exploring.md +6 -0
- package/skills/gitnexus-guide.md +4 -0
- package/skills/gitnexus-impact-analysis.md +6 -0
- package/skills/gitnexus-pr-review.md +5 -0
- package/skills/gitnexus-refactoring.md +4 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { performance } from 'node:perf_hooks';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { estimateTokens } from './metrics.js';
|
|
4
|
+
export function containsPlaceholderLeak(value) {
|
|
5
|
+
return /TODO|TBD|placeholder|<symbol-or-query>/i.test(String(value || ''));
|
|
6
|
+
}
|
|
4
7
|
function stringify(value) {
|
|
5
8
|
try {
|
|
6
9
|
return JSON.stringify(value ?? null);
|
|
@@ -53,7 +56,57 @@ function hasDeepDiveEvidence(output) {
|
|
|
53
56
|
const outgoingRefs = countRefs(output?.outgoing);
|
|
54
57
|
return processSymbols + definitions + candidates + rows + byDepth + impacted + incomingRefs + outgoingRefs > 0;
|
|
55
58
|
}
|
|
56
|
-
function
|
|
59
|
+
function hasQueryUnityEvidence(output) {
|
|
60
|
+
const symbols = [
|
|
61
|
+
...(Array.isArray(output?.process_symbols) ? output.process_symbols : []),
|
|
62
|
+
...(Array.isArray(output?.definitions) ? output.definitions : []),
|
|
63
|
+
];
|
|
64
|
+
return symbols.some((symbol) => {
|
|
65
|
+
const bindings = Array.isArray(symbol?.resourceBindings) ? symbol.resourceBindings.length : 0;
|
|
66
|
+
const scalarFields = Array.isArray(symbol?.serializedFields?.scalarFields) ? symbol.serializedFields.scalarFields.length : 0;
|
|
67
|
+
const referenceFields = Array.isArray(symbol?.serializedFields?.referenceFields) ? symbol.serializedFields.referenceFields.length : 0;
|
|
68
|
+
return bindings + scalarFields + referenceFields > 0;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function normalizeRate(count, total) {
|
|
72
|
+
if (!Number.isFinite(total) || total <= 0)
|
|
73
|
+
return 0;
|
|
74
|
+
const pct = (count / total) * 100;
|
|
75
|
+
return Number(pct.toFixed(3));
|
|
76
|
+
}
|
|
77
|
+
export function summarizePhase5ConfidenceCalibration(input) {
|
|
78
|
+
const artifactPath = String(input.baseline?.artifactPath || '').trim();
|
|
79
|
+
const gitCommit = String(input.baseline?.gitCommit || '').trim();
|
|
80
|
+
const sha256 = String(input.baseline?.sha256 || '').trim();
|
|
81
|
+
if (!artifactPath || !gitCommit || !sha256) {
|
|
82
|
+
throw new Error('baseline provenance missing: artifactPath/gitCommit/sha256 are required');
|
|
83
|
+
}
|
|
84
|
+
const baselineTotal = Number(input.baseline.totalEvaluated || input.current.totalEvaluated || 0);
|
|
85
|
+
const baselineFalseNegative = Number(input.baseline.falseNegativeCount || 0);
|
|
86
|
+
const baselineFalseConfidence = Number(input.baseline.falseConfidenceCount || 0);
|
|
87
|
+
const current = input.current;
|
|
88
|
+
const falseNegativeRateBaselinePct = normalizeRate(baselineFalseNegative, baselineTotal);
|
|
89
|
+
const falseNegativeRateCurrentPct = normalizeRate(current.falseNegativeCount, current.totalEvaluated);
|
|
90
|
+
const falseConfidenceRateBaselinePct = normalizeRate(baselineFalseConfidence, baselineTotal);
|
|
91
|
+
const falseConfidenceRateCurrentPct = normalizeRate(current.falseConfidenceCount, current.totalEvaluated);
|
|
92
|
+
return {
|
|
93
|
+
lowConfidenceHintCoverage: normalizeRate(current.lowConfidenceHintCovered, current.lowConfidenceCount),
|
|
94
|
+
falseConfidenceFailures: current.falseConfidenceCount,
|
|
95
|
+
falseNegativeFallbackCoverage: current.fallbackCovered,
|
|
96
|
+
falseNegativeRateBaselinePct,
|
|
97
|
+
falseNegativeRateCurrentPct,
|
|
98
|
+
falseNegativeRateDeltaPct: Number((falseNegativeRateCurrentPct - falseNegativeRateBaselinePct).toFixed(3)),
|
|
99
|
+
falseConfidenceRateBaselinePct,
|
|
100
|
+
falseConfidenceRateCurrentPct,
|
|
101
|
+
falseConfidenceRateDeltaPct: Number((falseConfidenceRateCurrentPct - falseConfidenceRateBaselinePct).toFixed(3)),
|
|
102
|
+
baseline: {
|
|
103
|
+
artifactPath,
|
|
104
|
+
gitCommit,
|
|
105
|
+
sha256,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function assertScenario(scenario, contextOnOutput, deepDiveExecutions, contextUnityHydration) {
|
|
57
110
|
const failures = [];
|
|
58
111
|
const bindings = Array.isArray(contextOnOutput?.resourceBindings) ? contextOnOutput.resourceBindings : [];
|
|
59
112
|
const hasBindings = bindings.length > 0;
|
|
@@ -90,11 +143,48 @@ function assertScenario(scenario, contextOnOutput, deepDiveOutputs, contextUnity
|
|
|
90
143
|
if (!hasBindings) {
|
|
91
144
|
failures.push('AssetRef: context(on) must include resourceBindings');
|
|
92
145
|
}
|
|
93
|
-
const deepDiveEvidence =
|
|
146
|
+
const deepDiveEvidence = deepDiveExecutions.some((step) => hasDeepDiveEvidence(step.output));
|
|
94
147
|
if (!deepDiveEvidence) {
|
|
95
148
|
failures.push('AssetRef: deep-dive must provide usage/dependency evidence');
|
|
96
149
|
}
|
|
97
150
|
}
|
|
151
|
+
const queryOnRuns = deepDiveExecutions.filter((step) => step.tool === 'query' && String(step.input?.unity_resources || '').toLowerCase() === 'on');
|
|
152
|
+
if (queryOnRuns.length > 0) {
|
|
153
|
+
const hasUnityEvidenceFromQueryOn = queryOnRuns.some((step) => hasQueryUnityEvidence(step.output));
|
|
154
|
+
if (!hasUnityEvidenceFromQueryOn) {
|
|
155
|
+
failures.push(`${scenario.symbol}: query(on) must include unity serialized/resource evidence`);
|
|
156
|
+
}
|
|
157
|
+
for (const step of queryOnRuns) {
|
|
158
|
+
const processes = Array.isArray(step.output?.processes) ? step.output.processes : [];
|
|
159
|
+
const hasUnityEvidence = hasQueryUnityEvidence(step.output);
|
|
160
|
+
if (hasUnityEvidence && processes.length === 0) {
|
|
161
|
+
failures.push(`${scenario.symbol}: empty process query(on) with unity evidence must emit confidence-guided fallback clue`);
|
|
162
|
+
}
|
|
163
|
+
for (const processRow of processes) {
|
|
164
|
+
const confidence = String(processRow?.confidence || '').toLowerCase();
|
|
165
|
+
const evidenceMode = String(processRow?.evidence_mode || '').toLowerCase();
|
|
166
|
+
const processSubtype = String(processRow?.process_subtype || '').toLowerCase();
|
|
167
|
+
if (confidence === 'low') {
|
|
168
|
+
const hint = processRow?.verification_hint;
|
|
169
|
+
const action = String(hint?.action || '').trim();
|
|
170
|
+
const target = String(hint?.target || '').trim();
|
|
171
|
+
const nextCommand = String(hint?.next_command || '').trim();
|
|
172
|
+
if (!action || !target || !nextCommand) {
|
|
173
|
+
failures.push(`${scenario.symbol}: low confidence query(on) row missing verification_hint action/target/next_command`);
|
|
174
|
+
}
|
|
175
|
+
else if (!/parity|asset|meta/i.test(nextCommand)) {
|
|
176
|
+
failures.push(`${scenario.symbol}: low confidence verification_hint must include parity/manual asset-meta guidance`);
|
|
177
|
+
}
|
|
178
|
+
else if (containsPlaceholderLeak(nextCommand)) {
|
|
179
|
+
failures.push(`${scenario.symbol}: low confidence verification_hint leaks placeholder command text`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (evidenceMode === 'direct_step' && processSubtype === 'static_calls' && confidence !== 'high') {
|
|
183
|
+
failures.push(`${scenario.symbol}: direct static chain rows must remain high confidence`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
98
188
|
return {
|
|
99
189
|
pass: failures.length === 0,
|
|
100
190
|
failures,
|
|
@@ -168,7 +258,7 @@ export async function runSymbolScenario(runner, scenario, repo) {
|
|
|
168
258
|
const t1 = performance.now();
|
|
169
259
|
const contextOn = await runContextWithDisambiguation(runner, scenario, contextOnInput);
|
|
170
260
|
steps.push(buildMetric('context-on', 'context', performance.now() - t1, contextOnInput, contextOn));
|
|
171
|
-
const
|
|
261
|
+
const deepDiveExecutions = [];
|
|
172
262
|
for (let i = 0; i < scenario.deepDivePlan.length; i += 1) {
|
|
173
263
|
const step = scenario.deepDivePlan[i];
|
|
174
264
|
const input = { ...(step.input || {}) };
|
|
@@ -177,12 +267,12 @@ export async function runSymbolScenario(runner, scenario, repo) {
|
|
|
177
267
|
}
|
|
178
268
|
const ts = performance.now();
|
|
179
269
|
const output = await invokeTool(runner, step.tool, input);
|
|
180
|
-
|
|
270
|
+
deepDiveExecutions.push({ tool: step.tool, input, output });
|
|
181
271
|
steps.push(buildMetric(`deep-dive-${i + 1}`, step.tool, performance.now() - ts, input, output));
|
|
182
272
|
}
|
|
183
273
|
return {
|
|
184
274
|
symbol: scenario.symbol,
|
|
185
275
|
steps,
|
|
186
|
-
assertions: assertScenario(scenario, contextOn,
|
|
276
|
+
assertions: assertScenario(scenario, contextOn, deepDiveExecutions, contextUnityHydration),
|
|
187
277
|
};
|
|
188
278
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
1
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { runSymbolScenario } from './retrieval-runner.js';
|
|
2
|
+
import { containsPlaceholderLeak, runSymbolScenario, summarizePhase5ConfidenceCalibration } from './retrieval-runner.js';
|
|
4
3
|
import { loadE2EConfig } from './config.js';
|
|
4
|
+
const { test: rawTest } = process.env.VITEST
|
|
5
|
+
? await import('vitest')
|
|
6
|
+
: await import('node:test');
|
|
7
|
+
const test = rawTest;
|
|
5
8
|
test('runSymbolScenario executes context off/on + deepDive and records metrics', async () => {
|
|
6
9
|
const mockToolRunner = {
|
|
7
10
|
context: async (input) => {
|
|
@@ -186,3 +189,159 @@ test('runSymbolScenario fails when compact context hydrationMeta.needsParityRetr
|
|
|
186
189
|
assert.equal(out.assertions.pass, false);
|
|
187
190
|
assert.ok(out.assertions.failures.some((f) => f.includes('hydrationMeta.needsParityRetry')));
|
|
188
191
|
});
|
|
192
|
+
test('runSymbolScenario fails when query(on) has no unity serialized/resource evidence', async () => {
|
|
193
|
+
const runner = {
|
|
194
|
+
context: async (input) => {
|
|
195
|
+
if (input.unity_resources === 'on') {
|
|
196
|
+
return {
|
|
197
|
+
status: 'found',
|
|
198
|
+
hydrationMeta: {
|
|
199
|
+
requestedMode: 'compact',
|
|
200
|
+
effectiveMode: 'compact',
|
|
201
|
+
isComplete: false,
|
|
202
|
+
needsParityRetry: true,
|
|
203
|
+
},
|
|
204
|
+
resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab', resourceType: 'prefab' }],
|
|
205
|
+
serializedFields: { scalarFields: [], referenceFields: [] },
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return { status: 'found' };
|
|
209
|
+
},
|
|
210
|
+
query: async () => ({ process_symbols: [{ id: 'Class:A' }] }),
|
|
211
|
+
impact: async () => ({ impactedCount: 1 }),
|
|
212
|
+
cypher: async () => ({ rows: [] }),
|
|
213
|
+
};
|
|
214
|
+
const out = await runSymbolScenario(runner, {
|
|
215
|
+
symbol: 'MainUIManager',
|
|
216
|
+
kind: 'component',
|
|
217
|
+
objectives: ['verify query evidence gate'],
|
|
218
|
+
deepDivePlan: [{ tool: 'query', input: { query: 'MainUIManager', unity_resources: 'on' } }],
|
|
219
|
+
});
|
|
220
|
+
assert.equal(out.assertions.pass, false);
|
|
221
|
+
assert.ok(out.assertions.failures.some((f) => f.includes('query(on) must include unity serialized/resource evidence')));
|
|
222
|
+
});
|
|
223
|
+
test('phase5 confidence calibration fails when low confidence process is missing verification_hint', async () => {
|
|
224
|
+
const runner = {
|
|
225
|
+
context: async (input) => {
|
|
226
|
+
if (input.unity_resources === 'on') {
|
|
227
|
+
return {
|
|
228
|
+
status: 'found',
|
|
229
|
+
hydrationMeta: {
|
|
230
|
+
requestedMode: 'compact',
|
|
231
|
+
effectiveMode: 'compact',
|
|
232
|
+
isComplete: false,
|
|
233
|
+
needsParityRetry: true,
|
|
234
|
+
},
|
|
235
|
+
resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab', resourceType: 'prefab' }],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return { status: 'found' };
|
|
239
|
+
},
|
|
240
|
+
query: async () => ({
|
|
241
|
+
processes: [{ confidence: 'low', evidence_mode: 'resource_heuristic' }],
|
|
242
|
+
process_symbols: [{ id: 'Class:A', resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab' }] }],
|
|
243
|
+
}),
|
|
244
|
+
impact: async () => ({ impactedCount: 0 }),
|
|
245
|
+
cypher: async () => ({ rows: [] }),
|
|
246
|
+
};
|
|
247
|
+
const out = await runSymbolScenario(runner, {
|
|
248
|
+
symbol: 'MainUIManager',
|
|
249
|
+
kind: 'component',
|
|
250
|
+
objectives: ['phase5 low confidence hint gate'],
|
|
251
|
+
deepDivePlan: [{ tool: 'query', input: { query: 'MainUIManager', unity_resources: 'on' } }],
|
|
252
|
+
});
|
|
253
|
+
assert.equal(out.assertions.pass, false);
|
|
254
|
+
assert.ok(out.assertions.failures.some((f) => /verification_hint/i.test(f)));
|
|
255
|
+
});
|
|
256
|
+
test('phase5 confidence calibration fails when empty process result with unity evidence has no fallback clue', async () => {
|
|
257
|
+
const runner = {
|
|
258
|
+
context: async (input) => {
|
|
259
|
+
if (input.unity_resources === 'on') {
|
|
260
|
+
return {
|
|
261
|
+
status: 'found',
|
|
262
|
+
hydrationMeta: {
|
|
263
|
+
requestedMode: 'compact',
|
|
264
|
+
effectiveMode: 'compact',
|
|
265
|
+
isComplete: false,
|
|
266
|
+
needsParityRetry: true,
|
|
267
|
+
},
|
|
268
|
+
resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab', resourceType: 'prefab' }],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return { status: 'found' };
|
|
272
|
+
},
|
|
273
|
+
query: async () => ({
|
|
274
|
+
processes: [],
|
|
275
|
+
process_symbols: [{ id: 'Class:A', resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab' }] }],
|
|
276
|
+
}),
|
|
277
|
+
impact: async () => ({ impactedCount: 0 }),
|
|
278
|
+
cypher: async () => ({ rows: [] }),
|
|
279
|
+
};
|
|
280
|
+
const out = await runSymbolScenario(runner, {
|
|
281
|
+
symbol: 'MainUIManager',
|
|
282
|
+
kind: 'component',
|
|
283
|
+
objectives: ['phase5 empty process fallback gate'],
|
|
284
|
+
deepDivePlan: [{ tool: 'query', input: { query: 'MainUIManager', unity_resources: 'on' } }],
|
|
285
|
+
});
|
|
286
|
+
assert.equal(out.assertions.pass, false);
|
|
287
|
+
assert.ok(out.assertions.failures.some((f) => /fallback|empty process/i.test(f)));
|
|
288
|
+
});
|
|
289
|
+
test('phase5 confidence calibration fails when direct static chain is not high confidence', async () => {
|
|
290
|
+
const runner = {
|
|
291
|
+
context: async (input) => {
|
|
292
|
+
if (input.unity_resources === 'on') {
|
|
293
|
+
return {
|
|
294
|
+
status: 'found',
|
|
295
|
+
hydrationMeta: {
|
|
296
|
+
requestedMode: 'compact',
|
|
297
|
+
effectiveMode: 'compact',
|
|
298
|
+
isComplete: false,
|
|
299
|
+
needsParityRetry: true,
|
|
300
|
+
},
|
|
301
|
+
resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab', resourceType: 'prefab' }],
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return { status: 'found' };
|
|
305
|
+
},
|
|
306
|
+
query: async () => ({
|
|
307
|
+
processes: [{
|
|
308
|
+
confidence: 'medium',
|
|
309
|
+
evidence_mode: 'direct_step',
|
|
310
|
+
process_subtype: 'static_calls',
|
|
311
|
+
verification_hint: { action: 'none', target: 'none', next_command: 'none' },
|
|
312
|
+
}],
|
|
313
|
+
process_symbols: [{ id: 'Class:A', resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab' }] }],
|
|
314
|
+
}),
|
|
315
|
+
impact: async () => ({ impactedCount: 0 }),
|
|
316
|
+
cypher: async () => ({ rows: [] }),
|
|
317
|
+
};
|
|
318
|
+
const out = await runSymbolScenario(runner, {
|
|
319
|
+
symbol: 'MainUIManager',
|
|
320
|
+
kind: 'component',
|
|
321
|
+
objectives: ['phase5 direct static confidence gate'],
|
|
322
|
+
deepDivePlan: [{ tool: 'query', input: { query: 'MainUIManager', unity_resources: 'on' } }],
|
|
323
|
+
});
|
|
324
|
+
assert.equal(out.assertions.pass, false);
|
|
325
|
+
assert.ok(out.assertions.failures.some((f) => /direct.*static.*high/i.test(f)));
|
|
326
|
+
});
|
|
327
|
+
test('phase5 confidence calibration summary requires baseline provenance fields', async () => {
|
|
328
|
+
assert.throws(() => summarizePhase5ConfidenceCalibration({
|
|
329
|
+
current: {
|
|
330
|
+
totalEvaluated: 4,
|
|
331
|
+
falseNegativeCount: 1,
|
|
332
|
+
falseConfidenceCount: 1,
|
|
333
|
+
lowConfidenceHintCovered: 1,
|
|
334
|
+
lowConfidenceCount: 2,
|
|
335
|
+
fallbackCovered: 2,
|
|
336
|
+
},
|
|
337
|
+
baseline: {
|
|
338
|
+
totalEvaluated: 4,
|
|
339
|
+
falseNegativeCount: 2,
|
|
340
|
+
falseConfidenceCount: 2,
|
|
341
|
+
},
|
|
342
|
+
}), /baseline provenance/i);
|
|
343
|
+
});
|
|
344
|
+
test('phase5 confidence calibration detects placeholder leakage in next_command', async () => {
|
|
345
|
+
assert.equal(containsPlaceholderLeak('Inspect <symbol-or-query> later'), true);
|
|
346
|
+
assert.equal(containsPlaceholderLeak('gitnexus query --unity-resources on'), false);
|
|
347
|
+
});
|
package/dist/cli/ai-context.js
CHANGED
|
@@ -42,6 +42,7 @@ function generateGitNexusContent(projectName, stats, skillScope, cliPackageSpec,
|
|
|
42
42
|
2. **Match your task to a skill below** and **read that skill file**
|
|
43
43
|
3. **Follow the skill's workflow and checklist**
|
|
44
44
|
4. **Follow config/state file rules:** \`docs/gitnexus-config-files.md\`
|
|
45
|
+
5. **If user asks to release/publish a specific version and this repo has \`DISTRIBUTION.md\`, execute that workflow in full-release mode by default** (unless user explicitly asks \`prepare-only\` or \`publish-only\`).
|
|
45
46
|
|
|
46
47
|
> If step 1 warns the index is stale, ask user whether to rebuild index via \`gitnexus analyze\` when local CLI exists; otherwise resolve the pinned npx package spec from \`~/.gitnexus/config.json\` (\`cliPackageSpec\` first, then \`cliVersion\`) and run \`${reindexCmd}\` with that exact package spec (it reuses previous analyze scope/options by default; add \`--no-reuse-options\` to reset). If user declines, explicitly warn that retrieval may not reflect current codebase. For build/analyze/test commands, use a 10-30 minute timeout; on failure/timeout, report exact tool output and do not auto-retry or silently fall back to glob/grep.
|
|
47
48
|
|
|
@@ -56,6 +57,12 @@ function generateGitNexusContent(projectName, stats, skillScope, cliPackageSpec,
|
|
|
56
57
|
| Tools, resources, schema reference | \`${skillRoot}/gitnexus-guide/SKILL.md\` |
|
|
57
58
|
| Index, status, clean, wiki CLI commands | \`${skillRoot}/gitnexus-cli/SKILL.md\` |${generatedRows}
|
|
58
59
|
|
|
60
|
+
## Unity Runtime Process 真理源
|
|
61
|
+
|
|
62
|
+
- 统一设计与实现对照文档:\`docs/unity-runtime-process-source-of-truth.md\`
|
|
63
|
+
- 涉及 Unity runtime process 的任务,先阅读该文档,再执行检索/实现/验收。
|
|
64
|
+
- 若历史设计文档与当前实现不一致,以该真理源文档和对应代码为准,并在变更后同步更新。
|
|
65
|
+
|
|
59
66
|
${GITNEXUS_END_MARKER}`;
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
@@ -106,6 +113,7 @@ async function upsertGitNexusSection(filePath, content) {
|
|
|
106
113
|
async function installSkills(repoPath) {
|
|
107
114
|
const skillsDir = path.join(repoPath, '.agents', 'skills', 'gitnexus');
|
|
108
115
|
const installedSkills = [];
|
|
116
|
+
const packageSkillsRoot = path.join(__dirname, '..', '..', 'skills');
|
|
109
117
|
// Skill definitions bundled with the package
|
|
110
118
|
const skills = [
|
|
111
119
|
{
|
|
@@ -140,7 +148,7 @@ async function installSkills(repoPath) {
|
|
|
140
148
|
// Create skill directory
|
|
141
149
|
await fs.mkdir(skillDir, { recursive: true });
|
|
142
150
|
// Try to read from package skills directory
|
|
143
|
-
const packageSkillPath = path.join(
|
|
151
|
+
const packageSkillPath = path.join(packageSkillsRoot, `${skill.name}.md`);
|
|
144
152
|
let skillContent;
|
|
145
153
|
try {
|
|
146
154
|
skillContent = await fs.readFile(packageSkillPath, 'utf-8');
|
|
@@ -167,8 +175,31 @@ Use GitNexus tools to accomplish this task.
|
|
|
167
175
|
console.warn(`Warning: Could not install skill ${skill.name}:`, err);
|
|
168
176
|
}
|
|
169
177
|
}
|
|
178
|
+
// Shared workflow contracts (if bundled).
|
|
179
|
+
const packageSharedDir = path.join(packageSkillsRoot, '_shared');
|
|
180
|
+
try {
|
|
181
|
+
await fs.access(packageSharedDir);
|
|
182
|
+
await copyDirRecursive(packageSharedDir, path.join(skillsDir, '_shared'));
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Optional: older bundles may not include shared docs.
|
|
186
|
+
}
|
|
170
187
|
return installedSkills;
|
|
171
188
|
}
|
|
189
|
+
async function copyDirRecursive(src, dest) {
|
|
190
|
+
await fs.mkdir(dest, { recursive: true });
|
|
191
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
192
|
+
for (const entry of entries) {
|
|
193
|
+
const srcPath = path.join(src, entry.name);
|
|
194
|
+
const destPath = path.join(dest, entry.name);
|
|
195
|
+
if (entry.isDirectory()) {
|
|
196
|
+
await copyDirRecursive(srcPath, destPath);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
await fs.copyFile(srcPath, destPath);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
172
203
|
/**
|
|
173
204
|
* Generate AI context files after indexing
|
|
174
205
|
*/
|
|
@@ -15,12 +15,18 @@ test('generateAIContextFiles installs repo skills under .agents/skills/gitnexus'
|
|
|
15
15
|
const agentsPath = path.join(repoPath, 'AGENTS.md');
|
|
16
16
|
const claudePath = path.join(repoPath, 'CLAUDE.md');
|
|
17
17
|
const skillPath = path.join(repoPath, '.agents', 'skills', 'gitnexus', 'gitnexus-exploring', 'SKILL.md');
|
|
18
|
+
const sharedRuntimeContractPath = path.join(repoPath, '.agents', 'skills', 'gitnexus', '_shared', 'unity-runtime-process-contract.md');
|
|
18
19
|
const legacyClaudeSkillsDir = path.join(repoPath, '.claude', 'skills');
|
|
19
20
|
const agentsContent = await fs.readFile(agentsPath, 'utf-8');
|
|
20
21
|
const claudeContent = await fs.readFile(claudePath, 'utf-8');
|
|
21
22
|
await fs.access(skillPath);
|
|
23
|
+
await fs.access(sharedRuntimeContractPath);
|
|
22
24
|
assert.match(agentsContent, /\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
23
25
|
assert.match(claudeContent, /\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
26
|
+
assert.match(agentsContent, /## Unity Runtime Process 真理源/);
|
|
27
|
+
assert.match(agentsContent, /docs\/unity-runtime-process-source-of-truth\.md/);
|
|
28
|
+
assert.match(claudeContent, /## Unity Runtime Process 真理源/);
|
|
29
|
+
assert.match(claudeContent, /docs\/unity-runtime-process-source-of-truth\.md/);
|
|
24
30
|
assert.ok(result.files.some((entry) => entry.includes('.agents/skills/gitnexus/')), 'expected generated file summary to include .agents/skills/gitnexus/');
|
|
25
31
|
await assert.rejects(fs.access(legacyClaudeSkillsDir));
|
|
26
32
|
}
|
|
@@ -43,6 +49,10 @@ test('generateAIContextFiles with global scope skips repo skill install', async
|
|
|
43
49
|
const claudeContent = await fs.readFile(claudePath, 'utf-8');
|
|
44
50
|
assert.match(agentsContent, /~\/\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
45
51
|
assert.match(claudeContent, /~\/\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
52
|
+
assert.match(agentsContent, /## Unity Runtime Process 真理源/);
|
|
53
|
+
assert.match(agentsContent, /docs\/unity-runtime-process-source-of-truth\.md/);
|
|
54
|
+
assert.match(claudeContent, /## Unity Runtime Process 真理源/);
|
|
55
|
+
assert.match(claudeContent, /docs\/unity-runtime-process-source-of-truth\.md/);
|
|
46
56
|
assert.ok(!result.files.some((entry) => entry.includes('.agents/skills/gitnexus/')), 'did not expect repo-local skills in generated file summary');
|
|
47
57
|
await assert.rejects(fs.access(localSkillsDir));
|
|
48
58
|
}
|
|
@@ -5,3 +5,4 @@ export interface FallbackInsertStats {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function formatUnityDiagnosticsSummary(diagnostics: string[] | undefined, previewLimit?: number): string[];
|
|
7
7
|
export declare function formatFallbackSummary(warnings: string[] | undefined, stats: FallbackInsertStats | undefined, previewLimit?: number): string[];
|
|
8
|
+
export declare function resolveFallbackStats(warnings: string[] | undefined, stats: FallbackInsertStats | undefined): FallbackInsertStats;
|
|
@@ -35,3 +35,24 @@ export function formatFallbackSummary(warnings, stats, previewLimit = 5) {
|
|
|
35
35
|
}
|
|
36
36
|
return lines;
|
|
37
37
|
}
|
|
38
|
+
export function resolveFallbackStats(warnings, stats) {
|
|
39
|
+
if (stats) {
|
|
40
|
+
return stats;
|
|
41
|
+
}
|
|
42
|
+
if (!warnings || warnings.length === 0) {
|
|
43
|
+
return {
|
|
44
|
+
attempted: 0,
|
|
45
|
+
succeeded: 0,
|
|
46
|
+
failed: 0,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const attempted = warnings.reduce((sum, warning) => {
|
|
50
|
+
const match = warning.match(/\((\d+)\s+edges\)/);
|
|
51
|
+
return sum + (match ? Number.parseInt(match[1] || '0', 10) : 0);
|
|
52
|
+
}, 0);
|
|
53
|
+
return {
|
|
54
|
+
attempted,
|
|
55
|
+
succeeded: 0,
|
|
56
|
+
failed: attempted,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { formatFallbackSummary, formatUnityDiagnosticsSummary } from './analyze-summary.js';
|
|
3
|
+
import { formatFallbackSummary, formatUnityDiagnosticsSummary, resolveFallbackStats } from './analyze-summary.js';
|
|
4
4
|
test('formatUnityDiagnosticsSummary returns empty when diagnostics are missing', () => {
|
|
5
5
|
const lines = formatUnityDiagnosticsSummary([]);
|
|
6
6
|
assert.deepEqual(lines, []);
|
|
@@ -56,3 +56,9 @@ test('formatFallbackSummary renders attempted/succeeded/failed with warning prev
|
|
|
56
56
|
'... 1 more',
|
|
57
57
|
]);
|
|
58
58
|
});
|
|
59
|
+
test('resolveFallbackStats prefers runtime fallback insert stats when available', () => {
|
|
60
|
+
assert.deepEqual(resolveFallbackStats(['Class->File (12 edges): missing rel pair in schema'], { attempted: 12, succeeded: 3, failed: 9 }), { attempted: 12, succeeded: 3, failed: 9 });
|
|
61
|
+
});
|
|
62
|
+
test('resolveFallbackStats derives attempted/failed from warnings when runtime stats are missing', () => {
|
|
63
|
+
assert.deepEqual(resolveFallbackStats(['Class->File (7 edges): missing rel pair in schema'], undefined), { attempted: 7, succeeded: 0, failed: 7 });
|
|
64
|
+
});
|
package/dist/cli/analyze.js
CHANGED
|
@@ -19,7 +19,7 @@ import { generateAIContextFiles } from './ai-context.js';
|
|
|
19
19
|
import { generateSkillFiles } from './skill-gen.js';
|
|
20
20
|
import fs from 'fs/promises';
|
|
21
21
|
import { resolveEffectiveAnalyzeOptions } from './analyze-options.js';
|
|
22
|
-
import { formatFallbackSummary, formatUnityDiagnosticsSummary } from './analyze-summary.js';
|
|
22
|
+
import { formatFallbackSummary, formatUnityDiagnosticsSummary, resolveFallbackStats } from './analyze-summary.js';
|
|
23
23
|
import { resolveChildProcessExit } from './exit-code.js';
|
|
24
24
|
import { toPipelineRuntimeSummary } from './analyze-runtime-summary.js';
|
|
25
25
|
import { resolveCliSpec } from '../config/cli-spec.js';
|
|
@@ -442,15 +442,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
442
442
|
console.log(` Context: ${aiContext.files.join(', ')}`);
|
|
443
443
|
}
|
|
444
444
|
if (lbugWarnings.length > 0) {
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
return sum + (match ? Number.parseInt(match[1], 10) : 0);
|
|
448
|
-
}, 0);
|
|
449
|
-
const fallbackLines = formatFallbackSummary(lbugWarnings, {
|
|
450
|
-
attempted: totalFallback,
|
|
451
|
-
succeeded: totalFallback,
|
|
452
|
-
failed: 0,
|
|
453
|
-
});
|
|
445
|
+
const fallbackStats = resolveFallbackStats(lbugWarnings, lbugResult.fallbackInsertStats);
|
|
446
|
+
const fallbackLines = formatFallbackSummary(lbugWarnings, fallbackStats);
|
|
454
447
|
for (const line of fallbackLines) {
|
|
455
448
|
console.log(` ${line}`);
|
|
456
449
|
}
|
package/dist/cli/eval-server.js
CHANGED
|
@@ -111,7 +111,7 @@ export function formatContextResult(result) {
|
|
|
111
111
|
if (procs.length > 0) {
|
|
112
112
|
lines.push(`Participates in ${procs.length} execution flow(s):`);
|
|
113
113
|
for (const p of procs) {
|
|
114
|
-
lines.push(` • ${p.name} (step ${p.step_index}/${p.step_count})`);
|
|
114
|
+
lines.push(` • ${p.name} (step ${p.step_index}/${p.step_count}, evidence=${p.evidence_mode || 'direct_step'}, confidence=${p.confidence || 'high'})`);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
if (sym.content) {
|
package/dist/cli/index.js
CHANGED
|
@@ -85,6 +85,7 @@ program
|
|
|
85
85
|
.option('--scope-preset <preset>', 'Scope preset for retrieval: unity-gameplay|unity-all')
|
|
86
86
|
.option('--unity-resources <mode>', 'Unity resource retrieval mode: off|on|auto', 'off')
|
|
87
87
|
.option('--unity-hydration <mode>', 'Unity hydration mode when resources are enabled: parity|compact', 'compact')
|
|
88
|
+
.option('--runtime-chain-verify <mode>', 'Runtime chain verification mode: off|on-demand', 'off')
|
|
88
89
|
.action(createLazyAction(() => import('./tool.js'), 'queryCommand'));
|
|
89
90
|
program
|
|
90
91
|
.command('context [name]')
|
|
@@ -95,6 +96,7 @@ program
|
|
|
95
96
|
.option('--content', 'Include full symbol source code')
|
|
96
97
|
.option('--unity-resources <mode>', 'Unity resource retrieval mode: off|on|auto', 'off')
|
|
97
98
|
.option('--unity-hydration <mode>', 'Unity hydration mode when resources are enabled: parity|compact', 'compact')
|
|
99
|
+
.option('--runtime-chain-verify <mode>', 'Runtime chain verification mode: off|on-demand', 'off')
|
|
98
100
|
.action(createLazyAction(() => import('./tool.js'), 'contextCommand'));
|
|
99
101
|
program
|
|
100
102
|
.command('unity-bindings <symbol>')
|
package/dist/cli/setup.js
CHANGED
|
@@ -450,6 +450,15 @@ async function installSkillsTo(targetDir) {
|
|
|
450
450
|
// Source skill not found — skip
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
|
+
// Shared workflow contracts distributed alongside skills.
|
|
454
|
+
const sharedSource = path.join(skillsRoot, '_shared');
|
|
455
|
+
try {
|
|
456
|
+
await fs.access(sharedSource);
|
|
457
|
+
await copyDirRecursive(sharedSource, path.join(targetDir, '_shared'));
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
// Optional shared contracts directory may be absent in older packages.
|
|
461
|
+
}
|
|
453
462
|
return installed;
|
|
454
463
|
}
|
|
455
464
|
/**
|
package/dist/cli/setup.test.js
CHANGED
|
@@ -90,8 +90,10 @@ test('setup installs global skills under ~/.agents/skills/gitnexus', async () =>
|
|
|
90
90
|
USERPROFILE: fakeHome,
|
|
91
91
|
});
|
|
92
92
|
const skillPath = path.join(fakeHome, '.agents', 'skills', 'gitnexus', 'gitnexus-exploring', 'SKILL.md');
|
|
93
|
+
const sharedRuntimeContractPath = path.join(fakeHome, '.agents', 'skills', 'gitnexus', '_shared', 'unity-runtime-process-contract.md');
|
|
93
94
|
const configPath = path.join(fakeHome, '.gitnexus', 'config.json');
|
|
94
95
|
await fs.access(skillPath);
|
|
96
|
+
await fs.access(sharedRuntimeContractPath);
|
|
95
97
|
const configRaw = await fs.readFile(configPath, 'utf-8');
|
|
96
98
|
const config = JSON.parse(configRaw);
|
|
97
99
|
assert.equal(config.setupScope, 'global');
|
package/dist/cli/tool.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export declare function queryCommand(queryText: string, options?: {
|
|
|
24
24
|
scopePreset?: 'unity-gameplay' | 'unity-all';
|
|
25
25
|
unityResources?: UnityResourcesMode;
|
|
26
26
|
unityHydration?: UnityHydrationMode;
|
|
27
|
+
runtimeChainVerify?: 'off' | 'on-demand';
|
|
27
28
|
}): Promise<void>;
|
|
28
29
|
export declare function contextCommand(name: string, options?: {
|
|
29
30
|
repo?: string;
|
|
@@ -32,6 +33,7 @@ export declare function contextCommand(name: string, options?: {
|
|
|
32
33
|
content?: boolean;
|
|
33
34
|
unityResources?: UnityResourcesMode;
|
|
34
35
|
unityHydration?: UnityHydrationMode;
|
|
36
|
+
runtimeChainVerify?: 'off' | 'on-demand';
|
|
35
37
|
}): Promise<void>;
|
|
36
38
|
export declare function impactCommand(target: string, options?: {
|
|
37
39
|
direction?: string;
|
package/dist/cli/tool.js
CHANGED
|
@@ -94,6 +94,7 @@ export async function queryCommand(queryText, options) {
|
|
|
94
94
|
scope_preset: options?.scopePreset,
|
|
95
95
|
unity_resources: options?.unityResources,
|
|
96
96
|
unity_hydration_mode: options?.unityHydration,
|
|
97
|
+
runtime_chain_verify: options?.runtimeChainVerify,
|
|
97
98
|
repo,
|
|
98
99
|
});
|
|
99
100
|
output(result);
|
|
@@ -112,6 +113,7 @@ export async function contextCommand(name, options) {
|
|
|
112
113
|
include_content: options?.content ?? false,
|
|
113
114
|
unity_resources: options?.unityResources,
|
|
114
115
|
unity_hydration_mode: options?.unityHydration,
|
|
116
|
+
runtime_chain_verify: options?.runtimeChainVerify,
|
|
115
117
|
repo,
|
|
116
118
|
});
|
|
117
119
|
output(result);
|
|
@@ -8,6 +8,8 @@ import { computeMRO } from './mro-processor.js';
|
|
|
8
8
|
import { processCommunities } from './community-processor.js';
|
|
9
9
|
import { processProcesses } from './process-processor.js';
|
|
10
10
|
import { processUnityResources } from './unity-resource-processor.js';
|
|
11
|
+
import { applyUnityLifecycleSyntheticCalls } from './unity-lifecycle-synthetic-calls.js';
|
|
12
|
+
import { resolveUnityLifecycleConfig } from './unity-lifecycle-config.js';
|
|
11
13
|
import { createResolutionContext } from './resolution-context.js';
|
|
12
14
|
import { createASTCache } from './ast-cache.js';
|
|
13
15
|
import { walkRepositoryPaths, readFileContents, walkUnityResourcePaths } from './filesystem-walker.js';
|
|
@@ -342,11 +344,20 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
342
344
|
reason: 'leiden-algorithm',
|
|
343
345
|
});
|
|
344
346
|
});
|
|
347
|
+
const unityLifecycleConfig = resolveUnityLifecycleConfig(process.env);
|
|
348
|
+
const persistLifecycleProcessMetadata = unityLifecycleConfig.persistLifecycleProcessMetadata;
|
|
349
|
+
const unityLifecycleSyntheticResult = applyUnityLifecycleSyntheticCalls(graph, unityLifecycleConfig);
|
|
350
|
+
const syntheticEdgeDetail = unityLifecycleSyntheticResult.syntheticEdgeCount > 0
|
|
351
|
+
? ` (Unity synthetic edges: ${unityLifecycleSyntheticResult.syntheticEdgeCount})`
|
|
352
|
+
: '';
|
|
353
|
+
if (isDev && unityLifecycleConfig.enabled) {
|
|
354
|
+
console.log(`[UnityLifecycle] enabled=${unityLifecycleConfig.enabled} hosts=${unityLifecycleSyntheticResult.hostCount} syntheticEdges=${unityLifecycleSyntheticResult.syntheticEdgeCount} rejectedHosts=${unityLifecycleSyntheticResult.rejectedHostCount}`);
|
|
355
|
+
}
|
|
345
356
|
// ── Phase 6: Processes ─────────────────────────────────────────────
|
|
346
357
|
onProgress({
|
|
347
358
|
phase: 'processes',
|
|
348
359
|
percent: 94,
|
|
349
|
-
message:
|
|
360
|
+
message: `Detecting execution flows...${syntheticEdgeDetail}`,
|
|
350
361
|
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
351
362
|
});
|
|
352
363
|
let symbolCount = 0;
|
|
@@ -378,17 +389,27 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
378
389
|
communities: proc.communities,
|
|
379
390
|
entryPointId: proc.entryPointId,
|
|
380
391
|
terminalId: proc.terminalId,
|
|
392
|
+
...(persistLifecycleProcessMetadata
|
|
393
|
+
? {
|
|
394
|
+
processSubtype: proc.processSubtype,
|
|
395
|
+
runtimeChainConfidence: proc.runtimeChainConfidence,
|
|
396
|
+
sourceReasons: proc.sourceReasons,
|
|
397
|
+
sourceConfidences: proc.sourceConfidences,
|
|
398
|
+
}
|
|
399
|
+
: {}),
|
|
381
400
|
}
|
|
382
401
|
});
|
|
383
402
|
});
|
|
384
403
|
processResult.steps.forEach(step => {
|
|
404
|
+
const persistedReason = persistLifecycleProcessMetadata ? (step.reason || 'trace-detection') : 'trace-detection';
|
|
405
|
+
const persistedConfidence = persistLifecycleProcessMetadata ? (step.confidence ?? 1.0) : 1.0;
|
|
385
406
|
graph.addRelationship({
|
|
386
407
|
id: `${step.nodeId}_step_${step.step}_${step.processId}`,
|
|
387
408
|
type: 'STEP_IN_PROCESS',
|
|
388
409
|
sourceId: step.nodeId,
|
|
389
410
|
targetId: step.processId,
|
|
390
|
-
confidence:
|
|
391
|
-
reason:
|
|
411
|
+
confidence: persistedConfidence,
|
|
412
|
+
reason: persistedReason,
|
|
392
413
|
step: step.step,
|
|
393
414
|
});
|
|
394
415
|
});
|