@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
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
import fs from 'fs/promises';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { initLbug, executeQuery, executeParameterized, closeLbug, isLbugReady } from '../core/lbug-adapter.js';
|
|
11
|
+
import { parseUnityHydrationMode, parseUnityResourcesMode } from '../../core/unity/options.js';
|
|
11
12
|
import { runUnityUiTrace } from '../../core/unity/ui-trace.js';
|
|
13
|
+
import { loadUnityContext } from './unity-enrichment.js';
|
|
14
|
+
import { hydrateUnityForSymbol } from './unity-runtime-hydration.js';
|
|
15
|
+
import { mergeProcessEvidence } from './process-evidence.js';
|
|
16
|
+
import { resolveUnityProcessConfidenceFieldsEnabled } from './unity-process-confidence-config.js';
|
|
17
|
+
import { resolveUnityRuntimeChainVerifyEnabled } from './unity-runtime-chain-verify-config.js';
|
|
18
|
+
import { verifyRuntimeChainOnDemand, } from './runtime-chain-verify.js';
|
|
12
19
|
// Embedding imports are lazy (dynamic import) to avoid loading onnxruntime-node
|
|
13
20
|
// at MCP server startup — crashes on unsupported Node ABI versions (#89)
|
|
14
21
|
// git utilities available if needed
|
|
@@ -74,6 +81,46 @@ function resolveQueryScopePreset(scopePreset) {
|
|
|
74
81
|
}
|
|
75
82
|
return undefined;
|
|
76
83
|
}
|
|
84
|
+
function aggregateProcessConfidence(rows) {
|
|
85
|
+
if (rows.some((row) => row.process_confidence === 'high'))
|
|
86
|
+
return 'high';
|
|
87
|
+
if (rows.some((row) => row.process_confidence === 'medium'))
|
|
88
|
+
return 'medium';
|
|
89
|
+
return 'low';
|
|
90
|
+
}
|
|
91
|
+
function aggregateProcessEvidenceMode(rows) {
|
|
92
|
+
if (rows.some((row) => row.process_evidence_mode === 'direct_step'))
|
|
93
|
+
return 'direct_step';
|
|
94
|
+
if (rows.some((row) => row.process_evidence_mode === 'method_projected'))
|
|
95
|
+
return 'method_projected';
|
|
96
|
+
return 'resource_heuristic';
|
|
97
|
+
}
|
|
98
|
+
function selectVerificationHint(rows) {
|
|
99
|
+
return rows.find((row) => row.verification_hint)?.verification_hint;
|
|
100
|
+
}
|
|
101
|
+
function aggregateRuntimeChainEvidenceLevel(rows) {
|
|
102
|
+
if (rows.some((row) => row.runtime_chain_evidence_level === 'verified_chain'))
|
|
103
|
+
return 'verified_chain';
|
|
104
|
+
if (rows.some((row) => row.runtime_chain_evidence_level === 'verified_segment'))
|
|
105
|
+
return 'verified_segment';
|
|
106
|
+
if (rows.some((row) => row.runtime_chain_evidence_level === 'clue'))
|
|
107
|
+
return 'clue';
|
|
108
|
+
return 'none';
|
|
109
|
+
}
|
|
110
|
+
function confidenceRank(confidence) {
|
|
111
|
+
if (confidence === 'high')
|
|
112
|
+
return 3;
|
|
113
|
+
if (confidence === 'medium')
|
|
114
|
+
return 2;
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
function evidenceModeRank(mode) {
|
|
118
|
+
if (mode === 'direct_step')
|
|
119
|
+
return 3;
|
|
120
|
+
if (mode === 'method_projected')
|
|
121
|
+
return 2;
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
77
124
|
export function filterBm25ResultsByScopePreset(rows, scopePreset) {
|
|
78
125
|
const preset = resolveQueryScopePreset(scopePreset);
|
|
79
126
|
if (!preset || preset === 'unity-all')
|
|
@@ -504,7 +551,19 @@ export class LocalBackend {
|
|
|
504
551
|
const processLimit = params.limit || 5;
|
|
505
552
|
const maxSymbolsPerProcess = params.max_symbols || 10;
|
|
506
553
|
const includeContent = params.include_content ?? false;
|
|
554
|
+
const confidenceFieldsEnabled = resolveUnityProcessConfidenceFieldsEnabled(process.env);
|
|
555
|
+
let unityResourcesMode = 'off';
|
|
556
|
+
let unityHydrationMode = 'compact';
|
|
557
|
+
try {
|
|
558
|
+
unityResourcesMode = parseUnityResourcesMode(params.unity_resources);
|
|
559
|
+
unityHydrationMode = parseUnityHydrationMode(params.unity_hydration_mode);
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
563
|
+
}
|
|
507
564
|
const searchQuery = params.query.trim();
|
|
565
|
+
const runtimeChainVerifyMode = String(params.runtime_chain_verify || 'off').trim().toLowerCase();
|
|
566
|
+
const runtimeChainVerifyEnabled = resolveUnityRuntimeChainVerifyEnabled(process.env);
|
|
508
567
|
// Step 1: Run hybrid search to get matching symbols
|
|
509
568
|
const searchLimit = processLimit * maxSymbolsPerProcess; // fetch enough raw results
|
|
510
569
|
const [bm25Results, semanticResults] = await Promise.all([
|
|
@@ -554,17 +613,42 @@ export class LocalBackend {
|
|
|
554
613
|
});
|
|
555
614
|
continue;
|
|
556
615
|
}
|
|
557
|
-
// Find processes this symbol participates in
|
|
558
|
-
let
|
|
616
|
+
// Find processes this symbol participates in (direct + method projection for class-like symbols).
|
|
617
|
+
let directProcessRows = [];
|
|
618
|
+
let projectedProcessRows = [];
|
|
559
619
|
try {
|
|
560
|
-
|
|
620
|
+
directProcessRows = await executeParameterized(repo.id, `
|
|
561
621
|
MATCH (n {id: $nodeId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
|
|
562
|
-
RETURN p.id AS pid, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount, r.step AS step
|
|
622
|
+
RETURN p.id AS pid, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, p.stepCount AS stepCount, r.step AS step
|
|
563
623
|
`, { nodeId: sym.nodeId });
|
|
564
624
|
}
|
|
565
625
|
catch (e) {
|
|
566
626
|
logQueryError('query:process-lookup', e);
|
|
567
627
|
}
|
|
628
|
+
const symIdLower = String(sym.nodeId).toLowerCase();
|
|
629
|
+
const isClassLike = ['Class', 'Interface', 'Struct', 'Trait', 'Impl', 'Record'].includes(String(sym.type || ''))
|
|
630
|
+
|| symIdLower.startsWith('class:')
|
|
631
|
+
|| symIdLower.startsWith('interface:')
|
|
632
|
+
|| symIdLower.startsWith('struct:')
|
|
633
|
+
|| symIdLower.startsWith('trait:')
|
|
634
|
+
|| symIdLower.startsWith('impl:')
|
|
635
|
+
|| symIdLower.startsWith('record:');
|
|
636
|
+
if (isClassLike) {
|
|
637
|
+
try {
|
|
638
|
+
projectedProcessRows = await executeParameterized(repo.id, `
|
|
639
|
+
MATCH (n {id: $nodeId})-[:CodeRelation {type: 'HAS_METHOD'}]->(m)
|
|
640
|
+
MATCH (m)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
|
|
641
|
+
RETURN p.id AS pid, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, p.stepCount AS stepCount, MIN(r.step) AS step
|
|
642
|
+
`, { nodeId: sym.nodeId });
|
|
643
|
+
}
|
|
644
|
+
catch (e) {
|
|
645
|
+
logQueryError('query:method-process-projection', e);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
let processRows = mergeProcessEvidence({
|
|
649
|
+
directRows: directProcessRows,
|
|
650
|
+
projectedRows: isClassLike ? projectedProcessRows : [],
|
|
651
|
+
});
|
|
568
652
|
// Get cluster membership + cohesion (cohesion used as internal ranking signal)
|
|
569
653
|
let cohesion = 0;
|
|
570
654
|
let module;
|
|
@@ -607,7 +691,57 @@ export class LocalBackend {
|
|
|
607
691
|
endLine: sym.endLine,
|
|
608
692
|
...(module ? { module } : {}),
|
|
609
693
|
...(includeContent && content ? { content } : {}),
|
|
694
|
+
...((unityResourcesMode !== 'off'
|
|
695
|
+
&& sym.nodeId
|
|
696
|
+
&& (sym.type === 'Class' || String(sym.nodeId).toLowerCase().startsWith('class:')))
|
|
697
|
+
? await hydrateUnityForSymbol({
|
|
698
|
+
mode: unityHydrationMode,
|
|
699
|
+
basePayload: await loadUnityContext(repo.id, sym.nodeId, (query) => executeQuery(repo.id, query)),
|
|
700
|
+
deps: {
|
|
701
|
+
executeQuery: (query, queryParams) => {
|
|
702
|
+
if (queryParams && Object.keys(queryParams).length > 0) {
|
|
703
|
+
return executeParameterized(repo.id, query, queryParams);
|
|
704
|
+
}
|
|
705
|
+
return executeQuery(repo.id, query);
|
|
706
|
+
},
|
|
707
|
+
repoPath: repo.repoPath,
|
|
708
|
+
storagePath: repo.storagePath,
|
|
709
|
+
indexedCommit: repo.lastCommit,
|
|
710
|
+
},
|
|
711
|
+
symbol: {
|
|
712
|
+
uid: sym.nodeId,
|
|
713
|
+
name: sym.name || '',
|
|
714
|
+
filePath: sym.filePath || '',
|
|
715
|
+
},
|
|
716
|
+
})
|
|
717
|
+
: {}),
|
|
610
718
|
};
|
|
719
|
+
if (processRows.length === 0 && unityResourcesMode !== 'off') {
|
|
720
|
+
const resourceBindings = Array.isArray(symbolEntry.resourceBindings)
|
|
721
|
+
? symbolEntry.resourceBindings
|
|
722
|
+
: [];
|
|
723
|
+
const needsParityRetry = Boolean(symbolEntry.hydrationMeta?.needsParityRetry);
|
|
724
|
+
const hasPartialUnityEvidence = resourceBindings.length > 0 || needsParityRetry;
|
|
725
|
+
if (hasPartialUnityEvidence) {
|
|
726
|
+
const firstBindingPath = String(resourceBindings[0]?.resourcePath || '').trim();
|
|
727
|
+
const verificationTarget = firstBindingPath || sym.filePath || sym.name || sym.nodeId;
|
|
728
|
+
processRows = mergeProcessEvidence({
|
|
729
|
+
directRows: [],
|
|
730
|
+
projectedRows: [],
|
|
731
|
+
heuristicRows: [{
|
|
732
|
+
pid: `proc:heuristic:${String(sym.nodeId || '').replace(/\s+/g, '_')}`,
|
|
733
|
+
label: `${String(sym.name || 'Symbol')} runtime heuristic clue`,
|
|
734
|
+
processType: 'unity_resource_heuristic',
|
|
735
|
+
processSubtype: 'unity_lifecycle',
|
|
736
|
+
runtimeChainConfidence: 'low',
|
|
737
|
+
step: 1,
|
|
738
|
+
stepCount: 1,
|
|
739
|
+
needsParityRetry,
|
|
740
|
+
verificationTarget,
|
|
741
|
+
}],
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
611
745
|
if (processRows.length === 0) {
|
|
612
746
|
// Symbol not in any process — goes to definitions
|
|
613
747
|
definitions.push(symbolEntry);
|
|
@@ -615,12 +749,12 @@ export class LocalBackend {
|
|
|
615
749
|
else {
|
|
616
750
|
// Add to each process it belongs to
|
|
617
751
|
for (const row of processRows) {
|
|
618
|
-
const pid = row.pid
|
|
619
|
-
const label = row.label
|
|
620
|
-
const hLabel = row.heuristicLabel
|
|
621
|
-
const pType = row.processType
|
|
622
|
-
const stepCount = row.stepCount
|
|
623
|
-
const step = row.step
|
|
752
|
+
const pid = String(row.pid || '');
|
|
753
|
+
const label = String(row.label || '');
|
|
754
|
+
const hLabel = String(row.heuristicLabel || label);
|
|
755
|
+
const pType = String(row.processType || '');
|
|
756
|
+
const stepCount = Number(row.stepCount || 0);
|
|
757
|
+
const step = Number(row.step || 0);
|
|
624
758
|
if (!processMap.has(pid)) {
|
|
625
759
|
processMap.set(pid, {
|
|
626
760
|
id: pid,
|
|
@@ -628,6 +762,8 @@ export class LocalBackend {
|
|
|
628
762
|
heuristicLabel: hLabel,
|
|
629
763
|
processType: pType,
|
|
630
764
|
stepCount,
|
|
765
|
+
processSubtype: String(row.processSubtype || ''),
|
|
766
|
+
runtimeChainConfidence: String(row.runtimeChainConfidence || ''),
|
|
631
767
|
totalScore: 0,
|
|
632
768
|
cohesionBoost: 0,
|
|
633
769
|
symbols: [],
|
|
@@ -640,6 +776,14 @@ export class LocalBackend {
|
|
|
640
776
|
...symbolEntry,
|
|
641
777
|
process_id: pid,
|
|
642
778
|
step_index: step,
|
|
779
|
+
process_subtype: String(row.processSubtype || ''),
|
|
780
|
+
process_evidence_mode: row.evidence_mode,
|
|
781
|
+
process_confidence: row.confidence,
|
|
782
|
+
...(confidenceFieldsEnabled ? {
|
|
783
|
+
runtime_chain_confidence: row.confidence,
|
|
784
|
+
runtime_chain_evidence_level: row.runtime_chain_evidence_level,
|
|
785
|
+
verification_hint: row.verification_hint,
|
|
786
|
+
} : {}),
|
|
643
787
|
});
|
|
644
788
|
}
|
|
645
789
|
}
|
|
@@ -659,25 +803,54 @@ export class LocalBackend {
|
|
|
659
803
|
priority: Math.round(p.priority * 1000) / 1000,
|
|
660
804
|
symbol_count: p.symbols.length,
|
|
661
805
|
process_type: p.processType,
|
|
806
|
+
process_subtype: p.processSubtype || undefined,
|
|
662
807
|
step_count: p.stepCount,
|
|
808
|
+
evidence_mode: aggregateProcessEvidenceMode(p.symbols),
|
|
809
|
+
confidence: aggregateProcessConfidence(p.symbols),
|
|
810
|
+
...(confidenceFieldsEnabled ? {
|
|
811
|
+
runtime_chain_confidence: aggregateProcessConfidence(p.symbols),
|
|
812
|
+
runtime_chain_evidence_level: aggregateRuntimeChainEvidenceLevel(p.symbols),
|
|
813
|
+
verification_hint: selectVerificationHint(p.symbols),
|
|
814
|
+
} : {}),
|
|
663
815
|
}));
|
|
664
816
|
const processSymbols = rankedProcesses.flatMap(p => p.symbols.slice(0, maxSymbolsPerProcess).map(s => ({
|
|
665
817
|
...s,
|
|
666
818
|
// remove internal fields
|
|
667
819
|
})));
|
|
668
|
-
// Deduplicate process_symbols by id
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
820
|
+
// Deduplicate process_symbols by id, keeping the highest-confidence/evidence variant.
|
|
821
|
+
const dedupedById = new Map();
|
|
822
|
+
for (const symbol of processSymbols) {
|
|
823
|
+
const existing = dedupedById.get(symbol.id);
|
|
824
|
+
if (!existing) {
|
|
825
|
+
dedupedById.set(symbol.id, symbol);
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
const existingScore = (confidenceRank(existing.process_confidence) * 10)
|
|
829
|
+
+ evidenceModeRank(existing.process_evidence_mode);
|
|
830
|
+
const nextScore = (confidenceRank(symbol.process_confidence) * 10)
|
|
831
|
+
+ evidenceModeRank(symbol.process_evidence_mode);
|
|
832
|
+
if (nextScore > existingScore) {
|
|
833
|
+
dedupedById.set(symbol.id, symbol);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const dedupedSymbols = [...dedupedById.values()];
|
|
837
|
+
const result = {
|
|
677
838
|
processes,
|
|
678
839
|
process_symbols: dedupedSymbols,
|
|
679
840
|
definitions: definitions.slice(0, 20), // cap standalone definitions
|
|
680
841
|
};
|
|
842
|
+
if (runtimeChainVerifyMode === 'on-demand' && runtimeChainVerifyEnabled) {
|
|
843
|
+
const resourceBindings = dedupedSymbols
|
|
844
|
+
.flatMap((symbol) => (Array.isArray(symbol.resourceBindings) ? symbol.resourceBindings : []))
|
|
845
|
+
.concat(definitions.flatMap((symbol) => (Array.isArray(symbol.resourceBindings) ? symbol.resourceBindings : [])));
|
|
846
|
+
result.runtime_chain = await verifyRuntimeChainOnDemand({
|
|
847
|
+
repoPath: repo.repoPath,
|
|
848
|
+
executeParameterized: (query, queryParams) => executeParameterized(repo.id, query, queryParams || {}),
|
|
849
|
+
queryText: searchQuery,
|
|
850
|
+
resourceBindings,
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
return result;
|
|
681
854
|
}
|
|
682
855
|
/**
|
|
683
856
|
* BM25 keyword search helper - uses LadybugDB FTS for always-fresh results
|
|
@@ -957,6 +1130,18 @@ export class LocalBackend {
|
|
|
957
1130
|
async context(repo, params) {
|
|
958
1131
|
await this.ensureInitialized(repo.id);
|
|
959
1132
|
const { name, uid, file_path, include_content } = params;
|
|
1133
|
+
const runtimeChainVerifyMode = String(params.runtime_chain_verify || 'off').trim().toLowerCase();
|
|
1134
|
+
const runtimeChainVerifyEnabled = resolveUnityRuntimeChainVerifyEnabled(process.env);
|
|
1135
|
+
const confidenceFieldsEnabled = resolveUnityProcessConfidenceFieldsEnabled(process.env);
|
|
1136
|
+
let unityResourcesMode = 'off';
|
|
1137
|
+
let unityHydrationMode = 'compact';
|
|
1138
|
+
try {
|
|
1139
|
+
unityResourcesMode = parseUnityResourcesMode(params.unity_resources);
|
|
1140
|
+
unityHydrationMode = parseUnityHydrationMode(params.unity_hydration_mode);
|
|
1141
|
+
}
|
|
1142
|
+
catch (error) {
|
|
1143
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
1144
|
+
}
|
|
960
1145
|
if (!name && !uid) {
|
|
961
1146
|
return { error: 'Either "name" or "uid" parameter is required.' };
|
|
962
1147
|
}
|
|
@@ -1011,6 +1196,9 @@ export class LocalBackend {
|
|
|
1011
1196
|
// Step 3: Build full context
|
|
1012
1197
|
const sym = symbols[0];
|
|
1013
1198
|
const symId = sym.id || sym[0];
|
|
1199
|
+
const symNodeId = String(symId || '');
|
|
1200
|
+
const symName = String(sym.name || sym[1] || '');
|
|
1201
|
+
const symFilePath = String(sym.filePath || sym[3] || '');
|
|
1014
1202
|
// Direct incoming refs for the selected symbol.
|
|
1015
1203
|
const directIncomingRows = await executeParameterized(repo.id, `
|
|
1016
1204
|
MATCH (caller)-[r:CodeRelation]->(n {id: $symId})
|
|
@@ -1068,17 +1256,34 @@ export class LocalBackend {
|
|
|
1068
1256
|
incomingRows = dedupe([...directIncomingRows, ...methodIncomingRows]);
|
|
1069
1257
|
outgoingRows = dedupe([...directOutgoingRows, ...methodOutgoingRows]);
|
|
1070
1258
|
}
|
|
1071
|
-
// Process participation
|
|
1072
|
-
let
|
|
1259
|
+
// Process participation with class-level method projection.
|
|
1260
|
+
let directProcessRows = [];
|
|
1261
|
+
let projectedProcessRows = [];
|
|
1073
1262
|
try {
|
|
1074
|
-
|
|
1263
|
+
directProcessRows = await executeParameterized(repo.id, `
|
|
1075
1264
|
MATCH (n {id: $symId})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
|
|
1076
|
-
RETURN p.id AS pid, p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
|
|
1265
|
+
RETURN p.id AS pid, p.heuristicLabel AS label, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, r.step AS step, p.stepCount AS stepCount
|
|
1077
1266
|
`, { symId });
|
|
1078
1267
|
}
|
|
1079
1268
|
catch (e) {
|
|
1080
1269
|
logQueryError('context:process-participation', e);
|
|
1081
1270
|
}
|
|
1271
|
+
if (isMethodContainer) {
|
|
1272
|
+
try {
|
|
1273
|
+
projectedProcessRows = await executeParameterized(repo.id, `
|
|
1274
|
+
MATCH (n {id: $symId})-[:CodeRelation {type: 'HAS_METHOD'}]->(m)
|
|
1275
|
+
MATCH (m)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
|
|
1276
|
+
RETURN p.id AS pid, p.heuristicLabel AS label, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, MIN(r.step) AS step, p.stepCount AS stepCount
|
|
1277
|
+
`, { symId });
|
|
1278
|
+
}
|
|
1279
|
+
catch (e) {
|
|
1280
|
+
logQueryError('context:method-process-projection', e);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
let processRows = mergeProcessEvidence({
|
|
1284
|
+
directRows: directProcessRows,
|
|
1285
|
+
projectedRows: projectedProcessRows,
|
|
1286
|
+
});
|
|
1082
1287
|
// Helper to categorize refs
|
|
1083
1288
|
const categorize = (rows) => {
|
|
1084
1289
|
const cats = {};
|
|
@@ -1096,13 +1301,13 @@ export class LocalBackend {
|
|
|
1096
1301
|
}
|
|
1097
1302
|
return cats;
|
|
1098
1303
|
};
|
|
1099
|
-
|
|
1304
|
+
const result = {
|
|
1100
1305
|
status: 'found',
|
|
1101
1306
|
symbol: {
|
|
1102
1307
|
uid: sym.id || sym[0],
|
|
1103
|
-
name:
|
|
1308
|
+
name: symName,
|
|
1104
1309
|
kind,
|
|
1105
|
-
filePath:
|
|
1310
|
+
filePath: symFilePath,
|
|
1106
1311
|
startLine: sym.startLine || sym[4],
|
|
1107
1312
|
endLine: sym.endLine || sym[5],
|
|
1108
1313
|
...(include_content && (sym.content || sym[6]) ? { content: sym.content || sym[6] } : {}),
|
|
@@ -1111,13 +1316,81 @@ export class LocalBackend {
|
|
|
1111
1316
|
outgoing: categorize(outgoingRows),
|
|
1112
1317
|
directIncoming: categorize(directIncomingRows),
|
|
1113
1318
|
directOutgoing: categorize(directOutgoingRows),
|
|
1114
|
-
processes:
|
|
1115
|
-
id: r.pid || r[0],
|
|
1116
|
-
name: r.label || r[1],
|
|
1117
|
-
step_index: r.step || r[2],
|
|
1118
|
-
step_count: r.stepCount || r[3],
|
|
1119
|
-
})),
|
|
1319
|
+
processes: [],
|
|
1120
1320
|
};
|
|
1321
|
+
if (unityResourcesMode !== 'off' && symNodeId && (kind === 'Class' || symNodeId.toLowerCase().startsWith('class:'))) {
|
|
1322
|
+
const unityContext = await loadUnityContext(repo.id, symNodeId, (query) => executeQuery(repo.id, query));
|
|
1323
|
+
const hydratedUnityContext = await hydrateUnityForSymbol({
|
|
1324
|
+
mode: unityHydrationMode,
|
|
1325
|
+
basePayload: unityContext,
|
|
1326
|
+
deps: {
|
|
1327
|
+
executeQuery: (query, queryParams) => {
|
|
1328
|
+
if (queryParams && Object.keys(queryParams).length > 0) {
|
|
1329
|
+
return executeParameterized(repo.id, query, queryParams);
|
|
1330
|
+
}
|
|
1331
|
+
return executeQuery(repo.id, query);
|
|
1332
|
+
},
|
|
1333
|
+
repoPath: repo.repoPath,
|
|
1334
|
+
storagePath: repo.storagePath,
|
|
1335
|
+
indexedCommit: repo.lastCommit,
|
|
1336
|
+
},
|
|
1337
|
+
symbol: {
|
|
1338
|
+
uid: symNodeId,
|
|
1339
|
+
name: symName,
|
|
1340
|
+
filePath: symFilePath,
|
|
1341
|
+
},
|
|
1342
|
+
});
|
|
1343
|
+
Object.assign(result, hydratedUnityContext);
|
|
1344
|
+
if (processRows.length === 0) {
|
|
1345
|
+
const resourceBindings = Array.isArray(result.resourceBindings)
|
|
1346
|
+
? result.resourceBindings
|
|
1347
|
+
: [];
|
|
1348
|
+
const needsParityRetry = Boolean(result.hydrationMeta?.needsParityRetry);
|
|
1349
|
+
if (resourceBindings.length > 0 || needsParityRetry) {
|
|
1350
|
+
const firstBindingPath = String(resourceBindings[0]?.resourcePath || '').trim();
|
|
1351
|
+
const verificationTarget = firstBindingPath || symFilePath || symName || symNodeId;
|
|
1352
|
+
processRows = mergeProcessEvidence({
|
|
1353
|
+
directRows: [],
|
|
1354
|
+
projectedRows: [],
|
|
1355
|
+
heuristicRows: [{
|
|
1356
|
+
pid: `proc:heuristic:${String(symNodeId || '').replace(/\s+/g, '_')}`,
|
|
1357
|
+
label: `${String(symName || 'Symbol')} runtime heuristic clue`,
|
|
1358
|
+
processType: 'unity_resource_heuristic',
|
|
1359
|
+
processSubtype: 'unity_lifecycle',
|
|
1360
|
+
runtimeChainConfidence: 'low',
|
|
1361
|
+
step: 1,
|
|
1362
|
+
stepCount: 1,
|
|
1363
|
+
needsParityRetry,
|
|
1364
|
+
verificationTarget,
|
|
1365
|
+
}],
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
result.processes = processRows.map((r) => ({
|
|
1371
|
+
id: r.pid || r[0],
|
|
1372
|
+
name: r.label || r[1],
|
|
1373
|
+
process_subtype: r.processSubtype || r[2],
|
|
1374
|
+
step_index: r.step || r[4],
|
|
1375
|
+
step_count: r.stepCount || r[5],
|
|
1376
|
+
evidence_mode: r.evidence_mode,
|
|
1377
|
+
confidence: r.confidence,
|
|
1378
|
+
...(confidenceFieldsEnabled ? {
|
|
1379
|
+
runtime_chain_confidence: r.confidence,
|
|
1380
|
+
runtime_chain_evidence_level: r.runtime_chain_evidence_level,
|
|
1381
|
+
verification_hint: r.verification_hint,
|
|
1382
|
+
} : {}),
|
|
1383
|
+
}));
|
|
1384
|
+
if (runtimeChainVerifyMode === 'on-demand' && runtimeChainVerifyEnabled) {
|
|
1385
|
+
result.runtime_chain = await verifyRuntimeChainOnDemand({
|
|
1386
|
+
repoPath: repo.repoPath,
|
|
1387
|
+
executeParameterized: (query, queryParams) => executeParameterized(repo.id, query, queryParams || {}),
|
|
1388
|
+
symbolName: symName,
|
|
1389
|
+
symbolFilePath: symFilePath,
|
|
1390
|
+
resourceBindings: Array.isArray(result.resourceBindings) ? result.resourceBindings : [],
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
return result;
|
|
1121
1394
|
}
|
|
1122
1395
|
/**
|
|
1123
1396
|
* Legacy explore — kept for backwards compatibility with resources.ts.
|
|
@@ -1171,7 +1444,7 @@ export class LocalBackend {
|
|
|
1171
1444
|
const processes = await executeParameterized(repo.id, `
|
|
1172
1445
|
MATCH (p:Process)
|
|
1173
1446
|
WHERE p.label = $processName OR p.heuristicLabel = $processName
|
|
1174
|
-
RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
|
|
1447
|
+
RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, p.stepCount AS stepCount
|
|
1175
1448
|
LIMIT 1
|
|
1176
1449
|
`, { processName: name });
|
|
1177
1450
|
if (processes.length === 0)
|
|
@@ -1180,16 +1453,24 @@ export class LocalBackend {
|
|
|
1180
1453
|
const procId = proc.id || proc[0];
|
|
1181
1454
|
const steps = await executeParameterized(repo.id, `
|
|
1182
1455
|
MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: $procId})
|
|
1183
|
-
RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
|
|
1456
|
+
RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step, r.reason AS reason, r.confidence AS confidence
|
|
1184
1457
|
ORDER BY r.step
|
|
1185
1458
|
`, { procId });
|
|
1186
1459
|
return {
|
|
1187
1460
|
process: {
|
|
1188
1461
|
id: procId, label: proc.label || proc[1], heuristicLabel: proc.heuristicLabel || proc[2],
|
|
1189
|
-
processType: proc.processType || proc[3],
|
|
1462
|
+
processType: proc.processType || proc[3],
|
|
1463
|
+
processSubtype: proc.processSubtype || proc[4],
|
|
1464
|
+
runtimeChainConfidence: proc.runtimeChainConfidence || proc[5],
|
|
1465
|
+
stepCount: proc.stepCount || proc[6],
|
|
1190
1466
|
},
|
|
1191
1467
|
steps: steps.map((s) => ({
|
|
1192
|
-
step: s.step || s[3],
|
|
1468
|
+
step: s.step || s[3],
|
|
1469
|
+
reason: s.reason || s[4],
|
|
1470
|
+
confidence: s.confidence || s[5],
|
|
1471
|
+
name: s.name || s[0],
|
|
1472
|
+
type: s.type || s[1],
|
|
1473
|
+
filePath: s.filePath || s[2],
|
|
1193
1474
|
})),
|
|
1194
1475
|
};
|
|
1195
1476
|
}
|
|
@@ -1527,13 +1808,12 @@ export class LocalBackend {
|
|
|
1527
1808
|
// Bridge class-like symbols through HAS_METHOD so class-level impact includes method-level dependencies.
|
|
1528
1809
|
if (shouldBridgeClassMethods) {
|
|
1529
1810
|
if (frontier.length > 0) {
|
|
1530
|
-
const bridgeIdList = frontier.map(id => `'${id.replace(/'/g, "''")}'`).join(', ');
|
|
1531
1811
|
try {
|
|
1532
|
-
const bridgeRows = await
|
|
1812
|
+
const bridgeRows = await executeParameterized(repo.id, `
|
|
1533
1813
|
MATCH (container)-[r:CodeRelation {type: 'HAS_METHOD'}]->(method)
|
|
1534
|
-
WHERE container.id IN
|
|
1814
|
+
WHERE container.id IN $frontierIds${confidenceFilter}
|
|
1535
1815
|
RETURN container.id AS containerId, method.id AS methodId, method.name AS methodName, labels(method)[0] AS methodType, method.filePath AS methodFilePath
|
|
1536
|
-
|
|
1816
|
+
`, { frontierIds: frontier });
|
|
1537
1817
|
for (const bridge of bridgeRows) {
|
|
1538
1818
|
const methodId = bridge.methodId || bridge[1];
|
|
1539
1819
|
const methodType = bridge.methodType || bridge[3];
|
|
@@ -1575,12 +1855,11 @@ export class LocalBackend {
|
|
|
1575
1855
|
// de-dupe traversal frontier (class + bridged methods)
|
|
1576
1856
|
traversalFrontier = [...new Set(traversalFrontier)];
|
|
1577
1857
|
// Batch frontier nodes into a single Cypher query per depth level
|
|
1578
|
-
const idList = traversalFrontier.map(id => `'${id.replace(/'/g, "''")}'`).join(', ');
|
|
1579
1858
|
const query = direction === 'upstream'
|
|
1580
|
-
? `MATCH (caller)-[r:CodeRelation]->(n) WHERE n.id IN
|
|
1581
|
-
: `MATCH (n)-[r:CodeRelation]->(callee) WHERE n.id IN
|
|
1859
|
+
? `MATCH (caller)-[r:CodeRelation]->(n) WHERE n.id IN $frontierIds AND r.type IN [${relTypeFilter}]${confidenceFilter} RETURN n.id AS sourceId, caller.id AS id, caller.name AS name, labels(caller)[0] AS type, caller.filePath AS filePath, r.type AS relType, r.confidence AS confidence`
|
|
1860
|
+
: `MATCH (n)-[r:CodeRelation]->(callee) WHERE n.id IN $frontierIds AND r.type IN [${relTypeFilter}]${confidenceFilter} RETURN n.id AS sourceId, callee.id AS id, callee.name AS name, labels(callee)[0] AS type, callee.filePath AS filePath, r.type AS relType, r.confidence AS confidence`;
|
|
1582
1861
|
try {
|
|
1583
|
-
const related = await
|
|
1862
|
+
const related = await executeParameterized(repo.id, query, { frontierIds: traversalFrontier });
|
|
1584
1863
|
for (const rel of related) {
|
|
1585
1864
|
const relId = rel.id || rel[1];
|
|
1586
1865
|
const filePath = rel.filePath || rel[4] || '';
|
|
@@ -1738,7 +2017,7 @@ export class LocalBackend {
|
|
|
1738
2017
|
try {
|
|
1739
2018
|
const processes = await executeQuery(repo.id, `
|
|
1740
2019
|
MATCH (p:Process)
|
|
1741
|
-
RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
|
|
2020
|
+
RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, p.stepCount AS stepCount
|
|
1742
2021
|
ORDER BY p.stepCount DESC
|
|
1743
2022
|
LIMIT ${limit}
|
|
1744
2023
|
`);
|
|
@@ -1748,7 +2027,9 @@ export class LocalBackend {
|
|
|
1748
2027
|
label: p.label || p[1],
|
|
1749
2028
|
heuristicLabel: p.heuristicLabel || p[2],
|
|
1750
2029
|
processType: p.processType || p[3],
|
|
1751
|
-
|
|
2030
|
+
processSubtype: p.processSubtype || p[4],
|
|
2031
|
+
runtimeChainConfidence: p.runtimeChainConfidence || p[5],
|
|
2032
|
+
stepCount: p.stepCount || p[6],
|
|
1752
2033
|
})),
|
|
1753
2034
|
};
|
|
1754
2035
|
}
|
|
@@ -1810,7 +2091,7 @@ export class LocalBackend {
|
|
|
1810
2091
|
const processes = await executeParameterized(repo.id, `
|
|
1811
2092
|
MATCH (p:Process)
|
|
1812
2093
|
WHERE p.label = $processName OR p.heuristicLabel = $processName
|
|
1813
|
-
RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.stepCount AS stepCount
|
|
2094
|
+
RETURN p.id AS id, p.label AS label, p.heuristicLabel AS heuristicLabel, p.processType AS processType, p.processSubtype AS processSubtype, p.runtimeChainConfidence AS runtimeChainConfidence, p.stepCount AS stepCount
|
|
1814
2095
|
LIMIT 1
|
|
1815
2096
|
`, { processName: name });
|
|
1816
2097
|
if (processes.length === 0)
|
|
@@ -1819,16 +2100,24 @@ export class LocalBackend {
|
|
|
1819
2100
|
const procId = proc.id || proc[0];
|
|
1820
2101
|
const steps = await executeParameterized(repo.id, `
|
|
1821
2102
|
MATCH (n)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p {id: $procId})
|
|
1822
|
-
RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step
|
|
2103
|
+
RETURN n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, r.step AS step, r.reason AS reason, r.confidence AS confidence
|
|
1823
2104
|
ORDER BY r.step
|
|
1824
2105
|
`, { procId });
|
|
1825
2106
|
return {
|
|
1826
2107
|
process: {
|
|
1827
2108
|
id: procId, label: proc.label || proc[1], heuristicLabel: proc.heuristicLabel || proc[2],
|
|
1828
|
-
processType: proc.processType || proc[3],
|
|
2109
|
+
processType: proc.processType || proc[3],
|
|
2110
|
+
processSubtype: proc.processSubtype || proc[4],
|
|
2111
|
+
runtimeChainConfidence: proc.runtimeChainConfidence || proc[5],
|
|
2112
|
+
stepCount: proc.stepCount || proc[6],
|
|
1829
2113
|
},
|
|
1830
2114
|
steps: steps.map((s) => ({
|
|
1831
|
-
step: s.step || s[3],
|
|
2115
|
+
step: s.step || s[3],
|
|
2116
|
+
reason: s.reason || s[4],
|
|
2117
|
+
confidence: s.confidence || s[5],
|
|
2118
|
+
name: s.name || s[0],
|
|
2119
|
+
type: s.type || s[1],
|
|
2120
|
+
filePath: s.filePath || s[2],
|
|
1832
2121
|
})),
|
|
1833
2122
|
};
|
|
1834
2123
|
}
|
|
@@ -2,7 +2,7 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { projectUnityBindings } from './unity-enrichment.js';
|
|
4
4
|
import { hydrateLazyBindings } from './unity-lazy-hydrator.js';
|
|
5
|
-
import { attachUnityHydrationMeta, mergeParityUnityBindings, mergeUnityBindings } from './
|
|
5
|
+
import { attachUnityHydrationMeta, mergeParityUnityBindings, mergeUnityBindings } from './unity-runtime-hydration.js';
|
|
6
6
|
test('summary-only rows hydrate and merge into full bindings with preserved field coverage', async () => {
|
|
7
7
|
const projected = projectUnityBindings([
|
|
8
8
|
{
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ProcessEvidenceMode = 'direct_step' | 'method_projected' | 'resource_heuristic';
|
|
2
|
+
export type ProcessConfidence = 'high' | 'medium' | 'low';
|
|
3
|
+
export interface VerificationHint {
|
|
4
|
+
action: 'rerun_parity_hydration' | 'manual_asset_meta_verification';
|
|
5
|
+
target: string;
|
|
6
|
+
next_command: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DeriveConfidenceInput {
|
|
9
|
+
evidenceMode: ProcessEvidenceMode;
|
|
10
|
+
processSubtype?: string;
|
|
11
|
+
hasPartialUnityEvidence?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface BuildVerificationHintInput {
|
|
14
|
+
confidence: ProcessConfidence;
|
|
15
|
+
needsParityRetry?: boolean;
|
|
16
|
+
target?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function deriveConfidence(input: DeriveConfidenceInput): ProcessConfidence;
|
|
19
|
+
export declare function buildVerificationHint(input: BuildVerificationHintInput): VerificationHint | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function deriveConfidence(input) {
|
|
2
|
+
if (input.evidenceMode === 'resource_heuristic') {
|
|
3
|
+
return 'low';
|
|
4
|
+
}
|
|
5
|
+
if (input.evidenceMode === 'method_projected') {
|
|
6
|
+
return 'medium';
|
|
7
|
+
}
|
|
8
|
+
if (String(input.processSubtype || '').toLowerCase() === 'unity_lifecycle') {
|
|
9
|
+
return 'medium';
|
|
10
|
+
}
|
|
11
|
+
return 'high';
|
|
12
|
+
}
|
|
13
|
+
export function buildVerificationHint(input) {
|
|
14
|
+
if (input.confidence !== 'low')
|
|
15
|
+
return undefined;
|
|
16
|
+
const target = String(input.target || '').trim() || 'unity-runtime-chain';
|
|
17
|
+
if (input.needsParityRetry) {
|
|
18
|
+
return {
|
|
19
|
+
action: 'rerun_parity_hydration',
|
|
20
|
+
target,
|
|
21
|
+
next_command: 'GITNEXUS_UNITY_PROCESS_CONFIDENCE_FIELDS=on gitnexus query --unity-resources on --unity-hydration parity "<symbol-or-query>"',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
action: 'manual_asset_meta_verification',
|
|
26
|
+
target,
|
|
27
|
+
next_command: 'Inspect asset + .meta linkage for this target, then rerun query/context with --unity-resources on --unity-hydration parity',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|