@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,395 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { analyzeRuleLabSlice } from '../../rule-lab/analyze.js';
4
- import { buildReviewPack } from '../../rule-lab/review-pack.js';
5
- import { curateRuleLabSlice } from '../../rule-lab/curate.js';
6
- import { promoteCuratedRules } from '../../rule-lab/promote.js';
7
- import { runRuleLabRegress } from '../../rule-lab/regress.js';
8
- function commandFor(stage, input) {
9
- switch (stage) {
10
- case 'analyze':
11
- return `gitnexus rule-lab analyze --run-id ${input.runId} --slice-id ${input.sliceId}`;
12
- case 'review-pack':
13
- return `gitnexus rule-lab review-pack --run-id ${input.runId} --slice-id ${input.sliceId} --max-tokens 6000`;
14
- case 'curate':
15
- return `gitnexus rule-lab curate --run-id ${input.runId} --slice-id ${input.sliceId} --input-path ${input.curationInputPath}`;
16
- case 'promote':
17
- return `gitnexus rule-lab promote --run-id ${input.runId} --slice-id ${input.sliceId}`;
18
- case 'regress':
19
- return `gitnexus rule-lab regress --precision 0.93 --coverage 0.85 --run-id ${input.runId}`;
20
- default:
21
- return 'gitnexus rule-lab <unknown>';
22
- }
23
- }
24
- async function mustExist(filePath) {
25
- await fs.access(filePath);
26
- }
27
- async function bootstrapReducedRuleLabRun(input) {
28
- const normalizedSeed = String(input.seed || Date.now())
29
- .replace(/[^a-zA-Z0-9]+/g, '-')
30
- .toLowerCase();
31
- const runId = `phase5-${normalizedSeed}-${Date.now()}`;
32
- const sliceId = 'slice-startup-exact-pair';
33
- const runRoot = path.join(input.repoPath, '.gitnexus', 'rules', 'lab', 'runs', runId);
34
- const sliceRoot = path.join(runRoot, 'slices', sliceId);
35
- const manifestPath = path.join(runRoot, 'manifest.json');
36
- const slicePath = path.join(sliceRoot, 'slice.json');
37
- await fs.mkdir(sliceRoot, { recursive: true });
38
- await fs.writeFile(manifestPath, `${JSON.stringify({
39
- run_id: runId,
40
- repo_path: input.repoPath,
41
- scope: 'full',
42
- generated_at: new Date().toISOString(),
43
- slices: [
44
- {
45
- id: sliceId,
46
- trigger_family: 'startup',
47
- resource_types: ['asset'],
48
- host_base_type: ['StartupNode'],
49
- required_hops: ['resource', 'code_runtime'],
50
- },
51
- ],
52
- stages: ['analyze'],
53
- next_actions: [`gitnexus rule-lab analyze --run-id ${runId} --slice-id ${sliceId}`],
54
- }, null, 2)}\n`, 'utf-8');
55
- await fs.writeFile(slicePath, `${JSON.stringify({
56
- id: sliceId,
57
- trigger_family: 'startup',
58
- resource_types: ['asset'],
59
- host_base_type: ['StartupNode'],
60
- required_hops: ['resource', 'code_runtime'],
61
- exact_pairs: [
62
- {
63
- id: 'pair-startup-1',
64
- binding_kind: 'method_triggers_method',
65
- draft_rule_id: 'demo.startup.v1',
66
- source_anchor: {
67
- file: 'Assets/Rules/startup.asset',
68
- line: 1,
69
- symbol: 'StartupNode.Initialize',
70
- },
71
- target_anchor: {
72
- file: 'Assets/Rules/startup.asset',
73
- line: 1,
74
- symbol: 'StartupNode.Bootstrap',
75
- },
76
- },
77
- ],
78
- }, null, 2)}\n`, 'utf-8');
79
- return {
80
- runId,
81
- sliceId,
82
- manifestPath,
83
- };
84
- }
85
- function buildFailureTaxonomy(runId) {
86
- return [
87
- {
88
- code: 'rule_not_matched',
89
- retry_hint: 'confirm trigger_family tokens and rerun analyze/promote',
90
- repro_command: `gitnexus rule-lab analyze --run-id ${runId} --slice-id <slice_id>`,
91
- },
92
- {
93
- code: 'rule_matched_but_evidence_missing',
94
- retry_hint: 'add stronger confirmed_chain anchors in curation input and promote again',
95
- repro_command: `gitnexus rule-lab curate --run-id ${runId} --slice-id <slice_id> --input-path <curation-input.json>`,
96
- },
97
- {
98
- code: 'precision_below_threshold',
99
- retry_hint: 'trim noisy rules or tighten curation semantics before regress',
100
- repro_command: `gitnexus rule-lab regress --precision 0.85 --coverage 0.92 --run-id ${runId}`,
101
- },
102
- {
103
- code: 'coverage_below_threshold',
104
- retry_hint: 'add additional approved rules/slices and rerun regress',
105
- repro_command: `gitnexus rule-lab regress --precision 0.95 --coverage 0.70 --run-id ${runId}`,
106
- },
107
- {
108
- code: 'probe_pass_rate_below_threshold',
109
- retry_hint: 're-run regress with refreshed probes and inspect replay commands',
110
- repro_command: `gitnexus rule-lab regress --precision 0.95 --coverage 0.90 --run-id ${runId}`,
111
- },
112
- {
113
- code: 'static_hardcode_detected',
114
- retry_hint: 'remove project-specific reload fallback constants/branches from runtime verifier',
115
- repro_command: 'rg -n "RESOURCE_ASSET_PATH|GRAPH_ASSET_PATH|RELOAD_GUID|shouldVerifyReloadChain|verifyReloadRuntimeChain" gitnexus/src/mcp/local/runtime-chain-verify.ts',
116
- },
117
- {
118
- code: 'dsl_lint_failed',
119
- retry_hint: 'fix promoted DSL placeholders and rerun promote/regress',
120
- repro_command: `gitnexus rule-lab promote --run-id ${runId} --slice-id <slice_id>`,
121
- },
122
- ];
123
- }
124
- function hasDslPlaceholders(text) {
125
- return /(^|\s)(unknown|todo|tbd)(\s|$)|<[^>]+>/i.test(text);
126
- }
127
- async function buildAuthenticityChecks(input) {
128
- const verifierPathCandidates = [
129
- path.join(input.repoPath, 'gitnexus', 'src', 'mcp', 'local', 'runtime-chain-verify.ts'),
130
- path.join(input.repoPath, 'src', 'mcp', 'local', 'runtime-chain-verify.ts'),
131
- ];
132
- let verifierPath = verifierPathCandidates[0];
133
- for (const candidate of verifierPathCandidates) {
134
- try {
135
- await fs.access(candidate);
136
- verifierPath = candidate;
137
- break;
138
- }
139
- catch {
140
- }
141
- }
142
- const verifierRaw = await fs.readFile(verifierPath, 'utf-8');
143
- const blockedSymbols = [
144
- 'RESOURCE_ASSET_PATH',
145
- 'GRAPH_ASSET_PATH',
146
- 'RELOAD_GUID',
147
- 'shouldVerifyReloadChain',
148
- 'verifyReloadRuntimeChain',
149
- ].filter((token) => verifierRaw.includes(token));
150
- let dslLintPass = true;
151
- for (const promotedPath of input.promotedFiles) {
152
- const raw = await fs.readFile(promotedPath, 'utf-8');
153
- if (hasDslPlaceholders(raw)) {
154
- dslLintPass = false;
155
- break;
156
- }
157
- }
158
- return {
159
- static_no_hardcoded_reload: {
160
- pass: blockedSymbols.length === 0,
161
- blocked_symbols: blockedSymbols,
162
- },
163
- dsl_lint_pass: dslLintPass,
164
- };
165
- }
166
- export async function buildPhase5RuleLabAcceptanceReport(input) {
167
- const repoPath = path.resolve(input.repoPath || process.cwd());
168
- const stageCoverage = [];
169
- const bootstrapped = await bootstrapReducedRuleLabRun({
170
- repoPath,
171
- seed: input.seed || 'phase5-acceptance',
172
- });
173
- const sliceId = bootstrapped.sliceId;
174
- const runId = bootstrapped.runId;
175
- const analyze = await analyzeRuleLabSlice({ repoPath, runId, sliceId });
176
- stageCoverage.push({
177
- stage: 'analyze',
178
- command: commandFor('analyze', { runId, sliceId }),
179
- status: 'passed',
180
- });
181
- const reviewPack = await buildReviewPack({ repoPath, runId, sliceId, maxTokens: 6000 });
182
- stageCoverage.push({
183
- stage: 'review-pack',
184
- command: commandFor('review-pack', { runId, sliceId }),
185
- status: 'passed',
186
- });
187
- const firstCandidate = analyze.candidates[0];
188
- const promotedRuleId = `demo.startup.${runId}.v1`;
189
- const curationInputPath = path.join(repoPath, '.gitnexus', 'rules', 'lab', 'runs', runId, 'slices', sliceId, 'curation-input.json');
190
- await fs.writeFile(curationInputPath, `${JSON.stringify({
191
- run_id: runId,
192
- slice_id: sliceId,
193
- curated: [
194
- {
195
- id: firstCandidate.id,
196
- rule_id: promotedRuleId,
197
- title: 'startup startup graph',
198
- match: {
199
- trigger_tokens: ['startup'],
200
- },
201
- topology: Array.isArray(firstCandidate.topology) && firstCandidate.topology.length > 0
202
- ? firstCandidate.topology
203
- : firstCandidate.evidence.hops.map((hop) => ({
204
- hop: hop.hop_type,
205
- from: { entity: 'resource' },
206
- to: { entity: 'script' },
207
- edge: { kind: 'binds_script' },
208
- })),
209
- closure: {
210
- required_hops: Array.isArray(firstCandidate.topology) && firstCandidate.topology.length > 0
211
- ? firstCandidate.topology.map((hop) => hop.hop)
212
- : ['code_runtime'],
213
- failure_map: {
214
- missing_evidence: 'rule_matched_but_evidence_missing',
215
- },
216
- },
217
- claims: {
218
- guarantees: ['startup trigger matching is confirmed'],
219
- non_guarantees: ['does not prove full runtime ordering'],
220
- next_action: 'gitnexus query "Startup Graph Trigger"',
221
- },
222
- confirmed_chain: {
223
- steps: firstCandidate.evidence.hops.map((hop) => ({ ...hop, hop_type: 'code_runtime' })),
224
- },
225
- guarantees: ['startup trigger matching is confirmed'],
226
- non_guarantees: ['does not prove full runtime ordering'],
227
- },
228
- ],
229
- }, null, 2)}\n`, 'utf-8');
230
- const curated = await curateRuleLabSlice({ repoPath, runId, sliceId, inputPath: curationInputPath });
231
- stageCoverage.push({
232
- stage: 'curate',
233
- command: commandFor('curate', { runId, sliceId, curationInputPath }),
234
- status: 'passed',
235
- });
236
- const promoted = await promoteCuratedRules({ repoPath, runId, sliceId, version: '1.0.0' });
237
- stageCoverage.push({
238
- stage: 'promote',
239
- command: commandFor('promote', { runId, sliceId }),
240
- status: 'passed',
241
- });
242
- const regress = await runRuleLabRegress({
243
- precision: 0.93,
244
- coverage: 0.85,
245
- probes: [
246
- {
247
- id: 'probe-startup-trigger',
248
- pass: true,
249
- replay_command: 'gitnexus query "Startup Graph Trigger" --runtime-chain-verify on-demand',
250
- },
251
- ],
252
- repoPath,
253
- runId,
254
- });
255
- stageCoverage.push({
256
- stage: 'regress',
257
- command: commandFor('regress', { runId, sliceId }),
258
- status: regress.pass ? 'passed' : 'failed',
259
- retry_hint: regress.pass ? undefined : regress.failures.join(','),
260
- });
261
- const artifactPaths = {
262
- manifest: bootstrapped.manifestPath,
263
- candidates: analyze.paths.candidatesPath,
264
- review_cards: reviewPack.paths.reviewCardsPath,
265
- curation_input: curationInputPath,
266
- curated: curated.paths.curatedPath,
267
- catalog: path.join(promoted.paths.rulesRoot, 'catalog.json'),
268
- promoted_files: promoted.promotedFiles,
269
- regress_report: regress.reportPath,
270
- };
271
- await mustExist(artifactPaths.manifest);
272
- await mustExist(artifactPaths.candidates);
273
- await mustExist(artifactPaths.review_cards);
274
- await mustExist(artifactPaths.curation_input);
275
- await mustExist(artifactPaths.curated);
276
- await mustExist(artifactPaths.catalog);
277
- await Promise.all(artifactPaths.promoted_files.map((filePath) => mustExist(filePath)));
278
- if (artifactPaths.regress_report) {
279
- await mustExist(artifactPaths.regress_report);
280
- }
281
- const authenticityChecks = await buildAuthenticityChecks({
282
- repoPath,
283
- promotedFiles: artifactPaths.promoted_files,
284
- });
285
- return {
286
- generated_at: new Date().toISOString(),
287
- repo_alias: input.repoAlias,
288
- repo_path: repoPath,
289
- run_id: runId,
290
- stage_coverage: stageCoverage,
291
- metrics: {
292
- precision: regress.metrics.precision,
293
- coverage: regress.metrics.coverage,
294
- probe_pass_rate: regress.metrics.probe_pass_rate,
295
- token_budget: reviewPack.meta.token_budget_estimate,
296
- },
297
- authenticity_checks: authenticityChecks,
298
- failure_classifications: buildFailureTaxonomy(runId),
299
- artifact_paths: artifactPaths,
300
- };
301
- }
302
- export async function runPhase5RuleLabGate(input) {
303
- const reportPath = path.resolve(input.reportPath);
304
- try {
305
- const raw = await fs.readFile(reportPath, 'utf-8');
306
- const report = JSON.parse(raw);
307
- if (!Array.isArray(report.stage_coverage) || report.stage_coverage.length !== 5) {
308
- return { pass: false, reason: 'stage_coverage_incomplete' };
309
- }
310
- if (typeof report.metrics?.precision !== 'number' || typeof report.metrics?.coverage !== 'number') {
311
- return { pass: false, reason: 'metrics_missing' };
312
- }
313
- if (!report.authenticity_checks?.static_no_hardcoded_reload?.pass) {
314
- return { pass: false, reason: 'static_hardcode_detected' };
315
- }
316
- if (!report.authenticity_checks?.dsl_lint_pass) {
317
- return { pass: false, reason: 'dsl_lint_failed' };
318
- }
319
- if (Number(report.metrics?.probe_pass_rate) < 0.85) {
320
- return { pass: false, reason: 'probe_pass_rate_below_threshold' };
321
- }
322
- return { pass: true };
323
- }
324
- catch {
325
- return { pass: false, reason: 'acceptance_report_missing' };
326
- }
327
- }
328
- export async function writePhase5RuleLabAcceptanceArtifacts(input) {
329
- const jsonPath = path.resolve(input.jsonPath);
330
- const mdPath = path.resolve(input.mdPath);
331
- await fs.mkdir(path.dirname(jsonPath), { recursive: true });
332
- await fs.writeFile(jsonPath, `${JSON.stringify(input.report, null, 2)}\n`, 'utf-8');
333
- const markdown = [
334
- '# Phase 5 Rule Lab Acceptance',
335
- '',
336
- `- generated_at: ${input.report.generated_at}`,
337
- `- repo_alias: ${input.report.repo_alias}`,
338
- `- run_id: ${input.report.run_id}`,
339
- '',
340
- '## Stage Coverage',
341
- ...input.report.stage_coverage.map((stage) => `- ${stage.stage}: ${stage.status} (${stage.command})`),
342
- '',
343
- '## Metrics',
344
- `- precision: ${input.report.metrics.precision}`,
345
- `- coverage: ${input.report.metrics.coverage}`,
346
- `- probe_pass_rate: ${input.report.metrics.probe_pass_rate}`,
347
- `- token_budget: ${input.report.metrics.token_budget}`,
348
- '',
349
- '## Authenticity Checks',
350
- `- static_no_hardcoded_reload.pass: ${input.report.authenticity_checks.static_no_hardcoded_reload.pass}`,
351
- `- static_no_hardcoded_reload.blocked_symbols: ${input.report.authenticity_checks.static_no_hardcoded_reload.blocked_symbols.join(', ') || 'none'}`,
352
- `- dsl_lint_pass: ${input.report.authenticity_checks.dsl_lint_pass}`,
353
- '',
354
- '## Failure Classifications',
355
- ...input.report.failure_classifications.map((failure) => `- ${failure.code}: ${failure.retry_hint} | repro: ${failure.repro_command}`),
356
- '',
357
- '## Artifact Paths',
358
- `- manifest: ${input.report.artifact_paths.manifest}`,
359
- `- candidates: ${input.report.artifact_paths.candidates}`,
360
- `- review_cards: ${input.report.artifact_paths.review_cards}`,
361
- `- curation_input: ${input.report.artifact_paths.curation_input}`,
362
- `- curated: ${input.report.artifact_paths.curated}`,
363
- `- catalog: ${input.report.artifact_paths.catalog}`,
364
- `- promoted_files: ${input.report.artifact_paths.promoted_files.join(', ')}`,
365
- `- regress_report: ${input.report.artifact_paths.regress_report || 'n/a'}`,
366
- '',
367
- ].join('\n');
368
- await fs.mkdir(path.dirname(mdPath), { recursive: true });
369
- await fs.writeFile(mdPath, markdown, 'utf-8');
370
- }
371
- async function main(argv) {
372
- const repoArgIndex = argv.indexOf('--repo-path');
373
- const repoPath = repoArgIndex >= 0 ? argv[repoArgIndex + 1] : process.cwd();
374
- const outJsonIndex = argv.indexOf('--out-json');
375
- const outMdIndex = argv.indexOf('--out-md');
376
- const outJson = outJsonIndex >= 0
377
- ? argv[outJsonIndex + 1]
378
- : path.resolve('docs/reports/2026-04-02-phase5-rule-lab-acceptance.json');
379
- const outMd = outMdIndex >= 0
380
- ? argv[outMdIndex + 1]
381
- : path.resolve('docs/reports/2026-04-02-phase5-rule-lab-acceptance.md');
382
- const report = await buildPhase5RuleLabAcceptanceReport({
383
- repoAlias: 'GitNexus',
384
- repoPath,
385
- seed: 'phase5-acceptance-main',
386
- });
387
- await writePhase5RuleLabAcceptanceArtifacts({ report, jsonPath: outJson, mdPath: outMd });
388
- process.stdout.write(`${JSON.stringify({ outJson, outMd, run_id: report.run_id }, null, 2)}\n`);
389
- }
390
- if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname)) {
391
- main(process.argv.slice(2)).catch((error) => {
392
- console.error(error instanceof Error ? error.message : String(error));
393
- process.exit(1);
394
- });
395
- }
@@ -1,41 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import fs from 'node:fs/promises';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { buildPhase5RuleLabAcceptanceReport, runPhase5RuleLabGate } from './phase5-rule-lab-acceptance-runner.js';
6
- const { test: rawTest } = process.env.VITEST
7
- ? await import('vitest')
8
- : await import('node:test');
9
- const test = rawTest;
10
- test('phase5 rule-lab acceptance runner emits complete stage coverage', async () => {
11
- const report = await buildPhase5RuleLabAcceptanceReport({ repoAlias: 'GitNexus' });
12
- assert.equal(report.stage_coverage.length, 5);
13
- assert.equal(typeof report.metrics.precision, 'number');
14
- });
15
- test('phase5 gate fails when required artifacts are missing', async () => {
16
- const gate = await runPhase5RuleLabGate({ reportPath: '/tmp/missing.json' });
17
- assert.equal(gate.pass, false);
18
- assert.equal(gate.reason, 'acceptance_report_missing');
19
- });
20
- test('phase5 gate fails when anti-hardcode scan or dsl lint fails', async () => {
21
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'phase5-gate-'));
22
- const reportPath = path.join(tmpDir, 'report.json');
23
- await fs.writeFile(reportPath, JSON.stringify({
24
- stage_coverage: [
25
- { stage: 'analyze', status: 'passed' },
26
- { stage: 'review-pack', status: 'passed' },
27
- { stage: 'curate', status: 'passed' },
28
- { stage: 'promote', status: 'passed' },
29
- { stage: 'regress', status: 'passed' },
30
- ],
31
- metrics: { precision: 0.93, coverage: 0.9, probe_pass_rate: 0.9 },
32
- authenticity_checks: {
33
- static_no_hardcoded_reload: { pass: false },
34
- dsl_lint_pass: false,
35
- },
36
- }), 'utf-8');
37
- const gate = await runPhase5RuleLabGate({ reportPath });
38
- assert.equal(gate.pass, false);
39
- assert.ok(['static_hardcode_detected', 'dsl_lint_failed'].includes(String(gate.reason)));
40
- await fs.rm(tmpDir, { recursive: true, force: true });
41
- });
@@ -1,38 +0,0 @@
1
- import type { Command } from 'commander';
2
- declare const RULE_LAB_COMMANDS: readonly ["analyze", "review-pack", "curate", "promote", "regress"];
3
- type RuleLabHandlerName = 'ruleLabAnalyzeCommand' | 'ruleLabReviewPackCommand' | 'ruleLabCurateCommand' | 'ruleLabPromoteCommand' | 'ruleLabRegressCommand';
4
- type LazyFactory = (handlerName: RuleLabHandlerName) => (...args: any[]) => void | Promise<void>;
5
- export declare function getRuleLabCommandNames(program: Command): string[];
6
- export declare function attachRuleLabCommands(program: Command, lazyFactory?: LazyFactory): void;
7
- export declare function ruleLabAnalyzeCommand(options: {
8
- repoPath?: string;
9
- runId: string;
10
- sliceId: string;
11
- }): Promise<void>;
12
- export declare function ruleLabReviewPackCommand(options: {
13
- repoPath?: string;
14
- runId: string;
15
- sliceId: string;
16
- maxTokens?: string | number;
17
- }): Promise<void>;
18
- export declare function ruleLabCurateCommand(options: {
19
- repoPath?: string;
20
- runId: string;
21
- sliceId: string;
22
- inputPath: string;
23
- }): Promise<void>;
24
- export declare function ruleLabPromoteCommand(options: {
25
- repoPath?: string;
26
- runId: string;
27
- sliceId: string;
28
- ruleVersion?: string;
29
- version?: string;
30
- }): Promise<void>;
31
- export declare function ruleLabRegressCommand(options: {
32
- precision: string | number;
33
- coverage: string | number;
34
- repoPath?: string;
35
- runId?: string;
36
- probesPath?: string;
37
- }): Promise<void>;
38
- export { RULE_LAB_COMMANDS };
@@ -1,148 +0,0 @@
1
- import { writeSync } from 'node:fs';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { analyzeRuleLabSlice } from '../rule-lab/analyze.js';
5
- import { buildReviewPack } from '../rule-lab/review-pack.js';
6
- import { curateRuleLabSlice } from '../rule-lab/curate.js';
7
- import { promoteCuratedRules } from '../rule-lab/promote.js';
8
- import { runRuleLabRegress } from '../rule-lab/regress.js';
9
- import { compileRules } from '../rule-lab/compile.js';
10
- const RULE_LAB_COMMANDS = ['analyze', 'review-pack', 'curate', 'promote', 'regress'];
11
- function output(data) {
12
- const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
13
- writeSync(1, `${text}\n`);
14
- }
15
- function resolveRepoPath(repoPath) {
16
- return path.resolve(repoPath || process.cwd());
17
- }
18
- function assertConcreteRunSliceIds(runId, sliceId) {
19
- if (/[<>]/.test(runId) || /[<>]/.test(sliceId)) {
20
- throw new Error('Invalid run/slice id: placeholder values are not allowed');
21
- }
22
- }
23
- export function getRuleLabCommandNames(program) {
24
- const root = program.commands.find((command) => command.name() === 'rule-lab');
25
- if (!root)
26
- return [];
27
- return root.commands.map((command) => command.name());
28
- }
29
- export function attachRuleLabCommands(program, lazyFactory) {
30
- const action = (handlerName) => {
31
- if (lazyFactory)
32
- return lazyFactory(handlerName);
33
- switch (handlerName) {
34
- case 'ruleLabAnalyzeCommand':
35
- return (options) => ruleLabAnalyzeCommand(options);
36
- case 'ruleLabReviewPackCommand':
37
- return (options) => ruleLabReviewPackCommand(options);
38
- case 'ruleLabCurateCommand':
39
- return (options) => ruleLabCurateCommand(options);
40
- case 'ruleLabPromoteCommand':
41
- return (options) => ruleLabPromoteCommand(options);
42
- case 'ruleLabRegressCommand':
43
- return (options) => ruleLabRegressCommand(options);
44
- default:
45
- return () => {
46
- throw new Error(`Unknown rule lab handler: ${handlerName}`);
47
- };
48
- }
49
- };
50
- const root = program
51
- .command('rule-lab')
52
- .description('Offline rule-lab workflow for analyze/review-pack/curate/promote/regress');
53
- root
54
- .command('analyze')
55
- .requiredOption('--run-id <id>', 'Rule-lab run id')
56
- .requiredOption('--slice-id <id>', 'Slice id')
57
- .option('--repo-path <path>', 'Repository path (default: cwd)')
58
- .action(action('ruleLabAnalyzeCommand'));
59
- root
60
- .command('review-pack')
61
- .requiredOption('--run-id <id>', 'Rule-lab run id')
62
- .requiredOption('--slice-id <id>', 'Slice id')
63
- .option('--repo-path <path>', 'Repository path (default: cwd)')
64
- .option('--max-tokens <n>', 'Token budget', '6000')
65
- .action(action('ruleLabReviewPackCommand'));
66
- root
67
- .command('curate')
68
- .requiredOption('--run-id <id>', 'Rule-lab run id')
69
- .requiredOption('--slice-id <id>', 'Slice id')
70
- .requiredOption('--input-path <path>', 'Path to curation input JSON')
71
- .option('--repo-path <path>', 'Repository path (default: cwd)')
72
- .action(action('ruleLabCurateCommand'));
73
- root
74
- .command('promote')
75
- .requiredOption('--run-id <id>', 'Rule-lab run id')
76
- .requiredOption('--slice-id <id>', 'Slice id')
77
- .option('--repo-path <path>', 'Repository path (default: cwd)')
78
- .option('--rule-version <version>', 'Promoted rule version', '1.0.0')
79
- .action(action('ruleLabPromoteCommand'));
80
- root
81
- .command('regress')
82
- .requiredOption('--precision <n>', 'Precision metric')
83
- .requiredOption('--coverage <n>', 'Coverage metric')
84
- .option('--repo-path <path>', 'Repository path (default: cwd)')
85
- .option('--run-id <id>', 'Run id (if provided, write report to .gitnexus/rules/reports)')
86
- .option('--probes-path <path>', 'Optional JSON file containing regress probes')
87
- .action(action('ruleLabRegressCommand'));
88
- root
89
- .command('compile')
90
- .description('Compile approved YAML rules into a JSON bundle')
91
- .option('--repo-path <path>', 'Repository path (default: cwd)')
92
- .option('--family <family>', 'Rule family to compile', 'analyze_rules')
93
- .action((options) => compileRules({ repoPath: options.repoPath, family: options.family }));
94
- }
95
- export async function ruleLabAnalyzeCommand(options) {
96
- assertConcreteRunSliceIds(options.runId, options.sliceId);
97
- const repoPath = resolveRepoPath(options?.repoPath);
98
- const result = await analyzeRuleLabSlice({
99
- repoPath,
100
- runId: options.runId,
101
- sliceId: options.sliceId,
102
- });
103
- output(result);
104
- }
105
- export async function ruleLabReviewPackCommand(options) {
106
- const result = await buildReviewPack({
107
- repoPath: resolveRepoPath(options?.repoPath),
108
- runId: options.runId,
109
- sliceId: options.sliceId,
110
- maxTokens: Number(options.maxTokens || 6000),
111
- });
112
- output(result);
113
- }
114
- export async function ruleLabCurateCommand(options) {
115
- const result = await curateRuleLabSlice({
116
- repoPath: resolveRepoPath(options?.repoPath),
117
- runId: options.runId,
118
- sliceId: options.sliceId,
119
- inputPath: path.resolve(options.inputPath),
120
- });
121
- output(result);
122
- }
123
- export async function ruleLabPromoteCommand(options) {
124
- const version = options.ruleVersion ?? options.version;
125
- const result = await promoteCuratedRules({
126
- repoPath: resolveRepoPath(options?.repoPath),
127
- runId: options.runId,
128
- sliceId: options.sliceId,
129
- version,
130
- });
131
- output(result);
132
- }
133
- export async function ruleLabRegressCommand(options) {
134
- let probes;
135
- if (options.probesPath) {
136
- const raw = await fs.readFile(path.resolve(options.probesPath), 'utf-8');
137
- probes = JSON.parse(raw);
138
- }
139
- const result = await runRuleLabRegress({
140
- precision: Number(options.precision),
141
- coverage: Number(options.coverage),
142
- repoPath: options.repoPath ? resolveRepoPath(options.repoPath) : undefined,
143
- runId: options.runId,
144
- probes,
145
- });
146
- output(result);
147
- }
148
- export { RULE_LAB_COMMANDS };
@@ -1 +0,0 @@
1
- export {};
@@ -1,31 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { Command } from 'commander';
3
- import { attachRuleLabCommands, getRuleLabCommandNames, ruleLabAnalyzeCommand } from './rule-lab.js';
4
- describe('rule-lab cli', () => {
5
- it('registers all rule-lab subcommands', async () => {
6
- const program = new Command();
7
- program.version('test-version');
8
- attachRuleLabCommands(program);
9
- const cmds = getRuleLabCommandNames(program);
10
- expect(cmds).toEqual(['analyze', 'review-pack', 'curate', 'promote', 'regress', 'compile']);
11
- });
12
- it('uses --rule-version for promote to avoid root --version collision', () => {
13
- const program = new Command();
14
- program.version('test-version');
15
- attachRuleLabCommands(program);
16
- const ruleLab = program.commands.find((command) => command.name() === 'rule-lab');
17
- expect(ruleLab).toBeTruthy();
18
- const promote = ruleLab?.commands.find((command) => command.name() === 'promote');
19
- expect(promote).toBeTruthy();
20
- const optionNames = (promote?.options || []).map((option) => option.long);
21
- expect(optionNames).toContain('--rule-version');
22
- expect(optionNames).not.toContain('--version');
23
- });
24
- it('rejects placeholder run/slice ids at analyze command boundary', async () => {
25
- await expect(ruleLabAnalyzeCommand({
26
- repoPath: process.cwd(),
27
- runId: '<run_id>',
28
- sliceId: '<slice_id>',
29
- })).rejects.toThrow(/placeholder/i);
30
- });
31
- });
@@ -1,9 +0,0 @@
1
- export interface ScopeManifestConfig {
2
- scopeRules: string[];
3
- directives: {
4
- extensions?: string;
5
- repoAlias?: string;
6
- embeddings?: string;
7
- };
8
- }
9
- export declare function parseScopeManifestConfig(raw: string): ScopeManifestConfig;