speckeeper 0.9.4 → 0.10.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.
- package/README.md +122 -2
- package/cli-contract.yaml +739 -25
- package/dist/cli.js +1177 -1
- package/dist/cli.js.map +1 -1
- package/docs/cli-reference.md +345 -20
- package/docs/design/cli-commands.md +181 -0
- package/docs/model-guide.md +102 -98
- package/package.json +13 -2
package/docs/model-guide.md
CHANGED
|
@@ -109,7 +109,10 @@ export interface Exporter<T> {
|
|
|
109
109
|
format: 'markdown' | 'json' | 'mermaid';
|
|
110
110
|
single?: (spec: T) => string;
|
|
111
111
|
index?: (specs: T[]) => string;
|
|
112
|
+
/** Subdirectory under docsDir (used with single + index/index.md) */
|
|
112
113
|
outputDir?: string;
|
|
114
|
+
/** Direct output file path relative to docsDir (used with index-only exporters) */
|
|
115
|
+
outputFile?: string;
|
|
113
116
|
filename?: (spec: T) => string;
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -138,34 +141,31 @@ export interface CheckResult {
|
|
|
138
141
|
}>;
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Deep Validation (replaces per-model externalChecker)
|
|
146
|
+
// ============================================================================
|
|
147
|
+
|
|
148
|
+
/** OpenAPI deep validation mapping */
|
|
149
|
+
export interface OpenAPIValidationMapping {
|
|
150
|
+
path: string;
|
|
151
|
+
method?: string;
|
|
152
|
+
parameters?: Array<{ name: string; in?: string; type?: string }>;
|
|
153
|
+
responseProperties?: Array<{ name: string; type?: string }>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** DDL deep validation mapping */
|
|
157
|
+
export interface DDLValidationMapping {
|
|
158
|
+
tableName: string;
|
|
159
|
+
columns?: Array<{ name: string; type?: string }>;
|
|
160
|
+
checkTypes?: boolean;
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* Example: Whether TestRef covers acceptanceCriteria of Requirement
|
|
164
|
+
* Deep validation rule for a specific source type.
|
|
165
|
+
* The mapper extracts expected structure from a spec for detailed comparison
|
|
166
|
+
* against the matched source object.
|
|
164
167
|
*/
|
|
165
|
-
export interface
|
|
166
|
-
/** Target model ID for coverage (e.g. 'requirement') */
|
|
167
|
-
targetModel: string;
|
|
168
|
-
/** Description of coverage check */
|
|
168
|
+
export interface DeepValidationRule<T, TMapping = unknown> {
|
|
169
169
|
```
|
|
170
170
|
<!--@embedoc:end-->
|
|
171
171
|
|
|
@@ -175,54 +175,54 @@ export interface CoverageChecker<T> {
|
|
|
175
175
|
**src/core/model.ts (Model Class Properties)**
|
|
176
176
|
|
|
177
177
|
```typescript
|
|
178
|
-
/** Execute coverage check */
|
|
179
|
-
check: (
|
|
180
|
-
specs: T[],
|
|
181
|
-
registry: Record<string, Map<string, unknown>>
|
|
182
|
-
) => CoverageResult;
|
|
183
178
|
}
|
|
184
179
|
|
|
185
|
-
// ============================================================================
|
|
186
|
-
// Renderer (for embeds)
|
|
187
|
-
// ============================================================================
|
|
188
|
-
|
|
189
180
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
181
|
+
* Deep validation configuration keyed by source type.
|
|
182
|
+
* Models define this to enable Level 2/3 checks beyond existence.
|
|
192
183
|
*/
|
|
193
|
-
export interface
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
markdown: {
|
|
198
|
-
/** Generate table */
|
|
199
|
-
table: (headers: string[], rows: (string | unknown)[][]) => string;
|
|
200
|
-
};
|
|
184
|
+
export interface DeepValidationConfig<T> {
|
|
185
|
+
openapi?: DeepValidationRule<T, OpenAPIValidationMapping>;
|
|
186
|
+
ddl?: DeepValidationRule<T, DDLValidationMapping>;
|
|
187
|
+
[sourceType: string]: DeepValidationRule<T, unknown> | undefined;
|
|
201
188
|
}
|
|
202
189
|
|
|
203
190
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
191
|
+
* Lookup key configuration keyed by source type.
|
|
192
|
+
* When a model's spec ID differs from the external identifier
|
|
193
|
+
* (e.g. entity ID "user" vs DDL table name "users"),
|
|
194
|
+
* define a mapper per source type to derive the external key.
|
|
195
|
+
* If not defined for a source type, spec.id is used as-is.
|
|
207
196
|
*/
|
|
208
|
-
export interface
|
|
209
|
-
|
|
210
|
-
format: string;
|
|
211
|
-
/** Rendering process */
|
|
212
|
-
render: (specs: T[], ctx: RenderContext) => string;
|
|
197
|
+
export interface LookupKeyConfig<T> {
|
|
198
|
+
[sourceType: string]: ((spec: T) => string) | undefined;
|
|
213
199
|
}
|
|
214
200
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Coverage result
|
|
203
|
+
*/
|
|
204
|
+
export interface CoverageResult {
|
|
205
|
+
/** Total target count */
|
|
206
|
+
total: number;
|
|
207
|
+
/** Covered count */
|
|
208
|
+
covered: number;
|
|
209
|
+
/** Uncovered count */
|
|
210
|
+
uncovered: number;
|
|
211
|
+
/** Coverage rate (%) */
|
|
212
|
+
coveragePercent: number;
|
|
213
|
+
/** Details of covered items */
|
|
214
|
+
coveredItems: { id: string; description?: string }[];
|
|
215
|
+
/** Details of uncovered items */
|
|
216
|
+
uncoveredItems: { id: string; description?: string; sourceId?: string }[];
|
|
217
|
+
}
|
|
218
218
|
|
|
219
219
|
/**
|
|
220
|
-
*
|
|
220
|
+
* Coverage checker definition
|
|
221
221
|
*
|
|
222
|
-
*
|
|
222
|
+
* Verify cross-model consistency (coverage).
|
|
223
|
+
* Example: Whether TestRef covers acceptanceCriteria of Requirement
|
|
223
224
|
*/
|
|
224
|
-
export
|
|
225
|
-
/** Singleton instance storage (per subclass) */
|
|
225
|
+
export interface CoverageChecker<T> {
|
|
226
226
|
```
|
|
227
227
|
<!--@embedoc:end-->
|
|
228
228
|
|
|
@@ -493,61 +493,65 @@ class TestRefModel extends Model<typeof TestRefSchema> {
|
|
|
493
493
|
protected exporters: Exporter<TestRef>[] = [
|
|
494
494
|
{
|
|
495
495
|
format: 'markdown',
|
|
496
|
-
|
|
496
|
+
index: (specs) => {
|
|
497
497
|
const lines: string[] = [];
|
|
498
|
-
lines.push(
|
|
499
|
-
lines.push('');
|
|
500
|
-
lines.push('## Test Source');
|
|
498
|
+
lines.push('# Test Reference List');
|
|
501
499
|
lines.push('');
|
|
502
|
-
lines.push(
|
|
503
|
-
lines.push(
|
|
504
|
-
|
|
505
|
-
lines.push(
|
|
500
|
+
lines.push('| ID | Description | Framework | Requirements Count |');
|
|
501
|
+
lines.push('|----|-------------|-----------|-------------------|');
|
|
502
|
+
for (const spec of specs) {
|
|
503
|
+
lines.push(
|
|
504
|
+
`| ${spec.id} | ${spec.description} | ${spec.source.framework} | ${spec.verifiesRequirements.length} |`,
|
|
505
|
+
);
|
|
506
506
|
}
|
|
507
507
|
lines.push('');
|
|
508
|
-
|
|
509
|
-
lines.push('## Verified Requirements');
|
|
510
|
-
lines.push('');
|
|
511
|
-
for (const reqId of spec.verifiesRequirements) {
|
|
512
|
-
lines.push(`- ${reqId}`);
|
|
513
|
-
}
|
|
508
|
+
lines.push('---');
|
|
514
509
|
lines.push('');
|
|
515
510
|
|
|
516
|
-
|
|
517
|
-
lines.push(
|
|
511
|
+
for (const spec of specs) {
|
|
512
|
+
lines.push(`## ${spec.id}: ${spec.description}`);
|
|
518
513
|
lines.push('');
|
|
519
|
-
lines.push(
|
|
514
|
+
lines.push('### Test Source');
|
|
515
|
+
lines.push('');
|
|
516
|
+
lines.push(`- **Path**: \`${spec.source.path}\``);
|
|
517
|
+
lines.push(`- **Framework**: ${spec.source.framework}`);
|
|
518
|
+
if (spec.source.resultPath) {
|
|
519
|
+
lines.push(`- **Result JSON**: \`${spec.source.resultPath}\``);
|
|
520
|
+
}
|
|
520
521
|
lines.push('');
|
|
521
|
-
}
|
|
522
522
|
|
|
523
|
-
|
|
524
|
-
lines.push('## Test Case Patterns');
|
|
523
|
+
lines.push('### Verified Requirements');
|
|
525
524
|
lines.push('');
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
for (const p of spec.testCasePatterns) {
|
|
529
|
-
lines.push(`| ${p.acceptanceCriteriaId} | \`${p.pattern}\` | ${p.description || '-'} |`);
|
|
525
|
+
for (const reqId of spec.verifiesRequirements) {
|
|
526
|
+
lines.push(`- ${reqId}`);
|
|
530
527
|
}
|
|
531
528
|
lines.push('');
|
|
532
|
-
}
|
|
533
529
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
530
|
+
if (spec.implementsCommand) {
|
|
531
|
+
lines.push('### Implemented Command');
|
|
532
|
+
lines.push('');
|
|
533
|
+
lines.push(`- ${spec.implementsCommand}`);
|
|
534
|
+
lines.push('');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (spec.testCasePatterns && spec.testCasePatterns.length > 0) {
|
|
538
|
+
lines.push('### Test Case Patterns');
|
|
539
|
+
lines.push('');
|
|
540
|
+
lines.push('| Acceptance Criteria ID | Pattern | Description |');
|
|
541
|
+
lines.push('|------------------------|---------|-------------|');
|
|
542
|
+
for (const p of spec.testCasePatterns) {
|
|
543
|
+
lines.push(`| ${p.acceptanceCriteriaId} | \`${p.pattern}\` | ${p.description || '-'} |`);
|
|
544
|
+
}
|
|
545
|
+
lines.push('');
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
lines.push('---');
|
|
549
|
+
lines.push('');
|
|
546
550
|
}
|
|
547
|
-
|
|
551
|
+
|
|
552
|
+
return lines.join('\n').replace(/\n---\n\n$/s, '\n');
|
|
548
553
|
},
|
|
549
|
-
|
|
550
|
-
filename: (spec) => spec.id,
|
|
554
|
+
outputFile: 'design/test-refs.md',
|
|
551
555
|
},
|
|
552
556
|
];
|
|
553
557
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "speckeeper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "TypeScript-first specification validation framework with external SSOT integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"prepublishOnly": "npm run build",
|
|
41
41
|
"docs": "embedoc build",
|
|
42
42
|
"docs:watch": "embedoc watch",
|
|
43
|
+
"dsl:generate": "npx agent-runtime generate --config dsl/agent-runtime.config.yaml",
|
|
43
44
|
"contract:validate": "npx cli-contracts validate",
|
|
44
45
|
"contract:generate": "npx cli-contracts generate",
|
|
45
46
|
"ci": "npm run ci:validate && npm run ci:generate && npm run ci:verify",
|
|
@@ -70,6 +71,14 @@
|
|
|
70
71
|
"yaml": "^2.3.4",
|
|
71
72
|
"zod": "^3.22.4"
|
|
72
73
|
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"agent-contracts-runtime": ">=0.13.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependenciesMeta": {
|
|
78
|
+
"agent-contracts-runtime": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
73
82
|
"devDependencies": {
|
|
74
83
|
"@eslint/js": "^9.39.2",
|
|
75
84
|
"@types/node": "^20.11.0",
|
|
@@ -77,7 +86,9 @@
|
|
|
77
86
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
78
87
|
"@typescript-eslint/parser": "^8.54.0",
|
|
79
88
|
"@vitest/coverage-v8": "^1.6.1",
|
|
80
|
-
"
|
|
89
|
+
"agent-contracts": "^0.21.0",
|
|
90
|
+
"agent-contracts-runtime": "^0.13.0",
|
|
91
|
+
"cli-contracts": "^0.6.1",
|
|
81
92
|
"eslint": "^9.39.2",
|
|
82
93
|
"tsup": "^8.0.1",
|
|
83
94
|
"typescript": "^5.3.3",
|