@veewo/gitnexus 1.5.0 → 1.5.2
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/agent-context/runner.js +3 -0
- package/dist/benchmark/agent-context/runner.test.js +22 -0
- package/dist/benchmark/agent-context/tool-runner.d.ts +7 -6
- package/dist/benchmark/agent-safe-query-context/io.d.ts +2 -0
- package/dist/benchmark/agent-safe-query-context/io.js +86 -0
- package/dist/benchmark/agent-safe-query-context/io.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/io.test.js +13 -0
- package/dist/benchmark/agent-safe-query-context/report.d.ts +57 -0
- package/dist/benchmark/agent-safe-query-context/report.js +159 -0
- package/dist/benchmark/agent-safe-query-context/report.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/report.test.js +362 -0
- package/dist/benchmark/agent-safe-query-context/runner.d.ts +44 -0
- package/dist/benchmark/agent-safe-query-context/runner.js +406 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.js +290 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.d.ts +20 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.js +225 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.js +122 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.d.ts +47 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.js +128 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.js +155 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.d.ts +9 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.js +77 -0
- package/dist/benchmark/agent-safe-query-context/types.d.ts +61 -0
- package/dist/benchmark/agent-safe-query-context/types.js +8 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.d.ts +47 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.js +89 -0
- package/dist/benchmark/runtime-poc/runner.d.ts +31 -0
- package/dist/benchmark/runtime-poc/runner.js +163 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.d.ts +8 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.js +21 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.d.ts +0 -1
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.js +53 -51
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.js +0 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +1 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +82 -18
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +1 -2
- package/dist/benchmark/u2-e2e/retrieval-runner.js +15 -7
- package/dist/benchmark/u2-e2e/retrieval-runner.test.js +46 -0
- package/dist/cli/ai-context.js +2 -12
- package/dist/cli/ai-context.test.js +8 -0
- package/dist/cli/analyze-runtime-summary.js +1 -0
- package/dist/cli/analyze-runtime-summary.test.js +2 -0
- package/dist/cli/analyze-summary.d.ts +2 -0
- package/dist/cli/analyze-summary.js +24 -0
- package/dist/cli/analyze-summary.test.js +65 -1
- package/dist/cli/analyze.js +5 -1
- package/dist/cli/benchmark-agent-safe-query-context.d.ts +20 -0
- package/dist/cli/benchmark-agent-safe-query-context.js +39 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.d.ts +1 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.js +271 -0
- package/dist/cli/benchmark.d.ts +29 -0
- package/dist/cli/benchmark.js +55 -0
- package/dist/cli/index.js +23 -0
- package/dist/cli/rule-lab.d.ts +3 -7
- package/dist/cli/rule-lab.js +13 -22
- package/dist/cli/rule-lab.test.js +23 -3
- package/dist/cli/tool.d.ts +2 -0
- package/dist/cli/tool.js +2 -0
- package/dist/core/config/unity-config.d.ts +0 -1
- package/dist/core/config/unity-config.js +0 -1
- package/dist/core/ingestion/pipeline.js +35 -6
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +18 -20
- package/dist/core/ingestion/unity-parity-seed.d.ts +2 -1
- package/dist/core/ingestion/unity-parity-seed.js +8 -0
- package/dist/core/ingestion/unity-resource-processor.d.ts +11 -0
- package/dist/core/ingestion/unity-resource-processor.js +102 -0
- package/dist/core/ingestion/unity-resource-processor.test.js +449 -0
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +15 -0
- package/dist/core/ingestion/unity-runtime-binding-rules.js +178 -30
- package/dist/core/lbug/csv-generator.test.js +2 -2
- package/dist/core/unity/doc-contract.test.d.ts +1 -0
- package/dist/core/unity/doc-contract.test.js +30 -0
- package/dist/core/unity/prefab-source-scan.d.ts +25 -0
- package/dist/core/unity/prefab-source-scan.js +152 -0
- package/dist/core/unity/prefab-source-scan.test.d.ts +1 -0
- package/dist/core/unity/prefab-source-scan.test.js +70 -0
- package/dist/core/unity/scan-context.d.ts +12 -0
- package/dist/core/unity/scan-context.js +50 -2
- package/dist/core/unity/scan-context.test.js +74 -0
- package/dist/mcp/local/agent-safe-response.d.ts +10 -0
- package/dist/mcp/local/agent-safe-response.js +639 -0
- package/dist/mcp/local/derived-process-reader.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +18 -1
- package/dist/mcp/local/local-backend.js +319 -125
- package/dist/mcp/local/process-confidence.d.ts +1 -2
- package/dist/mcp/local/process-confidence.js +0 -3
- package/dist/mcp/local/process-confidence.test.js +4 -2
- package/dist/mcp/local/process-evidence.d.ts +1 -8
- package/dist/mcp/local/process-evidence.js +1 -23
- package/dist/mcp/local/process-evidence.test.js +2 -16
- package/dist/mcp/local/process-ref.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-closure-evaluator.d.ts +33 -0
- package/dist/mcp/local/runtime-chain-closure-evaluator.js +273 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.d.ts +23 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.js +131 -0
- package/dist/mcp/local/runtime-chain-verify.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-verify.js +149 -138
- package/dist/mcp/local/runtime-chain-verify.test.js +126 -68
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.js +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +37 -4
- package/dist/mcp/local/runtime-claim.d.ts +11 -0
- package/dist/mcp/local/runtime-claim.js +28 -0
- package/dist/mcp/local/unity-evidence-view.d.ts +1 -1
- package/dist/mcp/local/unity-evidence-view.js +1 -1
- package/dist/mcp/local/unity-evidence-view.test.js +22 -0
- package/dist/mcp/tools.js +51 -21
- package/dist/rule-lab/analyze.d.ts +2 -1
- package/dist/rule-lab/analyze.js +94 -59
- package/dist/rule-lab/analyze.test.js +238 -20
- package/dist/rule-lab/curate.d.ts +2 -1
- package/dist/rule-lab/curate.js +24 -3
- package/dist/rule-lab/curate.test.js +65 -0
- package/dist/rule-lab/curation-input-builder.d.ts +45 -0
- package/dist/rule-lab/curation-input-builder.js +133 -0
- package/dist/rule-lab/promote.js +80 -7
- package/dist/rule-lab/promote.test.js +150 -0
- package/dist/rule-lab/review-pack.d.ts +3 -0
- package/dist/rule-lab/review-pack.js +41 -1
- package/dist/rule-lab/review-pack.test.js +67 -0
- package/dist/rule-lab/types.d.ts +29 -0
- package/dist/types/pipeline.d.ts +3 -0
- package/package.json +4 -3
- package/scripts/run-node-tests.mjs +61 -0
- package/skills/_shared/unity-rule-authoring-contract.md +64 -0
- package/skills/_shared/unity-runtime-process-contract.md +16 -0
- package/skills/gitnexus-cli.md +8 -0
- package/skills/gitnexus-debugging.md +9 -0
- package/skills/gitnexus-exploring.md +66 -18
- package/skills/gitnexus-guide.md +42 -3
- package/skills/gitnexus-impact-analysis.md +8 -0
- package/skills/gitnexus-pr-review.md +8 -0
- package/skills/gitnexus-refactoring.md +8 -0
- package/skills/gitnexus-unity-rule-gen.md +66 -312
|
@@ -7,6 +7,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
import { generateId } from '../../lib/utils.js';
|
|
8
8
|
import { createKnowledgeGraph } from '../graph/graph.js';
|
|
9
9
|
import { closeLbug, executeQuery, initLbug, loadGraphToLbug } from '../lbug/lbug-adapter.js';
|
|
10
|
+
import { buildUnityScanContext } from '../unity/scan-context.js';
|
|
11
|
+
import { streamPrefabSourceRefs } from '../unity/prefab-source-scan.js';
|
|
10
12
|
import { processUnityResources } from './unity-resource-processor.js';
|
|
11
13
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
12
14
|
const fixtureRoot = path.resolve(here, '../../../src/core/unity/__fixtures__/mini-unity');
|
|
@@ -82,6 +84,49 @@ test('processUnityResources emits schema-compatible component-instance edges and
|
|
|
82
84
|
assert.ok(result.timingsMs.resolve >= 0);
|
|
83
85
|
assert.ok(result.timingsMs.graphWrite > 0);
|
|
84
86
|
});
|
|
87
|
+
test('fixture run emits scene->prefab UNITY_ASSET_GUID_REF and keeps script ref edges', async () => {
|
|
88
|
+
const graph = createKnowledgeGraph();
|
|
89
|
+
for (const symbol of symbols) {
|
|
90
|
+
const filePath = `Assets/Scripts/${symbol}.cs`;
|
|
91
|
+
const fileId = generateId('File', filePath);
|
|
92
|
+
const classId = generateId('Class', `${filePath}:${symbol}`);
|
|
93
|
+
graph.addNode({
|
|
94
|
+
id: fileId,
|
|
95
|
+
label: 'File',
|
|
96
|
+
properties: {
|
|
97
|
+
name: `${symbol}.cs`,
|
|
98
|
+
filePath,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
graph.addNode({
|
|
102
|
+
id: classId,
|
|
103
|
+
label: 'Class',
|
|
104
|
+
properties: {
|
|
105
|
+
name: symbol,
|
|
106
|
+
filePath,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
graph.addRelationship({
|
|
110
|
+
id: generateId('DEFINES', `${fileId}->${classId}`),
|
|
111
|
+
type: 'DEFINES',
|
|
112
|
+
sourceId: fileId,
|
|
113
|
+
targetId: classId,
|
|
114
|
+
confidence: 1.0,
|
|
115
|
+
reason: '',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
await processUnityResources(graph, { repoPath: fixtureRoot });
|
|
119
|
+
const guidRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF');
|
|
120
|
+
const scenePrefabRef = guidRefs.find((rel) => {
|
|
121
|
+
const reason = JSON.parse(String(rel.reason || '{}'));
|
|
122
|
+
return (reason.resourcePath === 'Assets/Scene/MainUIManager.unity'
|
|
123
|
+
&& reason.targetResourcePath === 'Assets/Prefabs/BattleMode.prefab'
|
|
124
|
+
&& reason.fieldName === 'm_SourcePrefab');
|
|
125
|
+
});
|
|
126
|
+
assert.ok(scenePrefabRef);
|
|
127
|
+
const scriptRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_GRAPH_NODE_SCRIPT_REF');
|
|
128
|
+
assert.ok(scriptRefs.length > 0);
|
|
129
|
+
});
|
|
85
130
|
test('processUnityResources persists UNITY_RESOURCE_SUMMARY in LadybugDB', async () => {
|
|
86
131
|
const graph = createKnowledgeGraph();
|
|
87
132
|
for (const symbol of symbols) {
|
|
@@ -518,6 +563,410 @@ test('processUnityResources writes asset-guid and graph-node reference edges fro
|
|
|
518
563
|
assert.equal(guidReason.targetResourcePath, 'Assets/Graphs/Weapon.asset');
|
|
519
564
|
assert.equal(guidReason.fieldName, 'gungraph');
|
|
520
565
|
});
|
|
566
|
+
test('processUnityResources emits prefab-source edges from scanContext.prefabSourceRefs only', async () => {
|
|
567
|
+
const graph = createKnowledgeGraph();
|
|
568
|
+
const fakeScanContext = {
|
|
569
|
+
symbolToScriptPath: new Map(),
|
|
570
|
+
scriptPathToGuid: new Map(),
|
|
571
|
+
guidToResourceHits: new Map(),
|
|
572
|
+
prefabSourceRefs: [
|
|
573
|
+
{
|
|
574
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
575
|
+
targetGuid: '99999999999999999999999999999999',
|
|
576
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
577
|
+
fileId: '100100000',
|
|
578
|
+
fieldName: 'm_SourcePrefab',
|
|
579
|
+
sourceLayer: 'scene',
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
resourceDocCache: new Map(),
|
|
583
|
+
};
|
|
584
|
+
const result = await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
585
|
+
buildScanContext: async () => fakeScanContext,
|
|
586
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
587
|
+
});
|
|
588
|
+
const guidRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF');
|
|
589
|
+
assert.equal(guidRefs.length, 1);
|
|
590
|
+
const reason = JSON.parse(String(guidRefs[0]?.reason || '{}'));
|
|
591
|
+
assert.equal(reason.fieldName, 'm_SourcePrefab');
|
|
592
|
+
assert.equal(reason.sourceLayer, 'scene');
|
|
593
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab-source: emitted=1')));
|
|
594
|
+
});
|
|
595
|
+
test('processUnityResources consumes prefab-source stream incrementally and emits edges', async () => {
|
|
596
|
+
const graph = createKnowledgeGraph();
|
|
597
|
+
const fakeScanContext = {
|
|
598
|
+
symbolToScriptPath: new Map(),
|
|
599
|
+
scriptPathToGuid: new Map(),
|
|
600
|
+
guidToResourceHits: new Map(),
|
|
601
|
+
assetGuidToPath: new Map([['99999999999999999999999999999999', 'Assets/Prefabs/BattleMode.prefab']]),
|
|
602
|
+
streamPrefabSourceRefs: async function* () {
|
|
603
|
+
yield {
|
|
604
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
605
|
+
targetGuid: '99999999999999999999999999999999',
|
|
606
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
607
|
+
fileId: '100100000',
|
|
608
|
+
fieldName: 'm_SourcePrefab',
|
|
609
|
+
sourceLayer: 'scene',
|
|
610
|
+
};
|
|
611
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
612
|
+
yield {
|
|
613
|
+
sourceResourcePath: 'Assets/Scene/Global.unity',
|
|
614
|
+
targetGuid: '99999999999999999999999999999999',
|
|
615
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
616
|
+
fileId: '100100001',
|
|
617
|
+
fieldName: 'm_SourcePrefab',
|
|
618
|
+
sourceLayer: 'scene',
|
|
619
|
+
};
|
|
620
|
+
},
|
|
621
|
+
prefabSourceRefs: [],
|
|
622
|
+
resourceDocCache: new Map(),
|
|
623
|
+
};
|
|
624
|
+
const result = await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
625
|
+
buildScanContext: async () => fakeScanContext,
|
|
626
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
627
|
+
});
|
|
628
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab-source: emitted=2')));
|
|
629
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab_source.rows_emitted=2')));
|
|
630
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab_source.rows_parsed=')));
|
|
631
|
+
});
|
|
632
|
+
test('prefab-source accounting invariant closes', async () => {
|
|
633
|
+
const graph = createKnowledgeGraph();
|
|
634
|
+
const fakeScanContext = {
|
|
635
|
+
symbolToScriptPath: new Map(),
|
|
636
|
+
scriptPathToGuid: new Map(),
|
|
637
|
+
guidToResourceHits: new Map(),
|
|
638
|
+
assetGuidToPath: new Map([['99999999999999999999999999999999', 'Assets/Prefabs/BattleMode.prefab']]),
|
|
639
|
+
streamPrefabSourceRefs: async function* () {
|
|
640
|
+
yield {
|
|
641
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
642
|
+
targetGuid: '99999999999999999999999999999999',
|
|
643
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
644
|
+
fileId: '100100000',
|
|
645
|
+
fieldName: 'm_SourcePrefab',
|
|
646
|
+
sourceLayer: 'scene',
|
|
647
|
+
};
|
|
648
|
+
yield {
|
|
649
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
650
|
+
targetGuid: '00000000000000000000000000000000',
|
|
651
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
652
|
+
fileId: '100100001',
|
|
653
|
+
fieldName: 'm_SourcePrefab',
|
|
654
|
+
sourceLayer: 'scene',
|
|
655
|
+
};
|
|
656
|
+
yield {
|
|
657
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
658
|
+
targetGuid: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
659
|
+
targetResourcePath: '__PLACEHOLDER__',
|
|
660
|
+
fileId: '100100002',
|
|
661
|
+
fieldName: 'm_SourcePrefab',
|
|
662
|
+
sourceLayer: 'scene',
|
|
663
|
+
};
|
|
664
|
+
yield {
|
|
665
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
666
|
+
targetGuid: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
|
667
|
+
targetResourcePath: '',
|
|
668
|
+
fileId: '100100003',
|
|
669
|
+
fieldName: 'm_SourcePrefab',
|
|
670
|
+
sourceLayer: 'scene',
|
|
671
|
+
};
|
|
672
|
+
yield {
|
|
673
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
674
|
+
targetGuid: '99999999999999999999999999999999',
|
|
675
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
676
|
+
fileId: '100100000',
|
|
677
|
+
fieldName: 'm_SourcePrefab',
|
|
678
|
+
sourceLayer: 'scene',
|
|
679
|
+
};
|
|
680
|
+
},
|
|
681
|
+
prefabSourceRefs: [],
|
|
682
|
+
resourceDocCache: new Map(),
|
|
683
|
+
};
|
|
684
|
+
const result = await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
685
|
+
buildScanContext: async () => fakeScanContext,
|
|
686
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
687
|
+
});
|
|
688
|
+
assert.equal(result.prefabSourceStats.rowsParsed, result.prefabSourceStats.rowsFilteredZeroGuid
|
|
689
|
+
+ result.prefabSourceStats.rowsFilteredPlaceholder
|
|
690
|
+
+ result.prefabSourceStats.rowsFilteredUnresolved
|
|
691
|
+
+ result.prefabSourceStats.rowsDeduped
|
|
692
|
+
+ result.prefabSourceStats.rowsEmitted);
|
|
693
|
+
});
|
|
694
|
+
test('no cross-signal dedupe collision when same source has script-guid and prefab-source refs', async () => {
|
|
695
|
+
const graph = createKnowledgeGraph();
|
|
696
|
+
const filePath = 'Assets/Scripts/MainUIManager.cs';
|
|
697
|
+
const classId = generateId('Class', `${filePath}:MainUIManager`);
|
|
698
|
+
graph.addNode({
|
|
699
|
+
id: classId,
|
|
700
|
+
label: 'Class',
|
|
701
|
+
properties: { name: 'MainUIManager', filePath },
|
|
702
|
+
});
|
|
703
|
+
const fakeScanContext = {
|
|
704
|
+
symbolToScriptPath: new Map([['MainUIManager', filePath]]),
|
|
705
|
+
scriptPathToGuid: new Map([[filePath, 'dddddddddddddddddddddddddddddddd']]),
|
|
706
|
+
guidToResourceHits: new Map([
|
|
707
|
+
['dddddddddddddddddddddddddddddddd', [{ resourcePath: 'Assets/Scene/MainUIManager.unity', resourceType: 'scene', line: 1, lineText: 'guid' }]],
|
|
708
|
+
]),
|
|
709
|
+
assetGuidToPath: new Map([['99999999999999999999999999999999', 'Assets/Prefabs/BattleMode.prefab']]),
|
|
710
|
+
streamPrefabSourceRefs: async function* () {
|
|
711
|
+
yield {
|
|
712
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
713
|
+
targetGuid: '99999999999999999999999999999999',
|
|
714
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
715
|
+
fileId: '100100000',
|
|
716
|
+
fieldName: 'm_SourcePrefab',
|
|
717
|
+
sourceLayer: 'scene',
|
|
718
|
+
};
|
|
719
|
+
},
|
|
720
|
+
prefabSourceRefs: [],
|
|
721
|
+
resourceDocCache: new Map(),
|
|
722
|
+
};
|
|
723
|
+
await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
724
|
+
buildScanContext: async () => fakeScanContext,
|
|
725
|
+
resolveBindings: async () => ({
|
|
726
|
+
symbol: 'MainUIManager',
|
|
727
|
+
scriptPath: filePath,
|
|
728
|
+
scriptGuid: 'dddddddddddddddddddddddddddddddd',
|
|
729
|
+
resourceBindings: [
|
|
730
|
+
{
|
|
731
|
+
resourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
732
|
+
resourceType: 'scene',
|
|
733
|
+
bindingKind: 'direct',
|
|
734
|
+
componentObjectId: '11400000',
|
|
735
|
+
evidence: { line: 1, lineText: 'guid' },
|
|
736
|
+
serializedFields: { scalarFields: [], referenceFields: [] },
|
|
737
|
+
resolvedReferences: [
|
|
738
|
+
{
|
|
739
|
+
fieldName: 'gungraph',
|
|
740
|
+
sourceLayer: 'scene',
|
|
741
|
+
fileId: '11400000',
|
|
742
|
+
guid: '99999999999999999999999999999999',
|
|
743
|
+
fromList: false,
|
|
744
|
+
resolution: 'external-asset',
|
|
745
|
+
target: { assetPath: 'Assets/Prefabs/BattleMode.prefab' },
|
|
746
|
+
},
|
|
747
|
+
],
|
|
748
|
+
},
|
|
749
|
+
],
|
|
750
|
+
serializedFields: { scalarFields: [], referenceFields: [] },
|
|
751
|
+
unityDiagnostics: [],
|
|
752
|
+
}),
|
|
753
|
+
});
|
|
754
|
+
const refs = [...graph.iterRelationships()]
|
|
755
|
+
.filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF')
|
|
756
|
+
.map((rel) => JSON.parse(String(rel.reason || '{}')));
|
|
757
|
+
assert.ok(refs.some((reason) => reason.fieldName === 'm_SourcePrefab'));
|
|
758
|
+
assert.ok(refs.some((reason) => reason.fieldName === 'gungraph'));
|
|
759
|
+
});
|
|
760
|
+
test('scan-context does not write graph edges directly; processor remains sole writer', async () => {
|
|
761
|
+
const graph = createKnowledgeGraph();
|
|
762
|
+
const context = await buildUnityScanContext({ repoRoot: fixtureRoot });
|
|
763
|
+
assert.equal([...graph.iterRelationships()].length, 0);
|
|
764
|
+
await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
765
|
+
buildScanContext: async () => context,
|
|
766
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
767
|
+
});
|
|
768
|
+
assert.ok([...graph.iterRelationships()].some((rel) => rel.type === 'UNITY_ASSET_GUID_REF'));
|
|
769
|
+
});
|
|
770
|
+
test('prefab source pass can be disabled via env toggle', async () => {
|
|
771
|
+
const graph = createKnowledgeGraph();
|
|
772
|
+
const fakeScanContext = {
|
|
773
|
+
symbolToScriptPath: new Map(),
|
|
774
|
+
scriptPathToGuid: new Map(),
|
|
775
|
+
guidToResourceHits: new Map(),
|
|
776
|
+
prefabSourceRefs: [
|
|
777
|
+
{
|
|
778
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
779
|
+
targetGuid: '99999999999999999999999999999999',
|
|
780
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
781
|
+
fileId: '100100000',
|
|
782
|
+
fieldName: 'm_SourcePrefab',
|
|
783
|
+
sourceLayer: 'scene',
|
|
784
|
+
},
|
|
785
|
+
],
|
|
786
|
+
resourceDocCache: new Map(),
|
|
787
|
+
};
|
|
788
|
+
const originalValue = process.env.GITNEXUS_DISABLE_PREFAB_SOURCE_PASS;
|
|
789
|
+
process.env.GITNEXUS_DISABLE_PREFAB_SOURCE_PASS = '1';
|
|
790
|
+
try {
|
|
791
|
+
const result = await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
792
|
+
buildScanContext: async () => fakeScanContext,
|
|
793
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
794
|
+
});
|
|
795
|
+
const guidRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF');
|
|
796
|
+
assert.equal(guidRefs.length, 0);
|
|
797
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab-source: skipped')));
|
|
798
|
+
}
|
|
799
|
+
finally {
|
|
800
|
+
if (typeof originalValue === 'undefined') {
|
|
801
|
+
delete process.env.GITNEXUS_DISABLE_PREFAB_SOURCE_PASS;
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
process.env.GITNEXUS_DISABLE_PREFAB_SOURCE_PASS = originalValue;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
test('prefab nested source dedupes duplicate PrefabInstance rows', async () => {
|
|
809
|
+
const graph = createKnowledgeGraph();
|
|
810
|
+
const fakeScanContext = {
|
|
811
|
+
symbolToScriptPath: new Map(),
|
|
812
|
+
scriptPathToGuid: new Map(),
|
|
813
|
+
guidToResourceHits: new Map(),
|
|
814
|
+
prefabSourceRefs: [
|
|
815
|
+
{
|
|
816
|
+
sourceResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
817
|
+
targetGuid: '99999999999999999999999999999999',
|
|
818
|
+
targetResourcePath: 'Assets/Prefabs/Nested.prefab',
|
|
819
|
+
fileId: '100100000',
|
|
820
|
+
fieldName: 'm_SourcePrefab',
|
|
821
|
+
sourceLayer: 'prefab',
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
sourceResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
825
|
+
targetGuid: '99999999999999999999999999999999',
|
|
826
|
+
targetResourcePath: 'Assets/Prefabs/Nested.prefab',
|
|
827
|
+
fileId: '100100000',
|
|
828
|
+
fieldName: 'm_SourcePrefab',
|
|
829
|
+
sourceLayer: 'prefab',
|
|
830
|
+
},
|
|
831
|
+
],
|
|
832
|
+
resourceDocCache: new Map(),
|
|
833
|
+
};
|
|
834
|
+
await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
835
|
+
buildScanContext: async () => fakeScanContext,
|
|
836
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
837
|
+
});
|
|
838
|
+
const guidRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF');
|
|
839
|
+
assert.equal(guidRefs.length, 1);
|
|
840
|
+
const reason = JSON.parse(String(guidRefs[0]?.reason || '{}'));
|
|
841
|
+
assert.equal(reason.guid, '99999999999999999999999999999999');
|
|
842
|
+
});
|
|
843
|
+
test('drops placeholder unresolved and zero-guid prefab-source rows and reports filtered counters', async () => {
|
|
844
|
+
const graph = createKnowledgeGraph();
|
|
845
|
+
const fakeScanContext = {
|
|
846
|
+
symbolToScriptPath: new Map(),
|
|
847
|
+
scriptPathToGuid: new Map(),
|
|
848
|
+
guidToResourceHits: new Map(),
|
|
849
|
+
prefabSourceRefs: [
|
|
850
|
+
{
|
|
851
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
852
|
+
targetGuid: '00000000000000000000000000000000',
|
|
853
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
854
|
+
fileId: '1',
|
|
855
|
+
fieldName: 'm_SourcePrefab',
|
|
856
|
+
sourceLayer: 'scene',
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
860
|
+
targetGuid: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
861
|
+
targetResourcePath: '__PLACEHOLDER__',
|
|
862
|
+
fileId: '2',
|
|
863
|
+
fieldName: 'm_SourcePrefab',
|
|
864
|
+
sourceLayer: 'scene',
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
868
|
+
targetGuid: 'cccccccccccccccccccccccccccccccc',
|
|
869
|
+
targetResourcePath: '',
|
|
870
|
+
fileId: '3',
|
|
871
|
+
fieldName: 'm_SourcePrefab',
|
|
872
|
+
sourceLayer: 'scene',
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
876
|
+
targetGuid: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
|
877
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
878
|
+
fileId: '4',
|
|
879
|
+
fieldName: 'm_SourcePrefab',
|
|
880
|
+
sourceLayer: 'scene',
|
|
881
|
+
},
|
|
882
|
+
],
|
|
883
|
+
resourceDocCache: new Map(),
|
|
884
|
+
};
|
|
885
|
+
const result = await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
886
|
+
buildScanContext: async () => fakeScanContext,
|
|
887
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
888
|
+
});
|
|
889
|
+
const guidRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF');
|
|
890
|
+
assert.equal(guidRefs.length, 1);
|
|
891
|
+
const reason = JSON.parse(String(guidRefs[0]?.reason || '{}'));
|
|
892
|
+
assert.equal(reason.targetResourcePath, 'Assets/Prefabs/BattleMode.prefab');
|
|
893
|
+
assert.notEqual(reason.guid, '00000000000000000000000000000000');
|
|
894
|
+
assert.ok(result.prefabSourceStats.rowsFilteredZeroGuid > 0);
|
|
895
|
+
assert.ok(result.prefabSourceStats.rowsFilteredPlaceholder > 0);
|
|
896
|
+
assert.ok(result.prefabSourceStats.rowsFilteredUnresolved > 0);
|
|
897
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab_source.rows_filtered_zero_guid=')));
|
|
898
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab_source.rows_filtered_placeholder=')));
|
|
899
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab_source.rows_filtered_unresolved=')));
|
|
900
|
+
});
|
|
901
|
+
test('per-file scan failure is isolated and does not abort subsequent file emission', async () => {
|
|
902
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-prefab-source-file-error-'));
|
|
903
|
+
const graph = createKnowledgeGraph();
|
|
904
|
+
try {
|
|
905
|
+
const sceneDir = path.join(tempRoot, 'Assets/Scene');
|
|
906
|
+
const prefabDir = path.join(tempRoot, 'Assets/Prefabs');
|
|
907
|
+
await fs.mkdir(path.join(sceneDir, 'Broken.unity'), { recursive: true });
|
|
908
|
+
await fs.mkdir(prefabDir, { recursive: true });
|
|
909
|
+
await fs.writeFile(path.join(sceneDir, 'Good.unity'), [
|
|
910
|
+
'--- !u!1001 &100100000',
|
|
911
|
+
'PrefabInstance:',
|
|
912
|
+
' m_SourcePrefab: {fileID: 100100000, guid: 99999999999999999999999999999999, type: 3}',
|
|
913
|
+
].join('\n'), 'utf-8');
|
|
914
|
+
await fs.writeFile(path.join(prefabDir, 'BattleMode.prefab'), '--- !u!1 &1\nGameObject:\n', 'utf-8');
|
|
915
|
+
const fakeScanContext = {
|
|
916
|
+
symbolToScriptPath: new Map(),
|
|
917
|
+
scriptPathToGuid: new Map(),
|
|
918
|
+
guidToResourceHits: new Map(),
|
|
919
|
+
assetGuidToPath: new Map([['99999999999999999999999999999999', 'Assets/Prefabs/BattleMode.prefab']]),
|
|
920
|
+
streamPrefabSourceRefs: (options) => streamPrefabSourceRefs({
|
|
921
|
+
repoRoot: tempRoot,
|
|
922
|
+
resourceFiles: ['Assets/Scene/Broken.unity', 'Assets/Scene/Good.unity'],
|
|
923
|
+
assetGuidToPath: new Map([['99999999999999999999999999999999', 'Assets/Prefabs/BattleMode.prefab']]),
|
|
924
|
+
hooks: options?.hooks,
|
|
925
|
+
}),
|
|
926
|
+
prefabSourceRefs: [],
|
|
927
|
+
resourceDocCache: new Map(),
|
|
928
|
+
};
|
|
929
|
+
const result = await processUnityResources(graph, { repoPath: tempRoot }, {
|
|
930
|
+
buildScanContext: async () => fakeScanContext,
|
|
931
|
+
resolveBindings: async () => ({ resourceBindings: [], unityDiagnostics: [] }),
|
|
932
|
+
});
|
|
933
|
+
assert.ok(result.prefabSourceStats.rowsEmitted > 0);
|
|
934
|
+
assert.ok(result.diagnostics.some((line) => line.includes('prefab_source.file_errors=1')));
|
|
935
|
+
}
|
|
936
|
+
finally {
|
|
937
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
test('extracts prefab source refs without class binding resolve', async () => {
|
|
941
|
+
const graph = createKnowledgeGraph();
|
|
942
|
+
let resolveBindingsCallCount = 0;
|
|
943
|
+
const fakeScanContext = {
|
|
944
|
+
symbolToScriptPath: new Map(),
|
|
945
|
+
scriptPathToGuid: new Map(),
|
|
946
|
+
guidToResourceHits: new Map(),
|
|
947
|
+
prefabSourceRefs: [
|
|
948
|
+
{
|
|
949
|
+
sourceResourcePath: 'Assets/Scene/MainUIManager.unity',
|
|
950
|
+
targetGuid: '99999999999999999999999999999999',
|
|
951
|
+
targetResourcePath: 'Assets/Prefabs/BattleMode.prefab',
|
|
952
|
+
fileId: '100100000',
|
|
953
|
+
fieldName: 'm_SourcePrefab',
|
|
954
|
+
sourceLayer: 'scene',
|
|
955
|
+
},
|
|
956
|
+
],
|
|
957
|
+
resourceDocCache: new Map(),
|
|
958
|
+
};
|
|
959
|
+
await processUnityResources(graph, { repoPath: fixtureRoot }, {
|
|
960
|
+
buildScanContext: async () => fakeScanContext,
|
|
961
|
+
resolveBindings: async () => {
|
|
962
|
+
resolveBindingsCallCount += 1;
|
|
963
|
+
return ({ resourceBindings: [], unityDiagnostics: [] });
|
|
964
|
+
},
|
|
965
|
+
});
|
|
966
|
+
const guidRefs = [...graph.iterRelationships()].filter((rel) => rel.type === 'UNITY_ASSET_GUID_REF');
|
|
967
|
+
assert.equal(resolveBindingsCallCount, 0);
|
|
968
|
+
assert.equal(guidRefs.length, 1);
|
|
969
|
+
});
|
|
521
970
|
test('processUnityResources writes compact UNITY_RESOURCE_SUMMARY reason by default', async () => {
|
|
522
971
|
const graph = createKnowledgeGraph();
|
|
523
972
|
const classId = generateId('Class', 'Assets/Scripts/Compact.cs:CompactSymbol');
|
|
@@ -7,5 +7,20 @@ export interface UnityRuntimeBindingResult {
|
|
|
7
7
|
ruleId: string;
|
|
8
8
|
edgesInjected: number;
|
|
9
9
|
}>;
|
|
10
|
+
diagnostics: UnityRuntimeBindingDiagnostics;
|
|
11
|
+
}
|
|
12
|
+
export interface UnityRuntimeBindingDiagnostics {
|
|
13
|
+
rulesEvaluated: number;
|
|
14
|
+
bindingsEvaluated: number;
|
|
15
|
+
bindingsByKind: Record<string, number>;
|
|
16
|
+
methodLookupCalls: number;
|
|
17
|
+
methodLookupCacheHits: number;
|
|
18
|
+
sceneRuntimeTraversalCalls: number;
|
|
19
|
+
sceneRuntimeTraversalCacheHits: number;
|
|
20
|
+
sceneRuntimeResourcesVisited: number;
|
|
21
|
+
anomalies: string[];
|
|
22
|
+
shouldAgentReport: boolean;
|
|
23
|
+
agentReportReason: string;
|
|
24
|
+
summary: string[];
|
|
10
25
|
}
|
|
11
26
|
export declare function applyUnityRuntimeBindingRules(graph: KnowledgeGraph, rules: RuntimeClaimRule[], config: UnityConfig): UnityRuntimeBindingResult;
|