@veewo/gitnexus 1.5.0-rc.4 → 1.5.1
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/analyze-runner.d.ts +1 -1
- package/dist/benchmark/analyze-runner.js +4 -3
- package/dist/benchmark/analyze-runner.test.js +7 -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.d.ts +0 -1
- package/dist/cli/ai-context.js +5 -6
- package/dist/cli/ai-context.test.js +8 -0
- package/dist/cli/analyze-options.js +58 -34
- package/dist/cli/analyze-options.test.js +57 -0
- package/dist/cli/analyze-runtime-summary.js +2 -0
- package/dist/cli/analyze-runtime-summary.test.js +12 -0
- package/dist/cli/analyze-summary.d.ts +4 -0
- package/dist/cli/analyze-summary.js +43 -0
- package/dist/cli/analyze-summary.test.js +65 -1
- package/dist/cli/analyze.d.ts +11 -0
- package/dist/cli/analyze.js +34 -5
- package/dist/cli/analyze.test.d.ts +1 -0
- package/dist/cli/analyze.test.js +25 -0
- package/dist/cli/benchmark-agent-context.js +1 -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-unity.js +1 -1
- package/dist/cli/benchmark-unity.test.js +5 -1
- package/dist/cli/benchmark.d.ts +29 -0
- package/dist/cli/benchmark.js +55 -0
- package/dist/cli/index.js +27 -2
- 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/scope-manifest-config.d.ts +9 -0
- package/dist/cli/scope-manifest-config.js +37 -0
- package/dist/cli/setup.js +40 -41
- package/dist/cli/setup.test.js +14 -14
- package/dist/cli/sync-manifest.d.ts +27 -0
- package/dist/cli/sync-manifest.js +200 -0
- package/dist/cli/sync-manifest.test.d.ts +1 -0
- package/dist/cli/sync-manifest.test.js +88 -0
- package/dist/cli/tool.d.ts +2 -0
- package/dist/cli/tool.js +2 -0
- package/dist/core/config/unity-config.d.ts +1 -1
- package/dist/core/config/unity-config.js +1 -1
- package/dist/core/ingestion/call-processor.d.ts +2 -1
- package/dist/core/ingestion/call-processor.js +28 -6
- package/dist/core/ingestion/heritage-processor.d.ts +2 -1
- package/dist/core/ingestion/heritage-processor.js +30 -7
- package/dist/core/ingestion/import-processor.d.ts +2 -1
- package/dist/core/ingestion/import-processor.js +28 -6
- package/dist/core/ingestion/parsing-processor.d.ts +5 -3
- package/dist/core/ingestion/parsing-processor.js +46 -13
- package/dist/core/ingestion/pipeline.js +100 -19
- 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 +16 -1
- package/dist/core/ingestion/unity-runtime-binding-rules.js +193 -42
- package/dist/core/ingestion/workers/parse-worker.d.ts +2 -0
- package/dist/core/ingestion/workers/parse-worker.js +50 -6
- package/dist/core/lbug/csv-generator.test.js +2 -2
- package/dist/core/tree-sitter/csharp-define-profile.d.ts +6 -0
- package/dist/core/tree-sitter/csharp-define-profile.js +43 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.d.ts +14 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.js +261 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +10 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- 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 +16 -0
- package/package.json +14 -13
- package/scripts/check-sync-manifest-traceability.mjs +203 -0
- package/scripts/run-node-tests.mjs +61 -0
- package/scripts/tree-sitter-audit-classify.mjs +172 -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 +44 -4
- 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
|
@@ -1,32 +1,73 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
function
|
|
4
|
-
const
|
|
5
|
-
|
|
1
|
+
import { extractRuntimeGraphCandidates } from './runtime-chain-graph-candidates.js';
|
|
2
|
+
import { evaluateRuntimeClosure } from './runtime-chain-closure-evaluator.js';
|
|
3
|
+
function hasStructuredVerifierAnchors(input) {
|
|
4
|
+
const hasValue = (value) => String(value || '').trim().length > 0;
|
|
5
|
+
if (hasValue(input.resourceSeedPath))
|
|
6
|
+
return true;
|
|
7
|
+
if (hasValue(input.symbolName))
|
|
8
|
+
return true;
|
|
9
|
+
if (hasValue(input.symbolFilePath))
|
|
10
|
+
return true;
|
|
11
|
+
if (Array.isArray(input.mappedSeedTargets) && input.mappedSeedTargets.some((value) => hasValue(value)))
|
|
12
|
+
return true;
|
|
13
|
+
if (Array.isArray(input.resourceBindings)
|
|
14
|
+
&& input.resourceBindings.some((binding) => hasValue(binding?.resourcePath)))
|
|
15
|
+
return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
function toGraphOnlyRuntimeChainResult(input) {
|
|
19
|
+
const nextCommand = buildDefaultVerifyNextCommand({
|
|
20
|
+
queryText: input.queryText,
|
|
21
|
+
symbolName: input.symbolName,
|
|
22
|
+
resourceSeedPath: input.resourceSeedPath,
|
|
23
|
+
});
|
|
24
|
+
const closure = evaluateRuntimeClosure({
|
|
25
|
+
queryText: input.queryText,
|
|
26
|
+
symbolName: input.symbolName,
|
|
27
|
+
resourceSeedPath: input.resourceSeedPath,
|
|
28
|
+
mappedSeedTargets: input.mappedSeedTargets,
|
|
29
|
+
resourceBindings: input.resourceBindings,
|
|
30
|
+
candidates: input.candidates,
|
|
31
|
+
nextCommand,
|
|
32
|
+
});
|
|
33
|
+
const hops = input.candidates.slice(0, 20).map((candidate) => ({
|
|
34
|
+
hop_type: String(candidate.reason || '').startsWith('unity-rule-')
|
|
35
|
+
|| String(candidate.reason || '').toLowerCase().includes('bridge')
|
|
36
|
+
? 'code_loader'
|
|
37
|
+
: 'code_runtime',
|
|
38
|
+
anchor: `${candidate.sourceFilePath || candidate.sourceName}:${candidate.sourceStartLine || 1}->${candidate.targetFilePath || candidate.targetName}:${candidate.targetStartLine || 1}`,
|
|
39
|
+
confidence: String(candidate.reason || '').startsWith('unity-rule-') ? 'high' : 'medium',
|
|
40
|
+
note: String(candidate.reason || '').startsWith('unity-rule-')
|
|
41
|
+
? `Synthetic edge observed in graph (${candidate.reason}).`
|
|
42
|
+
: 'Graph continuity candidate from structured anchors.',
|
|
43
|
+
snippet: `${candidate.sourceName} -> ${candidate.targetName}`,
|
|
44
|
+
}));
|
|
45
|
+
return {
|
|
46
|
+
status: closure.status,
|
|
47
|
+
evidence_level: closure.evidence_level,
|
|
48
|
+
evidence_source: 'query_time',
|
|
49
|
+
hops,
|
|
50
|
+
gaps: closure.gaps,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function resolveFollowUpSubject(input) {
|
|
54
|
+
const seedPath = String(input.resourceSeedPath || '').trim();
|
|
55
|
+
if (seedPath)
|
|
56
|
+
return seedPath;
|
|
57
|
+
const symbolName = String(input.symbolName || '').trim();
|
|
58
|
+
if (symbolName)
|
|
59
|
+
return symbolName;
|
|
60
|
+
const queryText = String(input.queryText || '').trim();
|
|
61
|
+
if (queryText)
|
|
62
|
+
return queryText;
|
|
63
|
+
return 'unity-runtime-chain';
|
|
64
|
+
}
|
|
65
|
+
function buildDefaultVerifyNextCommand(input) {
|
|
66
|
+
const escapedQuery = resolveFollowUpSubject(input)
|
|
6
67
|
.replace(/\\/g, '\\\\')
|
|
7
68
|
.replace(/"/g, '\\"');
|
|
8
69
|
return `node gitnexus/dist/cli/index.js query --unity-resources on --unity-hydration parity --runtime-chain-verify on-demand "${escapedQuery}"`;
|
|
9
70
|
}
|
|
10
|
-
function buildRuntimeMatchHaystack(input) {
|
|
11
|
-
return [
|
|
12
|
-
input.queryText,
|
|
13
|
-
input.resourceSeedPath,
|
|
14
|
-
input.symbolName,
|
|
15
|
-
input.symbolFilePath,
|
|
16
|
-
...(input.mappedSeedTargets || []),
|
|
17
|
-
...(input.resourceBindings || []).map((binding) => binding.resourcePath),
|
|
18
|
-
]
|
|
19
|
-
.filter(Boolean)
|
|
20
|
-
.join(' ')
|
|
21
|
-
.toLowerCase();
|
|
22
|
-
}
|
|
23
|
-
function parseTriggerTokens(triggerFamily) {
|
|
24
|
-
return String(triggerFamily || '')
|
|
25
|
-
.toLowerCase()
|
|
26
|
-
.split(/[\s,|/]+/)
|
|
27
|
-
.map((token) => token.trim())
|
|
28
|
-
.filter((token) => token.length > 0);
|
|
29
|
-
}
|
|
30
71
|
async function verifyRuleDrivenRuntimeChain(input) {
|
|
31
72
|
const ruleId = input.rule?.id;
|
|
32
73
|
if (!ruleId) {
|
|
@@ -71,8 +112,23 @@ async function verifyRuleDrivenRuntimeChain(input) {
|
|
|
71
112
|
};
|
|
72
113
|
}
|
|
73
114
|
export async function verifyRuntimeChainOnDemand(input) {
|
|
74
|
-
if (!input.rule)
|
|
75
|
-
|
|
115
|
+
if (!input.rule) {
|
|
116
|
+
if (!hasStructuredVerifierAnchors(input) || !String(input.symbolName || '').trim())
|
|
117
|
+
return undefined;
|
|
118
|
+
const candidates = await extractRuntimeGraphCandidates({
|
|
119
|
+
executeParameterized: input.executeParameterized,
|
|
120
|
+
symbolName: input.symbolName,
|
|
121
|
+
symbolFilePath: input.symbolFilePath,
|
|
122
|
+
});
|
|
123
|
+
return toGraphOnlyRuntimeChainResult({
|
|
124
|
+
queryText: input.queryText,
|
|
125
|
+
symbolName: input.symbolName,
|
|
126
|
+
resourceSeedPath: input.resourceSeedPath,
|
|
127
|
+
mappedSeedTargets: input.mappedSeedTargets,
|
|
128
|
+
resourceBindings: input.resourceBindings,
|
|
129
|
+
candidates,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
76
132
|
return await verifyRuleDrivenRuntimeChain(input);
|
|
77
133
|
}
|
|
78
134
|
function buildFailureRuntimeClaim(input) {
|
|
@@ -96,130 +152,85 @@ function buildFailureRuntimeClaim(input) {
|
|
|
96
152
|
next_action: input.next_action,
|
|
97
153
|
};
|
|
98
154
|
}
|
|
99
|
-
function
|
|
100
|
-
const
|
|
101
|
-
const tokens = Array.isArray(rule.match?.trigger_tokens) && rule.match.trigger_tokens.length > 0
|
|
102
|
-
? rule.match.trigger_tokens
|
|
103
|
-
: parseTriggerTokens(rule.trigger_family);
|
|
104
|
-
if (tokens.length === 0)
|
|
105
|
-
return Number.NEGATIVE_INFINITY;
|
|
106
|
-
let score = 0;
|
|
107
|
-
let matchedTrigger = false;
|
|
108
|
-
for (const token of tokens) {
|
|
109
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
110
|
-
if (!normalized)
|
|
111
|
-
continue;
|
|
112
|
-
if (haystack.includes(normalized)) {
|
|
113
|
-
matchedTrigger = true;
|
|
114
|
-
score += 10 + normalized.length;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (!matchedTrigger)
|
|
118
|
-
return Number.NEGATIVE_INFINITY;
|
|
119
|
-
const boostLists = [
|
|
120
|
-
...(Array.isArray(rule.match?.host_base_type) ? [rule.match.host_base_type] : []),
|
|
121
|
-
...(Array.isArray(rule.host_base_type) ? [rule.host_base_type] : []),
|
|
122
|
-
];
|
|
123
|
-
for (const list of boostLists) {
|
|
124
|
-
for (const token of list) {
|
|
125
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
126
|
-
if (normalized && haystack.includes(normalized))
|
|
127
|
-
score += 20 + normalized.length;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const resourceLists = [
|
|
131
|
-
...(Array.isArray(rule.match?.resource_types) ? [rule.match.resource_types] : []),
|
|
132
|
-
...(Array.isArray(rule.resource_types) ? [rule.resource_types] : []),
|
|
133
|
-
];
|
|
134
|
-
for (const list of resourceLists) {
|
|
135
|
-
for (const token of list) {
|
|
136
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
137
|
-
if (normalized && haystack.includes(normalized))
|
|
138
|
-
score += 4 + normalized.length;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
for (const token of rule.match?.module_scope || []) {
|
|
142
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
143
|
-
if (normalized && haystack.includes(normalized))
|
|
144
|
-
score += 8 + normalized.length;
|
|
145
|
-
}
|
|
146
|
-
return score;
|
|
147
|
-
}
|
|
148
|
-
export async function verifyRuntimeClaimOnDemand(input) {
|
|
149
|
-
let registry;
|
|
150
|
-
try {
|
|
151
|
-
registry = await loadRuleRegistry(input.repoPath, input.rulesRoot);
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
if (error instanceof RuleRegistryLoadError) {
|
|
155
|
-
if (error.code === 'rule_catalog_missing' || error.code === 'rule_file_missing') {
|
|
156
|
-
return buildFailureRuntimeClaim({
|
|
157
|
-
reason: 'rule_not_matched',
|
|
158
|
-
next_action: buildDefaultVerifyNextCommand(input.queryText),
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
const activeRules = registry.activeRules || [];
|
|
165
|
-
const fallbackNextAction = buildDefaultVerifyNextCommand(input.queryText);
|
|
166
|
-
if (activeRules.length === 0) {
|
|
167
|
-
return buildFailureRuntimeClaim({
|
|
168
|
-
reason: 'rule_not_matched',
|
|
169
|
-
next_action: fallbackNextAction,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
const matchedRule = [...activeRules]
|
|
173
|
-
.map((rule) => ({ rule, score: scoreRuntimeClaimRule(rule, input) }))
|
|
174
|
-
.filter((entry) => Number.isFinite(entry.score))
|
|
175
|
-
.sort((a, b) => (b.score - a.score) || a.rule.id.localeCompare(b.rule.id))[0]?.rule;
|
|
176
|
-
if (!matchedRule) {
|
|
177
|
-
return buildFailureRuntimeClaim({
|
|
178
|
-
reason: 'rule_not_matched',
|
|
179
|
-
next_action: fallbackNextAction,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
const runtimeChain = await verifyRuntimeChainOnDemand({
|
|
183
|
-
...input,
|
|
184
|
-
requiredHops: matchedRule.required_hops,
|
|
185
|
-
rule: matchedRule,
|
|
186
|
-
});
|
|
187
|
-
if (!runtimeChain) {
|
|
188
|
-
return buildFailureRuntimeClaim({
|
|
189
|
-
reason: 'rule_matched_but_evidence_missing',
|
|
190
|
-
next_action: matchedRule.next_action || buildDefaultVerifyNextCommand(input.queryText),
|
|
191
|
-
rule: matchedRule,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
const normalizedStatus = (runtimeChain.status === 'pending' ? 'failed' : runtimeChain.status);
|
|
155
|
+
function buildGraphOnlyRuntimeClaim(input) {
|
|
156
|
+
const normalizedStatus = (input.runtimeChain.status === 'pending' ? 'failed' : input.runtimeChain.status);
|
|
195
157
|
const verificationFailed = normalizedStatus === 'failed'
|
|
196
|
-
|| (runtimeChain.evidence_level === 'none' && normalizedStatus !== 'verified_full');
|
|
197
|
-
const
|
|
198
|
-
|
|
158
|
+
|| (input.runtimeChain.evidence_level === 'none' && normalizedStatus !== 'verified_full');
|
|
159
|
+
const nextAction = buildDefaultVerifyNextCommand({
|
|
160
|
+
queryText: input.queryText,
|
|
161
|
+
symbolName: input.symbolName,
|
|
162
|
+
resourceSeedPath: input.resourceSeedPath,
|
|
163
|
+
});
|
|
164
|
+
const base = {
|
|
165
|
+
rule_id: 'graph-only.runtime-closure.v1',
|
|
166
|
+
rule_version: '1.0.0',
|
|
167
|
+
scope: {
|
|
168
|
+
resource_types: ['asset', 'prefab', 'unity'],
|
|
169
|
+
host_base_type: input.symbolName ? [input.symbolName] : [],
|
|
170
|
+
trigger_family: 'graph_only',
|
|
171
|
+
},
|
|
199
172
|
status: verificationFailed ? 'failed' : normalizedStatus,
|
|
200
|
-
evidence_level: runtimeChain.evidence_level,
|
|
201
|
-
|
|
202
|
-
|
|
173
|
+
evidence_level: input.runtimeChain.evidence_level,
|
|
174
|
+
guarantees: (!verificationFailed && normalizedStatus === 'verified_full')
|
|
175
|
+
? ['runtime_chain_graph_closure']
|
|
176
|
+
: [],
|
|
177
|
+
non_guarantees: [
|
|
178
|
+
'no_runtime_execution',
|
|
179
|
+
'no_dynamic_data_flow_proof',
|
|
180
|
+
'no_state_transition_proof',
|
|
181
|
+
],
|
|
182
|
+
hops: input.runtimeChain.hops,
|
|
183
|
+
gaps: input.runtimeChain.gaps,
|
|
203
184
|
...(verificationFailed
|
|
204
185
|
? {
|
|
205
186
|
reason: 'rule_matched_but_verification_failed',
|
|
206
|
-
next_action:
|
|
187
|
+
next_action: nextAction,
|
|
207
188
|
}
|
|
208
189
|
: {}),
|
|
209
|
-
}
|
|
210
|
-
const chainClosed =
|
|
211
|
-
&&
|
|
212
|
-
&&
|
|
190
|
+
};
|
|
191
|
+
const chainClosed = base.status === 'verified_full'
|
|
192
|
+
&& base.evidence_level === 'verified_chain'
|
|
193
|
+
&& base.gaps.length === 0;
|
|
213
194
|
if (input.minimumEvidenceSatisfied === false && !chainClosed) {
|
|
214
195
|
return {
|
|
215
|
-
...
|
|
196
|
+
...base,
|
|
216
197
|
status: 'failed',
|
|
217
198
|
evidence_level: 'clue',
|
|
218
199
|
guarantees: [],
|
|
219
|
-
non_guarantees: [...
|
|
200
|
+
non_guarantees: [...base.non_guarantees, 'minimum_evidence_contract_not_satisfied'],
|
|
220
201
|
reason: 'rule_matched_but_evidence_missing',
|
|
221
|
-
next_action:
|
|
202
|
+
next_action: nextAction,
|
|
222
203
|
};
|
|
223
204
|
}
|
|
224
|
-
return
|
|
205
|
+
return base;
|
|
206
|
+
}
|
|
207
|
+
export async function verifyRuntimeClaimOnDemand(input) {
|
|
208
|
+
const fallbackNextAction = buildDefaultVerifyNextCommand({
|
|
209
|
+
queryText: input.queryText,
|
|
210
|
+
symbolName: input.symbolName,
|
|
211
|
+
resourceSeedPath: input.resourceSeedPath,
|
|
212
|
+
});
|
|
213
|
+
if (!hasStructuredVerifierAnchors(input)) {
|
|
214
|
+
return buildFailureRuntimeClaim({
|
|
215
|
+
reason: 'rule_not_matched',
|
|
216
|
+
next_action: fallbackNextAction,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
const graphOnlyRuntimeChain = await verifyRuntimeChainOnDemand({
|
|
220
|
+
...input,
|
|
221
|
+
rule: undefined,
|
|
222
|
+
});
|
|
223
|
+
if (graphOnlyRuntimeChain) {
|
|
224
|
+
return buildGraphOnlyRuntimeClaim({
|
|
225
|
+
runtimeChain: graphOnlyRuntimeChain,
|
|
226
|
+
queryText: input.queryText,
|
|
227
|
+
symbolName: input.symbolName,
|
|
228
|
+
resourceSeedPath: input.resourceSeedPath,
|
|
229
|
+
minimumEvidenceSatisfied: input.minimumEvidenceSatisfied,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return buildFailureRuntimeClaim({
|
|
233
|
+
reason: 'rule_not_matched',
|
|
234
|
+
next_action: fallbackNextAction,
|
|
235
|
+
});
|
|
225
236
|
}
|
|
@@ -29,18 +29,6 @@ async function fileExists(filePath) {
|
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
async function resolveWorkspaceRulesRoot() {
|
|
33
|
-
const candidates = [
|
|
34
|
-
path.resolve('.gitnexus/rules'),
|
|
35
|
-
path.resolve('../.gitnexus/rules'),
|
|
36
|
-
];
|
|
37
|
-
for (const candidate of candidates) {
|
|
38
|
-
if (await fileExists(path.join(candidate, 'catalog.json'))) {
|
|
39
|
-
return candidate;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return candidates[0];
|
|
43
|
-
}
|
|
44
32
|
function makeExecuteParameterized(repoPath) {
|
|
45
33
|
return async (query, params) => {
|
|
46
34
|
const q = String(query || '');
|
|
@@ -231,65 +219,109 @@ describe('runtime chain verify', () => {
|
|
|
231
219
|
expect(out).toBeUndefined();
|
|
232
220
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
233
221
|
});
|
|
234
|
-
it('runtime chain gaps are actionable under
|
|
222
|
+
it('runtime chain gaps are actionable under graph-only verification', async () => {
|
|
235
223
|
const out = await verifyRuntimeChainOnDemand({
|
|
236
224
|
repoPath: await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-gaps-')),
|
|
237
225
|
queryText: 'Reload',
|
|
226
|
+
symbolName: 'ReloadBase',
|
|
227
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
|
|
238
228
|
executeParameterized: async () => [],
|
|
239
229
|
resourceBindings: [],
|
|
240
|
-
rule: {
|
|
241
|
-
id: 'demo.reload.strict.v2',
|
|
242
|
-
version: '2.0.0',
|
|
243
|
-
trigger_family: 'reload',
|
|
244
|
-
resource_types: ['asset'],
|
|
245
|
-
host_base_type: ['ReloadBase'],
|
|
246
|
-
required_hops: ['resource', 'guid_map', 'code_loader', 'code_runtime'],
|
|
247
|
-
guarantees: ['strict_chain_closed'],
|
|
248
|
-
non_guarantees: ['strict_no_runtime_execution'],
|
|
249
|
-
next_action: 'node strict',
|
|
250
|
-
file_path: '.gitnexus/rules/approved/demo.reload.strict.v2.yaml',
|
|
251
|
-
},
|
|
252
|
-
requiredHops: ['resource', 'guid_map', 'code_loader', 'code_runtime'],
|
|
253
230
|
});
|
|
254
231
|
expect(out?.gaps.length).toBeGreaterThan(0);
|
|
255
232
|
expect(out?.gaps.every((gap) => !!gap.next_command)).toBe(true);
|
|
256
233
|
});
|
|
257
|
-
it('
|
|
234
|
+
it('builds graph-only follow-up command from symbol anchor when query text is missing', async () => {
|
|
235
|
+
const out = await verifyRuntimeClaimOnDemand({
|
|
236
|
+
repoPath: await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-anchor-symbol-')),
|
|
237
|
+
executeParameterized: async () => [],
|
|
238
|
+
queryText: '',
|
|
239
|
+
symbolName: 'InitGlobal',
|
|
240
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Core/GameBootstrap.cs',
|
|
241
|
+
resourceBindings: [],
|
|
242
|
+
});
|
|
243
|
+
expect(out.next_action).toContain('InitGlobal');
|
|
244
|
+
expect(out.next_action).not.toContain('Reload NEON.Game.Graph.Nodes.Reloads');
|
|
245
|
+
expect(out.gaps.every((gap) => !gap.next_command.includes('Reload NEON.Game.Graph.Nodes.Reloads'))).toBe(true);
|
|
246
|
+
expect(out.gaps.every((gap) => gap.next_command.includes('InitGlobal'))).toBe(true);
|
|
247
|
+
});
|
|
248
|
+
it('prefers resource seed path in follow-up command subject when seed is present', async () => {
|
|
249
|
+
const seedPath = 'Assets/NEON/DataAssets/Powerups/Startup/init_global.asset';
|
|
250
|
+
const out = await verifyRuntimeClaimOnDemand({
|
|
251
|
+
repoPath: await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-anchor-seed-')),
|
|
252
|
+
executeParameterized: async () => [],
|
|
253
|
+
queryText: '',
|
|
254
|
+
symbolName: 'InitGlobal',
|
|
255
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Core/GameBootstrap.cs',
|
|
256
|
+
resourceSeedPath: seedPath,
|
|
257
|
+
resourceBindings: [],
|
|
258
|
+
});
|
|
259
|
+
expect(out.next_action).toContain(seedPath);
|
|
260
|
+
expect(out.next_action).not.toContain('Reload NEON.Game.Graph.Nodes.Reloads');
|
|
261
|
+
expect(out.gaps.every((gap) => !gap.next_command.includes('Reload NEON.Game.Graph.Nodes.Reloads'))).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
it('accepts seed-to-mapped resource equivalence for bind segment in graph-only verifier', async () => {
|
|
258
264
|
const repoPath = await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-mapped-resource-'));
|
|
259
|
-
const out = await
|
|
265
|
+
const out = await verifyRuntimeClaimOnDemand({
|
|
260
266
|
repoPath,
|
|
261
267
|
queryText: 'EnergyByAttackCount Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/0_初始武器/1_weapon_0_james_new.asset',
|
|
268
|
+
symbolName: 'EnergyNode',
|
|
269
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs',
|
|
262
270
|
resourceSeedPath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/0_初始武器/1_weapon_0_james_new.asset',
|
|
263
271
|
mappedSeedTargets: ['Assets/NEON/Graphs/PlayerGun/Gungraph_use/1_weapon_0_james1.asset'],
|
|
264
|
-
executeParameterized: async () => [],
|
|
265
272
|
resourceBindings: [{ resourcePath: 'Assets/NEON/Graphs/PlayerGun/Gungraph_use/1_weapon_0_james1.asset' }],
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
273
|
+
executeParameterized: async (query) => {
|
|
274
|
+
const q = String(query || '');
|
|
275
|
+
if (q.includes('WHERE n.name IN $symbolNames')) {
|
|
276
|
+
return [{
|
|
277
|
+
id: 'Class:Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs:EnergyNode',
|
|
278
|
+
name: 'EnergyNode',
|
|
279
|
+
type: 'Class',
|
|
280
|
+
filePath: 'Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs',
|
|
281
|
+
startLine: 1,
|
|
282
|
+
}];
|
|
283
|
+
}
|
|
284
|
+
if (q.includes("MATCH (s {id: $symbolId})-[r:CodeRelation {type: 'CALLS'}]->(t)")) {
|
|
285
|
+
return [{
|
|
286
|
+
sourceId: 'Method:Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs:Apply',
|
|
287
|
+
sourceName: 'Apply',
|
|
288
|
+
sourceFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs',
|
|
289
|
+
sourceStartLine: 22,
|
|
290
|
+
targetId: 'Method:Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyRuntime.cs:Run',
|
|
291
|
+
targetName: 'Run',
|
|
292
|
+
targetFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyRuntime.cs',
|
|
293
|
+
targetStartLine: 10,
|
|
294
|
+
reason: 'unity-rule-method-bridge:demo.energy.seed-map.v1',
|
|
295
|
+
}];
|
|
296
|
+
}
|
|
297
|
+
if (q.includes("MATCH (n {id: $symbolId})-[:CodeRelation {type: 'HAS_METHOD'}]->(m)")
|
|
298
|
+
&& q.includes("MATCH (m)-[r:CodeRelation {type: 'CALLS'}]->(t)")) {
|
|
299
|
+
return [{
|
|
300
|
+
sourceId: 'Method:Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs:Bootstrap',
|
|
301
|
+
sourceName: 'Bootstrap',
|
|
302
|
+
sourceFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs',
|
|
303
|
+
sourceStartLine: 8,
|
|
304
|
+
targetId: 'Method:Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs:Apply',
|
|
305
|
+
targetName: 'Apply',
|
|
306
|
+
targetFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Energy/EnergyNode.cs',
|
|
307
|
+
targetStartLine: 22,
|
|
308
|
+
reason: 'static-call',
|
|
309
|
+
}];
|
|
310
|
+
}
|
|
311
|
+
return [];
|
|
277
312
|
},
|
|
278
|
-
requiredHops: ['resource'],
|
|
279
313
|
});
|
|
280
|
-
expect(out
|
|
281
|
-
expect(out
|
|
282
|
-
expect(out
|
|
314
|
+
expect(out.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
315
|
+
expect(out.status).toBe('verified_full');
|
|
316
|
+
expect(out.gaps.length).toBe(0);
|
|
283
317
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
284
318
|
});
|
|
285
319
|
it('phase2 runtime claim returns explicit rule_not_matched', async () => {
|
|
286
|
-
const workspaceRulesRoot = await resolveWorkspaceRulesRoot();
|
|
287
320
|
const out = await verifyRuntimeClaimOnDemand({
|
|
288
321
|
repoPath: path.resolve('.'),
|
|
289
322
|
queryText: 'CompletelyUnrelatedChain',
|
|
290
323
|
executeParameterized: async () => [],
|
|
291
324
|
resourceBindings: [],
|
|
292
|
-
rulesRoot: workspaceRulesRoot,
|
|
293
325
|
});
|
|
294
326
|
expect(out.status).toBe('failed');
|
|
295
327
|
expect(out.reason).toBe('rule_not_matched');
|
|
@@ -328,33 +360,32 @@ describe('runtime chain verify', () => {
|
|
|
328
360
|
expect(out.next_action).toBeTruthy();
|
|
329
361
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
330
362
|
});
|
|
331
|
-
it('phase2 runtime claim uses
|
|
363
|
+
it('phase2 runtime claim uses graph-only metadata', async () => {
|
|
332
364
|
const repoPath = await makeTempRepo();
|
|
333
|
-
const workspaceRulesRoot = await resolveWorkspaceRulesRoot();
|
|
334
365
|
const out = await verifyRuntimeClaimOnDemand({
|
|
335
366
|
repoPath,
|
|
336
367
|
queryText: 'Reload NEON.Game.Graph.Nodes.Reloads',
|
|
368
|
+
symbolName: 'ReloadBase',
|
|
369
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
|
|
337
370
|
executeParameterized: makeExecuteParameterized(repoPath),
|
|
371
|
+
resourceSeedPath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset',
|
|
338
372
|
resourceBindings: [{ resourcePath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset' }],
|
|
339
|
-
rulesRoot: workspaceRulesRoot,
|
|
340
373
|
});
|
|
341
|
-
expect(out.rule_id).toBe('
|
|
374
|
+
expect(out.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
342
375
|
expect(out.rule_version).toBe('1.0.0');
|
|
343
376
|
});
|
|
344
377
|
it('phase2 next_action remains shell-parsable when unmatched', async () => {
|
|
345
|
-
const workspaceRulesRoot = await resolveWorkspaceRulesRoot();
|
|
346
378
|
const out = await verifyRuntimeClaimOnDemand({
|
|
347
379
|
repoPath: path.resolve('.'),
|
|
348
380
|
queryText: 'CompletelyUnrelatedChain',
|
|
349
381
|
executeParameterized: async () => [],
|
|
350
382
|
resourceBindings: [],
|
|
351
|
-
rulesRoot: workspaceRulesRoot,
|
|
352
383
|
});
|
|
353
384
|
expect(out.reason).toBe('rule_not_matched');
|
|
354
385
|
expect(typeof out.next_action).toBe('string');
|
|
355
386
|
expect(hasBalancedShellQuotes(String(out.next_action || ''))).toBe(true);
|
|
356
387
|
});
|
|
357
|
-
it('phase2 runtime claim
|
|
388
|
+
it('phase2 runtime claim is stable under different rulesRoot values (graph-only)', async () => {
|
|
358
389
|
const repoPath = await makeTempRepo();
|
|
359
390
|
const executeParameterized = makeExecuteParameterized(repoPath);
|
|
360
391
|
const strictExecuteParameterized = async (query, params) => {
|
|
@@ -408,6 +439,9 @@ describe('runtime chain verify', () => {
|
|
|
408
439
|
const strict = await verifyRuntimeClaimOnDemand({
|
|
409
440
|
repoPath,
|
|
410
441
|
queryText: 'Reload NEON.Game.Graph.Nodes.Reloads',
|
|
442
|
+
symbolName: 'ReloadBase',
|
|
443
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
|
|
444
|
+
resourceSeedPath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset',
|
|
411
445
|
executeParameterized: strictExecuteParameterized,
|
|
412
446
|
resourceBindings: [{ resourcePath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset' }],
|
|
413
447
|
rulesRoot: strictRulesRoot,
|
|
@@ -415,15 +449,20 @@ describe('runtime chain verify', () => {
|
|
|
415
449
|
const relaxed = await verifyRuntimeClaimOnDemand({
|
|
416
450
|
repoPath,
|
|
417
451
|
queryText: 'Reload NEON.Game.Graph.Nodes.Reloads',
|
|
452
|
+
symbolName: 'ReloadBase',
|
|
453
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
|
|
454
|
+
resourceSeedPath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset',
|
|
418
455
|
executeParameterized,
|
|
419
456
|
resourceBindings: [{ resourcePath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset' }],
|
|
420
457
|
rulesRoot: relaxedRulesRoot,
|
|
421
458
|
});
|
|
422
|
-
expect(strict.
|
|
423
|
-
expect(relaxed.
|
|
459
|
+
expect(strict.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
460
|
+
expect(relaxed.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
461
|
+
expect(strict.status).toBe(relaxed.status);
|
|
462
|
+
expect(strict.evidence_level).toBe(relaxed.evidence_level);
|
|
424
463
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
425
464
|
});
|
|
426
|
-
it('phase2 runtime claim guarantees/non_guarantees
|
|
465
|
+
it('phase2 runtime claim guarantees/non_guarantees follow graph-only contract', async () => {
|
|
427
466
|
const repoPath = await makeTempRepo();
|
|
428
467
|
const rulesRoot = await writeRules(repoPath, {
|
|
429
468
|
'approved/demo.reload.claim-semantics.v1.yaml': [
|
|
@@ -474,17 +513,21 @@ describe('runtime chain verify', () => {
|
|
|
474
513
|
const out = await verifyRuntimeClaimOnDemand({
|
|
475
514
|
repoPath,
|
|
476
515
|
queryText: 'Reload runtime start sequence',
|
|
516
|
+
symbolName: 'ReloadBase',
|
|
517
|
+
symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
|
|
518
|
+
resourceSeedPath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset',
|
|
477
519
|
executeParameterized: makeExecuteParameterized(repoPath),
|
|
478
520
|
resourceBindings: [{ resourcePath: 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb/1_weapon_orb_key.asset' }],
|
|
479
521
|
rulesRoot,
|
|
480
522
|
});
|
|
481
|
-
expect(out.rule_id).toBe('
|
|
482
|
-
expect(out.
|
|
483
|
-
expect(out.
|
|
484
|
-
expect(out.non_guarantees).
|
|
523
|
+
expect(out.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
524
|
+
expect(out.non_guarantees).toContain('no_runtime_execution');
|
|
525
|
+
expect(out.non_guarantees).toContain('no_dynamic_data_flow_proof');
|
|
526
|
+
expect(out.non_guarantees).toContain('no_state_transition_proof');
|
|
527
|
+
expect(out.non_guarantees).not.toContain('custom_non_guarantee_from_rule');
|
|
485
528
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
486
529
|
});
|
|
487
|
-
it('phase2
|
|
530
|
+
it('phase2 without structured anchors returns rule_not_matched', async () => {
|
|
488
531
|
const repoPath = await makeTempRepo();
|
|
489
532
|
const rulesRoot = await writeRules(repoPath, {
|
|
490
533
|
'approved/demo.startup.v1.yaml': [
|
|
@@ -511,10 +554,8 @@ describe('runtime chain verify', () => {
|
|
|
511
554
|
resourceBindings: [{ resourcePath: 'Assets/Custom/Rules/startup.asset' }],
|
|
512
555
|
rulesRoot,
|
|
513
556
|
});
|
|
514
|
-
expect(out.rule_id).toBe('
|
|
515
|
-
expect(out.
|
|
516
|
-
expect(out.evidence_level).toBe('verified_segment');
|
|
517
|
-
expect(out.reason).toBeUndefined();
|
|
557
|
+
expect(out.rule_id).toBe('none');
|
|
558
|
+
expect(out.reason).toBe('rule_not_matched');
|
|
518
559
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
519
560
|
});
|
|
520
561
|
it('phase2 rule_not_matched does not leak first rule next_action', async () => {
|
|
@@ -549,7 +590,7 @@ describe('runtime chain verify', () => {
|
|
|
549
590
|
expect(String(out.next_action || '')).toContain('Reload runtime start sequence');
|
|
550
591
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
551
592
|
});
|
|
552
|
-
it('phase5
|
|
593
|
+
it('phase5 promote artifacts do not alter query-time graph-only rule identity', async () => {
|
|
553
594
|
const repoPath = await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-rule-lab-promote-'));
|
|
554
595
|
const sliceDir = path.join(repoPath, '.gitnexus', 'rules', 'lab', 'runs', 'run-x', 'slices', 'slice-a');
|
|
555
596
|
await fs.mkdir(sliceDir, { recursive: true });
|
|
@@ -573,10 +614,27 @@ describe('runtime chain verify', () => {
|
|
|
573
614
|
const out = await verifyRuntimeClaimOnDemand({
|
|
574
615
|
repoPath,
|
|
575
616
|
queryText: 'Startup Graph Trigger',
|
|
576
|
-
|
|
617
|
+
symbolName: 'StartupNode',
|
|
618
|
+
symbolFilePath: 'Assets/Rules/StartupNode.cs',
|
|
619
|
+
resourceSeedPath: 'Assets/Rules/startup.asset',
|
|
620
|
+
executeParameterized: async (query) => {
|
|
621
|
+
if (String(query || '').includes('WHERE n.name IN $symbolNames')) {
|
|
622
|
+
return [{
|
|
623
|
+
id: 'Class:Assets/Rules/StartupNode.cs:StartupNode',
|
|
624
|
+
name: 'StartupNode',
|
|
625
|
+
type: 'Class',
|
|
626
|
+
filePath: 'Assets/Rules/StartupNode.cs',
|
|
627
|
+
startLine: 1,
|
|
628
|
+
}];
|
|
629
|
+
}
|
|
630
|
+
return [];
|
|
631
|
+
},
|
|
577
632
|
resourceBindings: [{ resourcePath: 'Assets/Rules/startup.asset' }],
|
|
578
633
|
});
|
|
579
|
-
expect(out.rule_id).toBe('
|
|
634
|
+
expect(out.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
635
|
+
expect(out.rule_id).not.toBe('demo.startup.v1');
|
|
636
|
+
expect(out.status).toBe('verified_partial');
|
|
637
|
+
expect(out.evidence_level).toBe('verified_segment');
|
|
580
638
|
expect(out.reason).toBeUndefined();
|
|
581
639
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
582
640
|
});
|
|
@@ -60,5 +60,9 @@ export declare class RuleRegistryLoadError extends Error {
|
|
|
60
60
|
constructor(code: RuleRegistryLoadErrorCode, message: string, details?: Record<string, string>);
|
|
61
61
|
}
|
|
62
62
|
export declare function parseRuleYaml(raw: string, filePath: string): RuntimeClaimRule;
|
|
63
|
+
/**
|
|
64
|
+
* Runtime claim rule registry remains the source for analyze-time synthetic-edge production
|
|
65
|
+
* and offline governance/report workflows. Query-time runtime closure verification is graph-only.
|
|
66
|
+
*/
|
|
63
67
|
export declare function loadRuleRegistry(repoPath: string, rulesRoot?: string): Promise<RuntimeClaimRuleRegistry>;
|
|
64
68
|
export declare function loadAnalyzeRules(repoPath: string, rulesRoot?: string): Promise<RuntimeClaimRule[]>;
|
|
@@ -215,6 +215,10 @@ export function parseRuleYaml(raw, filePath) {
|
|
|
215
215
|
file_path: filePath,
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Runtime claim rule registry remains the source for analyze-time synthetic-edge production
|
|
220
|
+
* and offline governance/report workflows. Query-time runtime closure verification is graph-only.
|
|
221
|
+
*/
|
|
218
222
|
export async function loadRuleRegistry(repoPath, rulesRoot) {
|
|
219
223
|
const normalizedRepoPath = path.resolve(repoPath);
|
|
220
224
|
const root = rulesRoot
|