@veewo/gitnexus 1.5.6 → 1.5.8
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/analyze-runner.d.ts +0 -2
- package/dist/benchmark/analyze-runner.js +0 -6
- package/dist/benchmark/analyze-runner.test.js +1 -10
- package/dist/benchmark/runner.d.ts +0 -2
- package/dist/benchmark/runner.js +0 -2
- package/dist/benchmark/u2-e2e/neonspark-full-e2e.js +0 -11
- package/dist/benchmark/u2-performance-sampler.js +3 -16
- package/dist/cli/ai-context.js +1 -7
- package/dist/cli/analyze-options.d.ts +19 -6
- package/dist/cli/analyze-options.js +76 -71
- package/dist/cli/analyze-options.test.js +78 -73
- package/dist/cli/analyze-runtime-summary.js +0 -1
- package/dist/cli/analyze-runtime-summary.test.js +0 -2
- package/dist/cli/analyze-summary.d.ts +0 -2
- package/dist/cli/analyze-summary.js +0 -24
- package/dist/cli/analyze-summary.test.js +1 -65
- package/dist/cli/analyze.d.ts +2 -4
- package/dist/cli/analyze.js +14 -30
- package/dist/cli/analyze.test.js +9 -15
- package/dist/cli/benchmark-agent-context.d.ts +0 -2
- package/dist/cli/benchmark-agent-context.js +0 -2
- package/dist/cli/benchmark-agent-safe-query-context.d.ts +0 -2
- package/dist/cli/benchmark-agent-safe-query-context.js +0 -2
- package/dist/cli/benchmark-unity.d.ts +0 -2
- package/dist/cli/benchmark-unity.js +0 -2
- package/dist/cli/clean.d.ts +2 -3
- package/dist/cli/clean.js +4 -25
- package/dist/cli/index.js +1 -12
- package/dist/core/ingestion/pipeline.js +1 -44
- package/dist/mcp/local/agent-safe-response.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +0 -23
- package/dist/mcp/local/local-backend.js +69 -248
- package/dist/mcp/local/runtime-chain-verify.test.js +0 -49
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +0 -11
- package/dist/mcp/local/runtime-claim-rule-registry.js +0 -159
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +67 -214
- package/dist/mcp/tools.js +0 -70
- package/dist/storage/repo-manager.d.ts +1 -0
- package/dist/types/pipeline.d.ts +0 -3
- package/package.json +1 -1
- package/skills/gitnexus-cli.md +62 -38
- package/vendor/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
- package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
- package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
- package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +122 -0
- package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
- package/vendor/tree-sitter-dart/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
- package/vendor/tree-sitter-dart/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/vendor/tree-sitter-proto/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
- package/vendor/tree-sitter-proto/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +0 -60
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +0 -395
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.d.ts +0 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +0 -41
- package/dist/cli/rule-lab.d.ts +0 -38
- package/dist/cli/rule-lab.js +0 -148
- package/dist/cli/rule-lab.test.d.ts +0 -1
- package/dist/cli/rule-lab.test.js +0 -31
- package/dist/cli/scope-manifest-config.d.ts +0 -9
- package/dist/cli/scope-manifest-config.js +0 -37
- package/dist/cli/sync-manifest.d.ts +0 -27
- package/dist/cli/sync-manifest.js +0 -200
- package/dist/cli/sync-manifest.test.d.ts +0 -1
- package/dist/cli/sync-manifest.test.js +0 -88
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +0 -26
- package/dist/core/ingestion/unity-runtime-binding-rules.js +0 -408
- package/dist/rule-lab/analyze.d.ts +0 -13
- package/dist/rule-lab/analyze.js +0 -125
- package/dist/rule-lab/analyze.test.d.ts +0 -1
- package/dist/rule-lab/analyze.test.js +0 -246
- package/dist/rule-lab/compile.d.ts +0 -5
- package/dist/rule-lab/compile.js +0 -51
- package/dist/rule-lab/compiled-bundles.d.ts +0 -30
- package/dist/rule-lab/compiled-bundles.js +0 -36
- package/dist/rule-lab/curate.d.ts +0 -33
- package/dist/rule-lab/curate.js +0 -155
- package/dist/rule-lab/curate.test.d.ts +0 -1
- package/dist/rule-lab/curate.test.js +0 -137
- package/dist/rule-lab/curation-input-builder.d.ts +0 -45
- package/dist/rule-lab/curation-input-builder.js +0 -133
- package/dist/rule-lab/discover.d.ts +0 -13
- package/dist/rule-lab/discover.js +0 -74
- package/dist/rule-lab/discover.test.d.ts +0 -1
- package/dist/rule-lab/discover.test.js +0 -42
- package/dist/rule-lab/paths.d.ts +0 -21
- package/dist/rule-lab/paths.js +0 -37
- package/dist/rule-lab/paths.test.d.ts +0 -1
- package/dist/rule-lab/paths.test.js +0 -46
- package/dist/rule-lab/promote.d.ts +0 -26
- package/dist/rule-lab/promote.js +0 -387
- package/dist/rule-lab/promote.test.d.ts +0 -1
- package/dist/rule-lab/promote.test.js +0 -314
- package/dist/rule-lab/regress.d.ts +0 -60
- package/dist/rule-lab/regress.js +0 -122
- package/dist/rule-lab/regress.test.d.ts +0 -1
- package/dist/rule-lab/regress.test.js +0 -68
- package/dist/rule-lab/review-pack.d.ts +0 -34
- package/dist/rule-lab/review-pack.js +0 -165
- package/dist/rule-lab/review-pack.test.d.ts +0 -1
- package/dist/rule-lab/review-pack.test.js +0 -116
- package/dist/rule-lab/types.d.ts +0 -135
- package/dist/rule-lab/types.js +0 -1
- package/skills/_shared/unity-rule-authoring-contract.md +0 -64
- package/skills/gitnexus-unity-rule-gen.md +0 -107
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { curateRuleLabSlice } from './curate.js';
|
|
6
|
-
describe('rule-lab curate', () => {
|
|
7
|
-
it('rejects curation input with empty confirmed_chain.steps', async () => {
|
|
8
|
-
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-curate-'));
|
|
9
|
-
const inputPath = path.join(repoRoot, 'curation-input.json');
|
|
10
|
-
await fs.writeFile(inputPath, JSON.stringify({
|
|
11
|
-
run_id: 'run-x',
|
|
12
|
-
slice_id: 'slice-a',
|
|
13
|
-
curated: [
|
|
14
|
-
{
|
|
15
|
-
id: 'candidate-1',
|
|
16
|
-
title: 'reload rule',
|
|
17
|
-
match: { trigger_tokens: ['reload'] },
|
|
18
|
-
topology: [
|
|
19
|
-
{ hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
|
|
20
|
-
],
|
|
21
|
-
closure: {
|
|
22
|
-
required_hops: ['resource'],
|
|
23
|
-
failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
|
|
24
|
-
},
|
|
25
|
-
claims: {
|
|
26
|
-
guarantees: ['reload_chain_closed'],
|
|
27
|
-
non_guarantees: ['no_runtime_execution'],
|
|
28
|
-
next_action: 'gitnexus query "Reload"',
|
|
29
|
-
},
|
|
30
|
-
confirmed_chain: { steps: [] },
|
|
31
|
-
guarantees: ['can verify reload trigger'],
|
|
32
|
-
non_guarantees: ['does not prove runtime ordering'],
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
}), 'utf-8');
|
|
36
|
-
await expect(curateRuleLabSlice({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a', inputPath })).rejects.toThrow(/confirmed_chain\.steps/i);
|
|
37
|
-
await fs.rm(repoRoot, { recursive: true, force: true });
|
|
38
|
-
});
|
|
39
|
-
it('writes dsl-draft.json and rejects missing failure mapping', async () => {
|
|
40
|
-
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-curate-'));
|
|
41
|
-
const inputPath = path.join(repoRoot, 'curation-input.json');
|
|
42
|
-
await fs.writeFile(inputPath, JSON.stringify({
|
|
43
|
-
run_id: 'run-x',
|
|
44
|
-
slice_id: 'slice-a',
|
|
45
|
-
curated: [
|
|
46
|
-
{
|
|
47
|
-
id: 'candidate-1',
|
|
48
|
-
rule_id: 'demo.reload.v2',
|
|
49
|
-
match: { trigger_tokens: ['reload'] },
|
|
50
|
-
topology: [
|
|
51
|
-
{ hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
|
|
52
|
-
],
|
|
53
|
-
closure: { required_hops: ['resource'] },
|
|
54
|
-
claims: {
|
|
55
|
-
guarantees: ['reload_chain_closed'],
|
|
56
|
-
non_guarantees: ['no_runtime_execution'],
|
|
57
|
-
next_action: 'gitnexus query "Reload"',
|
|
58
|
-
},
|
|
59
|
-
confirmed_chain: {
|
|
60
|
-
steps: [
|
|
61
|
-
{ hop_type: 'resource', anchor: 'Assets/Example.prefab:1', snippet: 'ReloadGraph' },
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
guarantees: ['can verify reload trigger'],
|
|
65
|
-
non_guarantees: ['does not prove runtime ordering'],
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
}), 'utf-8');
|
|
69
|
-
await expect(curateRuleLabSlice({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a', inputPath })).rejects.toThrow(/failure_map/i);
|
|
70
|
-
await fs.rm(repoRoot, { recursive: true, force: true });
|
|
71
|
-
});
|
|
72
|
-
it('preserves multi-candidate curation and writes dsl-drafts with compatibility warning', async () => {
|
|
73
|
-
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-curate-'));
|
|
74
|
-
const inputPath = path.join(repoRoot, 'curation-input.json');
|
|
75
|
-
const curated = [
|
|
76
|
-
{
|
|
77
|
-
id: 'candidate-1',
|
|
78
|
-
rule_id: 'demo.rule.first.v1',
|
|
79
|
-
title: 'first rule',
|
|
80
|
-
match: {
|
|
81
|
-
trigger_tokens: ['reload'],
|
|
82
|
-
resource_types: ['syncvar_hook'],
|
|
83
|
-
host_base_type: ['network_behaviour'],
|
|
84
|
-
},
|
|
85
|
-
topology: [
|
|
86
|
-
{ hop: 'code_runtime', from: { entity: 'script' }, to: { entity: 'runtime' }, edge: { kind: 'calls' } },
|
|
87
|
-
],
|
|
88
|
-
closure: { required_hops: ['code_runtime'], failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' } },
|
|
89
|
-
claims: {
|
|
90
|
-
guarantees: ['reload_chain_closed'],
|
|
91
|
-
non_guarantees: ['no_runtime_execution'],
|
|
92
|
-
next_action: 'gitnexus query "reload"',
|
|
93
|
-
},
|
|
94
|
-
confirmed_chain: { steps: [{ hop_type: 'code_runtime', anchor: 'Assets/A.cs:1', snippet: 'A' }] },
|
|
95
|
-
guarantees: ['reload_chain_closed'],
|
|
96
|
-
non_guarantees: ['no_runtime_execution'],
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id: 'candidate-2',
|
|
100
|
-
rule_id: 'demo.rule.second.v1',
|
|
101
|
-
title: 'second rule',
|
|
102
|
-
match: {
|
|
103
|
-
trigger_tokens: ['reload'],
|
|
104
|
-
resource_types: ['syncvar_hook'],
|
|
105
|
-
host_base_type: ['network_behaviour'],
|
|
106
|
-
},
|
|
107
|
-
topology: [
|
|
108
|
-
{ hop: 'code_runtime', from: { entity: 'script' }, to: { entity: 'runtime' }, edge: { kind: 'calls' } },
|
|
109
|
-
],
|
|
110
|
-
closure: { required_hops: ['code_runtime'], failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' } },
|
|
111
|
-
claims: {
|
|
112
|
-
guarantees: ['reload_chain_closed'],
|
|
113
|
-
non_guarantees: ['no_runtime_execution'],
|
|
114
|
-
next_action: 'gitnexus query "reload"',
|
|
115
|
-
},
|
|
116
|
-
confirmed_chain: { steps: [{ hop_type: 'code_runtime', anchor: 'Assets/B.cs:2', snippet: 'B' }] },
|
|
117
|
-
guarantees: ['reload_chain_closed'],
|
|
118
|
-
non_guarantees: ['no_runtime_execution'],
|
|
119
|
-
},
|
|
120
|
-
];
|
|
121
|
-
await fs.writeFile(inputPath, JSON.stringify({ run_id: 'run-x', slice_id: 'slice-a', curated }, null, 2), 'utf-8');
|
|
122
|
-
const out = await curateRuleLabSlice({
|
|
123
|
-
repoPath: repoRoot,
|
|
124
|
-
runId: 'run-x',
|
|
125
|
-
sliceId: 'slice-a',
|
|
126
|
-
inputPath,
|
|
127
|
-
});
|
|
128
|
-
const baseDir = path.dirname(out.paths.curatedPath);
|
|
129
|
-
const curatedOut = JSON.parse(await fs.readFile(out.paths.curatedPath, 'utf-8'));
|
|
130
|
-
const drafts = JSON.parse(await fs.readFile(path.join(baseDir, 'dsl-drafts.json'), 'utf-8'));
|
|
131
|
-
const legacy = JSON.parse(await fs.readFile(path.join(baseDir, 'dsl-draft.json'), 'utf-8'));
|
|
132
|
-
expect(curatedOut.curated).toHaveLength(2);
|
|
133
|
-
expect(drafts.drafts).toHaveLength(2);
|
|
134
|
-
expect(legacy.compatibility_warning).toMatch(/multi-draft/i);
|
|
135
|
-
await fs.rm(repoRoot, { recursive: true, force: true });
|
|
136
|
-
});
|
|
137
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { RuleLabCandidate, RuleLabSlice, UnityResourceBinding } from './types.js';
|
|
2
|
-
interface CurationInputItem {
|
|
3
|
-
id: string;
|
|
4
|
-
rule_id: string;
|
|
5
|
-
title: string;
|
|
6
|
-
match: {
|
|
7
|
-
trigger_tokens: string[];
|
|
8
|
-
symbol_kind: string[];
|
|
9
|
-
module_scope: string[];
|
|
10
|
-
resource_types: string[];
|
|
11
|
-
host_base_type: string[];
|
|
12
|
-
};
|
|
13
|
-
topology: NonNullable<RuleLabCandidate['topology']>;
|
|
14
|
-
closure: {
|
|
15
|
-
required_hops: string[];
|
|
16
|
-
failure_map: Record<string, string>;
|
|
17
|
-
};
|
|
18
|
-
claims: {
|
|
19
|
-
guarantees: string[];
|
|
20
|
-
non_guarantees: string[];
|
|
21
|
-
next_action: string;
|
|
22
|
-
};
|
|
23
|
-
confirmed_chain: {
|
|
24
|
-
steps: Array<{
|
|
25
|
-
hop_type?: string;
|
|
26
|
-
anchor: string;
|
|
27
|
-
snippet: string;
|
|
28
|
-
}>;
|
|
29
|
-
};
|
|
30
|
-
guarantees: string[];
|
|
31
|
-
non_guarantees: string[];
|
|
32
|
-
resource_bindings: UnityResourceBinding[];
|
|
33
|
-
}
|
|
34
|
-
export interface CurationInputDocument {
|
|
35
|
-
run_id: string;
|
|
36
|
-
slice_id: string;
|
|
37
|
-
curated: CurationInputItem[];
|
|
38
|
-
}
|
|
39
|
-
export declare function buildCurationInput(input: {
|
|
40
|
-
runId: string;
|
|
41
|
-
sliceId: string;
|
|
42
|
-
slice: RuleLabSlice;
|
|
43
|
-
candidates: RuleLabCandidate[];
|
|
44
|
-
}): CurationInputDocument;
|
|
45
|
-
export {};
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
function unique(values) {
|
|
2
|
-
return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
|
|
3
|
-
}
|
|
4
|
-
function splitSymbol(symbol) {
|
|
5
|
-
const raw = String(symbol || '').trim();
|
|
6
|
-
const parts = raw.split('.');
|
|
7
|
-
if (parts.length < 2) {
|
|
8
|
-
return { className: '', methodName: '' };
|
|
9
|
-
}
|
|
10
|
-
const className = String(parts[parts.length - 2] || '').trim();
|
|
11
|
-
const methodName = String(parts[parts.length - 1] || '').trim();
|
|
12
|
-
return { className, methodName };
|
|
13
|
-
}
|
|
14
|
-
function assertResolvedBindingParts(parts, side, candidateId) {
|
|
15
|
-
const className = String(parts.className || '').trim();
|
|
16
|
-
const methodName = String(parts.methodName || '').trim();
|
|
17
|
-
if (!className || !methodName) {
|
|
18
|
-
throw new Error(`binding_unresolved: ${side} symbol unresolved for candidate ${candidateId}`);
|
|
19
|
-
}
|
|
20
|
-
if (/^unknown/i.test(className) || /^unknown/i.test(methodName)) {
|
|
21
|
-
throw new Error(`binding_unresolved: ${side} symbol contains unknown placeholder for candidate ${candidateId}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function assertResolvedSceneToken(value, candidateId) {
|
|
25
|
-
const token = String(value || '').trim();
|
|
26
|
-
if (!token) {
|
|
27
|
-
throw new Error(`binding_unresolved: scene token unresolved for candidate ${candidateId}`);
|
|
28
|
-
}
|
|
29
|
-
if (/^unknown/i.test(token)) {
|
|
30
|
-
throw new Error(`binding_unresolved: scene token contains unknown placeholder for candidate ${candidateId}`);
|
|
31
|
-
}
|
|
32
|
-
return token;
|
|
33
|
-
}
|
|
34
|
-
function buildBinding(candidate, pair) {
|
|
35
|
-
const source = splitSymbol(pair.source_anchor.symbol);
|
|
36
|
-
assertResolvedBindingParts(source, 'source', candidate.id);
|
|
37
|
-
const kind = candidate.binding_kind === 'method_triggers_scene_load'
|
|
38
|
-
? 'method_triggers_scene_load'
|
|
39
|
-
: 'method_triggers_method';
|
|
40
|
-
if (kind === 'method_triggers_scene_load') {
|
|
41
|
-
const sceneName = assertResolvedSceneToken(String(pair.target_anchor.symbol || pair.target_anchor.file || ''), candidate.id);
|
|
42
|
-
return [{
|
|
43
|
-
kind,
|
|
44
|
-
host_class_pattern: source.className,
|
|
45
|
-
loader_methods: [source.methodName],
|
|
46
|
-
scene_name: sceneName,
|
|
47
|
-
description: `Derived from exact pair ${String(pair.id || candidate.id)}`,
|
|
48
|
-
}];
|
|
49
|
-
}
|
|
50
|
-
const target = splitSymbol(pair.target_anchor.symbol);
|
|
51
|
-
assertResolvedBindingParts(target, 'target', candidate.id);
|
|
52
|
-
return [{
|
|
53
|
-
kind,
|
|
54
|
-
source_class_pattern: source.className,
|
|
55
|
-
source_method: source.methodName,
|
|
56
|
-
target_class_pattern: target.className,
|
|
57
|
-
target_method: target.methodName,
|
|
58
|
-
description: `Derived from exact pair ${String(pair.id || candidate.id)}`,
|
|
59
|
-
}];
|
|
60
|
-
}
|
|
61
|
-
function buildConfirmedChain(candidate, pair) {
|
|
62
|
-
const hops = (candidate.evidence?.hops || []).filter((hop) => String(hop.anchor || '').trim() && String(hop.snippet || '').trim());
|
|
63
|
-
if (hops.length > 0)
|
|
64
|
-
return hops;
|
|
65
|
-
const sourceAnchor = String(pair.source_anchor.file || '').trim();
|
|
66
|
-
const targetAnchor = String(pair.target_anchor.file || '').trim();
|
|
67
|
-
const sourceSnippet = String(pair.source_anchor.symbol || '').trim();
|
|
68
|
-
const targetSnippet = String(pair.target_anchor.symbol || '').trim();
|
|
69
|
-
const fallback = [
|
|
70
|
-
sourceAnchor ? {
|
|
71
|
-
hop_type: 'code_runtime',
|
|
72
|
-
anchor: `${sourceAnchor}:${Number(pair.source_anchor.line || 1)}`,
|
|
73
|
-
snippet: sourceSnippet || 'source',
|
|
74
|
-
} : undefined,
|
|
75
|
-
targetAnchor ? {
|
|
76
|
-
hop_type: 'code_runtime',
|
|
77
|
-
anchor: `${targetAnchor}:${Number(pair.target_anchor.line || 1)}`,
|
|
78
|
-
snippet: targetSnippet || 'target',
|
|
79
|
-
} : undefined,
|
|
80
|
-
].filter((item) => Boolean(item));
|
|
81
|
-
if (fallback.length === 0) {
|
|
82
|
-
throw new Error(`confirmed_chain_empty: no evidence or anchor fallback for candidate ${candidate.id}`);
|
|
83
|
-
}
|
|
84
|
-
return fallback;
|
|
85
|
-
}
|
|
86
|
-
export function buildCurationInput(input) {
|
|
87
|
-
const curated = input.candidates.map((candidate) => {
|
|
88
|
-
const pair = candidate.exact_pair;
|
|
89
|
-
if (!pair) {
|
|
90
|
-
throw new Error(`binding_unresolved: exact_pair missing for candidate ${candidate.id}`);
|
|
91
|
-
}
|
|
92
|
-
const requiredHops = unique((candidate.topology || []).map((hop) => hop.hop));
|
|
93
|
-
const guaranteed = unique(candidate.claims?.guarantees || [`exact pair candidate: ${candidate.id}`]);
|
|
94
|
-
const nonGuaranteed = unique(candidate.claims?.non_guarantees || ['sparse gap path only']);
|
|
95
|
-
const bindings = buildBinding(candidate, pair);
|
|
96
|
-
const confirmedChain = buildConfirmedChain(candidate, pair);
|
|
97
|
-
return {
|
|
98
|
-
id: candidate.id,
|
|
99
|
-
rule_id: String(candidate.draft_rule_id || candidate.id),
|
|
100
|
-
title: String(candidate.title || ''),
|
|
101
|
-
match: {
|
|
102
|
-
trigger_tokens: [input.slice.trigger_family],
|
|
103
|
-
symbol_kind: ['method'],
|
|
104
|
-
module_scope: [input.slice.id],
|
|
105
|
-
resource_types: [...input.slice.resource_types],
|
|
106
|
-
host_base_type: [...input.slice.host_base_type],
|
|
107
|
-
},
|
|
108
|
-
topology: candidate.topology || [],
|
|
109
|
-
closure: {
|
|
110
|
-
required_hops: requiredHops.length > 0 ? requiredHops : ['code_runtime'],
|
|
111
|
-
failure_map: {
|
|
112
|
-
missing_evidence: 'rule_matched_but_evidence_missing',
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
claims: {
|
|
116
|
-
guarantees: guaranteed,
|
|
117
|
-
non_guarantees: nonGuaranteed,
|
|
118
|
-
next_action: String(candidate.claims?.next_action || `gitnexus query "${input.slice.trigger_family}"`),
|
|
119
|
-
},
|
|
120
|
-
confirmed_chain: {
|
|
121
|
-
steps: confirmedChain,
|
|
122
|
-
},
|
|
123
|
-
guarantees: guaranteed,
|
|
124
|
-
non_guarantees: nonGuaranteed,
|
|
125
|
-
resource_bindings: bindings,
|
|
126
|
-
};
|
|
127
|
-
});
|
|
128
|
-
return {
|
|
129
|
-
run_id: input.runId,
|
|
130
|
-
slice_id: input.sliceId,
|
|
131
|
-
curated,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { getRuleLabPaths } from './paths.js';
|
|
2
|
-
import type { RuleLabManifest, RuleLabScope } from './types.js';
|
|
3
|
-
export interface DiscoverInput {
|
|
4
|
-
repoPath: string;
|
|
5
|
-
scope: RuleLabScope;
|
|
6
|
-
seed?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface DiscoverOutput {
|
|
9
|
-
runId: string;
|
|
10
|
-
manifest: RuleLabManifest;
|
|
11
|
-
paths: ReturnType<typeof getRuleLabPaths>;
|
|
12
|
-
}
|
|
13
|
-
export declare function discoverRuleLabRun(input: DiscoverInput): Promise<DiscoverOutput>;
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { loadRuleRegistry } from '../mcp/local/runtime-claim-rule-registry.js';
|
|
5
|
-
import { buildRunId, getRuleLabPaths } from './paths.js';
|
|
6
|
-
import { loadCompiledRuleBundle } from './compiled-bundles.js';
|
|
7
|
-
function buildSliceId(rule) {
|
|
8
|
-
const hash = createHash('sha1')
|
|
9
|
-
.update(JSON.stringify({
|
|
10
|
-
id: rule.id,
|
|
11
|
-
trigger_family: rule.trigger_family,
|
|
12
|
-
resource_types: [...rule.resource_types].sort(),
|
|
13
|
-
host_base_type: [...rule.host_base_type].sort(),
|
|
14
|
-
required_hops: [...(rule.required_hops || [])].sort(),
|
|
15
|
-
}))
|
|
16
|
-
.digest('hex')
|
|
17
|
-
.slice(0, 10);
|
|
18
|
-
return `slice-${hash}`;
|
|
19
|
-
}
|
|
20
|
-
async function writeJson(filePath, value) {
|
|
21
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
22
|
-
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
|
|
23
|
-
}
|
|
24
|
-
export async function discoverRuleLabRun(input) {
|
|
25
|
-
const normalizedRepoPath = path.resolve(input.repoPath);
|
|
26
|
-
const analyzeBundle = await loadCompiledRuleBundle(normalizedRepoPath, 'analyze_rules');
|
|
27
|
-
const registry = analyzeBundle ? undefined : await loadRuleRegistry(normalizedRepoPath);
|
|
28
|
-
const runId = buildRunId({
|
|
29
|
-
repo: path.basename(normalizedRepoPath),
|
|
30
|
-
scope: input.scope,
|
|
31
|
-
seed: input.seed || 'default',
|
|
32
|
-
});
|
|
33
|
-
const runPaths = getRuleLabPaths(normalizedRepoPath, runId);
|
|
34
|
-
const sourceRules = analyzeBundle?.rules || registry?.activeRules || [];
|
|
35
|
-
const slices = sourceRules.map((rule) => ({
|
|
36
|
-
id: buildSliceId(rule),
|
|
37
|
-
trigger_family: rule.trigger_family,
|
|
38
|
-
resource_types: rule.resource_types,
|
|
39
|
-
host_base_type: rule.host_base_type,
|
|
40
|
-
required_hops: rule.required_hops,
|
|
41
|
-
}));
|
|
42
|
-
const manifest = {
|
|
43
|
-
run_id: runId,
|
|
44
|
-
repo_path: normalizedRepoPath,
|
|
45
|
-
scope: input.scope,
|
|
46
|
-
generated_at: new Date().toISOString(),
|
|
47
|
-
slices,
|
|
48
|
-
stages: ['discover'],
|
|
49
|
-
next_actions: [
|
|
50
|
-
`gitnexus rule-lab analyze --run-id ${runId}`,
|
|
51
|
-
`gitnexus rule-lab review-pack --run-id ${runId}`,
|
|
52
|
-
],
|
|
53
|
-
};
|
|
54
|
-
await writeJson(runPaths.manifestPath, manifest);
|
|
55
|
-
await writeJson(path.join(runPaths.runRoot, 'slice-plan.json'), {
|
|
56
|
-
run_id: runId,
|
|
57
|
-
generated_at: manifest.generated_at,
|
|
58
|
-
slices: slices.map((slice) => ({
|
|
59
|
-
slice_id: slice.id,
|
|
60
|
-
trigger_family: slice.trigger_family,
|
|
61
|
-
required_hops: slice.required_hops || [],
|
|
62
|
-
candidate_count_target: 2,
|
|
63
|
-
})),
|
|
64
|
-
});
|
|
65
|
-
await Promise.all(slices.map(async (slice) => {
|
|
66
|
-
const slicePath = path.join(runPaths.slicesRoot, slice.id, 'slice.json');
|
|
67
|
-
await writeJson(slicePath, slice);
|
|
68
|
-
}));
|
|
69
|
-
return {
|
|
70
|
-
runId,
|
|
71
|
-
manifest,
|
|
72
|
-
paths: runPaths,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { discoverRuleLabRun } from './discover.js';
|
|
6
|
-
describe('rule-lab discover', () => {
|
|
7
|
-
it('writes manifest with slices and next_actions', async () => {
|
|
8
|
-
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-discover-'));
|
|
9
|
-
const rulesRoot = path.join(repoRoot, '.gitnexus', 'rules');
|
|
10
|
-
await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
|
|
11
|
-
await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({
|
|
12
|
-
rules: [
|
|
13
|
-
{
|
|
14
|
-
id: 'demo.reload.rule.v1',
|
|
15
|
-
version: '1.0.0',
|
|
16
|
-
file: 'approved/demo.reload.rule.v1.yaml',
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
}), 'utf-8');
|
|
20
|
-
await fs.writeFile(path.join(rulesRoot, 'approved', 'demo.reload.rule.v1.yaml'), [
|
|
21
|
-
'id: demo.reload.rule.v1',
|
|
22
|
-
'version: 1.0.0',
|
|
23
|
-
'trigger_family: reload',
|
|
24
|
-
'resource_types:',
|
|
25
|
-
' - prefab',
|
|
26
|
-
'host_base_type:',
|
|
27
|
-
' - ReloadBase',
|
|
28
|
-
].join('\n'), 'utf-8');
|
|
29
|
-
const out = await discoverRuleLabRun({ repoPath: repoRoot, scope: 'full' });
|
|
30
|
-
expect(out.manifest.slices.length).toBeGreaterThan(0);
|
|
31
|
-
expect(out.manifest.next_actions.join(' ')).toContain('rule-lab analyze');
|
|
32
|
-
const manifestOnDisk = JSON.parse(await fs.readFile(out.paths.manifestPath, 'utf-8'));
|
|
33
|
-
expect(manifestOnDisk.run_id).toBe(out.manifest.run_id);
|
|
34
|
-
expect(Array.isArray(manifestOnDisk.slices)).toBe(true);
|
|
35
|
-
const slicePlanPath = path.join(out.paths.runRoot, 'slice-plan.json');
|
|
36
|
-
const slicePlan = JSON.parse(await fs.readFile(slicePlanPath, 'utf-8'));
|
|
37
|
-
expect(slicePlan.run_id).toBe(out.manifest.run_id);
|
|
38
|
-
expect(Array.isArray(slicePlan.slices)).toBe(true);
|
|
39
|
-
expect(slicePlan.slices[0]).toHaveProperty('candidate_count_target');
|
|
40
|
-
await fs.rm(repoRoot, { recursive: true, force: true });
|
|
41
|
-
});
|
|
42
|
-
});
|
package/dist/rule-lab/paths.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { RuleLabScope } from './types.js';
|
|
2
|
-
export interface BuildRunIdInput {
|
|
3
|
-
repo: string;
|
|
4
|
-
scope: RuleLabScope;
|
|
5
|
-
seed: string;
|
|
6
|
-
}
|
|
7
|
-
export interface RuleLabPaths {
|
|
8
|
-
rulesRoot: string;
|
|
9
|
-
compiledRoot: string;
|
|
10
|
-
runsRoot: string;
|
|
11
|
-
runRoot: string;
|
|
12
|
-
slicesRoot: string;
|
|
13
|
-
manifestPath: string;
|
|
14
|
-
candidatesPath: string;
|
|
15
|
-
reviewCardsPath: string;
|
|
16
|
-
curatedPath: string;
|
|
17
|
-
promotedRoot: string;
|
|
18
|
-
reportsRoot: string;
|
|
19
|
-
}
|
|
20
|
-
export declare function buildRunId(input: BuildRunIdInput): string;
|
|
21
|
-
export declare function getRuleLabPaths(repoPath: string, runId: string, sliceId?: string): RuleLabPaths;
|
package/dist/rule-lab/paths.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
function normalizeIdPart(value) {
|
|
4
|
-
return String(value || '')
|
|
5
|
-
.trim()
|
|
6
|
-
.toLowerCase()
|
|
7
|
-
.replace(/[^a-z0-9._-]+/g, '-')
|
|
8
|
-
.replace(/^-+|-+$/g, '') || 'unknown';
|
|
9
|
-
}
|
|
10
|
-
export function buildRunId(input) {
|
|
11
|
-
return createHash('sha1')
|
|
12
|
-
.update(`${normalizeIdPart(input.repo)}:${input.scope}:${input.seed}`)
|
|
13
|
-
.digest('hex')
|
|
14
|
-
.slice(0, 12);
|
|
15
|
-
}
|
|
16
|
-
export function getRuleLabPaths(repoPath, runId, sliceId = 'default') {
|
|
17
|
-
const normalizedRepoPath = path.resolve(repoPath);
|
|
18
|
-
const rulesRoot = path.join(normalizedRepoPath, '.gitnexus', 'rules');
|
|
19
|
-
const compiledRoot = path.join(rulesRoot, 'compiled');
|
|
20
|
-
const runsRoot = path.join(rulesRoot, 'lab', 'runs');
|
|
21
|
-
const runRoot = path.join(runsRoot, normalizeIdPart(runId));
|
|
22
|
-
const slicesRoot = path.join(runRoot, 'slices');
|
|
23
|
-
const sliceRoot = path.join(slicesRoot, normalizeIdPart(sliceId));
|
|
24
|
-
return {
|
|
25
|
-
rulesRoot,
|
|
26
|
-
compiledRoot,
|
|
27
|
-
runsRoot,
|
|
28
|
-
runRoot,
|
|
29
|
-
slicesRoot,
|
|
30
|
-
manifestPath: path.join(runRoot, 'manifest.json'),
|
|
31
|
-
candidatesPath: path.join(sliceRoot, 'candidates.jsonl'),
|
|
32
|
-
reviewCardsPath: path.join(sliceRoot, 'review-cards.md'),
|
|
33
|
-
curatedPath: path.join(sliceRoot, 'curated.json'),
|
|
34
|
-
promotedRoot: path.join(rulesRoot, 'approved'),
|
|
35
|
-
reportsRoot: path.join(rulesRoot, 'reports'),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import { buildRunId, getRuleLabPaths } from './paths.js';
|
|
5
|
-
const TEST_DIR = path.dirname(new URL(import.meta.url).pathname);
|
|
6
|
-
describe('rule-lab paths', () => {
|
|
7
|
-
it('builds deterministic run/slice paths under .gitnexus/rules/lab/runs', () => {
|
|
8
|
-
const runId = buildRunId({ repo: 'GitNexus', scope: 'full', seed: 'abc' });
|
|
9
|
-
const p = getRuleLabPaths('/repo', runId, 'slice-a');
|
|
10
|
-
expect(p.manifestPath).toContain('/.gitnexus/rules/lab/runs/');
|
|
11
|
-
expect(p.candidatesPath).toContain('/slices/slice-a/candidates.jsonl');
|
|
12
|
-
});
|
|
13
|
-
it('exposes DSL v2 topology and closure schema fields', async () => {
|
|
14
|
-
const sample = {
|
|
15
|
-
id: 'demo.reload.v2',
|
|
16
|
-
match: { trigger_tokens: ['reload'] },
|
|
17
|
-
topology: [
|
|
18
|
-
{
|
|
19
|
-
hop: 'resource',
|
|
20
|
-
from: { entity: 'resource' },
|
|
21
|
-
to: { entity: 'script' },
|
|
22
|
-
edge: { kind: 'binds_script' },
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
closure: {
|
|
26
|
-
required_hops: ['resource'],
|
|
27
|
-
failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
|
|
28
|
-
},
|
|
29
|
-
claims: {
|
|
30
|
-
guarantees: ['g1'],
|
|
31
|
-
non_guarantees: ['ng1'],
|
|
32
|
-
next_action: 'gitnexus query "Reload"',
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
expect(sample.topology[0].edge.kind).toBe('binds_script');
|
|
36
|
-
const ruleDslSchema = JSON.parse(await fs.readFile(path.join(TEST_DIR, 'schema', 'rule-dsl.schema.json'), 'utf-8'));
|
|
37
|
-
const draftSchema = JSON.parse(await fs.readFile(path.join(TEST_DIR, 'schema', 'dsl-draft.schema.json'), 'utf-8'));
|
|
38
|
-
expect(ruleDslSchema.properties).toHaveProperty('match');
|
|
39
|
-
expect(ruleDslSchema.properties).toHaveProperty('topology');
|
|
40
|
-
expect(ruleDslSchema.properties).toHaveProperty('closure');
|
|
41
|
-
expect(ruleDslSchema.properties).toHaveProperty('claims');
|
|
42
|
-
expect(draftSchema.properties).toHaveProperty('topology');
|
|
43
|
-
expect(draftSchema.properties).toHaveProperty('closure');
|
|
44
|
-
expect(draftSchema.properties).toHaveProperty('claims');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { getRuleLabPaths } from './paths.js';
|
|
2
|
-
interface CatalogEntry {
|
|
3
|
-
id: string;
|
|
4
|
-
version: string;
|
|
5
|
-
enabled: boolean;
|
|
6
|
-
file: string;
|
|
7
|
-
family?: string;
|
|
8
|
-
}
|
|
9
|
-
interface CatalogShape {
|
|
10
|
-
version: number;
|
|
11
|
-
rules: CatalogEntry[];
|
|
12
|
-
}
|
|
13
|
-
export interface PromoteInput {
|
|
14
|
-
repoPath: string;
|
|
15
|
-
runId: string;
|
|
16
|
-
sliceId: string;
|
|
17
|
-
version?: string;
|
|
18
|
-
}
|
|
19
|
-
export interface PromoteOutput {
|
|
20
|
-
catalog: CatalogShape;
|
|
21
|
-
promotedFiles: string[];
|
|
22
|
-
compiledPaths: Record<'analyze_rules' | 'retrieval_rules' | 'verification_rules', string>;
|
|
23
|
-
paths: ReturnType<typeof getRuleLabPaths>;
|
|
24
|
-
}
|
|
25
|
-
export declare function promoteCuratedRules(input: PromoteInput): Promise<PromoteOutput>;
|
|
26
|
-
export {};
|