@veewo/gitnexus 1.4.11-rc → 1.5.0-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/hydration-policy-repeatability-runner.d.ts +55 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.js +190 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.test.js +13 -0
- package/dist/benchmark/u2-e2e/phase1-process-ref-acceptance-runner.d.ts +22 -0
- package/dist/benchmark/u2-e2e/phase1-process-ref-acceptance-runner.js +100 -0
- package/dist/benchmark/u2-e2e/phase1-process-ref-acceptance-runner.test.d.ts +1 -0
- package/dist/benchmark/u2-e2e/phase1-process-ref-acceptance-runner.test.js +13 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.d.ts +27 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.js +118 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.d.ts +1 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.js +16 -0
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +60 -0
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +331 -0
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.d.ts +1 -0
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +42 -0
- package/dist/benchmark/u2-e2e/reload-v1-acceptance-runner.js +4 -4
- package/dist/benchmark/unity-lazy-context-sampler.d.ts +6 -0
- package/dist/benchmark/unity-lazy-context-sampler.js +49 -13
- package/dist/benchmark/unity-lazy-context-sampler.test.js +4 -0
- package/dist/cli/ai-context.js +6 -7
- package/dist/cli/ai-context.test.js +6 -8
- package/dist/cli/eval-server.js +0 -3
- package/dist/cli/index.js +8 -0
- package/dist/cli/mcp.js +0 -3
- package/dist/cli/rule-lab.d.ts +42 -0
- package/dist/cli/rule-lab.js +157 -0
- package/dist/cli/rule-lab.test.d.ts +1 -0
- package/dist/cli/rule-lab.test.js +11 -0
- package/dist/cli/tool.d.ts +7 -1
- package/dist/cli/tool.js +6 -0
- package/dist/core/config/unity-config.d.ts +20 -0
- package/dist/core/config/unity-config.js +46 -0
- package/dist/core/graph/types.d.ts +1 -1
- package/dist/core/ingestion/pipeline.js +38 -13
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.d.ts +0 -2
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.js +26 -213
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +1 -1
- package/dist/core/ingestion/unity-resource-processor.js +87 -22
- package/dist/core/ingestion/unity-resource-processor.test.js +67 -2
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +11 -0
- package/dist/core/ingestion/unity-runtime-binding-rules.js +179 -0
- package/dist/core/unity/options.d.ts +4 -0
- package/dist/core/unity/options.js +18 -0
- package/dist/core/unity/options.test.js +11 -1
- package/dist/core/unity/resolver.js +11 -1
- package/dist/core/unity/resolver.test.js +62 -0
- package/dist/core/unity/yaml-object-graph.js +1 -1
- package/dist/core/unity/yaml-object-graph.test.js +16 -0
- package/dist/mcp/local/derived-process-reader.d.ts +2 -0
- package/dist/mcp/local/derived-process-reader.js +15 -0
- package/dist/mcp/local/local-backend.d.ts +56 -0
- package/dist/mcp/local/local-backend.js +1003 -53
- package/dist/mcp/local/local-backend.unity-merge.test.js +1 -1
- package/dist/mcp/local/process-confidence.js +1 -1
- package/dist/mcp/local/process-evidence.d.ts +1 -0
- package/dist/mcp/local/process-evidence.js +22 -0
- package/dist/mcp/local/process-evidence.test.js +11 -1
- package/dist/mcp/local/process-ref.d.ts +24 -0
- package/dist/mcp/local/process-ref.js +33 -0
- package/dist/mcp/local/process-ref.test.d.ts +1 -0
- package/dist/mcp/local/process-ref.test.js +24 -0
- package/dist/mcp/local/runtime-chain-verify.d.ts +15 -1
- package/dist/mcp/local/runtime-chain-verify.js +191 -187
- package/dist/mcp/local/runtime-chain-verify.test.js +546 -19
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +63 -0
- package/dist/mcp/local/runtime-claim-rule-registry.js +308 -0
- package/dist/mcp/local/runtime-claim-rule-registry.test.d.ts +1 -0
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +215 -0
- package/dist/mcp/local/runtime-claim.d.ts +38 -0
- package/dist/mcp/local/runtime-claim.js +54 -0
- package/dist/mcp/local/runtime-claim.test.d.ts +1 -0
- package/dist/mcp/local/runtime-claim.test.js +27 -0
- package/dist/mcp/local/unity-enrichment.d.ts +1 -0
- package/dist/mcp/local/unity-enrichment.js +1 -1
- package/dist/mcp/local/unity-evidence-view.d.ts +26 -0
- package/dist/mcp/local/unity-evidence-view.js +96 -0
- package/dist/mcp/local/unity-evidence-view.test.d.ts +1 -0
- package/dist/mcp/local/unity-evidence-view.test.js +39 -0
- package/dist/mcp/local/unity-lazy-hydrator.d.ts +2 -2
- package/dist/mcp/local/unity-lazy-hydrator.js +3 -3
- package/dist/mcp/local/unity-lazy-hydrator.test.js +4 -4
- package/dist/mcp/local/unity-parity-cache.js +2 -6
- package/dist/mcp/local/unity-parity-seed-loader.d.ts +1 -0
- package/dist/mcp/local/unity-parity-seed-loader.js +10 -16
- package/dist/mcp/local/unity-parity-seed-loader.test.js +3 -12
- package/dist/mcp/local/unity-runtime-hydration.d.ts +3 -2
- package/dist/mcp/local/unity-runtime-hydration.js +13 -16
- package/dist/mcp/local/unity-runtime-hydration.test.js +15 -1
- package/dist/mcp/resources.js +13 -0
- package/dist/mcp/tools.js +166 -13
- package/dist/rule-lab/analyze.d.ts +12 -0
- package/dist/rule-lab/analyze.js +90 -0
- package/dist/rule-lab/analyze.test.d.ts +1 -0
- package/dist/rule-lab/analyze.test.js +28 -0
- package/dist/rule-lab/compile.d.ts +5 -0
- package/dist/rule-lab/compile.js +51 -0
- package/dist/rule-lab/compiled-bundles.d.ts +30 -0
- package/dist/rule-lab/compiled-bundles.js +36 -0
- package/dist/rule-lab/curate.d.ts +32 -0
- package/dist/rule-lab/curate.js +134 -0
- package/dist/rule-lab/curate.test.d.ts +1 -0
- package/dist/rule-lab/curate.test.js +72 -0
- package/dist/rule-lab/discover.d.ts +13 -0
- package/dist/rule-lab/discover.js +74 -0
- package/dist/rule-lab/discover.test.d.ts +1 -0
- package/dist/rule-lab/discover.test.js +42 -0
- package/dist/rule-lab/paths.d.ts +21 -0
- package/dist/rule-lab/paths.js +37 -0
- package/dist/rule-lab/paths.test.d.ts +1 -0
- package/dist/rule-lab/paths.test.js +46 -0
- package/dist/rule-lab/promote.d.ts +26 -0
- package/dist/rule-lab/promote.js +314 -0
- package/dist/rule-lab/promote.test.d.ts +1 -0
- package/dist/rule-lab/promote.test.js +164 -0
- package/dist/rule-lab/regress.d.ts +60 -0
- package/dist/rule-lab/regress.js +122 -0
- package/dist/rule-lab/regress.test.d.ts +1 -0
- package/dist/rule-lab/regress.test.js +68 -0
- package/dist/rule-lab/review-pack.d.ts +31 -0
- package/dist/rule-lab/review-pack.js +125 -0
- package/dist/rule-lab/review-pack.test.d.ts +1 -0
- package/dist/rule-lab/review-pack.test.js +49 -0
- package/dist/rule-lab/types.d.ts +99 -0
- package/dist/rule-lab/types.js +1 -0
- package/package.json +1 -1
- package/skills/_shared/unity-hydration-contract.md +11 -0
- package/skills/_shared/unity-ui-trace-contract.md +33 -0
- package/skills/gitnexus-cli.md +11 -25
- package/skills/gitnexus-guide.md +2 -0
- package/skills/gitnexus-unity-rule-gen.md +318 -0
- package/dist/core/ingestion/unity-lifecycle-config.d.ts +0 -5
- package/dist/core/ingestion/unity-lifecycle-config.js +0 -25
- package/dist/mcp/local/unity-lazy-config.d.ts +0 -6
- package/dist/mcp/local/unity-lazy-config.js +0 -7
- package/dist/mcp/local/unity-lazy-config.test.js +0 -9
- package/dist/mcp/local/unity-process-confidence-config.d.ts +0 -1
- package/dist/mcp/local/unity-process-confidence-config.js +0 -4
- package/dist/mcp/local/unity-runtime-chain-verify-config.d.ts +0 -1
- package/dist/mcp/local/unity-runtime-chain-verify-config.js +0 -10
- /package/dist/{mcp/local/unity-lazy-config.test.d.ts → benchmark/u2-e2e/hydration-policy-repeatability-runner.test.d.ts} +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { generateId } from '../../lib/utils.js';
|
|
2
|
+
const RULE_EDGE_CONFIDENCE = 0.75;
|
|
3
|
+
export function applyUnityRuntimeBindingRules(graph, rules, _config) {
|
|
4
|
+
const ruleResults = [];
|
|
5
|
+
let totalEdges = 0;
|
|
6
|
+
const existingPairs = new Set();
|
|
7
|
+
for (const rel of graph.iterRelationships()) {
|
|
8
|
+
if (rel.type === 'CALLS')
|
|
9
|
+
existingPairs.add(`${rel.sourceId}->${rel.targetId}`);
|
|
10
|
+
}
|
|
11
|
+
const addSyntheticEdge = (sourceId, targetId, reason) => {
|
|
12
|
+
if (sourceId === targetId)
|
|
13
|
+
return false;
|
|
14
|
+
const key = `${sourceId}->${targetId}`;
|
|
15
|
+
if (existingPairs.has(key))
|
|
16
|
+
return false;
|
|
17
|
+
graph.addRelationship({
|
|
18
|
+
id: generateId('CALLS', `${sourceId}->${targetId}:${reason}`),
|
|
19
|
+
sourceId,
|
|
20
|
+
targetId,
|
|
21
|
+
type: 'CALLS',
|
|
22
|
+
confidence: RULE_EDGE_CONFIDENCE,
|
|
23
|
+
reason,
|
|
24
|
+
});
|
|
25
|
+
existingPairs.add(key);
|
|
26
|
+
return true;
|
|
27
|
+
};
|
|
28
|
+
// Pre-build indexes
|
|
29
|
+
const methodsByClassId = new Map();
|
|
30
|
+
const classNodes = [];
|
|
31
|
+
for (const rel of graph.iterRelationships()) {
|
|
32
|
+
if (rel.type !== 'HAS_METHOD')
|
|
33
|
+
continue;
|
|
34
|
+
const method = graph.getNode(rel.targetId);
|
|
35
|
+
if (!method || (method.label !== 'Method' && method.label !== 'Function'))
|
|
36
|
+
continue;
|
|
37
|
+
const list = methodsByClassId.get(rel.sourceId) ?? [];
|
|
38
|
+
list.push(method);
|
|
39
|
+
methodsByClassId.set(rel.sourceId, list);
|
|
40
|
+
}
|
|
41
|
+
for (const node of graph.iterNodes()) {
|
|
42
|
+
if (node.label === 'Class')
|
|
43
|
+
classNodes.push(node);
|
|
44
|
+
}
|
|
45
|
+
// Collect UNITY_ASSET_GUID_REF and UNITY_COMPONENT_INSTANCE edges
|
|
46
|
+
const assetGuidRefs = [];
|
|
47
|
+
const componentInstances = [];
|
|
48
|
+
for (const rel of graph.iterRelationships()) {
|
|
49
|
+
if (rel.type === 'UNITY_ASSET_GUID_REF')
|
|
50
|
+
assetGuidRefs.push(rel);
|
|
51
|
+
else if (rel.type === 'UNITY_COMPONENT_INSTANCE')
|
|
52
|
+
componentInstances.push(rel);
|
|
53
|
+
}
|
|
54
|
+
for (const rule of rules) {
|
|
55
|
+
let ruleEdges = 0;
|
|
56
|
+
for (const binding of rule.resource_bindings ?? []) {
|
|
57
|
+
ruleEdges += processBinding(binding, rule.id, assetGuidRefs, componentInstances, methodsByClassId, classNodes, addSyntheticEdge);
|
|
58
|
+
}
|
|
59
|
+
if (rule.lifecycle_overrides?.additional_entry_points?.length) {
|
|
60
|
+
ruleEdges += processLifecycleOverrides(rule, methodsByClassId, classNodes, addSyntheticEdge);
|
|
61
|
+
}
|
|
62
|
+
totalEdges += ruleEdges;
|
|
63
|
+
ruleResults.push({ ruleId: rule.id, edgesInjected: ruleEdges });
|
|
64
|
+
}
|
|
65
|
+
return { edgesInjected: totalEdges, ruleResults };
|
|
66
|
+
}
|
|
67
|
+
function findMethodsOnResource(resourceFileId, componentInstances, methodsByClassId, entryPoints) {
|
|
68
|
+
const results = [];
|
|
69
|
+
const entrySet = new Set(entryPoints);
|
|
70
|
+
for (const ci of componentInstances) {
|
|
71
|
+
if (ci.targetId !== resourceFileId)
|
|
72
|
+
continue;
|
|
73
|
+
const classId = ci.sourceId;
|
|
74
|
+
for (const method of methodsByClassId.get(classId) ?? []) {
|
|
75
|
+
if (entrySet.has(method.properties.name))
|
|
76
|
+
results.push(method);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return results;
|
|
80
|
+
}
|
|
81
|
+
function processBinding(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, classNodes, addEdge) {
|
|
82
|
+
if (binding.kind === 'asset_ref_loads_components') {
|
|
83
|
+
return processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, addEdge);
|
|
84
|
+
}
|
|
85
|
+
if (binding.kind === 'method_triggers_field_load') {
|
|
86
|
+
return processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, classNodes, addEdge);
|
|
87
|
+
}
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
function processAssetRefLoadsComponents(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, addEdge) {
|
|
91
|
+
let count = 0;
|
|
92
|
+
const pattern = binding.ref_field_pattern ? new RegExp(binding.ref_field_pattern) : null;
|
|
93
|
+
const entryPoints = binding.target_entry_points ?? [];
|
|
94
|
+
if (!pattern || entryPoints.length === 0)
|
|
95
|
+
return 0;
|
|
96
|
+
const runtimeRootId = generateId('Method', 'unity-runtime-root');
|
|
97
|
+
for (const ref of assetGuidRefs) {
|
|
98
|
+
let fieldName = '';
|
|
99
|
+
try {
|
|
100
|
+
const parsed = JSON.parse(ref.reason);
|
|
101
|
+
fieldName = parsed.fieldName ?? '';
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!pattern.test(fieldName))
|
|
107
|
+
continue;
|
|
108
|
+
const targetMethods = findMethodsOnResource(ref.targetId, componentInstances, methodsByClassId, entryPoints);
|
|
109
|
+
for (const method of targetMethods) {
|
|
110
|
+
if (addEdge(runtimeRootId, method.id, `unity-rule-resource-load:${ruleId}`))
|
|
111
|
+
count++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return count;
|
|
115
|
+
}
|
|
116
|
+
function processMethodTriggersFieldLoad(binding, ruleId, assetGuidRefs, componentInstances, methodsByClassId, classNodes, addEdge) {
|
|
117
|
+
let count = 0;
|
|
118
|
+
const classPattern = binding.host_class_pattern ? new RegExp(binding.host_class_pattern) : null;
|
|
119
|
+
const loaderMethodNames = new Set(binding.loader_methods ?? []);
|
|
120
|
+
const entryPoints = binding.target_entry_points ?? [];
|
|
121
|
+
const defaultEntryPoints = ['OnEnable', 'Awake', 'Start'];
|
|
122
|
+
const resolvedEntryPoints = entryPoints.length > 0 ? entryPoints : defaultEntryPoints;
|
|
123
|
+
if (!classPattern || loaderMethodNames.size === 0)
|
|
124
|
+
return 0;
|
|
125
|
+
// Build asset ref index by source file
|
|
126
|
+
const refsBySource = new Map();
|
|
127
|
+
for (const ref of assetGuidRefs) {
|
|
128
|
+
const list = refsBySource.get(ref.sourceId) ?? [];
|
|
129
|
+
list.push(ref);
|
|
130
|
+
refsBySource.set(ref.sourceId, list);
|
|
131
|
+
}
|
|
132
|
+
for (const cls of classNodes) {
|
|
133
|
+
if (!classPattern.test(cls.properties.name))
|
|
134
|
+
continue;
|
|
135
|
+
const methods = methodsByClassId.get(cls.id) ?? [];
|
|
136
|
+
const loaders = methods.filter(m => loaderMethodNames.has(m.properties.name));
|
|
137
|
+
if (loaders.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
// Find resource files this class is mounted on
|
|
140
|
+
const resourceFileIds = new Set();
|
|
141
|
+
for (const ci of componentInstances) {
|
|
142
|
+
if (ci.sourceId === cls.id)
|
|
143
|
+
resourceFileIds.add(ci.targetId);
|
|
144
|
+
}
|
|
145
|
+
// Follow asset refs from those resource files
|
|
146
|
+
for (const resourceFileId of resourceFileIds) {
|
|
147
|
+
for (const ref of refsBySource.get(resourceFileId) ?? []) {
|
|
148
|
+
const targetMethods = findMethodsOnResource(ref.targetId, componentInstances, methodsByClassId, resolvedEntryPoints);
|
|
149
|
+
for (const loader of loaders) {
|
|
150
|
+
for (const target of targetMethods) {
|
|
151
|
+
if (addEdge(loader.id, target.id, `unity-rule-loader-bridge:${ruleId}`))
|
|
152
|
+
count++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return count;
|
|
159
|
+
}
|
|
160
|
+
function processLifecycleOverrides(rule, methodsByClassId, classNodes, addEdge) {
|
|
161
|
+
const overrides = rule.lifecycle_overrides;
|
|
162
|
+
if (!overrides?.additional_entry_points?.length)
|
|
163
|
+
return 0;
|
|
164
|
+
const entrySet = new Set(overrides.additional_entry_points);
|
|
165
|
+
const scopePattern = overrides.scope ? new RegExp(overrides.scope) : null;
|
|
166
|
+
let count = 0;
|
|
167
|
+
const runtimeRootId = generateId('Method', 'unity-runtime-root');
|
|
168
|
+
for (const cls of classNodes) {
|
|
169
|
+
if (scopePattern && !scopePattern.test(cls.properties.filePath ?? cls.properties.name))
|
|
170
|
+
continue;
|
|
171
|
+
for (const method of methodsByClassId.get(cls.id) ?? []) {
|
|
172
|
+
if (!entrySet.has(method.properties.name))
|
|
173
|
+
continue;
|
|
174
|
+
if (addEdge(runtimeRootId, method.id, `unity-rule-lifecycle-override:${rule.id}`))
|
|
175
|
+
count++;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return count;
|
|
179
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type UnityResourcesMode = 'off' | 'on' | 'auto';
|
|
2
2
|
export type UnityHydrationMode = 'parity' | 'compact';
|
|
3
|
+
export type UnityEvidenceMode = 'summary' | 'focused' | 'full';
|
|
4
|
+
export type HydrationPolicy = 'fast' | 'balanced' | 'strict';
|
|
3
5
|
export declare function parseUnityResourcesMode(raw?: string): UnityResourcesMode;
|
|
4
6
|
export declare function parseUnityHydrationMode(raw?: string): UnityHydrationMode;
|
|
7
|
+
export declare function parseUnityEvidenceMode(raw?: string): UnityEvidenceMode;
|
|
8
|
+
export declare function parseHydrationPolicy(raw?: string): HydrationPolicy;
|
|
@@ -16,3 +16,21 @@ export function parseUnityHydrationMode(raw) {
|
|
|
16
16
|
}
|
|
17
17
|
throw new Error('Invalid unity hydration mode. Use parity|compact.');
|
|
18
18
|
}
|
|
19
|
+
export function parseUnityEvidenceMode(raw) {
|
|
20
|
+
if (!raw)
|
|
21
|
+
return 'summary';
|
|
22
|
+
const normalized = raw.trim().toLowerCase();
|
|
23
|
+
if (normalized === 'summary' || normalized === 'focused' || normalized === 'full') {
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
throw new Error('Invalid unity evidence mode. Use summary|focused|full.');
|
|
27
|
+
}
|
|
28
|
+
export function parseHydrationPolicy(raw) {
|
|
29
|
+
if (!raw)
|
|
30
|
+
return 'balanced';
|
|
31
|
+
const normalized = raw.trim().toLowerCase();
|
|
32
|
+
if (normalized === 'fast' || normalized === 'balanced' || normalized === 'strict') {
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
throw new Error('Invalid hydration policy. Use fast|balanced|strict.');
|
|
36
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { parseUnityHydrationMode, parseUnityResourcesMode } from './options.js';
|
|
3
|
+
import { parseHydrationPolicy, parseUnityEvidenceMode, parseUnityHydrationMode, parseUnityResourcesMode, } from './options.js';
|
|
4
4
|
test('parseUnityResourcesMode defaults to off', () => {
|
|
5
5
|
assert.equal(parseUnityResourcesMode(undefined), 'off');
|
|
6
6
|
});
|
|
@@ -15,3 +15,13 @@ test('parseUnityHydrationMode validates mode', () => {
|
|
|
15
15
|
assert.equal(parseUnityHydrationMode('compact'), 'compact');
|
|
16
16
|
assert.throws(() => parseUnityHydrationMode('bad'), /unity hydration mode/i);
|
|
17
17
|
});
|
|
18
|
+
test('parseUnityEvidenceMode defaults to summary and validates mode', () => {
|
|
19
|
+
assert.equal(parseUnityEvidenceMode(undefined), 'summary');
|
|
20
|
+
assert.equal(parseUnityEvidenceMode('focused'), 'focused');
|
|
21
|
+
assert.throws(() => parseUnityEvidenceMode('bad'), /unity evidence mode/i);
|
|
22
|
+
});
|
|
23
|
+
test('parseHydrationPolicy defaults to balanced and validates mode', () => {
|
|
24
|
+
assert.equal(parseHydrationPolicy(undefined), 'balanced');
|
|
25
|
+
assert.equal(parseHydrationPolicy('strict'), 'strict');
|
|
26
|
+
assert.throws(() => parseHydrationPolicy('bad'), /hydration policy/i);
|
|
27
|
+
});
|
|
@@ -24,7 +24,14 @@ export async function resolveUnityBindings(input) {
|
|
|
24
24
|
continue;
|
|
25
25
|
}
|
|
26
26
|
const blocks = await getResourceBlocks(input.repoRoot, hit.resourcePath, input.scanContext);
|
|
27
|
-
const matchedComponents = blocks.filter((block) =>
|
|
27
|
+
const matchedComponents = blocks.filter((block) => {
|
|
28
|
+
if (block.objectType !== 'MonoBehaviour')
|
|
29
|
+
return false;
|
|
30
|
+
const scriptRefGuid = extractGuidFromScriptField(block.fields.m_Script || '');
|
|
31
|
+
if (!scriptRefGuid)
|
|
32
|
+
return false;
|
|
33
|
+
return scriptRefGuid.toLowerCase() === scriptGuid.toLowerCase();
|
|
34
|
+
});
|
|
28
35
|
if (matchedComponents.length === 0) {
|
|
29
36
|
unityDiagnostics.push(`No MonoBehaviour block matched script guid ${scriptGuid} in ${hit.resourcePath}.`);
|
|
30
37
|
continue;
|
|
@@ -413,6 +420,9 @@ function extractFileId(rawValue) {
|
|
|
413
420
|
return undefined;
|
|
414
421
|
return rawValue.match(/fileID:\s*(\d+)/)?.[1];
|
|
415
422
|
}
|
|
423
|
+
function extractGuidFromScriptField(rawValue) {
|
|
424
|
+
return rawValue.match(/guid:\s*([0-9a-f]{32})/i)?.[1];
|
|
425
|
+
}
|
|
416
426
|
function parseObjectReference(rawValue) {
|
|
417
427
|
const trimmed = rawValue.trim();
|
|
418
428
|
if (!trimmed.startsWith('{') || !trimmed.includes('fileID:')) {
|
|
@@ -234,6 +234,68 @@ test('resolveUnityBindings deepParseLargeResources can override lightweight fall
|
|
|
234
234
|
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
235
235
|
}
|
|
236
236
|
});
|
|
237
|
+
test('resolveUnityBindings matches MonoBehaviour script guid case-insensitively', async () => {
|
|
238
|
+
const tempRoot = await fs.mkdtemp(path.join(path.dirname(fixtureRoot), 'tmp-guid-case-unity-'));
|
|
239
|
+
const scriptsDir = path.join(tempRoot, 'Assets/Scripts');
|
|
240
|
+
const assetDir = path.join(tempRoot, 'Assets/Data');
|
|
241
|
+
await fs.mkdir(scriptsDir, { recursive: true });
|
|
242
|
+
await fs.mkdir(assetDir, { recursive: true });
|
|
243
|
+
try {
|
|
244
|
+
const scriptPath = 'Assets/Scripts/CaseGuidSymbol.cs';
|
|
245
|
+
const assetPath = 'Assets/Data/CaseGuid.asset';
|
|
246
|
+
const scriptGuid = 'abcdefabcdefabcdefabcdefabcdefab';
|
|
247
|
+
await fs.writeFile(path.join(tempRoot, scriptPath), 'public class CaseGuidSymbol {}', 'utf-8');
|
|
248
|
+
await fs.writeFile(path.join(tempRoot, `${scriptPath}.meta`), `guid: ${scriptGuid}\n`, 'utf-8');
|
|
249
|
+
await fs.writeFile(path.join(tempRoot, assetPath), `--- !u!114 &11400000\nMonoBehaviour:\n m_Script: {fileID: 11500000, guid: ${scriptGuid.toUpperCase()}, type: 3}\n value: 1\n`, 'utf-8');
|
|
250
|
+
const scanContext = await buildUnityScanContext({
|
|
251
|
+
repoRoot: tempRoot,
|
|
252
|
+
scopedPaths: [scriptPath, `${scriptPath}.meta`, assetPath],
|
|
253
|
+
symbolDeclarations: [{ symbol: 'CaseGuidSymbol', scriptPath }],
|
|
254
|
+
});
|
|
255
|
+
const resolved = await resolveUnityBindings({
|
|
256
|
+
repoRoot: tempRoot,
|
|
257
|
+
symbol: 'CaseGuidSymbol',
|
|
258
|
+
scanContext,
|
|
259
|
+
});
|
|
260
|
+
assert.equal(resolved.resourceBindings.length, 1);
|
|
261
|
+
assert.equal(resolved.resourceBindings[0]?.resourcePath, assetPath);
|
|
262
|
+
assert.equal(resolved.unityDiagnostics.length, 0);
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
test('resolveUnityBindings parses MonoBehaviour blocks with negative object ids', async () => {
|
|
269
|
+
const tempRoot = await fs.mkdtemp(path.join(path.dirname(fixtureRoot), 'tmp-negative-object-id-'));
|
|
270
|
+
const scriptsDir = path.join(tempRoot, 'Assets/Scripts');
|
|
271
|
+
const assetDir = path.join(tempRoot, 'Assets/Graphs');
|
|
272
|
+
await fs.mkdir(scriptsDir, { recursive: true });
|
|
273
|
+
await fs.mkdir(assetDir, { recursive: true });
|
|
274
|
+
try {
|
|
275
|
+
const scriptPath = 'Assets/Scripts/NegativeIdSymbol.cs';
|
|
276
|
+
const assetPath = 'Assets/Graphs/NegativeId.asset';
|
|
277
|
+
const scriptGuid = '1b63118991a192f4d8ac217fd7fe49ce';
|
|
278
|
+
await fs.writeFile(path.join(tempRoot, scriptPath), 'public class NegativeIdSymbol {}', 'utf-8');
|
|
279
|
+
await fs.writeFile(path.join(tempRoot, `${scriptPath}.meta`), `guid: ${scriptGuid}\n`, 'utf-8');
|
|
280
|
+
await fs.writeFile(path.join(tempRoot, assetPath), `--- !u!114 &-8618438378761226257\nMonoBehaviour:\n m_Script: {fileID: 11500000, guid: ${scriptGuid}, type: 3}\n m_Name: Negative Id Node\n`, 'utf-8');
|
|
281
|
+
const scanContext = await buildUnityScanContext({
|
|
282
|
+
repoRoot: tempRoot,
|
|
283
|
+
scopedPaths: [scriptPath, `${scriptPath}.meta`, assetPath],
|
|
284
|
+
symbolDeclarations: [{ symbol: 'NegativeIdSymbol', scriptPath }],
|
|
285
|
+
});
|
|
286
|
+
const resolved = await resolveUnityBindings({
|
|
287
|
+
repoRoot: tempRoot,
|
|
288
|
+
symbol: 'NegativeIdSymbol',
|
|
289
|
+
scanContext,
|
|
290
|
+
});
|
|
291
|
+
assert.equal(resolved.resourceBindings.length, 1);
|
|
292
|
+
assert.equal(resolved.resourceBindings[0]?.resourcePath, assetPath);
|
|
293
|
+
assert.equal(resolved.unityDiagnostics.length, 0);
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
297
|
+
}
|
|
298
|
+
});
|
|
237
299
|
test('extractAssetRefPathReferences parses nested _relativePath rows and marks sprite assets', () => {
|
|
238
300
|
const refs = extractAssetRefPathReferences({
|
|
239
301
|
scalarFields: [
|
|
@@ -24,7 +24,7 @@ export function parseUnityYamlObjects(text) {
|
|
|
24
24
|
currentBody = [];
|
|
25
25
|
};
|
|
26
26
|
for (const line of lines) {
|
|
27
|
-
const header = line.match(/^--- !u!\d+ &(
|
|
27
|
+
const header = line.match(/^--- !u!\d+ &(-?\d+)(?:\s+(\w+))?\s*$/);
|
|
28
28
|
if (header) {
|
|
29
29
|
flush();
|
|
30
30
|
currentHeader = {
|
|
@@ -47,3 +47,19 @@ MonoBehaviour:
|
|
|
47
47
|
assert.equal(mono.fields['- {fileID'], undefined);
|
|
48
48
|
assert.equal(mono.fields.needPause, '0');
|
|
49
49
|
});
|
|
50
|
+
test('parseUnityYamlObjects supports negative object ids in headers', () => {
|
|
51
|
+
const yamlWithNegativeIds = `--- !u!114 &-8618438378761226257
|
|
52
|
+
MonoBehaviour:
|
|
53
|
+
m_Script: {fileID: 11500000, guid: 1b63118991a192f4d8ac217fd7fe49ce, type: 3}
|
|
54
|
+
m_Name: Energy By Attack Count
|
|
55
|
+
--- !u!114 &11400000
|
|
56
|
+
MonoBehaviour:
|
|
57
|
+
m_Name: Graph Root
|
|
58
|
+
`;
|
|
59
|
+
const blocks = parseUnityYamlObjects(yamlWithNegativeIds);
|
|
60
|
+
assert.equal(blocks.length, 2);
|
|
61
|
+
assert.equal(blocks[0]?.objectId, '-8618438378761226257');
|
|
62
|
+
assert.equal(blocks[0]?.objectType, 'MonoBehaviour');
|
|
63
|
+
assert.match(blocks[0]?.fields.m_Script || '', /guid:\s*1b63118991a192f4d8ac217fd7fe49ce/);
|
|
64
|
+
assert.equal(blocks[1]?.objectId, '11400000');
|
|
65
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function getDerivedProcessDetailResource(id, _backend, repoName) {
|
|
2
|
+
const normalizedId = String(id || '').trim();
|
|
3
|
+
if (!normalizedId) {
|
|
4
|
+
return 'error: derived-process id is required';
|
|
5
|
+
}
|
|
6
|
+
const lines = [
|
|
7
|
+
`id: "${normalizedId}"`,
|
|
8
|
+
'kind: derived',
|
|
9
|
+
'origin: resource_heuristic',
|
|
10
|
+
`reader_uri: "gitnexus://repo/${encodeURIComponent(String(repoName || ''))}/derived-process/${encodeURIComponent(normalizedId)}"`,
|
|
11
|
+
'readable: true',
|
|
12
|
+
'note: "Derived process references are synthesized at query/context runtime from symbol + evidence fingerprints."',
|
|
13
|
+
];
|
|
14
|
+
return lines.join('\n');
|
|
15
|
+
}
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ResolvedUnityBinding } from '../../core/unity/resolver.js';
|
|
9
9
|
import { type UnityContextPayload, type UnityHydrationMeta } from './unity-enrichment.js';
|
|
10
|
+
import type { VerificationHint } from './process-confidence.js';
|
|
10
11
|
import { type RegistryEntry } from '../../storage/repo-manager.js';
|
|
11
12
|
/**
|
|
12
13
|
* Quick test-file detection for filtering impact results.
|
|
13
14
|
* Matches common test file patterns across all supported languages.
|
|
14
15
|
*/
|
|
15
16
|
export declare function isTestFilePath(filePath: string): boolean;
|
|
17
|
+
type ResourceSeedMode = 'strict' | 'balanced';
|
|
16
18
|
export interface ExpandedSymbolCandidate {
|
|
17
19
|
id: string;
|
|
18
20
|
name: string;
|
|
@@ -21,6 +23,54 @@ export interface ExpandedSymbolCandidate {
|
|
|
21
23
|
startLine?: number;
|
|
22
24
|
endLine?: number;
|
|
23
25
|
}
|
|
26
|
+
export declare function parseResourceSeedMode(raw: unknown): ResourceSeedMode;
|
|
27
|
+
export declare function extractUnityResourcePaths(text: string): string[];
|
|
28
|
+
export declare function resolveSeedPath(input: {
|
|
29
|
+
queryText?: string;
|
|
30
|
+
resourcePathPrefix?: string;
|
|
31
|
+
filePath?: string;
|
|
32
|
+
}): string | undefined;
|
|
33
|
+
interface NextHopPayload {
|
|
34
|
+
kind: 'resource' | 'symbol' | 'process' | 'verify';
|
|
35
|
+
target: string;
|
|
36
|
+
why: string;
|
|
37
|
+
next_command: string;
|
|
38
|
+
}
|
|
39
|
+
interface RetrievalRuleHint {
|
|
40
|
+
id: string;
|
|
41
|
+
next_action: string;
|
|
42
|
+
host_base_type?: string[];
|
|
43
|
+
}
|
|
44
|
+
export declare function pickVerificationTarget(input: {
|
|
45
|
+
seedMode: ResourceSeedMode;
|
|
46
|
+
seedPath?: string;
|
|
47
|
+
mappedSeedTargets: string[];
|
|
48
|
+
resourceBindings: ResolvedUnityBinding[];
|
|
49
|
+
fallback: string;
|
|
50
|
+
}): string;
|
|
51
|
+
export declare function buildNextHops(input: {
|
|
52
|
+
seedPath?: string;
|
|
53
|
+
mappedSeedTargets: string[];
|
|
54
|
+
resourceBindings: ResolvedUnityBinding[];
|
|
55
|
+
verificationHint?: VerificationHint;
|
|
56
|
+
retrievalRule?: RetrievalRuleHint;
|
|
57
|
+
repoName?: string;
|
|
58
|
+
symbolName: string;
|
|
59
|
+
queryForSymbol: string;
|
|
60
|
+
}): NextHopPayload[];
|
|
61
|
+
export declare function pickRetrievalRuleHintFromBundle(input: {
|
|
62
|
+
queryText?: string;
|
|
63
|
+
symbolName?: string;
|
|
64
|
+
seedPath?: string;
|
|
65
|
+
rules: Array<{
|
|
66
|
+
id: string;
|
|
67
|
+
trigger_tokens?: string[];
|
|
68
|
+
host_base_type?: string[];
|
|
69
|
+
resource_types?: string[];
|
|
70
|
+
next_action: string;
|
|
71
|
+
}>;
|
|
72
|
+
}): RetrievalRuleHint | undefined;
|
|
73
|
+
export declare function resolveSeedTargetsFromResourceFile(repoPath: string, seedPath: string): Promise<string[]>;
|
|
24
74
|
export declare function filterBm25ResultsByScopePreset<T extends {
|
|
25
75
|
filePath?: string;
|
|
26
76
|
}>(rows: T[], scopePreset?: string): T[];
|
|
@@ -110,6 +160,12 @@ export declare class LocalBackend {
|
|
|
110
160
|
}>>;
|
|
111
161
|
callTool(method: string, params: any): Promise<any>;
|
|
112
162
|
private unityUiTrace;
|
|
163
|
+
private ruleLabDiscover;
|
|
164
|
+
private ruleLabAnalyze;
|
|
165
|
+
private ruleLabReviewPack;
|
|
166
|
+
private ruleLabCurate;
|
|
167
|
+
private ruleLabPromote;
|
|
168
|
+
private ruleLabRegress;
|
|
113
169
|
/**
|
|
114
170
|
* Query tool — process-grouped search.
|
|
115
171
|
*
|