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.
@@ -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
- * Coverage result
143
- */
144
- export interface CoverageResult {
145
- /** Total target count */
146
- total: number;
147
- /** Covered count */
148
- covered: number;
149
- /** Uncovered count */
150
- uncovered: number;
151
- /** Coverage rate (%) */
152
- coveragePercent: number;
153
- /** Details of covered items */
154
- coveredItems: { id: string; description?: string }[];
155
- /** Details of uncovered items */
156
- uncoveredItems: { id: string; description?: string; sourceId?: string }[];
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
- * Coverage checker definition
161
- *
162
- * Verify cross-model consistency (coverage).
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 CoverageChecker<T> {
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
- * Render context
191
- * Simplified version compatible with embedoc's EmbedContext
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 RenderContext {
194
- /** Parameters (filter conditions other than format, etc.) */
195
- params: Record<string, string | undefined>;
196
- /** Markdown helpers */
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
- * Renderer definition
205
- *
206
- * Model-specific rendering called from embeds
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 Renderer<T> {
209
- /** Format ID ('table', 'list', 'detail', 'spec-chapter', etc.) */
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
- // Model Base Class
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
- * Model base class
220
+ * Coverage checker definition
221
221
  *
222
- * @template TSchema - Zod schema type
222
+ * Verify cross-model consistency (coverage).
223
+ * Example: Whether TestRef covers acceptanceCriteria of Requirement
223
224
  */
224
- export abstract class Model<TSchema extends ZodType> {
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
- single: (spec) => {
496
+ index: (specs) => {
497
497
  const lines: string[] = [];
498
- lines.push(`# ${spec.id}: ${spec.description}`);
499
- lines.push('');
500
- lines.push('## Test Source');
498
+ lines.push('# Test Reference List');
501
499
  lines.push('');
502
- lines.push(`- **Path**: \`${spec.source.path}\``);
503
- lines.push(`- **Framework**: ${spec.source.framework}`);
504
- if (spec.source.resultPath) {
505
- lines.push(`- **Result JSON**: \`${spec.source.resultPath}\``);
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
- if (spec.implementsCommand) {
517
- lines.push('## Implemented Command');
511
+ for (const spec of specs) {
512
+ lines.push(`## ${spec.id}: ${spec.description}`);
518
513
  lines.push('');
519
- lines.push(`- ${spec.implementsCommand}`);
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
- if (spec.testCasePatterns && spec.testCasePatterns.length > 0) {
524
- lines.push('## Test Case Patterns');
523
+ lines.push('### Verified Requirements');
525
524
  lines.push('');
526
- lines.push('| Acceptance Criteria ID | Pattern | Description |');
527
- lines.push('|------------------------|---------|-------------|');
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
- return lines.join('\n');
535
- },
536
- index: (specs) => {
537
- const lines: string[] = [];
538
- lines.push('# Test Reference List');
539
- lines.push('');
540
- lines.push('| ID | Description | Framework | Requirements Count |');
541
- lines.push('|----|-------------|-----------|-------------------|');
542
- for (const spec of specs) {
543
- lines.push(
544
- `| [${spec.id}](./${spec.id}.md) | ${spec.description} | ${spec.source.framework} | ${spec.verifiesRequirements.length} |`,
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
- return lines.join('\n');
551
+
552
+ return lines.join('\n').replace(/\n---\n\n$/s, '\n');
548
553
  },
549
- outputDir: 'test-refs',
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.9.4",
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
- "cli-contracts": "^0.2.0",
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",