@plures/praxis 1.2.0 → 1.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -96
- package/dist/browser/{adapter-TM4IS5KT.js → adapter-CIMBGDC7.js} +5 -3
- package/dist/browser/{chunk-LE2ZJYFC.js → chunk-K377RW4V.js} +76 -0
- package/dist/{node/chunk-JQ64KMLN.js → browser/chunk-MBVHLOU2.js} +12 -1
- package/dist/browser/index.d.ts +32 -5
- package/dist/browser/index.js +15 -7
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +1 -1
- package/dist/browser/{reactive-engine.svelte-C9OpcTHf.d.ts → reactive-engine.svelte-9aS0kTa8.d.ts} +136 -1
- package/dist/node/{adapter-K6DOX6XS.js → adapter-75ISSMWD.js} +5 -3
- package/dist/node/chunk-5RH7UAQC.js +486 -0
- package/dist/{browser/chunk-JQ64KMLN.js → node/chunk-MBVHLOU2.js} +12 -1
- package/dist/node/{chunk-LE2ZJYFC.js → chunk-PRPQO6R5.js} +3 -72
- package/dist/node/chunk-R2PSBPKQ.js +150 -0
- package/dist/node/chunk-WZ6B3LZ6.js +638 -0
- package/dist/node/cli/index.cjs +2316 -832
- package/dist/node/cli/index.js +18 -0
- package/dist/node/components/index.d.cts +3 -2
- package/dist/node/components/index.d.ts +3 -2
- package/dist/node/index.cjs +620 -38
- package/dist/node/index.d.cts +259 -5
- package/dist/node/index.d.ts +259 -5
- package/dist/node/index.js +55 -65
- package/dist/node/integrations/svelte.cjs +76 -0
- package/dist/node/integrations/svelte.d.cts +2 -2
- package/dist/node/integrations/svelte.d.ts +2 -2
- package/dist/node/integrations/svelte.js +2 -1
- package/dist/node/{reactive-engine.svelte-1M4m_C_v.d.cts → reactive-engine.svelte-BFIZfawz.d.cts} +199 -1
- package/dist/node/{reactive-engine.svelte-ChNFn4Hj.d.ts → reactive-engine.svelte-CRNqHlbv.d.ts} +199 -1
- package/dist/node/reverse-W7THPV45.js +193 -0
- package/dist/node/{terminal-adapter-CWka-yL8.d.ts → terminal-adapter-B-UK_Vdz.d.ts} +28 -3
- package/dist/node/{terminal-adapter-CDzxoLKR.d.cts → terminal-adapter-BQSIF5bf.d.cts} +28 -3
- package/dist/node/validate-CNHUULQE.js +180 -0
- package/docs/core/pluresdb-integration.md +15 -15
- package/docs/decision-ledger/BEHAVIOR_LEDGER.md +225 -0
- package/docs/decision-ledger/DecisionLedger.tla +180 -0
- package/docs/decision-ledger/IMPLEMENTATION_SUMMARY.md +217 -0
- package/docs/decision-ledger/LATEST.md +166 -0
- package/docs/guides/cicd-pipeline.md +142 -0
- package/package.json +2 -2
- package/src/__tests__/cli-validate.test.ts +197 -0
- package/src/__tests__/decision-ledger.test.ts +485 -0
- package/src/__tests__/reverse-generator.test.ts +189 -0
- package/src/__tests__/scanner.test.ts +215 -0
- package/src/cli/commands/reverse.ts +289 -0
- package/src/cli/commands/validate.ts +264 -0
- package/src/cli/index.ts +47 -0
- package/src/core/pluresdb/adapter.ts +45 -2
- package/src/core/rules.ts +133 -0
- package/src/decision-ledger/README.md +400 -0
- package/src/decision-ledger/REVERSE_ENGINEERING.md +484 -0
- package/src/decision-ledger/facts-events.ts +121 -0
- package/src/decision-ledger/index.ts +70 -0
- package/src/decision-ledger/ledger.ts +246 -0
- package/src/decision-ledger/logic-ledger.ts +158 -0
- package/src/decision-ledger/reverse-generator.ts +426 -0
- package/src/decision-ledger/scanner.ts +506 -0
- package/src/decision-ledger/types.ts +247 -0
- package/src/decision-ledger/validation.ts +336 -0
- package/src/dsl/index.ts +13 -2
- package/src/index.browser.ts +2 -0
- package/src/index.ts +36 -0
- package/src/integrations/pluresdb.ts +14 -2
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision Ledger - Reverse Contract Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates contracts from existing code using AI (GitHub Copilot/OpenAI) or
|
|
5
|
+
* non-AI heuristic approaches.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Contract, Example, Assumption, Reference } from './types.js';
|
|
9
|
+
import type { RuleDescriptor, ConstraintDescriptor } from '../core/rules.js';
|
|
10
|
+
import { defineContract } from './types.js';
|
|
11
|
+
import { inferContractFromFile } from './scanner.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AI provider for contract generation.
|
|
15
|
+
*/
|
|
16
|
+
export type AIProvider = 'none' | 'github-copilot' | 'openai' | 'auto';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Options for reverse contract generation.
|
|
20
|
+
*/
|
|
21
|
+
export interface ReverseGenerationOptions {
|
|
22
|
+
/** AI provider to use */
|
|
23
|
+
aiProvider?: AIProvider;
|
|
24
|
+
/** OpenAI API key (if using OpenAI) */
|
|
25
|
+
openaiApiKey?: string;
|
|
26
|
+
/** GitHub token (if using GitHub Copilot) */
|
|
27
|
+
githubToken?: string;
|
|
28
|
+
/** Confidence threshold for AI-generated content (0.0 to 1.0) */
|
|
29
|
+
confidenceThreshold?: number;
|
|
30
|
+
/** Whether to include assumptions */
|
|
31
|
+
includeAssumptions?: boolean;
|
|
32
|
+
/** Whether to generate examples from tests */
|
|
33
|
+
generateExamples?: boolean;
|
|
34
|
+
/** Source file path for the rule/constraint */
|
|
35
|
+
sourceFile?: string;
|
|
36
|
+
/** Test file paths associated with the rule */
|
|
37
|
+
testFiles?: string[];
|
|
38
|
+
/** Spec file paths associated with the rule */
|
|
39
|
+
specFiles?: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Result of reverse contract generation.
|
|
44
|
+
*/
|
|
45
|
+
export interface GenerationResult {
|
|
46
|
+
/** Generated contract */
|
|
47
|
+
contract: Contract;
|
|
48
|
+
/** Confidence score (0.0 to 1.0) */
|
|
49
|
+
confidence: number;
|
|
50
|
+
/** Method used for generation */
|
|
51
|
+
method: 'ai' | 'heuristic' | 'hybrid';
|
|
52
|
+
/** Warnings or issues encountered */
|
|
53
|
+
warnings: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Generate a contract from an existing rule or constraint.
|
|
58
|
+
*
|
|
59
|
+
* @param descriptor The rule or constraint descriptor
|
|
60
|
+
* @param options Generation options
|
|
61
|
+
* @returns Generation result
|
|
62
|
+
*/
|
|
63
|
+
export async function generateContractFromRule(
|
|
64
|
+
descriptor: RuleDescriptor | ConstraintDescriptor,
|
|
65
|
+
options: ReverseGenerationOptions = {}
|
|
66
|
+
): Promise<GenerationResult> {
|
|
67
|
+
const {
|
|
68
|
+
aiProvider = 'none',
|
|
69
|
+
confidenceThreshold = 0.7,
|
|
70
|
+
includeAssumptions = true,
|
|
71
|
+
generateExamples = true,
|
|
72
|
+
sourceFile,
|
|
73
|
+
testFiles = [],
|
|
74
|
+
specFiles = [],
|
|
75
|
+
} = options;
|
|
76
|
+
|
|
77
|
+
const warnings: string[] = [];
|
|
78
|
+
|
|
79
|
+
// Attempt AI-powered generation first
|
|
80
|
+
if (aiProvider !== 'none') {
|
|
81
|
+
try {
|
|
82
|
+
const aiResult = await generateWithAI(descriptor, options);
|
|
83
|
+
if (aiResult.confidence >= confidenceThreshold) {
|
|
84
|
+
return aiResult;
|
|
85
|
+
}
|
|
86
|
+
warnings.push(
|
|
87
|
+
`AI confidence ${aiResult.confidence} below threshold ${confidenceThreshold}, falling back to heuristic`
|
|
88
|
+
);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
warnings.push(`AI generation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Fallback to heuristic generation
|
|
95
|
+
const heuristicResult = await generateWithHeuristics(
|
|
96
|
+
descriptor,
|
|
97
|
+
{ sourceFile, testFiles, specFiles, includeAssumptions, generateExamples }
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...heuristicResult,
|
|
102
|
+
warnings: [...warnings, ...heuristicResult.warnings],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generate contract using AI.
|
|
108
|
+
*/
|
|
109
|
+
async function generateWithAI(
|
|
110
|
+
descriptor: RuleDescriptor | ConstraintDescriptor,
|
|
111
|
+
options: ReverseGenerationOptions
|
|
112
|
+
): Promise<GenerationResult> {
|
|
113
|
+
const { aiProvider, openaiApiKey, githubToken } = options;
|
|
114
|
+
|
|
115
|
+
if (aiProvider === 'openai') {
|
|
116
|
+
if (!openaiApiKey) {
|
|
117
|
+
throw new Error('OpenAI API key is required for OpenAI provider');
|
|
118
|
+
}
|
|
119
|
+
return await generateWithOpenAI(descriptor, openaiApiKey, options);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (aiProvider === 'github-copilot') {
|
|
123
|
+
if (!githubToken) {
|
|
124
|
+
throw new Error('GitHub token is required for GitHub Copilot provider');
|
|
125
|
+
}
|
|
126
|
+
return await generateWithGitHubCopilot(descriptor, githubToken, options);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (aiProvider === 'auto') {
|
|
130
|
+
// Try GitHub Copilot first, then OpenAI
|
|
131
|
+
if (githubToken) {
|
|
132
|
+
return await generateWithGitHubCopilot(descriptor, githubToken, options);
|
|
133
|
+
}
|
|
134
|
+
if (openaiApiKey) {
|
|
135
|
+
return await generateWithOpenAI(descriptor, openaiApiKey, options);
|
|
136
|
+
}
|
|
137
|
+
throw new Error('Auto AI provider requires either GitHub token or OpenAI API key');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
throw new Error(`Unsupported AI provider: ${aiProvider}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Generate contract using OpenAI.
|
|
145
|
+
*/
|
|
146
|
+
async function generateWithOpenAI(
|
|
147
|
+
descriptor: RuleDescriptor | ConstraintDescriptor,
|
|
148
|
+
_apiKey: string,
|
|
149
|
+
options: ReverseGenerationOptions
|
|
150
|
+
): Promise<GenerationResult> {
|
|
151
|
+
// Build prompt for OpenAI
|
|
152
|
+
// const prompt = buildPromptForContract(descriptor, options);
|
|
153
|
+
|
|
154
|
+
// In a real implementation, this would call the OpenAI API
|
|
155
|
+
// For now, we'll return a placeholder indicating AI would be used
|
|
156
|
+
// const warnings: string[] = [
|
|
157
|
+
// 'OpenAI integration is a placeholder - implement with actual API calls',
|
|
158
|
+
// ];
|
|
159
|
+
|
|
160
|
+
// Fallback to heuristic for now
|
|
161
|
+
return await generateWithHeuristics(descriptor, {
|
|
162
|
+
sourceFile: options.sourceFile,
|
|
163
|
+
testFiles: options.testFiles,
|
|
164
|
+
specFiles: options.specFiles,
|
|
165
|
+
includeAssumptions: options.includeAssumptions,
|
|
166
|
+
generateExamples: options.generateExamples,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Generate contract using GitHub Copilot.
|
|
172
|
+
*/
|
|
173
|
+
async function generateWithGitHubCopilot(
|
|
174
|
+
descriptor: RuleDescriptor | ConstraintDescriptor,
|
|
175
|
+
_token: string,
|
|
176
|
+
options: ReverseGenerationOptions
|
|
177
|
+
): Promise<GenerationResult> {
|
|
178
|
+
// Build prompt for GitHub Copilot
|
|
179
|
+
// const prompt = buildPromptForContract(descriptor, options);
|
|
180
|
+
|
|
181
|
+
// In a real implementation, this would call the GitHub Copilot API
|
|
182
|
+
// For now, we'll return a placeholder indicating AI would be used
|
|
183
|
+
// const warnings: string[] = [
|
|
184
|
+
// 'GitHub Copilot integration is a placeholder - implement with actual API calls',
|
|
185
|
+
// ];
|
|
186
|
+
|
|
187
|
+
// Fallback to heuristic for now
|
|
188
|
+
return await generateWithHeuristics(descriptor, {
|
|
189
|
+
sourceFile: options.sourceFile,
|
|
190
|
+
testFiles: options.testFiles,
|
|
191
|
+
specFiles: options.specFiles,
|
|
192
|
+
includeAssumptions: options.includeAssumptions,
|
|
193
|
+
generateExamples: options.generateExamples,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Generate contract using heuristic analysis.
|
|
199
|
+
*/
|
|
200
|
+
async function generateWithHeuristics(
|
|
201
|
+
descriptor: RuleDescriptor | ConstraintDescriptor,
|
|
202
|
+
options: {
|
|
203
|
+
sourceFile?: string;
|
|
204
|
+
testFiles?: string[];
|
|
205
|
+
specFiles?: string[];
|
|
206
|
+
includeAssumptions?: boolean;
|
|
207
|
+
generateExamples?: boolean;
|
|
208
|
+
}
|
|
209
|
+
): Promise<GenerationResult> {
|
|
210
|
+
const warnings: string[] = [];
|
|
211
|
+
const { sourceFile, testFiles = [], specFiles = [] } = options;
|
|
212
|
+
|
|
213
|
+
// Start with basic information
|
|
214
|
+
let behavior = descriptor.description || `Process ${descriptor.id}`;
|
|
215
|
+
let examples: Example[] = [];
|
|
216
|
+
let invariants: string[] = [];
|
|
217
|
+
let assumptions: Assumption[] = [];
|
|
218
|
+
let references: Reference[] = [];
|
|
219
|
+
|
|
220
|
+
// Infer from source file if available
|
|
221
|
+
if (sourceFile) {
|
|
222
|
+
try {
|
|
223
|
+
const inferred = await inferContractFromFile(sourceFile, descriptor.id);
|
|
224
|
+
if (inferred.behavior) {
|
|
225
|
+
behavior = inferred.behavior;
|
|
226
|
+
}
|
|
227
|
+
if (inferred.invariants && inferred.invariants.length > 0) {
|
|
228
|
+
invariants = inferred.invariants;
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
warnings.push(`Failed to analyze source file: ${error instanceof Error ? error.message : String(error)}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Extract examples from test files
|
|
236
|
+
if (options.generateExamples && testFiles.length > 0) {
|
|
237
|
+
for (const testFile of testFiles) {
|
|
238
|
+
try {
|
|
239
|
+
const testExamples = await extractExamplesFromTests(testFile, descriptor.id);
|
|
240
|
+
examples.push(...testExamples);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
warnings.push(`Failed to extract examples from ${testFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// If no examples found, create a default one
|
|
248
|
+
if (examples.length === 0) {
|
|
249
|
+
examples.push({
|
|
250
|
+
given: `System is in a valid state`,
|
|
251
|
+
when: `${descriptor.id} is triggered`,
|
|
252
|
+
then: `Expected outcome is produced`,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (!options.generateExamples) {
|
|
256
|
+
warnings.push('Example generation disabled - using default example');
|
|
257
|
+
} else if (!testFiles || testFiles.length === 0) {
|
|
258
|
+
warnings.push('No test files provided - using default example');
|
|
259
|
+
} else {
|
|
260
|
+
warnings.push('No examples could be extracted from provided test files - using default example');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Add references to spec files
|
|
265
|
+
if (specFiles.length > 0) {
|
|
266
|
+
references = specFiles.map((file) => ({
|
|
267
|
+
type: 'spec',
|
|
268
|
+
url: file,
|
|
269
|
+
description: `Specification for ${descriptor.id}`,
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Generate assumptions if requested
|
|
274
|
+
if (options.includeAssumptions) {
|
|
275
|
+
assumptions = generateDefaultAssumptions(descriptor);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Create the contract
|
|
279
|
+
const contract = defineContract({
|
|
280
|
+
ruleId: descriptor.id,
|
|
281
|
+
behavior,
|
|
282
|
+
examples,
|
|
283
|
+
invariants: invariants.length > 0 ? invariants : [`${descriptor.id} maintains system invariants`],
|
|
284
|
+
assumptions,
|
|
285
|
+
references,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Calculate confidence based on what we found
|
|
289
|
+
// Use a normalized approach to avoid exceeding cap
|
|
290
|
+
let confidence = 0.5; // Base confidence for heuristic
|
|
291
|
+
const bonuses = [];
|
|
292
|
+
|
|
293
|
+
if (sourceFile) bonuses.push(0.1);
|
|
294
|
+
if (testFiles.length > 0) bonuses.push(0.2);
|
|
295
|
+
if (specFiles.length > 0) bonuses.push(0.1);
|
|
296
|
+
if (examples.length > 1) bonuses.push(0.1);
|
|
297
|
+
|
|
298
|
+
// Sum bonuses but cap total at 0.4 (to reach 0.9 with 0.5 base)
|
|
299
|
+
const totalBonus = Math.min(bonuses.reduce((sum, b) => sum + b, 0), 0.4);
|
|
300
|
+
confidence = confidence + totalBonus;
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
contract,
|
|
304
|
+
confidence,
|
|
305
|
+
method: 'heuristic',
|
|
306
|
+
warnings,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Extract examples from test files.
|
|
312
|
+
*/
|
|
313
|
+
async function extractExamplesFromTests(
|
|
314
|
+
testFile: string,
|
|
315
|
+
_ruleId: string
|
|
316
|
+
): Promise<Example[]> {
|
|
317
|
+
const fs = await import('node:fs/promises');
|
|
318
|
+
const content = await fs.readFile(testFile, 'utf-8');
|
|
319
|
+
const examples: Example[] = [];
|
|
320
|
+
|
|
321
|
+
// Look for test descriptions that might indicate Given/When/Then
|
|
322
|
+
// Improved pattern to handle escaped quotes and template literals
|
|
323
|
+
// Using simpler pattern for template literals to avoid expensive matching on large files
|
|
324
|
+
const testPattern =
|
|
325
|
+
/(?:it|test)\s*\(\s*(?:'((?:\\'|[^'])*)'|"((?:\\"|[^"])*)"|`((?:\\`|[^`])*?)`)/g;
|
|
326
|
+
let match;
|
|
327
|
+
|
|
328
|
+
while ((match = testPattern.exec(content)) !== null) {
|
|
329
|
+
const description = match[1] ?? match[2] ?? match[3] ?? '';
|
|
330
|
+
|
|
331
|
+
// Try to parse as Given/When/Then
|
|
332
|
+
if (description.includes('when') || description.includes('should')) {
|
|
333
|
+
examples.push(parseTestDescription(description));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return examples;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Parse a test description into a Given/When/Then example.
|
|
342
|
+
*/
|
|
343
|
+
function parseTestDescription(description: string): Example {
|
|
344
|
+
// Simple heuristic parsing with word boundaries
|
|
345
|
+
const parts = description.split(/\b(?:when|should)\b/i);
|
|
346
|
+
|
|
347
|
+
if (parts.length >= 2) {
|
|
348
|
+
return {
|
|
349
|
+
given: parts[0].trim() || 'Initial state',
|
|
350
|
+
when: parts.length > 2 ? parts[1].trim() : 'Action is triggered',
|
|
351
|
+
then: parts[parts.length - 1].trim() || 'Expected outcome occurs',
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
given: description,
|
|
357
|
+
when: 'Action is triggered',
|
|
358
|
+
then: 'Expected outcome occurs',
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Generate default assumptions for a rule.
|
|
364
|
+
*/
|
|
365
|
+
function generateDefaultAssumptions(
|
|
366
|
+
descriptor: RuleDescriptor | ConstraintDescriptor
|
|
367
|
+
): Assumption[] {
|
|
368
|
+
return [
|
|
369
|
+
{
|
|
370
|
+
id: `${descriptor.id}-assumption-1`,
|
|
371
|
+
statement: 'Input data is valid and well-formed',
|
|
372
|
+
confidence: 0.8,
|
|
373
|
+
justification: 'Standard assumption for rule processing',
|
|
374
|
+
impacts: ['tests', 'code'],
|
|
375
|
+
status: 'active',
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: `${descriptor.id}-assumption-2`,
|
|
379
|
+
statement: 'System state is consistent before rule execution',
|
|
380
|
+
confidence: 0.7,
|
|
381
|
+
justification: 'Required for deterministic rule behavior',
|
|
382
|
+
impacts: ['spec', 'tests'],
|
|
383
|
+
status: 'active',
|
|
384
|
+
},
|
|
385
|
+
];
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Build a prompt for AI contract generation.
|
|
390
|
+
* Currently unused but kept for future AI integration.
|
|
391
|
+
* @internal
|
|
392
|
+
*/
|
|
393
|
+
export function buildPromptForContract(
|
|
394
|
+
descriptor: RuleDescriptor | ConstraintDescriptor,
|
|
395
|
+
options: ReverseGenerationOptions
|
|
396
|
+
): string {
|
|
397
|
+
const parts: string[] = [
|
|
398
|
+
'Generate a comprehensive contract for the following rule/constraint:',
|
|
399
|
+
'',
|
|
400
|
+
`ID: ${descriptor.id}`,
|
|
401
|
+
`Description: ${descriptor.description}`,
|
|
402
|
+
'',
|
|
403
|
+
'The contract should include:',
|
|
404
|
+
'1. A clear canonical behavior description',
|
|
405
|
+
'2. At least 2-3 Given/When/Then examples',
|
|
406
|
+
'3. Key invariants that must hold',
|
|
407
|
+
'4. Assumptions with confidence levels',
|
|
408
|
+
'',
|
|
409
|
+
];
|
|
410
|
+
|
|
411
|
+
if (options.testFiles && options.testFiles.length > 0) {
|
|
412
|
+
parts.push('Test files available:');
|
|
413
|
+
options.testFiles.forEach((file) => parts.push(`- ${file}`));
|
|
414
|
+
parts.push('');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (options.specFiles && options.specFiles.length > 0) {
|
|
418
|
+
parts.push('Specification files available:');
|
|
419
|
+
options.specFiles.forEach((file) => parts.push(`- ${file}`));
|
|
420
|
+
parts.push('');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
parts.push('Return the contract as a JSON object matching the Contract interface.');
|
|
424
|
+
|
|
425
|
+
return parts.join('\n');
|
|
426
|
+
}
|