@veewo/gitnexus 1.5.0-rc.4 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/benchmark/agent-context/runner.js +3 -0
  2. package/dist/benchmark/agent-context/runner.test.js +22 -0
  3. package/dist/benchmark/agent-context/tool-runner.d.ts +7 -6
  4. package/dist/benchmark/agent-safe-query-context/io.d.ts +2 -0
  5. package/dist/benchmark/agent-safe-query-context/io.js +86 -0
  6. package/dist/benchmark/agent-safe-query-context/io.test.d.ts +1 -0
  7. package/dist/benchmark/agent-safe-query-context/io.test.js +13 -0
  8. package/dist/benchmark/agent-safe-query-context/report.d.ts +57 -0
  9. package/dist/benchmark/agent-safe-query-context/report.js +159 -0
  10. package/dist/benchmark/agent-safe-query-context/report.test.d.ts +1 -0
  11. package/dist/benchmark/agent-safe-query-context/report.test.js +362 -0
  12. package/dist/benchmark/agent-safe-query-context/runner.d.ts +44 -0
  13. package/dist/benchmark/agent-safe-query-context/runner.js +406 -0
  14. package/dist/benchmark/agent-safe-query-context/runner.test.d.ts +1 -0
  15. package/dist/benchmark/agent-safe-query-context/runner.test.js +290 -0
  16. package/dist/benchmark/agent-safe-query-context/semantic-tuple.d.ts +20 -0
  17. package/dist/benchmark/agent-safe-query-context/semantic-tuple.js +225 -0
  18. package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.d.ts +1 -0
  19. package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.js +122 -0
  20. package/dist/benchmark/agent-safe-query-context/subagent-live.d.ts +47 -0
  21. package/dist/benchmark/agent-safe-query-context/subagent-live.js +128 -0
  22. package/dist/benchmark/agent-safe-query-context/subagent-live.test.d.ts +1 -0
  23. package/dist/benchmark/agent-safe-query-context/subagent-live.test.js +155 -0
  24. package/dist/benchmark/agent-safe-query-context/telemetry-tool.d.ts +9 -0
  25. package/dist/benchmark/agent-safe-query-context/telemetry-tool.js +77 -0
  26. package/dist/benchmark/agent-safe-query-context/types.d.ts +61 -0
  27. package/dist/benchmark/agent-safe-query-context/types.js +8 -0
  28. package/dist/benchmark/analyze-runner.d.ts +1 -1
  29. package/dist/benchmark/analyze-runner.js +4 -3
  30. package/dist/benchmark/analyze-runner.test.js +7 -0
  31. package/dist/benchmark/runtime-poc/provenance-artifact.d.ts +47 -0
  32. package/dist/benchmark/runtime-poc/provenance-artifact.js +89 -0
  33. package/dist/benchmark/runtime-poc/runner.d.ts +31 -0
  34. package/dist/benchmark/runtime-poc/runner.js +163 -0
  35. package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.d.ts +8 -0
  36. package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.js +21 -0
  37. package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.d.ts +0 -1
  38. package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.js +53 -51
  39. package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.js +0 -1
  40. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +1 -1
  41. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +82 -18
  42. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +1 -2
  43. package/dist/benchmark/u2-e2e/retrieval-runner.js +15 -7
  44. package/dist/benchmark/u2-e2e/retrieval-runner.test.js +46 -0
  45. package/dist/cli/ai-context.d.ts +0 -1
  46. package/dist/cli/ai-context.js +5 -6
  47. package/dist/cli/ai-context.test.js +8 -0
  48. package/dist/cli/analyze-options.js +58 -34
  49. package/dist/cli/analyze-options.test.js +57 -0
  50. package/dist/cli/analyze-runtime-summary.js +2 -0
  51. package/dist/cli/analyze-runtime-summary.test.js +12 -0
  52. package/dist/cli/analyze-summary.d.ts +4 -0
  53. package/dist/cli/analyze-summary.js +43 -0
  54. package/dist/cli/analyze-summary.test.js +65 -1
  55. package/dist/cli/analyze.d.ts +11 -0
  56. package/dist/cli/analyze.js +34 -5
  57. package/dist/cli/analyze.test.d.ts +1 -0
  58. package/dist/cli/analyze.test.js +25 -0
  59. package/dist/cli/benchmark-agent-context.js +1 -1
  60. package/dist/cli/benchmark-agent-safe-query-context.d.ts +20 -0
  61. package/dist/cli/benchmark-agent-safe-query-context.js +39 -0
  62. package/dist/cli/benchmark-agent-safe-query-context.test.d.ts +1 -0
  63. package/dist/cli/benchmark-agent-safe-query-context.test.js +271 -0
  64. package/dist/cli/benchmark-unity.js +1 -1
  65. package/dist/cli/benchmark-unity.test.js +5 -1
  66. package/dist/cli/benchmark.d.ts +29 -0
  67. package/dist/cli/benchmark.js +55 -0
  68. package/dist/cli/index.js +27 -2
  69. package/dist/cli/rule-lab.d.ts +3 -7
  70. package/dist/cli/rule-lab.js +13 -22
  71. package/dist/cli/rule-lab.test.js +23 -3
  72. package/dist/cli/scope-manifest-config.d.ts +9 -0
  73. package/dist/cli/scope-manifest-config.js +37 -0
  74. package/dist/cli/setup.js +40 -41
  75. package/dist/cli/setup.test.js +14 -14
  76. package/dist/cli/sync-manifest.d.ts +27 -0
  77. package/dist/cli/sync-manifest.js +200 -0
  78. package/dist/cli/sync-manifest.test.d.ts +1 -0
  79. package/dist/cli/sync-manifest.test.js +88 -0
  80. package/dist/cli/tool.d.ts +2 -0
  81. package/dist/cli/tool.js +2 -0
  82. package/dist/core/config/unity-config.d.ts +1 -1
  83. package/dist/core/config/unity-config.js +1 -1
  84. package/dist/core/ingestion/call-processor.d.ts +2 -1
  85. package/dist/core/ingestion/call-processor.js +28 -6
  86. package/dist/core/ingestion/heritage-processor.d.ts +2 -1
  87. package/dist/core/ingestion/heritage-processor.js +30 -7
  88. package/dist/core/ingestion/import-processor.d.ts +2 -1
  89. package/dist/core/ingestion/import-processor.js +28 -6
  90. package/dist/core/ingestion/parsing-processor.d.ts +5 -3
  91. package/dist/core/ingestion/parsing-processor.js +46 -13
  92. package/dist/core/ingestion/pipeline.js +100 -19
  93. package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +18 -20
  94. package/dist/core/ingestion/unity-parity-seed.d.ts +2 -1
  95. package/dist/core/ingestion/unity-parity-seed.js +8 -0
  96. package/dist/core/ingestion/unity-resource-processor.d.ts +11 -0
  97. package/dist/core/ingestion/unity-resource-processor.js +102 -0
  98. package/dist/core/ingestion/unity-resource-processor.test.js +449 -0
  99. package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +16 -1
  100. package/dist/core/ingestion/unity-runtime-binding-rules.js +193 -42
  101. package/dist/core/ingestion/workers/parse-worker.d.ts +2 -0
  102. package/dist/core/ingestion/workers/parse-worker.js +50 -6
  103. package/dist/core/lbug/csv-generator.test.js +2 -2
  104. package/dist/core/tree-sitter/csharp-define-profile.d.ts +6 -0
  105. package/dist/core/tree-sitter/csharp-define-profile.js +43 -0
  106. package/dist/core/tree-sitter/csharp-preproc-normalizer.d.ts +14 -0
  107. package/dist/core/tree-sitter/csharp-preproc-normalizer.js +261 -0
  108. package/dist/core/tree-sitter/parser-loader.d.ts +10 -0
  109. package/dist/core/tree-sitter/parser-loader.js +19 -0
  110. package/dist/core/unity/doc-contract.test.d.ts +1 -0
  111. package/dist/core/unity/doc-contract.test.js +30 -0
  112. package/dist/core/unity/prefab-source-scan.d.ts +25 -0
  113. package/dist/core/unity/prefab-source-scan.js +152 -0
  114. package/dist/core/unity/prefab-source-scan.test.d.ts +1 -0
  115. package/dist/core/unity/prefab-source-scan.test.js +70 -0
  116. package/dist/core/unity/scan-context.d.ts +12 -0
  117. package/dist/core/unity/scan-context.js +50 -2
  118. package/dist/core/unity/scan-context.test.js +74 -0
  119. package/dist/mcp/local/agent-safe-response.d.ts +10 -0
  120. package/dist/mcp/local/agent-safe-response.js +639 -0
  121. package/dist/mcp/local/derived-process-reader.js +1 -1
  122. package/dist/mcp/local/local-backend.d.ts +18 -1
  123. package/dist/mcp/local/local-backend.js +319 -125
  124. package/dist/mcp/local/process-confidence.d.ts +1 -2
  125. package/dist/mcp/local/process-confidence.js +0 -3
  126. package/dist/mcp/local/process-confidence.test.js +4 -2
  127. package/dist/mcp/local/process-evidence.d.ts +1 -8
  128. package/dist/mcp/local/process-evidence.js +1 -23
  129. package/dist/mcp/local/process-evidence.test.js +2 -16
  130. package/dist/mcp/local/process-ref.d.ts +1 -1
  131. package/dist/mcp/local/runtime-chain-closure-evaluator.d.ts +33 -0
  132. package/dist/mcp/local/runtime-chain-closure-evaluator.js +273 -0
  133. package/dist/mcp/local/runtime-chain-graph-candidates.d.ts +23 -0
  134. package/dist/mcp/local/runtime-chain-graph-candidates.js +131 -0
  135. package/dist/mcp/local/runtime-chain-verify.d.ts +1 -1
  136. package/dist/mcp/local/runtime-chain-verify.js +149 -138
  137. package/dist/mcp/local/runtime-chain-verify.test.js +126 -68
  138. package/dist/mcp/local/runtime-claim-rule-registry.d.ts +4 -0
  139. package/dist/mcp/local/runtime-claim-rule-registry.js +4 -0
  140. package/dist/mcp/local/runtime-claim-rule-registry.test.js +37 -4
  141. package/dist/mcp/local/runtime-claim.d.ts +11 -0
  142. package/dist/mcp/local/runtime-claim.js +28 -0
  143. package/dist/mcp/local/unity-evidence-view.d.ts +1 -1
  144. package/dist/mcp/local/unity-evidence-view.js +1 -1
  145. package/dist/mcp/local/unity-evidence-view.test.js +22 -0
  146. package/dist/mcp/tools.js +51 -21
  147. package/dist/rule-lab/analyze.d.ts +2 -1
  148. package/dist/rule-lab/analyze.js +94 -59
  149. package/dist/rule-lab/analyze.test.js +238 -20
  150. package/dist/rule-lab/curate.d.ts +2 -1
  151. package/dist/rule-lab/curate.js +24 -3
  152. package/dist/rule-lab/curate.test.js +65 -0
  153. package/dist/rule-lab/curation-input-builder.d.ts +45 -0
  154. package/dist/rule-lab/curation-input-builder.js +133 -0
  155. package/dist/rule-lab/promote.js +80 -7
  156. package/dist/rule-lab/promote.test.js +150 -0
  157. package/dist/rule-lab/review-pack.d.ts +3 -0
  158. package/dist/rule-lab/review-pack.js +41 -1
  159. package/dist/rule-lab/review-pack.test.js +67 -0
  160. package/dist/rule-lab/types.d.ts +29 -0
  161. package/dist/types/pipeline.d.ts +16 -0
  162. package/package.json +14 -13
  163. package/scripts/check-sync-manifest-traceability.mjs +203 -0
  164. package/scripts/run-node-tests.mjs +61 -0
  165. package/scripts/tree-sitter-audit-classify.mjs +172 -0
  166. package/skills/_shared/unity-rule-authoring-contract.md +64 -0
  167. package/skills/_shared/unity-runtime-process-contract.md +16 -0
  168. package/skills/gitnexus-cli.md +44 -4
  169. package/skills/gitnexus-debugging.md +9 -0
  170. package/skills/gitnexus-exploring.md +66 -18
  171. package/skills/gitnexus-guide.md +42 -3
  172. package/skills/gitnexus-impact-analysis.md +8 -0
  173. package/skills/gitnexus-pr-review.md +8 -0
  174. package/skills/gitnexus-refactoring.md +8 -0
  175. package/skills/gitnexus-unity-rule-gen.md +66 -312
@@ -0,0 +1,31 @@
1
+ type RuntimeStatus = 'verified_full' | 'verified_partial' | 'failed';
2
+ type RuntimeEvidenceLevel = 'verified_chain' | 'verified_segment' | 'clue' | 'none';
3
+ export interface RuntimePocCase {
4
+ case_id: string;
5
+ query_text: string;
6
+ symbol_name?: string;
7
+ resource_seed_path?: string;
8
+ mapped_seed_targets?: string[];
9
+ baseline: {
10
+ status: RuntimeStatus;
11
+ evidence_level: RuntimeEvidenceLevel;
12
+ reason?: string;
13
+ };
14
+ graph_only: {
15
+ status: RuntimeStatus;
16
+ evidence_level: RuntimeEvidenceLevel;
17
+ reason?: string;
18
+ };
19
+ }
20
+ export interface RuntimePocBenchmarkResult {
21
+ comparisonPath: string;
22
+ summaryPath: string;
23
+ provenanceArtifactPath: string;
24
+ provenanceIndexPath: string;
25
+ }
26
+ export declare function runRuntimePocBenchmark(input: {
27
+ repo: string;
28
+ reportDir: string;
29
+ casesPath?: string;
30
+ }): Promise<RuntimePocBenchmarkResult>;
31
+ export {};
@@ -0,0 +1,163 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { writeRuntimeProvenanceArtifact, } from './provenance-artifact.js';
4
+ function toIsoStamp(date = new Date()) {
5
+ return date.toISOString().replace(/\.\d{3}Z$/, 'Z');
6
+ }
7
+ function normalizeStatus(value) {
8
+ const normalized = String(value || '').trim().toLowerCase();
9
+ if (normalized === 'verified_full')
10
+ return 'verified_full';
11
+ if (normalized === 'verified_partial')
12
+ return 'verified_partial';
13
+ return 'failed';
14
+ }
15
+ function normalizeEvidence(value) {
16
+ const normalized = String(value || '').trim().toLowerCase();
17
+ if (normalized === 'verified_chain')
18
+ return 'verified_chain';
19
+ if (normalized === 'verified_segment')
20
+ return 'verified_segment';
21
+ if (normalized === 'clue')
22
+ return 'clue';
23
+ return 'none';
24
+ }
25
+ function classifyFailureBucket(input) {
26
+ if (normalizeStatus(input.graph_only.status) !== 'failed')
27
+ return 'none';
28
+ const reason = String(input.graph_only.reason || '').trim().toLowerCase();
29
+ if (reason.includes('anchor'))
30
+ return 'anchor_gap';
31
+ if (reason.includes('bind') || reason.includes('guid'))
32
+ return 'bind_gap';
33
+ if (reason.includes('bridge') || reason.includes('loader'))
34
+ return 'bridge_gap';
35
+ if (reason.includes('runtime'))
36
+ return 'runtime_gap';
37
+ if (reason.includes('policy') || reason.includes('precision'))
38
+ return 'precision_penalty';
39
+ return 'unknown_gap';
40
+ }
41
+ function buildComparisonRows(cases) {
42
+ return cases.map((item) => {
43
+ const baselineStatus = normalizeStatus(item.baseline.status);
44
+ const graphStatus = normalizeStatus(item.graph_only.status);
45
+ const failureBucket = classifyFailureBucket(item);
46
+ return {
47
+ case_id: String(item.case_id || '').trim(),
48
+ baseline_status: baselineStatus,
49
+ graph_only_status: graphStatus,
50
+ baseline_evidence_level: normalizeEvidence(item.baseline.evidence_level),
51
+ graph_only_evidence_level: normalizeEvidence(item.graph_only.evidence_level),
52
+ verified_full_false_positive: baselineStatus !== 'verified_full' && graphStatus === 'verified_full',
53
+ regression: baselineStatus === 'verified_full' && graphStatus !== 'verified_full',
54
+ failure_bucket: graphStatus === 'failed' ? failureBucket : 'none',
55
+ };
56
+ });
57
+ }
58
+ function rate(count, total) {
59
+ if (total <= 0)
60
+ return 0;
61
+ return Number((count / total).toFixed(6));
62
+ }
63
+ function toMarkdown(report) {
64
+ return [
65
+ '# Runtime PoC Comparison',
66
+ '',
67
+ `- Generated At: ${report.generated_at}`,
68
+ `- Repo: ${report.repo}`,
69
+ `- Total Cases: ${report.summary.total_cases}`,
70
+ `- verified_full_false_positive_rate: ${report.summary.verified_full_false_positive_rate}`,
71
+ `- regression_count: ${report.summary.regression_count}`,
72
+ `- graph_failed_count: ${report.summary.graph_failed_count}`,
73
+ '',
74
+ '## Rows',
75
+ '',
76
+ '| case_id | baseline_status | graph_only_status | false_positive | regression | failure_bucket |',
77
+ '| --- | --- | --- | --- | --- | --- |',
78
+ ...report.comparison_rows.map((row) => (`| ${row.case_id} | ${row.baseline_status} | ${row.graph_only_status} | ${row.verified_full_false_positive} | ${row.regression} | ${row.failure_bucket} |`)),
79
+ '',
80
+ ].join('\n');
81
+ }
82
+ async function loadCases(casesPath) {
83
+ if (!casesPath) {
84
+ return [
85
+ {
86
+ case_id: 'default-case-1',
87
+ query_text: 'Reload GunGraph',
88
+ symbol_name: 'GunGraph',
89
+ resource_seed_path: 'Assets/NEON/DataAssets/weapon.asset',
90
+ mapped_seed_targets: ['Assets/NEON/Graphs/weapon_graph.asset'],
91
+ baseline: { status: 'verified_full', evidence_level: 'verified_chain' },
92
+ graph_only: { status: 'verified_full', evidence_level: 'verified_chain' },
93
+ },
94
+ {
95
+ case_id: 'default-case-2',
96
+ query_text: 'Reload WeaponPowerUp',
97
+ symbol_name: 'WeaponPowerUp',
98
+ resource_seed_path: 'Assets/NEON/DataAssets/orb.asset',
99
+ mapped_seed_targets: ['Assets/NEON/Graphs/global.asset'],
100
+ baseline: { status: 'failed', evidence_level: 'none', reason: 'rule_not_matched' },
101
+ graph_only: { status: 'failed', evidence_level: 'none', reason: 'anchor_gap' },
102
+ },
103
+ ];
104
+ }
105
+ const raw = await fs.readFile(path.resolve(casesPath), 'utf-8');
106
+ const parsed = JSON.parse(raw);
107
+ if (!Array.isArray(parsed)) {
108
+ throw new Error('runtime-poc cases file must be a JSON array');
109
+ }
110
+ return parsed;
111
+ }
112
+ function toProvenanceRecords(cases) {
113
+ return cases.map((item) => ({
114
+ scenario_id: String(item.case_id || '').trim(),
115
+ query_text: String(item.query_text || '').trim(),
116
+ ...(String(item.symbol_name || '').trim() ? { symbol_name: String(item.symbol_name).trim() } : {}),
117
+ ...(String(item.resource_seed_path || '').trim()
118
+ ? { resource_seed_path: String(item.resource_seed_path).trim() }
119
+ : {}),
120
+ mapped_seed_targets: Array.isArray(item.mapped_seed_targets) ? item.mapped_seed_targets : [],
121
+ runtime_claim: {
122
+ status: normalizeStatus(item.graph_only.status),
123
+ evidence_level: normalizeEvidence(item.graph_only.evidence_level),
124
+ ...(String(item.graph_only.reason || '').trim() ? { reason: String(item.graph_only.reason).trim() } : {}),
125
+ },
126
+ }));
127
+ }
128
+ export async function runRuntimePocBenchmark(input) {
129
+ const reportDir = path.resolve(input.reportDir);
130
+ await fs.mkdir(reportDir, { recursive: true });
131
+ const cases = await loadCases(input.casesPath);
132
+ const rows = buildComparisonRows(cases);
133
+ const falsePositiveCount = rows.filter((row) => row.verified_full_false_positive).length;
134
+ const regressionCount = rows.filter((row) => row.regression).length;
135
+ const graphFailedCount = rows.filter((row) => row.graph_only_status === 'failed').length;
136
+ const report = {
137
+ generated_at: toIsoStamp(),
138
+ repo: String(input.repo || '').trim(),
139
+ comparison_rows: rows,
140
+ summary: {
141
+ total_cases: rows.length,
142
+ verified_full_false_positive_count: falsePositiveCount,
143
+ verified_full_false_positive_rate: rate(falsePositiveCount, rows.length),
144
+ regression_count: regressionCount,
145
+ graph_failed_count: graphFailedCount,
146
+ },
147
+ };
148
+ const comparisonPath = path.join(reportDir, 'runtime-poc-comparison.json');
149
+ await fs.writeFile(comparisonPath, `${JSON.stringify(report, null, 2)}\n`, 'utf-8');
150
+ const summaryPath = path.join(reportDir, 'runtime-poc-summary.md');
151
+ await fs.writeFile(summaryPath, `${toMarkdown(report)}\n`, 'utf-8');
152
+ const provenance = await writeRuntimeProvenanceArtifact({
153
+ reportDir,
154
+ repo: report.repo,
155
+ records: toProvenanceRecords(cases),
156
+ });
157
+ return {
158
+ comparisonPath,
159
+ summaryPath,
160
+ provenanceArtifactPath: provenance.artifactPath,
161
+ provenanceIndexPath: provenance.indexPath,
162
+ };
163
+ }
@@ -39,6 +39,14 @@ export interface HydrationPolicyRepeatabilityReport {
39
39
  requiresArray: boolean;
40
40
  populatedWhenIncomplete: boolean;
41
41
  };
42
+ semantic_contract: {
43
+ coreAdjustedDelta: {
44
+ strictFallbackRuns: number;
45
+ strictFallbackAdjustedRuns: number;
46
+ nonStrictAdjustedRuns: number;
47
+ };
48
+ downgradeOnlyWhenStrictFallback: boolean;
49
+ };
42
50
  contractCompatibility: {
43
51
  needsParityRetryRetained: boolean;
44
52
  };
@@ -22,6 +22,12 @@ function snapshotFromResponse(out) {
22
22
  return {
23
23
  status: String(out?.runtime_claim?.status || 'unknown'),
24
24
  evidence_level: String(out?.runtime_claim?.evidence_level || 'none'),
25
+ verification_core_status: String(out?.runtime_claim?.verification_core_status || 'unknown'),
26
+ verification_core_evidence_level: String(out?.runtime_claim?.verification_core_evidence_level || 'none'),
27
+ policy_adjusted: Boolean(out?.runtime_claim?.policy_adjusted),
28
+ policy_adjust_reason: out?.runtime_claim?.policy_adjust_reason
29
+ ? String(out.runtime_claim.policy_adjust_reason)
30
+ : undefined,
25
31
  reason: out?.runtime_claim?.reason ? String(out.runtime_claim.reason) : undefined,
26
32
  fallbackToCompact: Boolean(out?.hydrationMeta?.fallbackToCompact),
27
33
  };
@@ -100,11 +106,18 @@ export async function buildHydrationPolicyRepeatabilityReport(input) {
100
106
  const balancedConsistency = classifyConsistency(snapshotsByPolicy.get('balanced') || []);
101
107
  const strictConsistency = classifyConsistency(snapshotsByPolicy.get('strict') || []);
102
108
  const strictRows = snapshotsByPolicy.get('strict') || [];
109
+ const fastRows = snapshotsByPolicy.get('fast') || [];
110
+ const balancedRows = snapshotsByPolicy.get('balanced') || [];
103
111
  const strictFallbackDowngraded = strictRows.every((row) => {
104
112
  if (!row.fallbackToCompact)
105
113
  return true;
106
114
  return row.status === 'verified_partial' && row.evidence_level === 'verified_segment';
107
115
  });
116
+ const strictFallbackRuns = strictRows.filter((row) => row.fallbackToCompact).length;
117
+ const strictFallbackAdjustedRuns = strictRows.filter((row) => row.fallbackToCompact && row.policy_adjusted).length;
118
+ const nonStrictAdjustedRuns = [...fastRows, ...balancedRows].filter((row) => row.policy_adjusted).length;
119
+ const downgradeOnlyWhenStrictFallback = nonStrictAdjustedRuns === 0
120
+ && strictFallbackAdjustedRuns <= strictFallbackRuns;
108
121
  const policy_mode_matrix = {};
109
122
  for (const policy of policies) {
110
123
  const compactOut = await callPolicyProbe({
@@ -155,6 +168,14 @@ export async function buildHydrationPolicyRepeatabilityReport(input) {
155
168
  requiresArray,
156
169
  populatedWhenIncomplete,
157
170
  },
171
+ semantic_contract: {
172
+ coreAdjustedDelta: {
173
+ strictFallbackRuns,
174
+ strictFallbackAdjustedRuns,
175
+ nonStrictAdjustedRuns,
176
+ },
177
+ downgradeOnlyWhenStrictFallback,
178
+ },
158
179
  contractCompatibility: {
159
180
  needsParityRetryRetained,
160
181
  },
@@ -17,7 +17,6 @@ export interface Phase2RuntimeClaimAcceptanceReport {
17
17
  evidence_missing_reason?: string;
18
18
  verification_failed_reason?: string;
19
19
  unmatched_reason?: string;
20
- gate_disabled_reason?: string;
21
20
  };
22
21
  reproduction_commands: Record<string, string>;
23
22
  }
@@ -1,58 +1,62 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { LocalBackend } from '../../mcp/local/local-backend.js';
4
+ import { verifyRuntimeClaimOnDemand } from '../../mcp/local/runtime-chain-verify.js';
5
+ function makeSymbolOnlyExecutor(symbolName, filePath) {
6
+ return async (query, params) => {
7
+ if (!String(query || '').includes('WHERE n.name IN $symbolNames')) {
8
+ return [];
9
+ }
10
+ const names = Array.isArray(params?.symbolNames) ? params?.symbolNames : [];
11
+ if (!names.includes(symbolName))
12
+ return [];
13
+ return [{
14
+ id: `Class:${filePath}:${symbolName}`,
15
+ name: symbolName,
16
+ type: 'Class',
17
+ filePath,
18
+ startLine: 1,
19
+ }];
20
+ };
21
+ }
5
22
  export async function buildPhase2RuntimeClaimAcceptanceReport(input) {
6
- const backend = new LocalBackend();
7
- const ready = await backend.init();
8
- if (!ready) {
9
- throw new Error('LocalBackend failed to initialize for phase2 acceptance runner');
10
- }
11
- const matched = await backend.callTool('query', {
12
- repo: input.repoAlias,
13
- query: 'Reload',
14
- unity_resources: 'on',
15
- runtime_chain_verify: 'on-demand',
23
+ const repoPath = path.resolve('.');
24
+ const verificationFailedClaim = await verifyRuntimeClaimOnDemand({
25
+ repoPath,
26
+ queryText: 'Reload',
27
+ symbolName: 'ReloadBase',
28
+ symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
29
+ resourceSeedPath: 'Assets/Rules/reload.asset',
30
+ resourceBindings: [{ resourcePath: 'Assets/Rules/reload.asset' }],
31
+ executeParameterized: makeSymbolOnlyExecutor('ReloadBase', 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs'),
16
32
  });
17
- const evidenceMissing = await backend.callTool('query', {
18
- repo: input.repoAlias,
19
- query: 'Reload',
20
- unity_resources: 'on',
21
- runtime_chain_verify: 'on-demand',
22
- unity_evidence_mode: 'summary',
23
- max_bindings: 1,
24
- max_reference_fields: 1,
33
+ const evidenceMissingClaim = await verifyRuntimeClaimOnDemand({
34
+ repoPath,
35
+ queryText: 'Reload',
36
+ symbolName: 'ReloadBase',
37
+ symbolFilePath: 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs',
38
+ resourceSeedPath: 'Assets/Rules/reload.asset',
39
+ resourceBindings: [{ resourcePath: 'Assets/Rules/reload.asset' }],
40
+ minimumEvidenceSatisfied: false,
41
+ executeParameterized: makeSymbolOnlyExecutor('ReloadBase', 'Assets/NEON/Code/Game/Graph/Nodes/Reloads/ReloadBase.cs'),
25
42
  });
26
- const unmatched = await backend.callTool('query', {
27
- repo: input.repoAlias,
28
- query: 'UnrelatedUnityChain',
29
- unity_resources: 'on',
30
- runtime_chain_verify: 'on-demand',
43
+ const unmatchedClaim = await verifyRuntimeClaimOnDemand({
44
+ repoPath,
45
+ queryText: 'UnrelatedUnityChain',
46
+ resourceBindings: [],
47
+ executeParameterized: async () => [],
31
48
  });
32
- // gate_disabled reason is no longer produced (env var gate removed in config migration)
33
- let gateDisabled;
34
- try {
35
- gateDisabled = await backend.callTool('query', {
36
- repo: input.repoAlias,
37
- query: 'Reload',
38
- unity_resources: 'on',
39
- runtime_chain_verify: 'off',
40
- });
41
- }
42
- finally {
43
- }
44
- const claim = matched.runtime_claim || {};
49
+ const verificationFailedReason = String(verificationFailedClaim.reason || 'rule_matched_but_verification_failed');
50
+ const claim = verificationFailedClaim;
45
51
  const requiredReasons = [
46
52
  'rule_not_matched',
47
53
  'rule_matched_but_evidence_missing',
48
54
  'rule_matched_but_verification_failed',
49
- 'gate_disabled',
50
55
  ];
51
56
  const reasons = [
52
- claim.reason,
53
- evidenceMissing.runtime_claim?.reason,
54
- unmatched.runtime_claim?.reason,
55
- gateDisabled.runtime_claim?.reason,
57
+ verificationFailedReason,
58
+ evidenceMissingClaim.reason,
59
+ unmatchedClaim.reason,
56
60
  ]
57
61
  .filter(Boolean)
58
62
  .map((reason) => String(reason));
@@ -75,20 +79,18 @@ export async function buildPhase2RuntimeClaimAcceptanceReport(input) {
75
79
  samples: {
76
80
  matched_status: claim.status,
77
81
  matched_reason: claim.reason,
78
- evidence_missing_reason: evidenceMissing.runtime_claim?.reason,
79
- verification_failed_reason: claim.reason,
80
- unmatched_reason: unmatched.runtime_claim?.reason,
81
- gate_disabled_reason: gateDisabled.runtime_claim?.reason,
82
+ evidence_missing_reason: evidenceMissingClaim.reason,
83
+ verification_failed_reason: verificationFailedReason,
84
+ unmatched_reason: unmatchedClaim.reason,
82
85
  },
83
86
  reproduction_commands: {
84
- rule_matched_but_verification_failed: `gitnexus query --repo ${input.repoAlias} --runtime-chain-verify on-demand --unity-resources on "Reload"`,
85
- rule_matched_but_evidence_missing: `gitnexus query --repo ${input.repoAlias} --runtime-chain-verify on-demand --unity-resources on --unity-evidence-mode summary --max-bindings 1 --max-reference-fields 1 "Reload"`,
86
- rule_not_matched: `gitnexus query --repo ${input.repoAlias} --runtime-chain-verify on-demand --unity-resources on "UnrelatedUnityChain"`,
87
- gate_disabled: `gitnexus query --repo ${input.repoAlias} --runtime-chain-verify off --unity-resources on "Reload"`,
87
+ rule_matched_but_verification_failed: 'gitnexus query --runtime-chain-verify on-demand --unity-resources on "Reload"',
88
+ rule_matched_but_evidence_missing: 'gitnexus query --runtime-chain-verify on-demand --unity-resources on --unity-evidence-mode summary --max-bindings 1 --max-reference-fields 1 "Reload"',
89
+ rule_not_matched: 'gitnexus query --runtime-chain-verify on-demand --unity-resources on "UnrelatedUnityChain"',
88
90
  },
89
91
  };
90
92
  if (!coverage_pass) {
91
- throw new Error(`phase2 failure classification coverage is incomplete (${failure_classification_coverage.length}/4). Missing: ${failure_classification_missing.join(', ')}`);
93
+ throw new Error(`phase2 failure classification coverage is incomplete (${failure_classification_coverage.length}/3). Missing: ${failure_classification_missing.join(', ')}`);
92
94
  }
93
95
  return report;
94
96
  }
@@ -12,5 +12,4 @@ test('phase2 runtime_claim acceptance report tracks contract + failure classific
12
12
  assert.equal(report.failure_classification_coverage.includes('rule_not_matched'), true);
13
13
  assert.equal(report.failure_classification_coverage.includes('rule_matched_but_evidence_missing'), true);
14
14
  assert.equal(report.failure_classification_coverage.includes('rule_matched_but_verification_failed'), true);
15
- assert.equal(report.failure_classification_coverage.includes('gate_disabled'), true);
16
15
  });
@@ -1,5 +1,5 @@
1
1
  export interface Phase5StageCoverageRow {
2
- stage: 'discover' | 'analyze' | 'review-pack' | 'curate' | 'promote' | 'regress';
2
+ stage: 'analyze' | 'review-pack' | 'curate' | 'promote' | 'regress';
3
3
  command: string;
4
4
  status: 'passed' | 'failed';
5
5
  retry_hint?: string;
@@ -1,6 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { discoverRuleLabRun } from '../../rule-lab/discover.js';
4
3
  import { analyzeRuleLabSlice } from '../../rule-lab/analyze.js';
5
4
  import { buildReviewPack } from '../../rule-lab/review-pack.js';
6
5
  import { curateRuleLabSlice } from '../../rule-lab/curate.js';
@@ -8,8 +7,6 @@ import { promoteCuratedRules } from '../../rule-lab/promote.js';
8
7
  import { runRuleLabRegress } from '../../rule-lab/regress.js';
9
8
  function commandFor(stage, input) {
10
9
  switch (stage) {
11
- case 'discover':
12
- return 'gitnexus rule-lab discover --scope full';
13
10
  case 'analyze':
14
11
  return `gitnexus rule-lab analyze --run-id ${input.runId} --slice-id ${input.sliceId}`;
15
12
  case 'review-pack':
@@ -27,11 +24,69 @@ function commandFor(stage, input) {
27
24
  async function mustExist(filePath) {
28
25
  await fs.access(filePath);
29
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
+ }
30
85
  function buildFailureTaxonomy(runId) {
31
86
  return [
32
87
  {
33
88
  code: 'rule_not_matched',
34
- retry_hint: 'confirm trigger_family tokens and rerun discover/analyze/promote',
89
+ retry_hint: 'confirm trigger_family tokens and rerun analyze/promote',
35
90
  repro_command: `gitnexus rule-lab analyze --run-id ${runId} --slice-id <slice_id>`,
36
91
  },
37
92
  {
@@ -70,7 +125,20 @@ function hasDslPlaceholders(text) {
70
125
  return /(^|\s)(unknown|todo|tbd)(\s|$)|<[^>]+>/i.test(text);
71
126
  }
72
127
  async function buildAuthenticityChecks(input) {
73
- const verifierPath = path.join(input.repoPath, 'gitnexus', 'src', 'mcp', 'local', 'runtime-chain-verify.ts');
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
+ }
74
142
  const verifierRaw = await fs.readFile(verifierPath, 'utf-8');
75
143
  const blockedSymbols = [
76
144
  'RESOURCE_ASSET_PATH',
@@ -98,17 +166,12 @@ async function buildAuthenticityChecks(input) {
98
166
  export async function buildPhase5RuleLabAcceptanceReport(input) {
99
167
  const repoPath = path.resolve(input.repoPath || process.cwd());
100
168
  const stageCoverage = [];
101
- const discover = await discoverRuleLabRun({ repoPath, scope: 'full', seed: input.seed || 'phase5-acceptance' });
102
- stageCoverage.push({
103
- stage: 'discover',
104
- command: commandFor('discover', {}),
105
- status: 'passed',
169
+ const bootstrapped = await bootstrapReducedRuleLabRun({
170
+ repoPath,
171
+ seed: input.seed || 'phase5-acceptance',
106
172
  });
107
- if (discover.manifest.slices.length === 0) {
108
- throw new Error('discover produced no slices');
109
- }
110
- const sliceId = discover.manifest.slices[0].id;
111
- const runId = discover.runId;
173
+ const sliceId = bootstrapped.sliceId;
174
+ const runId = bootstrapped.runId;
112
175
  const analyze = await analyzeRuleLabSlice({ repoPath, runId, sliceId });
113
176
  stageCoverage.push({
114
177
  stage: 'analyze',
@@ -122,6 +185,7 @@ export async function buildPhase5RuleLabAcceptanceReport(input) {
122
185
  status: 'passed',
123
186
  });
124
187
  const firstCandidate = analyze.candidates[0];
188
+ const promotedRuleId = `demo.startup.${runId}.v1`;
125
189
  const curationInputPath = path.join(repoPath, '.gitnexus', 'rules', 'lab', 'runs', runId, 'slices', sliceId, 'curation-input.json');
126
190
  await fs.writeFile(curationInputPath, `${JSON.stringify({
127
191
  run_id: runId,
@@ -129,7 +193,7 @@ export async function buildPhase5RuleLabAcceptanceReport(input) {
129
193
  curated: [
130
194
  {
131
195
  id: firstCandidate.id,
132
- rule_id: 'demo.startup.v1',
196
+ rule_id: promotedRuleId,
133
197
  title: 'startup startup graph',
134
198
  match: {
135
199
  trigger_tokens: ['startup'],
@@ -195,7 +259,7 @@ export async function buildPhase5RuleLabAcceptanceReport(input) {
195
259
  retry_hint: regress.pass ? undefined : regress.failures.join(','),
196
260
  });
197
261
  const artifactPaths = {
198
- manifest: discover.paths.manifestPath,
262
+ manifest: bootstrapped.manifestPath,
199
263
  candidates: analyze.paths.candidatesPath,
200
264
  review_cards: reviewPack.paths.reviewCardsPath,
201
265
  curation_input: curationInputPath,
@@ -240,7 +304,7 @@ export async function runPhase5RuleLabGate(input) {
240
304
  try {
241
305
  const raw = await fs.readFile(reportPath, 'utf-8');
242
306
  const report = JSON.parse(raw);
243
- if (!Array.isArray(report.stage_coverage) || report.stage_coverage.length !== 6) {
307
+ if (!Array.isArray(report.stage_coverage) || report.stage_coverage.length !== 5) {
244
308
  return { pass: false, reason: 'stage_coverage_incomplete' };
245
309
  }
246
310
  if (typeof report.metrics?.precision !== 'number' || typeof report.metrics?.coverage !== 'number') {
@@ -9,7 +9,7 @@ const { test: rawTest } = process.env.VITEST
9
9
  const test = rawTest;
10
10
  test('phase5 rule-lab acceptance runner emits complete stage coverage', async () => {
11
11
  const report = await buildPhase5RuleLabAcceptanceReport({ repoAlias: 'GitNexus' });
12
- assert.equal(report.stage_coverage.length, 6);
12
+ assert.equal(report.stage_coverage.length, 5);
13
13
  assert.equal(typeof report.metrics.precision, 'number');
14
14
  });
15
15
  test('phase5 gate fails when required artifacts are missing', async () => {
@@ -22,7 +22,6 @@ test('phase5 gate fails when anti-hardcode scan or dsl lint fails', async () =>
22
22
  const reportPath = path.join(tmpDir, 'report.json');
23
23
  await fs.writeFile(reportPath, JSON.stringify({
24
24
  stage_coverage: [
25
- { stage: 'discover', status: 'passed' },
26
25
  { stage: 'analyze', status: 'passed' },
27
26
  { stage: 'review-pack', status: 'passed' },
28
27
  { stage: 'curate', status: 'passed' },
@@ -191,16 +191,17 @@ function assertScenario(scenario, contextOnOutput, deepDiveExecutions, contextUn
191
191
  };
192
192
  }
193
193
  async function invokeTool(runner, tool, input) {
194
+ const params = withLegacyFullProfile(tool, input);
194
195
  if (tool === 'query') {
195
- return runner.query(input);
196
+ return runner.query(params);
196
197
  }
197
198
  if (tool === 'context') {
198
- return runner.context(input);
199
+ return runner.context(params);
199
200
  }
200
201
  if (tool === 'impact') {
201
- return runner.impact(input);
202
+ return runner.impact(params);
202
203
  }
203
- return runner.cypher(input);
204
+ return runner.cypher(params);
204
205
  }
205
206
  function selectDisambiguationUid(symbol, output) {
206
207
  const expectedFile = `${symbol}.cs`.toLowerCase();
@@ -222,20 +223,21 @@ function selectDisambiguationUid(symbol, output) {
222
223
  return undefined;
223
224
  }
224
225
  async function runContextWithDisambiguation(runner, scenario, input) {
225
- const first = await runner.context(input);
226
+ const normalizedInput = withLegacyFullProfile('context', input);
227
+ const first = await runner.context(normalizedInput);
226
228
  if (first?.status !== 'ambiguous') {
227
229
  return first;
228
230
  }
229
231
  const hint = typeof scenario.contextFileHint === 'string' ? scenario.contextFileHint.trim() : '';
230
232
  if (hint) {
231
- const hinted = await runner.context({ ...input, file_path: hint });
233
+ const hinted = await runner.context(withLegacyFullProfile('context', { ...input, file_path: hint }));
232
234
  if (hinted?.status !== 'ambiguous') {
233
235
  return hinted;
234
236
  }
235
237
  }
236
238
  const uid = selectDisambiguationUid(scenario.symbol, first);
237
239
  if (uid) {
238
- return runner.context({ ...input, uid });
240
+ return runner.context(withLegacyFullProfile('context', { ...input, uid }));
239
241
  }
240
242
  return first;
241
243
  }
@@ -276,3 +278,9 @@ export async function runSymbolScenario(runner, scenario, repo) {
276
278
  assertions: assertScenario(scenario, contextOn, deepDiveExecutions, contextUnityHydration),
277
279
  };
278
280
  }
281
+ function withLegacyFullProfile(tool, input) {
282
+ if ((tool === 'query' || tool === 'context') && !('response_profile' in input)) {
283
+ return { ...input, response_profile: 'full' };
284
+ }
285
+ return input;
286
+ }