@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.
Files changed (104) hide show
  1. package/dist/benchmark/analyze-runner.d.ts +0 -2
  2. package/dist/benchmark/analyze-runner.js +0 -6
  3. package/dist/benchmark/analyze-runner.test.js +1 -10
  4. package/dist/benchmark/runner.d.ts +0 -2
  5. package/dist/benchmark/runner.js +0 -2
  6. package/dist/benchmark/u2-e2e/neonspark-full-e2e.js +0 -11
  7. package/dist/benchmark/u2-performance-sampler.js +3 -16
  8. package/dist/cli/ai-context.js +1 -7
  9. package/dist/cli/analyze-options.d.ts +19 -6
  10. package/dist/cli/analyze-options.js +76 -71
  11. package/dist/cli/analyze-options.test.js +78 -73
  12. package/dist/cli/analyze-runtime-summary.js +0 -1
  13. package/dist/cli/analyze-runtime-summary.test.js +0 -2
  14. package/dist/cli/analyze-summary.d.ts +0 -2
  15. package/dist/cli/analyze-summary.js +0 -24
  16. package/dist/cli/analyze-summary.test.js +1 -65
  17. package/dist/cli/analyze.d.ts +2 -4
  18. package/dist/cli/analyze.js +14 -30
  19. package/dist/cli/analyze.test.js +9 -15
  20. package/dist/cli/benchmark-agent-context.d.ts +0 -2
  21. package/dist/cli/benchmark-agent-context.js +0 -2
  22. package/dist/cli/benchmark-agent-safe-query-context.d.ts +0 -2
  23. package/dist/cli/benchmark-agent-safe-query-context.js +0 -2
  24. package/dist/cli/benchmark-unity.d.ts +0 -2
  25. package/dist/cli/benchmark-unity.js +0 -2
  26. package/dist/cli/clean.d.ts +2 -3
  27. package/dist/cli/clean.js +4 -25
  28. package/dist/cli/index.js +1 -12
  29. package/dist/core/ingestion/pipeline.js +1 -44
  30. package/dist/mcp/local/agent-safe-response.js +1 -1
  31. package/dist/mcp/local/local-backend.d.ts +0 -23
  32. package/dist/mcp/local/local-backend.js +69 -248
  33. package/dist/mcp/local/runtime-chain-verify.test.js +0 -49
  34. package/dist/mcp/local/runtime-claim-rule-registry.d.ts +0 -11
  35. package/dist/mcp/local/runtime-claim-rule-registry.js +0 -159
  36. package/dist/mcp/local/runtime-claim-rule-registry.test.js +67 -214
  37. package/dist/mcp/tools.js +0 -70
  38. package/dist/storage/repo-manager.d.ts +1 -0
  39. package/dist/types/pipeline.d.ts +0 -3
  40. package/package.json +1 -1
  41. package/skills/gitnexus-cli.md +62 -38
  42. package/vendor/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
  43. package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
  44. package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
  45. package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +122 -0
  46. package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
  47. package/vendor/tree-sitter-dart/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  48. package/vendor/tree-sitter-dart/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  49. package/vendor/tree-sitter-proto/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  50. package/vendor/tree-sitter-proto/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  51. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +0 -60
  52. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +0 -395
  53. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.d.ts +0 -1
  54. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +0 -41
  55. package/dist/cli/rule-lab.d.ts +0 -38
  56. package/dist/cli/rule-lab.js +0 -148
  57. package/dist/cli/rule-lab.test.d.ts +0 -1
  58. package/dist/cli/rule-lab.test.js +0 -31
  59. package/dist/cli/scope-manifest-config.d.ts +0 -9
  60. package/dist/cli/scope-manifest-config.js +0 -37
  61. package/dist/cli/sync-manifest.d.ts +0 -27
  62. package/dist/cli/sync-manifest.js +0 -200
  63. package/dist/cli/sync-manifest.test.d.ts +0 -1
  64. package/dist/cli/sync-manifest.test.js +0 -88
  65. package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +0 -26
  66. package/dist/core/ingestion/unity-runtime-binding-rules.js +0 -408
  67. package/dist/rule-lab/analyze.d.ts +0 -13
  68. package/dist/rule-lab/analyze.js +0 -125
  69. package/dist/rule-lab/analyze.test.d.ts +0 -1
  70. package/dist/rule-lab/analyze.test.js +0 -246
  71. package/dist/rule-lab/compile.d.ts +0 -5
  72. package/dist/rule-lab/compile.js +0 -51
  73. package/dist/rule-lab/compiled-bundles.d.ts +0 -30
  74. package/dist/rule-lab/compiled-bundles.js +0 -36
  75. package/dist/rule-lab/curate.d.ts +0 -33
  76. package/dist/rule-lab/curate.js +0 -155
  77. package/dist/rule-lab/curate.test.d.ts +0 -1
  78. package/dist/rule-lab/curate.test.js +0 -137
  79. package/dist/rule-lab/curation-input-builder.d.ts +0 -45
  80. package/dist/rule-lab/curation-input-builder.js +0 -133
  81. package/dist/rule-lab/discover.d.ts +0 -13
  82. package/dist/rule-lab/discover.js +0 -74
  83. package/dist/rule-lab/discover.test.d.ts +0 -1
  84. package/dist/rule-lab/discover.test.js +0 -42
  85. package/dist/rule-lab/paths.d.ts +0 -21
  86. package/dist/rule-lab/paths.js +0 -37
  87. package/dist/rule-lab/paths.test.d.ts +0 -1
  88. package/dist/rule-lab/paths.test.js +0 -46
  89. package/dist/rule-lab/promote.d.ts +0 -26
  90. package/dist/rule-lab/promote.js +0 -387
  91. package/dist/rule-lab/promote.test.d.ts +0 -1
  92. package/dist/rule-lab/promote.test.js +0 -314
  93. package/dist/rule-lab/regress.d.ts +0 -60
  94. package/dist/rule-lab/regress.js +0 -122
  95. package/dist/rule-lab/regress.test.d.ts +0 -1
  96. package/dist/rule-lab/regress.test.js +0 -68
  97. package/dist/rule-lab/review-pack.d.ts +0 -34
  98. package/dist/rule-lab/review-pack.js +0 -165
  99. package/dist/rule-lab/review-pack.test.d.ts +0 -1
  100. package/dist/rule-lab/review-pack.test.js +0 -116
  101. package/dist/rule-lab/types.d.ts +0 -135
  102. package/dist/rule-lab/types.js +0 -1
  103. package/skills/_shared/unity-rule-authoring-contract.md +0 -64
  104. package/skills/gitnexus-unity-rule-gen.md +0 -107
@@ -1,314 +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 { promoteCuratedRules } from './promote.js';
6
- import { loadRuleRegistry } from '../mcp/local/runtime-claim-rule-registry.js';
7
- describe('rule-lab promote', () => {
8
- it('promotes curated candidate into approved yaml and catalog entry', async () => {
9
- const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-promote-'));
10
- const rulesRoot = path.join(repoRoot, '.gitnexus', 'rules');
11
- const sliceDir = path.join(rulesRoot, 'lab', 'runs', 'run-x', 'slices', 'slice-a');
12
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
13
- await fs.mkdir(sliceDir, { recursive: true });
14
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({ version: 1, rules: [] }, null, 2), 'utf-8');
15
- await fs.writeFile(path.join(sliceDir, 'curated.json'), JSON.stringify({
16
- run_id: 'run-x',
17
- slice_id: 'slice-a',
18
- curated: [
19
- {
20
- id: 'candidate-1',
21
- rule_id: 'demo.rule.v1',
22
- title: 'demo rule',
23
- match: { trigger_tokens: ['reload'] },
24
- topology: [
25
- { hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
26
- ],
27
- closure: {
28
- required_hops: ['resource'],
29
- failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
30
- },
31
- claims: {
32
- guarantees: ['can verify startup graph trigger'],
33
- non_guarantees: ['does not prove all runtime states'],
34
- next_action: 'gitnexus query "reload"',
35
- },
36
- confirmed_chain: {
37
- steps: [{ hop_type: 'resource', anchor: 'Assets/Demo.prefab:12', snippet: 'Reload' }],
38
- },
39
- guarantees: ['can verify startup graph trigger'],
40
- non_guarantees: ['does not prove all runtime states'],
41
- },
42
- ],
43
- }, null, 2), 'utf-8');
44
- const out = await promoteCuratedRules({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a' });
45
- expect(out.catalog.rules.some((r) => r.id === 'demo.rule.v1')).toBe(true);
46
- expect(out.promotedFiles[0]).toMatch(/rules\/approved\/.*\.yaml$/);
47
- await fs.rm(repoRoot, { recursive: true, force: true });
48
- });
49
- it('emits stage-aware compiled bundles for analyze, retrieval, and verification', async () => {
50
- const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-promote-'));
51
- const rulesRoot = path.join(repoRoot, '.gitnexus', 'rules');
52
- const sliceDir = path.join(rulesRoot, 'lab', 'runs', 'run-x', 'slices', 'slice-a');
53
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
54
- await fs.mkdir(sliceDir, { recursive: true });
55
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({ version: 1, rules: [] }, null, 2), 'utf-8');
56
- await fs.writeFile(path.join(sliceDir, 'curated.json'), JSON.stringify({
57
- run_id: 'run-x',
58
- slice_id: 'slice-a',
59
- curated: [
60
- {
61
- id: 'candidate-1',
62
- rule_id: 'demo.rule.v2',
63
- title: 'demo rule',
64
- match: {
65
- trigger_tokens: ['reload'],
66
- resource_types: ['asset'],
67
- host_base_type: ['ReloadBase'],
68
- },
69
- topology: [
70
- { hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
71
- { hop: 'code_runtime', from: { entity: 'script' }, to: { entity: 'runtime' }, edge: { kind: 'calls' } },
72
- ],
73
- closure: {
74
- required_hops: ['resource', 'code_runtime'],
75
- failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
76
- },
77
- claims: {
78
- guarantees: ['reload_chain_closed'],
79
- non_guarantees: ['no_runtime_execution'],
80
- next_action: 'gitnexus query "reload"',
81
- },
82
- confirmed_chain: {
83
- steps: [{ hop_type: 'resource', anchor: 'Assets/Demo.prefab:12', snippet: 'Reload' }],
84
- },
85
- guarantees: ['reload_chain_closed'],
86
- non_guarantees: ['no_runtime_execution'],
87
- },
88
- ],
89
- }, null, 2), 'utf-8');
90
- const out = await promoteCuratedRules({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a', version: '2.0.0' });
91
- expect(out).toHaveProperty('compiledPaths');
92
- const analyzeBundlePath = path.join(rulesRoot, 'compiled', 'analyze_rules.v2.json');
93
- const retrievalBundlePath = path.join(rulesRoot, 'compiled', 'retrieval_rules.v2.json');
94
- const verificationBundlePath = path.join(rulesRoot, 'compiled', 'verification_rules.v2.json');
95
- const analyzeBundle = JSON.parse(await fs.readFile(analyzeBundlePath, 'utf-8'));
96
- const retrievalBundle = JSON.parse(await fs.readFile(retrievalBundlePath, 'utf-8'));
97
- const verificationBundle = JSON.parse(await fs.readFile(verificationBundlePath, 'utf-8'));
98
- expect(analyzeBundle.family).toBe('analyze_rules');
99
- expect(retrievalBundle.family).toBe('retrieval_rules');
100
- expect(verificationBundle.family).toBe('verification_rules');
101
- expect(analyzeBundle.rules[0].id).toBe('demo.rule.v2');
102
- expect(retrievalBundle.rules[0].claims.next_action).toBe('gitnexus query "reload"');
103
- expect(verificationBundle.rules[0].closure.required_hops).toEqual(['resource', 'code_runtime']);
104
- await fs.rm(repoRoot, { recursive: true, force: true });
105
- });
106
- it('rejects promote when resource_types or host_base_type are unknown', async () => {
107
- const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-promote-'));
108
- const rulesRoot = path.join(repoRoot, '.gitnexus', 'rules');
109
- const sliceDir = path.join(rulesRoot, 'lab', 'runs', 'run-x', 'slices', 'slice-a');
110
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
111
- await fs.mkdir(sliceDir, { recursive: true });
112
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({ version: 1, rules: [] }, null, 2), 'utf-8');
113
- await fs.writeFile(path.join(sliceDir, 'curated.json'), JSON.stringify({
114
- run_id: 'run-x',
115
- slice_id: 'slice-a',
116
- curated: [
117
- {
118
- id: 'candidate-unknown',
119
- rule_id: 'demo.rule.v2',
120
- match: { trigger_tokens: ['reload'] },
121
- topology: [
122
- { hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
123
- ],
124
- closure: {
125
- required_hops: ['resource'],
126
- failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
127
- },
128
- claims: {
129
- guarantees: ['reload_chain_closed'],
130
- non_guarantees: ['no_runtime_execution'],
131
- next_action: 'gitnexus query "reload"',
132
- },
133
- confirmed_chain: {
134
- steps: [{ hop_type: 'resource', anchor: 'Assets/Demo.prefab:9', snippet: 'Reload' }],
135
- },
136
- guarantees: ['reload_chain_closed'],
137
- non_guarantees: ['no_runtime_execution'],
138
- },
139
- ],
140
- }, null, 2), 'utf-8');
141
- await fs.writeFile(path.join(sliceDir, 'dsl-draft.json'), JSON.stringify({
142
- id: 'demo.rule.v2',
143
- version: '2.0.0',
144
- match: {
145
- trigger_tokens: ['reload'],
146
- resource_types: ['unknown'],
147
- host_base_type: ['unknown'],
148
- },
149
- topology: [
150
- { hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
151
- ],
152
- closure: {
153
- required_hops: ['resource'],
154
- failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
155
- },
156
- claims: {
157
- guarantees: ['reload_chain_closed'],
158
- non_guarantees: ['no_runtime_execution'],
159
- next_action: 'gitnexus query "reload"',
160
- },
161
- }, null, 2), 'utf-8');
162
- await expect(promoteCuratedRules({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a' })).rejects.toThrow(/unknown/i);
163
- await fs.rm(repoRoot, { recursive: true, force: true });
164
- });
165
- it('promotes every curated item when dsl-drafts includes multiple candidates', async () => {
166
- const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-promote-multi-'));
167
- const rulesRoot = path.join(repoRoot, '.gitnexus', 'rules');
168
- const sliceDir = path.join(rulesRoot, 'lab', 'runs', 'run-x', 'slices', 'slice-a');
169
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
170
- await fs.mkdir(sliceDir, { recursive: true });
171
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({ version: 1, rules: [] }, null, 2), 'utf-8');
172
- const curated = {
173
- run_id: 'run-x',
174
- slice_id: 'slice-a',
175
- curated: [
176
- {
177
- id: 'candidate-1',
178
- rule_id: 'demo.multi.first.v1',
179
- title: 'demo first',
180
- match: { trigger_tokens: ['reload'], resource_types: ['syncvar_hook'], host_base_type: ['network_behaviour'] },
181
- topology: [{ hop: 'code_runtime', from: { entity: 'script' }, to: { entity: 'runtime' }, edge: { kind: 'calls' } }],
182
- closure: { required_hops: ['code_runtime'], failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' } },
183
- claims: { guarantees: ['reload_chain_closed'], non_guarantees: ['no_runtime_execution'], next_action: 'gitnexus query "reload"' },
184
- confirmed_chain: { steps: [{ hop_type: 'code_runtime', anchor: 'Assets/A.cs:1', snippet: 'A' }] },
185
- guarantees: ['reload_chain_closed'],
186
- non_guarantees: ['no_runtime_execution'],
187
- },
188
- {
189
- id: 'candidate-2',
190
- rule_id: 'demo.multi.second.v1',
191
- title: 'demo second',
192
- match: { trigger_tokens: ['reload'], resource_types: ['syncvar_hook'], host_base_type: ['network_behaviour'] },
193
- topology: [{ hop: 'code_runtime', from: { entity: 'script' }, to: { entity: 'runtime' }, edge: { kind: 'calls' } }],
194
- closure: { required_hops: ['code_runtime'], failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' } },
195
- claims: { guarantees: ['reload_chain_closed'], non_guarantees: ['no_runtime_execution'], next_action: 'gitnexus query "reload"' },
196
- confirmed_chain: { steps: [{ hop_type: 'code_runtime', anchor: 'Assets/B.cs:2', snippet: 'B' }] },
197
- guarantees: ['reload_chain_closed'],
198
- non_guarantees: ['no_runtime_execution'],
199
- },
200
- ],
201
- };
202
- await fs.writeFile(path.join(sliceDir, 'curated.json'), JSON.stringify(curated, null, 2), 'utf-8');
203
- await fs.writeFile(path.join(sliceDir, 'dsl-drafts.json'), JSON.stringify({
204
- drafts: [
205
- {
206
- id: 'demo.multi.first.v1',
207
- version: '2.0.0',
208
- match: curated.curated[0].match,
209
- topology: curated.curated[0].topology,
210
- closure: curated.curated[0].closure,
211
- claims: curated.curated[0].claims,
212
- },
213
- {
214
- id: 'demo.multi.second.v1',
215
- version: '2.0.0',
216
- match: curated.curated[1].match,
217
- topology: curated.curated[1].topology,
218
- closure: curated.curated[1].closure,
219
- claims: curated.curated[1].claims,
220
- },
221
- ],
222
- }, null, 2), 'utf-8');
223
- await fs.writeFile(path.join(sliceDir, 'dsl-draft.json'), JSON.stringify({
224
- compatibility_warning: 'multi-draft compatibility alias',
225
- primary_draft_id: 'demo.multi.first.v1',
226
- }, null, 2), 'utf-8');
227
- const out = await promoteCuratedRules({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a' });
228
- expect(out.promotedFiles).toHaveLength(2);
229
- await fs.rm(repoRoot, { recursive: true, force: true });
230
- });
231
- it('writes all binding fields into yaml and keeps them parseable via loadRuleRegistry', async () => {
232
- const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'rule-lab-promote-bindings-roundtrip-'));
233
- const rulesRoot = path.join(repoRoot, '.gitnexus', 'rules');
234
- const sliceDir = path.join(rulesRoot, 'lab', 'runs', 'run-x', 'slices', 'slice-a');
235
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
236
- await fs.mkdir(sliceDir, { recursive: true });
237
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({ version: 1, rules: [] }, null, 2), 'utf-8');
238
- await fs.writeFile(path.join(sliceDir, 'curated.json'), JSON.stringify({
239
- run_id: 'run-x',
240
- slice_id: 'slice-a',
241
- curated: [
242
- {
243
- id: 'candidate-1',
244
- rule_id: 'demo.bindings.roundtrip.v1',
245
- title: 'demo bindings',
246
- match: {
247
- trigger_tokens: ['reload'],
248
- resource_types: ['syncvar_hook'],
249
- host_base_type: ['network_behaviour'],
250
- },
251
- topology: [
252
- { hop: 'code_runtime', from: { entity: 'script' }, to: { entity: 'runtime' }, edge: { kind: 'calls' } },
253
- ],
254
- closure: {
255
- required_hops: ['code_runtime'],
256
- failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
257
- },
258
- claims: {
259
- guarantees: ['binding_fields_roundtrip'],
260
- non_guarantees: ['no_runtime_execution'],
261
- next_action: 'gitnexus query "reload"',
262
- },
263
- confirmed_chain: {
264
- steps: [{ hop_type: 'code_runtime', anchor: 'Assets/Demo.cs:12', snippet: 'Demo.Trigger' }],
265
- },
266
- guarantees: ['binding_fields_roundtrip'],
267
- non_guarantees: ['no_runtime_execution'],
268
- resource_bindings: [
269
- {
270
- kind: 'method_triggers_scene_load',
271
- host_class_pattern: 'BattleController',
272
- loader_methods: ['EnterBattle'],
273
- scene_name: 'BattleScene',
274
- },
275
- {
276
- kind: 'method_triggers_method',
277
- source_class_pattern: 'SourceClass',
278
- source_method: 'Emit',
279
- target_class_pattern: 'TargetClass',
280
- target_method: 'Handle',
281
- },
282
- ],
283
- },
284
- ],
285
- }, null, 2), 'utf-8');
286
- const out = await promoteCuratedRules({ repoPath: repoRoot, runId: 'run-x', sliceId: 'slice-a', version: '1.0.0' });
287
- const yaml = await fs.readFile(out.promotedFiles[0], 'utf-8');
288
- expect(yaml).toContain('scene_name: BattleScene');
289
- expect(yaml).toContain('host_class_pattern: BattleController');
290
- expect(yaml).toContain('loader_methods:');
291
- expect(yaml).toContain('- EnterBattle');
292
- expect(yaml).toContain('target_class_pattern: TargetClass');
293
- expect(yaml).toContain('target_method: Handle');
294
- await fs.rm(path.join(rulesRoot, 'compiled'), { recursive: true, force: true });
295
- const registry = await loadRuleRegistry(repoRoot);
296
- const rule = registry.activeRules.find((item) => item.id === 'demo.bindings.roundtrip.v1');
297
- expect(rule).toBeTruthy();
298
- expect(rule?.resource_bindings).toBeDefined();
299
- expect(rule?.resource_bindings?.[0].kind).toBe('method_triggers_scene_load');
300
- expect(rule?.resource_bindings?.[0].host_class_pattern).toBe('BattleController');
301
- expect(rule?.resource_bindings?.[0].loader_methods).toEqual(['EnterBattle']);
302
- expect(rule?.resource_bindings?.[0].scene_name).toBe('BattleScene');
303
- expect(rule?.resource_bindings?.[0].source_class_pattern).toBeUndefined();
304
- expect(rule?.resource_bindings?.[0].source_method).toBeUndefined();
305
- expect(rule?.resource_bindings?.[0].target_class_pattern).toBeUndefined();
306
- expect(rule?.resource_bindings?.[0].target_method).toBeUndefined();
307
- expect(rule?.resource_bindings?.[1].kind).toBe('method_triggers_method');
308
- expect(rule?.resource_bindings?.[1].source_class_pattern).toBe('SourceClass');
309
- expect(rule?.resource_bindings?.[1].source_method).toBe('Emit');
310
- expect(rule?.resource_bindings?.[1].target_class_pattern).toBe('TargetClass');
311
- expect(rule?.resource_bindings?.[1].target_method).toBe('Handle');
312
- await fs.rm(repoRoot, { recursive: true, force: true });
313
- });
314
- });
@@ -1,60 +0,0 @@
1
- export interface RuleLabRegressInput {
2
- precision: number;
3
- coverage: number;
4
- probes?: Array<{
5
- id: string;
6
- pass: boolean;
7
- replay_command: string;
8
- bucket?: 'anchor' | 'holdout' | 'negative';
9
- key_resource_hit?: boolean;
10
- next_hop_usable?: boolean;
11
- hint_drift?: boolean;
12
- false_positive_anchor_leak?: boolean;
13
- }>;
14
- repoPath?: string;
15
- runId?: string;
16
- }
17
- export interface RuleLabRegressOutput {
18
- pass: boolean;
19
- failures: string[];
20
- metrics: {
21
- precision: number;
22
- coverage: number;
23
- probe_pass_rate: number;
24
- key_resource_hit_rate: number;
25
- next_hop_usability_rate: number;
26
- hint_drift_rate: number;
27
- };
28
- bucket_metrics: {
29
- anchor: {
30
- total: number;
31
- passed: number;
32
- anchor_pass_rate: number;
33
- };
34
- holdout: {
35
- total: number;
36
- usable: number;
37
- next_hop_usability_rate: number;
38
- };
39
- negative: {
40
- total: number;
41
- false_positive: number;
42
- false_positive_rate: number;
43
- };
44
- };
45
- threshold_checks: {
46
- precision_pass: boolean;
47
- coverage_pass: boolean;
48
- probe_pass_rate_pass: boolean;
49
- anchor_pass: boolean;
50
- holdout_pass: boolean;
51
- negative_pass: boolean;
52
- };
53
- probe_results: Array<{
54
- id: string;
55
- pass: boolean;
56
- replay_command: string;
57
- }>;
58
- reportPath?: string;
59
- }
60
- export declare function runRuleLabRegress(input: RuleLabRegressInput): Promise<RuleLabRegressOutput>;
@@ -1,122 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- const PRECISION_THRESHOLD = 0.9;
4
- const COVERAGE_THRESHOLD = 0.8;
5
- const PROBE_PASS_RATE_THRESHOLD = 0.85;
6
- function buildReportMarkdown(output) {
7
- const lines = [];
8
- lines.push('# Rule Lab Regression Report');
9
- lines.push('');
10
- lines.push('## Metrics');
11
- lines.push(`- metrics.precision: ${output.metrics.precision}`);
12
- lines.push(`- metrics.coverage: ${output.metrics.coverage}`);
13
- lines.push(`- metrics.probe_pass_rate: ${output.metrics.probe_pass_rate}`);
14
- lines.push(`- metrics.key_resource_hit_rate: ${output.metrics.key_resource_hit_rate}`);
15
- lines.push(`- metrics.next_hop_usability_rate: ${output.metrics.next_hop_usability_rate}`);
16
- lines.push(`- metrics.hint_drift_rate: ${output.metrics.hint_drift_rate}`);
17
- lines.push('');
18
- lines.push('## Gate');
19
- lines.push(`- pass: ${output.pass}`);
20
- lines.push(`- failures: ${output.failures.join(', ') || 'none'}`);
21
- lines.push(`- threshold_checks: ${JSON.stringify(output.threshold_checks)}`);
22
- lines.push('');
23
- lines.push('## Buckets');
24
- lines.push(`- anchor: ${JSON.stringify(output.bucket_metrics.anchor)}`);
25
- lines.push(`- holdout: ${JSON.stringify(output.bucket_metrics.holdout)}`);
26
- lines.push(`- negative: ${JSON.stringify(output.bucket_metrics.negative)}`);
27
- lines.push('');
28
- lines.push('## Probe Results');
29
- if (output.probe_results.length === 0) {
30
- lines.push('- none');
31
- }
32
- else {
33
- for (const probe of output.probe_results) {
34
- lines.push(`- ${probe.id}: pass=${probe.pass} | replay_command=${probe.replay_command}`);
35
- }
36
- }
37
- lines.push('');
38
- return `${lines.join('\n')}\n`;
39
- }
40
- export async function runRuleLabRegress(input) {
41
- const failures = [];
42
- const probes = Array.isArray(input.probes) ? input.probes : [];
43
- if (input.precision < PRECISION_THRESHOLD) {
44
- failures.push('precision_below_threshold');
45
- }
46
- if (input.coverage < COVERAGE_THRESHOLD) {
47
- failures.push('coverage_below_threshold');
48
- }
49
- const passedProbes = probes.filter((probe) => probe.pass).length;
50
- const keyResourceProbeCount = probes.filter((probe) => typeof probe.key_resource_hit === 'boolean').length;
51
- const keyResourceHitCount = probes.filter((probe) => probe.key_resource_hit === true).length;
52
- const nextHopProbeCount = probes.filter((probe) => typeof probe.next_hop_usable === 'boolean').length;
53
- const nextHopUsableCount = probes.filter((probe) => probe.next_hop_usable === true).length;
54
- const hintDriftProbeCount = probes.filter((probe) => typeof probe.hint_drift === 'boolean').length;
55
- const hintDriftCount = probes.filter((probe) => probe.hint_drift === true).length;
56
- const probePassRate = probes.length > 0
57
- ? passedProbes / probes.length
58
- : 0;
59
- if (probePassRate < PROBE_PASS_RATE_THRESHOLD) {
60
- failures.push('probe_pass_rate_below_threshold');
61
- }
62
- const anchorProbes = probes.filter((probe) => probe.bucket === 'anchor');
63
- const holdoutProbes = probes.filter((probe) => probe.bucket === 'holdout');
64
- const negativeProbes = probes.filter((probe) => probe.bucket === 'negative');
65
- const anchorPassed = anchorProbes.filter((probe) => probe.pass).length;
66
- const holdoutUsable = holdoutProbes.filter((probe) => probe.next_hop_usable === true).length;
67
- const negativeFalsePositive = negativeProbes.filter((probe) => probe.false_positive_anchor_leak === true).length;
68
- const anchorPassRate = anchorProbes.length > 0 ? anchorPassed / anchorProbes.length : 0;
69
- const holdoutUsabilityRate = holdoutProbes.length > 0 ? holdoutUsable / holdoutProbes.length : 0;
70
- const negativeFalsePositiveRate = negativeProbes.length > 0 ? negativeFalsePositive / negativeProbes.length : 0;
71
- if (anchorProbes.length === 0) {
72
- failures.push('anchor_bucket_missing');
73
- }
74
- if (holdoutProbes.length === 0) {
75
- failures.push('holdout_bucket_missing');
76
- }
77
- if (negativeProbes.length === 0) {
78
- failures.push('negative_bucket_missing');
79
- }
80
- if (anchorProbes.length > 0 && anchorPassRate < 1) {
81
- failures.push('anchor_pass_rate_below_threshold');
82
- }
83
- if (holdoutProbes.length > 0 && holdoutUsabilityRate < 0.85) {
84
- failures.push('holdout_next_hop_usability_below_threshold');
85
- }
86
- if (negativeProbes.length > 0 && negativeFalsePositiveRate > 0.1) {
87
- failures.push('negative_false_positive_rate_above_threshold');
88
- }
89
- const output = {
90
- pass: failures.length === 0,
91
- failures,
92
- metrics: {
93
- precision: input.precision,
94
- coverage: input.coverage,
95
- probe_pass_rate: probePassRate,
96
- key_resource_hit_rate: keyResourceProbeCount > 0 ? keyResourceHitCount / keyResourceProbeCount : 0,
97
- next_hop_usability_rate: nextHopProbeCount > 0 ? nextHopUsableCount / nextHopProbeCount : 0,
98
- hint_drift_rate: hintDriftProbeCount > 0 ? hintDriftCount / hintDriftProbeCount : 0,
99
- },
100
- bucket_metrics: {
101
- anchor: { total: anchorProbes.length, passed: anchorPassed, anchor_pass_rate: anchorPassRate },
102
- holdout: { total: holdoutProbes.length, usable: holdoutUsable, next_hop_usability_rate: holdoutUsabilityRate },
103
- negative: { total: negativeProbes.length, false_positive: negativeFalsePositive, false_positive_rate: negativeFalsePositiveRate },
104
- },
105
- threshold_checks: {
106
- precision_pass: input.precision >= PRECISION_THRESHOLD,
107
- coverage_pass: input.coverage >= COVERAGE_THRESHOLD,
108
- probe_pass_rate_pass: probePassRate >= PROBE_PASS_RATE_THRESHOLD,
109
- anchor_pass: anchorProbes.length > 0 && anchorPassRate >= 1,
110
- holdout_pass: holdoutProbes.length > 0 && holdoutUsabilityRate >= 0.85,
111
- negative_pass: negativeProbes.length > 0 && negativeFalsePositiveRate <= 0.1,
112
- },
113
- probe_results: probes,
114
- };
115
- if (input.repoPath && input.runId) {
116
- const reportPath = path.join(path.resolve(input.repoPath), '.gitnexus', 'rules', 'reports', `${input.runId}-regress.md`);
117
- await fs.mkdir(path.dirname(reportPath), { recursive: true });
118
- await fs.writeFile(reportPath, buildReportMarkdown(output), 'utf-8');
119
- output.reportPath = reportPath;
120
- }
121
- return output;
122
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,68 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { runRuleLabRegress } from './regress.js';
3
- describe('rule-lab regress', () => {
4
- it('fails when precision or coverage is below threshold', async () => {
5
- const out = await runRuleLabRegress({ precision: 0.85, coverage: 0.92 });
6
- expect(out.pass).toBe(false);
7
- expect(out.failures).toContain('precision_below_threshold');
8
- });
9
- it('fails regress when probe pass-rate is below threshold even if metrics are high', async () => {
10
- const out = await runRuleLabRegress({
11
- precision: 0.95,
12
- coverage: 0.95,
13
- probes: [
14
- { id: 'p1', pass: false, replay_command: 'gitnexus query "Reload"' },
15
- ],
16
- });
17
- expect(out.pass).toBe(false);
18
- expect(out.failures).toContain('probe_pass_rate_below_threshold');
19
- expect(out.metrics.probe_pass_rate).toBeLessThan(0.85);
20
- });
21
- it('reports stage-aware metrics and three-bucket threshold checks', async () => {
22
- const out = await runRuleLabRegress({
23
- precision: 0.95,
24
- coverage: 0.9,
25
- probes: [
26
- {
27
- id: 'anchor-1',
28
- bucket: 'anchor',
29
- pass: true,
30
- replay_command: 'gitnexus query "anchor"',
31
- key_resource_hit: true,
32
- next_hop_usable: true,
33
- hint_drift: false,
34
- false_positive_anchor_leak: false,
35
- },
36
- {
37
- id: 'holdout-1',
38
- bucket: 'holdout',
39
- pass: true,
40
- replay_command: 'gitnexus query "holdout"',
41
- key_resource_hit: true,
42
- next_hop_usable: true,
43
- hint_drift: false,
44
- false_positive_anchor_leak: false,
45
- },
46
- {
47
- id: 'negative-1',
48
- bucket: 'negative',
49
- pass: true,
50
- replay_command: 'gitnexus query "negative"',
51
- key_resource_hit: false,
52
- next_hop_usable: false,
53
- hint_drift: false,
54
- false_positive_anchor_leak: false,
55
- },
56
- ],
57
- });
58
- expect(out.metrics).toHaveProperty('key_resource_hit_rate');
59
- expect(out.metrics).toHaveProperty('next_hop_usability_rate');
60
- expect(out.metrics).toHaveProperty('hint_drift_rate');
61
- expect(out.bucket_metrics.anchor.anchor_pass_rate).toBe(1);
62
- expect(out.bucket_metrics.holdout.next_hop_usability_rate).toBe(1);
63
- expect(out.bucket_metrics.negative.false_positive_rate).toBe(0);
64
- expect(out.threshold_checks.anchor_pass).toBe(true);
65
- expect(out.threshold_checks.holdout_pass).toBe(true);
66
- expect(out.threshold_checks.negative_pass).toBe(true);
67
- });
68
- });
@@ -1,34 +0,0 @@
1
- import { getRuleLabPaths } from './paths.js';
2
- export interface ReviewPackInput {
3
- repoPath: string;
4
- runId: string;
5
- sliceId: string;
6
- maxTokens: number;
7
- }
8
- export interface ReviewPackCard {
9
- card_id: string;
10
- title: string;
11
- candidate_ids: string[];
12
- decision_inputs: {
13
- required_hops: string[];
14
- failure_map: Record<string, string>;
15
- guarantees: string[];
16
- non_guarantees: string[];
17
- draft_rule_ids: string[];
18
- aggregation_modes: string[];
19
- binding_kinds: string[];
20
- };
21
- }
22
- export interface ReviewPackMeta {
23
- token_budget: number;
24
- token_budget_estimate: number;
25
- truncated: boolean;
26
- total_candidates: number;
27
- included_candidates: number;
28
- }
29
- export interface ReviewPackOutput {
30
- paths: ReturnType<typeof getRuleLabPaths>;
31
- meta: ReviewPackMeta;
32
- cards: ReviewPackCard[];
33
- }
34
- export declare function buildReviewPack(input: ReviewPackInput): Promise<ReviewPackOutput>;