@unrdf/kgc-probe 26.4.2

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/src/types.mjs ADDED
@@ -0,0 +1,1028 @@
1
+ /**
2
+ * @fileoverview KGC Probe - Type definitions and Zod schemas
3
+ *
4
+ * Runtime-validatable schemas for:
5
+ * - KGC Markdown Frontmatter (o_hash, policy_id, receipts, bounds, etc.)
6
+ * - Block Metadata (query, extract, render, proof blocks)
7
+ * - Receipts (cryptographic proofs)
8
+ * - Observations (agent outputs)
9
+ * - Artifacts (scan results)
10
+ * - Configuration objects
11
+ *
12
+ * @module @unrdf/kgc-probe/types
13
+ */
14
+
15
+ import { z } from 'zod';
16
+
17
+ // ============================================================================
18
+ // CONSTANTS & REGEXES (per SPARC spec)
19
+ // ============================================================================
20
+
21
+ /**
22
+ * SHA-256 hash pattern (64 lowercase hex characters)
23
+ * @type {RegExp}
24
+ */
25
+ export const SHA256_REGEX = /^[a-f0-9]{64}$/;
26
+
27
+ /**
28
+ * UUID v4 pattern
29
+ * @type {RegExp}
30
+ */
31
+ export const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
32
+
33
+ /**
34
+ * Semver pattern (e.g., 1.0.0, 2.1.3-beta.1+build.123)
35
+ * @type {RegExp}
36
+ */
37
+ export const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
38
+
39
+ // ============================================================================
40
+ // KGC MARKDOWN FRONTMATTER SCHEMAS (SPARC Domain 1, 4, 8)
41
+ // ============================================================================
42
+
43
+ /**
44
+ * Source file reference schema
45
+ * References a specific range of lines in a source file with hash
46
+ * @type {z.ZodSchema}
47
+ */
48
+ export const SourceSchema = z.object({
49
+ path: z.string().min(1).describe('Path to source file (no ../ allowed)'),
50
+ lineStart: z.number().int().min(1).describe('Starting line number'),
51
+ lineEnd: z.number().int().min(1).describe('Ending line number'),
52
+ hash: z.string().regex(SHA256_REGEX, 'Must be 64-char hex SHA-256').describe('SHA-256 hash of source content')
53
+ }).refine(
54
+ (data) => data.lineEnd >= data.lineStart,
55
+ { message: 'lineEnd must be >= lineStart', path: ['lineEnd'] }
56
+ ).refine(
57
+ (data) => !data.path.includes('..'),
58
+ { message: 'Path must not contain directory traversal (..)', path: ['path'] }
59
+ );
60
+
61
+ /**
62
+ * Resource bounds schema
63
+ * Limits for queries, runtime, and file scans
64
+ * @type {z.ZodSchema}
65
+ */
66
+ export const BoundsSchema = z.object({
67
+ maxQueries: z.number().int().min(1).max(10000).describe('Maximum query result count [1, 10000]'),
68
+ maxRuntime: z.number().int().min(100).max(60000).describe('Maximum runtime in ms [100, 60000]'),
69
+ maxFileScans: z.number().int().min(1).max(1000).describe('Maximum file scans [1, 1000]')
70
+ });
71
+
72
+ /**
73
+ * Author schema
74
+ * @type {z.ZodSchema}
75
+ */
76
+ export const AuthorSchema = z.object({
77
+ name: z.string().min(1).describe('Author name'),
78
+ role: z.string().optional().describe('Author role (optional)')
79
+ });
80
+
81
+ /**
82
+ * Diataxis view types
83
+ * @type {z.ZodEnum}
84
+ */
85
+ export const DiatasisViewSchema = z.enum(['tutorial', 'how-to', 'reference', 'explanation']);
86
+
87
+ /**
88
+ * KGC Markdown Frontmatter schema (Domain 1: Frontmatter Parsing)
89
+ * Validates all frontmatter fields per SPARC specification
90
+ * @type {z.ZodSchema}
91
+ */
92
+ export const FrontmatterSchema = z.object({
93
+ o_hash: z.string()
94
+ .regex(SHA256_REGEX, 'o_hash must be 64-char lowercase hex SHA-256')
95
+ .describe('Universe snapshot hash'),
96
+ policy_id: z.string()
97
+ .regex(UUID_V4_REGEX, 'policy_id must be valid UUID v4')
98
+ .describe('Policy pack UUID'),
99
+ receipts: z.array(
100
+ z.string().regex(SHA256_REGEX, 'Receipt ID must be 64-char hex')
101
+ ).min(0).max(1000).describe('Array of receipt IDs [0-1000 items]'),
102
+ bounds: BoundsSchema.describe('Resource limits'),
103
+ views: z.array(DiatasisViewSchema)
104
+ .min(1).max(4)
105
+ .describe('Diataxis view types'),
106
+ sources: z.array(SourceSchema)
107
+ .min(0).max(100)
108
+ .describe('Source file references'),
109
+ version: z.string()
110
+ .regex(SEMVER_REGEX, 'version must be valid semver')
111
+ .describe('Document version'),
112
+ createdAt: z.string().datetime({ offset: true }).describe('Creation timestamp (ISO 8601)'),
113
+ lastProved: z.string().datetime({ offset: true }).describe('Last proof timestamp (ISO 8601)'),
114
+ tags: z.array(z.string().min(1).max(50)).max(20).optional().describe('Optional tags'),
115
+ authors: z.array(AuthorSchema).optional().describe('Optional authors')
116
+ }).refine(
117
+ (data) => new Date(data.lastProved) >= new Date(data.createdAt),
118
+ { message: 'lastProved must be >= createdAt', path: ['lastProved'] }
119
+ );
120
+
121
+ // ============================================================================
122
+ // BLOCK METADATA SCHEMAS (SPARC Domain 2, 5, 8)
123
+ // ============================================================================
124
+
125
+ /**
126
+ * Block types supported by KGC Markdown
127
+ * @type {z.ZodEnum}
128
+ */
129
+ export const BlockTypeSchema = z.enum(['kgc:query', 'kgc:proof', 'kgc:extract', 'kgc:render']);
130
+
131
+ /**
132
+ * Output format options
133
+ * @type {z.ZodEnum}
134
+ */
135
+ export const OutputFormatSchema = z.enum(['json', 'markdown', 'text']);
136
+
137
+ /**
138
+ * Determinism level options
139
+ * @type {z.ZodEnum}
140
+ */
141
+ export const DeterminismLevelSchema = z.enum(['strict', 'lenient', 'best-effort']);
142
+
143
+ /**
144
+ * Base block metadata schema (shared by all block types)
145
+ * @type {z.ZodSchema}
146
+ */
147
+ export const BlockMetadataSchema = z.object({
148
+ receiptId: z.string()
149
+ .regex(SHA256_REGEX, 'receiptId must be 64-char hex')
150
+ .describe('Receipt ID (SHA-256)'),
151
+ expectedOutputFormat: OutputFormatSchema.describe('Expected output format'),
152
+ determinismLevel: DeterminismLevelSchema.describe('Determinism guarantee level'),
153
+ metadata: z.record(z.any()).optional().describe('Block-type-specific metadata')
154
+ });
155
+
156
+ /**
157
+ * Query type options
158
+ * @type {z.ZodEnum}
159
+ */
160
+ export const QueryTypeSchema = z.enum(['sparql', 'n3', 'shacl']);
161
+
162
+ /**
163
+ * Result bounds for queries
164
+ * @type {z.ZodSchema}
165
+ */
166
+ export const ResultBoundsSchema = z.object({
167
+ minResults: z.number().int().min(0).describe('Minimum expected results'),
168
+ maxResults: z.number().int().min(0).describe('Maximum expected results')
169
+ });
170
+
171
+ /**
172
+ * Query block metadata schema (kgc:query)
173
+ * @type {z.ZodSchema}
174
+ */
175
+ export const QueryMetadataSchema = BlockMetadataSchema.extend({
176
+ queryType: QueryTypeSchema.describe('Query language type'),
177
+ resultBounds: ResultBoundsSchema.describe('Expected result count range'),
178
+ timeout: z.number().int().min(100).max(60000).describe('Query timeout in ms')
179
+ });
180
+
181
+ /**
182
+ * Extraction type options
183
+ * @type {z.ZodEnum}
184
+ */
185
+ export const ExtractionTypeSchema = z.enum(['exports', 'functions', 'classes', 'types', 'all']);
186
+
187
+ /**
188
+ * Extract block metadata schema (kgc:extract)
189
+ * @type {z.ZodSchema}
190
+ */
191
+ export const ExtractMetadataSchema = BlockMetadataSchema.extend({
192
+ extractionType: ExtractionTypeSchema.describe('What to extract'),
193
+ fileGlobs: z.array(z.string()).min(1).describe('File patterns to scan'),
194
+ includePrivate: z.boolean().default(false).describe('Include private members'),
195
+ includeDocstrings: z.boolean().default(true).describe('Include documentation')
196
+ });
197
+
198
+ /**
199
+ * Render block metadata schema (kgc:render)
200
+ * @type {z.ZodSchema}
201
+ */
202
+ export const RenderMetadataSchema = BlockMetadataSchema.extend({
203
+ templateName: z.string().min(1).describe('Template to use'),
204
+ sectionTitle: z.string().min(1).describe('Section heading'),
205
+ includeTableOfContents: z.boolean().default(false).describe('Include TOC')
206
+ });
207
+
208
+ /**
209
+ * Proof type options
210
+ * @type {z.ZodEnum}
211
+ */
212
+ export const ProofTypeSchema = z.enum(['merkle', 'hash-chain', 'single']);
213
+
214
+ /**
215
+ * Proof block metadata schema (kgc:proof)
216
+ * @type {z.ZodSchema}
217
+ */
218
+ export const ProofMetadataSchema = BlockMetadataSchema.extend({
219
+ proofType: ProofTypeSchema.describe('Type of cryptographic proof'),
220
+ verifyChain: z.boolean().default(true).describe('Verify dependency chain'),
221
+ validateSignatures: z.boolean().default(false).describe('Validate signatures')
222
+ });
223
+
224
+ // ============================================================================
225
+ // RECEIPT SCHEMAS (SPARC Domain 3, 7)
226
+ // ============================================================================
227
+
228
+ /**
229
+ * Receipt decision options
230
+ * @type {z.ZodEnum}
231
+ */
232
+ export const DecisionSchema = z.enum(['ADMIT', 'REJECT', 'PARTIAL']);
233
+
234
+ /**
235
+ * Merkle proof schema (optional in receipts)
236
+ * @type {z.ZodSchema}
237
+ */
238
+ export const MerkleProofSchema = z.object({
239
+ siblings: z.array(z.string().regex(SHA256_REGEX)).describe('Sibling hashes'),
240
+ root: z.string().regex(SHA256_REGEX).describe('Merkle root hash'),
241
+ index: z.number().int().min(0).describe('Leaf index'),
242
+ totalLeaves: z.number().int().min(1).describe('Total leaf count')
243
+ });
244
+
245
+ /**
246
+ * Receipt schema (Domain 3: Receipt Validation)
247
+ * Cryptographic proof of block execution
248
+ * @type {z.ZodSchema}
249
+ */
250
+ export const ReceiptSchema = z.object({
251
+ id: z.string()
252
+ .regex(SHA256_REGEX, 'Receipt ID must be SHA-256 of canonical body')
253
+ .describe('Receipt ID (SHA-256)'),
254
+ timestamp: z.string().datetime({ offset: true }).describe('Execution timestamp'),
255
+ o_hash: z.string()
256
+ .regex(SHA256_REGEX, 'o_hash must match frontmatter')
257
+ .describe('Universe snapshot hash'),
258
+ block_type: BlockTypeSchema.describe('Block type that produced this receipt'),
259
+ input_hash: z.string()
260
+ .regex(SHA256_REGEX, 'input_hash is SHA-256 of block metadata + body')
261
+ .describe('Hash of block input'),
262
+ output_hash: z.string()
263
+ .regex(SHA256_REGEX, 'output_hash is SHA-256 of generated content (RFC 8785)')
264
+ .describe('Hash of block output'),
265
+ decision: DecisionSchema.describe('Execution decision'),
266
+ metadata: z.record(z.any()).optional().describe('Block-specific metadata'),
267
+ dependencies: z.array(
268
+ z.string().regex(SHA256_REGEX)
269
+ ).optional().describe('Receipt IDs this depends on'),
270
+ merkle_proof: MerkleProofSchema.optional().describe('Merkle proof (batch receipts)')
271
+ });
272
+
273
+ // ============================================================================
274
+ // DYNAMIC SECTION SCHEMAS (SPARC Domain 4)
275
+ // ============================================================================
276
+
277
+ /**
278
+ * Dynamic section schema
279
+ * Content between <!-- kgc:dynamic --> markers
280
+ * @type {z.ZodSchema}
281
+ */
282
+ export const DynamicSectionSchema = z.object({
283
+ name: z.string().min(1).describe('Section identifier'),
284
+ receiptId: z.string().regex(SHA256_REGEX).describe('Associated receipt ID'),
285
+ lineStart: z.number().int().min(1).describe('Opening marker line'),
286
+ lineEnd: z.number().int().min(1).describe('Closing marker line'),
287
+ contentHash: z.string().regex(SHA256_REGEX).optional().describe('Computed content hash')
288
+ });
289
+
290
+ // ============================================================================
291
+ // PROBE ERROR SCHEMAS (SPARC Domain 10)
292
+ // ============================================================================
293
+
294
+ /**
295
+ * Error severity levels
296
+ * @type {z.ZodEnum}
297
+ */
298
+ export const ErrorSeveritySchema = z.enum(['error', 'warning', 'info']);
299
+
300
+ /**
301
+ * Error type enumeration (all possible probe errors)
302
+ * @type {z.ZodEnum}
303
+ */
304
+ export const ErrorTypeSchema = z.enum([
305
+ 'InvalidFrontmatter',
306
+ 'MissingReceipt',
307
+ 'MismatchedHash',
308
+ 'BoundsExceeded',
309
+ 'NonDeterministic',
310
+ 'InvalidBlockStructure',
311
+ 'CyclicDependency',
312
+ 'MissingDependency',
313
+ 'TimestampOrderViolation',
314
+ 'UniverseHashMismatch',
315
+ 'OrphanedSection',
316
+ 'UnmappedReceipt',
317
+ 'BrokenLink',
318
+ 'HeadingHierarchyViolation',
319
+ 'DuplicateHeading',
320
+ 'SchemaViolation'
321
+ ]);
322
+
323
+ /**
324
+ * Probe error schema with location and remediation
325
+ * @type {z.ZodSchema}
326
+ */
327
+ export const ProbeErrorSchema = z.object({
328
+ type: ErrorTypeSchema.describe('Error classification'),
329
+ severity: ErrorSeveritySchema.describe('Impact severity'),
330
+ message: z.string().min(1).describe('Human-readable message'),
331
+ location: z.object({
332
+ file: z.string().optional().describe('File path'),
333
+ line: z.number().int().min(1).optional().describe('Line number'),
334
+ field: z.string().optional().describe('Field name')
335
+ }).optional().describe('Error location'),
336
+ actual: z.unknown().optional().describe('Actual value observed'),
337
+ expected: z.unknown().optional().describe('Expected value'),
338
+ remediation: z.string().optional().describe('How to fix')
339
+ });
340
+
341
+ /**
342
+ * Probe warning (non-blocking issue)
343
+ * @type {z.ZodSchema}
344
+ */
345
+ export const ProbeWarningSchema = ProbeErrorSchema.extend({
346
+ severity: z.literal('warning')
347
+ });
348
+
349
+ // ============================================================================
350
+ // PROBE REPORT SCHEMAS (SPARC Success Metrics)
351
+ // ============================================================================
352
+
353
+ /**
354
+ * Domain validation result
355
+ * @type {z.ZodSchema}
356
+ */
357
+ export const DomainResultSchema = z.object({
358
+ domain: z.string().describe('Domain name'),
359
+ passed: z.boolean().describe('Whether domain checks passed'),
360
+ checks: z.number().int().min(0).describe('Number of checks run'),
361
+ errors: z.array(ProbeErrorSchema).describe('Errors found'),
362
+ warnings: z.array(ProbeWarningSchema).describe('Warnings found')
363
+ });
364
+
365
+ /**
366
+ * Complete probe report schema
367
+ * @type {z.ZodSchema}
368
+ */
369
+ export const ProbeReportSchema = z.object({
370
+ version: z.literal('1.0').describe('Report schema version'),
371
+ documentPath: z.string().describe('Scanned document path'),
372
+ timestamp: z.string().datetime({ offset: true }).describe('Scan timestamp'),
373
+ status: z.enum(['PASS', 'FAIL', 'PARTIAL']).describe('Overall status'),
374
+ domains: z.array(DomainResultSchema).describe('Results per domain'),
375
+ summary: z.object({
376
+ totalChecks: z.number().int().min(0).describe('Total checks run'),
377
+ totalPassed: z.number().int().min(0).describe('Checks passed'),
378
+ totalErrors: z.number().int().min(0).describe('Error count'),
379
+ totalWarnings: z.number().int().min(0).describe('Warning count'),
380
+ coverage: z.number().min(0).max(1).describe('Document coverage ratio')
381
+ }).describe('Summary statistics'),
382
+ metadata: z.object({
383
+ probeVersion: z.string().describe('Probe version'),
384
+ executionTimeMs: z.number().int().min(0).describe('Execution time'),
385
+ documentSize: z.number().int().min(0).optional().describe('Document size in bytes')
386
+ }).describe('Execution metadata')
387
+ });
388
+
389
+ // ============================================================================
390
+ // OBSERVATION SCHEMA (Agent outputs - existing)
391
+ // ============================================================================
392
+
393
+ /**
394
+ * Individual observation from an agent scan
395
+ * @type {z.ZodSchema}
396
+ */
397
+ export const ObservationSchema = z.object({
398
+ id: z.string().uuid('Observation must have UUID').describe('Globally unique observation ID'),
399
+ agent: z.string().describe('Agent identifier that produced this observation'),
400
+ timestamp: z.string().datetime().describe('ISO8601 timestamp when observation was made'),
401
+ kind: z.enum([
402
+ 'completeness',
403
+ 'consistency',
404
+ 'conformance',
405
+ 'coverage',
406
+ 'caching',
407
+ 'completeness_level',
408
+ 'coherence',
409
+ 'clustering',
410
+ 'classification',
411
+ 'collaboration',
412
+ 'guard_violation'
413
+ ]).describe('Type of observation (agent-specific)'),
414
+ severity: z.enum(['critical', 'warning', 'info']).default('info').describe('Impact severity'),
415
+ subject: z.string().describe('RDF node under observation'),
416
+ predicate: z.string().optional().describe('RDF property (optional)'),
417
+ object: z.string().optional().describe('RDF value (optional)'),
418
+ evidence: z.object({
419
+ query: z.string().describe('SPARQL query or algorithm used'),
420
+ result: z.unknown().describe('Computation result'),
421
+ witnesses: z.array(z.string()).describe('Confirming triple/node references')
422
+ }).describe('Proof and context for observation'),
423
+ metrics: z.object({
424
+ confidence: z.number().min(0).max(1).describe('Confidence score [0, 1]'),
425
+ coverage: z.number().min(0).max(1).describe('Coverage ratio [0, 1]'),
426
+ latency_ms: z.number().nonnegative().describe('Execution latency in ms')
427
+ }).describe('Quantitative metrics'),
428
+ tags: z.array(z.string()).default([]).describe('Searchable tags'),
429
+ xid: z.string().optional().describe('Correlation ID for tracing')
430
+ }).strict();
431
+
432
+ /**
433
+ * Type representing validated observation
434
+ * @typedef {z.infer<typeof ObservationSchema>} Observation
435
+ */
436
+ export const ObservationType = ObservationSchema;
437
+
438
+ // ============================================================================
439
+ // ARTIFACT SCHEMA
440
+ // ============================================================================
441
+
442
+ /**
443
+ * Artifact summary statistics
444
+ * @type {z.ZodSchema}
445
+ */
446
+ export const ArtifactSummarySchema = z.object({
447
+ total: z.number().nonnegative().describe('Total observation count'),
448
+ by_kind: z.record(z.string(), z.number().nonnegative()).describe('Count per observation kind'),
449
+ by_severity: z.record(z.enum(['critical', 'warning', 'info']), z.number().nonnegative()).describe('Count per severity'),
450
+ confidence_mean: z.number().min(0).max(1).describe('Mean confidence score'),
451
+ coverage_mean: z.number().min(0).max(1).describe('Mean coverage ratio')
452
+ }).describe('Summary statistics for artifact');
453
+
454
+ /**
455
+ * Complete artifact with observations and metadata
456
+ * @type {z.ZodSchema}
457
+ */
458
+ export const ArtifactSchema = z.object({
459
+ version: z.literal('1.0').describe('Artifact schema version'),
460
+ universe_id: z.string().describe('4D universe reference'),
461
+ snapshot_id: z.string().describe('KGC-4D snapshot hash'),
462
+ generated_at: z.string().datetime().describe('Generation timestamp'),
463
+ probe_run_id: z.string().uuid().describe('Unique probe run ID'),
464
+ shard_count: z.number().nonnegative().describe('Number of shards merged'),
465
+ shard_hash: z.string().regex(/^[a-f0-9]{64}$/).describe('Blake3 hash of all shards'),
466
+ observations: z.array(ObservationSchema).describe('All observations from this scan'),
467
+ summary: ArtifactSummarySchema.describe('Aggregated statistics'),
468
+ metadata: z.object({
469
+ agents_run: z.array(z.string()).describe('Agent IDs executed'),
470
+ guards_applied: z.array(z.string()).describe('Guard IDs applied'),
471
+ execution_time_ms: z.number().nonnegative().describe('Total execution time'),
472
+ storage_backend: z.string().describe('Storage implementation used'),
473
+ config: z.unknown().optional().describe('Scan configuration')
474
+ }).describe('Execution metadata'),
475
+ integrity: z.object({
476
+ checksum: z.string().regex(/^[a-f0-9]{64}$/).describe('Blake3 hash of observations'),
477
+ signature: z.string().optional().describe('Optional cryptographic signature'),
478
+ verified_at: z.string().datetime().optional().describe('Verification timestamp')
479
+ }).describe('Integrity verification')
480
+ }).strict();
481
+
482
+ /**
483
+ * Type representing validated artifact
484
+ * @typedef {z.infer<typeof ArtifactSchema>} Artifact
485
+ */
486
+ export const ArtifactType = ArtifactSchema;
487
+
488
+ // ============================================================================
489
+ // CONFIGURATION SCHEMAS
490
+ // ============================================================================
491
+
492
+ /**
493
+ * Probe scan configuration
494
+ * @type {z.ZodSchema}
495
+ */
496
+ export const ProbeConfigSchema = z.object({
497
+ universe_id: z.string().describe('Universe to scan'),
498
+ snapshot_id: z.string().optional().describe('Optional snapshot reference'),
499
+ agents: z.array(z.string()).optional().describe('Agent IDs to run (all if omitted)'),
500
+ guards: z.array(z.string()).optional().describe('Guard IDs to apply'),
501
+ distributed: z.boolean().default(false).describe('Enable multi-shard merge'),
502
+ persist: z.boolean().default(true).describe('Save artifact to storage'),
503
+ timeout_ms: z.number().positive().default(300000).describe('Scan timeout'),
504
+ batch_size: z.number().positive().default(100).describe('Observation batch size')
505
+ }).describe('Probe scan configuration');
506
+
507
+ /**
508
+ * Guard configuration
509
+ * @type {z.ZodSchema}
510
+ */
511
+ export const GuardConfigSchema = z.object({
512
+ quality_check: z.object({
513
+ critical_observations_threshold: z.number().positive().default(50),
514
+ confidence_min: z.number().min(0).max(1).default(0.6)
515
+ }).optional(),
516
+ completeness_check: z.object({
517
+ coverage_min: z.number().min(0).max(1).default(0.7)
518
+ }).optional(),
519
+ severity_limit: z.object({
520
+ critical_limit: z.number().positive().default(10)
521
+ }).optional()
522
+ }).describe('Guard thresholds and limits');
523
+
524
+ /**
525
+ * Storage configuration
526
+ * @type {z.ZodSchema}
527
+ */
528
+ export const StorageConfigSchema = z.object({
529
+ type: z.enum(['memory', 'file', 'database']).describe('Storage backend type'),
530
+ path: z.string().optional().describe('File system path (for file storage)'),
531
+ connectionString: z.string().optional().describe('Database connection (for database storage)')
532
+ }).describe('Storage backend configuration');
533
+
534
+ // ============================================================================
535
+ // AGENT INTERFACE
536
+ // ============================================================================
537
+
538
+ /**
539
+ * Base agent interface definition
540
+ * @type {z.ZodSchema}
541
+ */
542
+ export const AgentSchema = z.object({
543
+ id: z.string().describe('Agent identifier'),
544
+ kind: z.string().describe('Observation kind produced'),
545
+ description: z.string().describe('Human-readable description'),
546
+ scan: z.function().describe('Async scan function')
547
+ }).describe('Agent interface specification');
548
+
549
+ // ============================================================================
550
+ // GUARD VIOLATION
551
+ // ============================================================================
552
+
553
+ /**
554
+ * Guard validation violation
555
+ * @type {z.ZodSchema}
556
+ */
557
+ export const GuardViolationSchema = z.object({
558
+ guard_id: z.string().describe('Guard that detected violation'),
559
+ severity: z.enum(['critical', 'warning', 'info']).describe('Violation severity'),
560
+ details: z.object({
561
+ message: z.string().describe('Human-readable message'),
562
+ actual: z.unknown().describe('Actual value observed'),
563
+ threshold: z.unknown().describe('Expected/threshold value')
564
+ }).describe('Violation details')
565
+ }).describe('Guard validation violation');
566
+
567
+ /**
568
+ * Type representing guard violation
569
+ * @typedef {z.infer<typeof GuardViolationSchema>} GuardViolation
570
+ */
571
+ export const GuardViolationType = GuardViolationSchema;
572
+
573
+ // ============================================================================
574
+ // DIFF RESULT
575
+ // ============================================================================
576
+
577
+ /**
578
+ * Diff between two artifacts
579
+ * @type {z.ZodSchema}
580
+ */
581
+ export const DiffResultSchema = z.object({
582
+ added: z.array(ObservationSchema).describe('Observations in artifact2 but not artifact1'),
583
+ removed: z.array(ObservationSchema).describe('Observations in artifact1 but not artifact2'),
584
+ modified: z.array(z.object({
585
+ original: ObservationSchema,
586
+ updated: ObservationSchema,
587
+ changes: z.record(z.string(), z.unknown()).describe('Field changes')
588
+ })).describe('Observations present in both but with changes'),
589
+ summary: z.object({
590
+ total_changes: z.number().nonnegative(),
591
+ similarity_ratio: z.number().min(0).max(1).describe('Jaccard similarity')
592
+ }).describe('Diff summary')
593
+ }).describe('Artifact comparison result');
594
+
595
+ /**
596
+ * Type representing diff result
597
+ * @typedef {z.infer<typeof DiffResultSchema>} DiffResult
598
+ */
599
+ export const DiffResultType = DiffResultSchema;
600
+
601
+ // ============================================================================
602
+ // VALIDATION RESULTS
603
+ // ============================================================================
604
+
605
+ /**
606
+ * Scan execution result
607
+ * @type {z.ZodSchema}
608
+ */
609
+ export const ScanResultSchema = z.object({
610
+ artifact: ArtifactSchema.describe('Generated artifact'),
611
+ status: z.enum(['success', 'partial', 'failed']).describe('Execution status'),
612
+ errors: z.array(z.object({
613
+ agent: z.string().optional(),
614
+ guard: z.string().optional(),
615
+ error: z.string().describe('Error message')
616
+ })).describe('Execution errors')
617
+ }).describe('Complete scan result');
618
+
619
+ /**
620
+ * Type representing scan result
621
+ * @typedef {z.infer<typeof ScanResultSchema>} ScanResult
622
+ */
623
+ export const ScanResultType = ScanResultSchema;
624
+
625
+ // ============================================================================
626
+ // FACTORY FUNCTIONS (25 factories per SPARC spec)
627
+ // ============================================================================
628
+
629
+ /**
630
+ * Create valid frontmatter object with defaults
631
+ * @param {Partial<z.infer<typeof FrontmatterSchema>>} data - Partial frontmatter
632
+ * @returns {z.infer<typeof FrontmatterSchema>} Complete frontmatter
633
+ * @example
634
+ * const fm = createFrontmatter({
635
+ * o_hash: 'a'.repeat(64),
636
+ * policy_id: '550e8400-e29b-41d4-a716-446655440000'
637
+ * });
638
+ */
639
+ export function createFrontmatter(data) {
640
+ const now = new Date().toISOString();
641
+ const defaults = {
642
+ o_hash: '0'.repeat(64),
643
+ policy_id: '00000000-0000-4000-8000-000000000000',
644
+ receipts: [],
645
+ bounds: { maxQueries: 100, maxRuntime: 5000, maxFileScans: 50 },
646
+ views: ['reference'],
647
+ sources: [],
648
+ version: '1.0.0',
649
+ createdAt: now,
650
+ lastProved: now
651
+ };
652
+ return FrontmatterSchema.parse({ ...defaults, ...data });
653
+ }
654
+
655
+ /**
656
+ * Create valid bounds object
657
+ * @param {Partial<z.infer<typeof BoundsSchema>>} data - Partial bounds
658
+ * @returns {z.infer<typeof BoundsSchema>} Complete bounds
659
+ */
660
+ export function createBounds(data = {}) {
661
+ const defaults = { maxQueries: 100, maxRuntime: 5000, maxFileScans: 50 };
662
+ return BoundsSchema.parse({ ...defaults, ...data });
663
+ }
664
+
665
+ /**
666
+ * Create valid source reference
667
+ * @param {Partial<z.infer<typeof SourceSchema>>} data - Partial source
668
+ * @returns {z.infer<typeof SourceSchema>} Complete source
669
+ */
670
+ export function createSource(data) {
671
+ const defaults = {
672
+ path: 'src/index.mjs',
673
+ lineStart: 1,
674
+ lineEnd: 100,
675
+ hash: '0'.repeat(64)
676
+ };
677
+ return SourceSchema.parse({ ...defaults, ...data });
678
+ }
679
+
680
+ /**
681
+ * Create valid author object
682
+ * @param {Partial<z.infer<typeof AuthorSchema>>} data - Partial author
683
+ * @returns {z.infer<typeof AuthorSchema>} Complete author
684
+ */
685
+ export function createAuthor(data) {
686
+ const defaults = { name: 'Anonymous' };
687
+ return AuthorSchema.parse({ ...defaults, ...data });
688
+ }
689
+
690
+ /**
691
+ * Create valid block metadata
692
+ * @param {Partial<z.infer<typeof BlockMetadataSchema>>} data - Partial metadata
693
+ * @returns {z.infer<typeof BlockMetadataSchema>} Complete metadata
694
+ */
695
+ export function createBlockMetadata(data) {
696
+ const defaults = {
697
+ receiptId: '0'.repeat(64),
698
+ expectedOutputFormat: 'json',
699
+ determinismLevel: 'strict'
700
+ };
701
+ return BlockMetadataSchema.parse({ ...defaults, ...data });
702
+ }
703
+
704
+ /**
705
+ * Create valid query metadata
706
+ * @param {Partial<z.infer<typeof QueryMetadataSchema>>} data - Partial metadata
707
+ * @returns {z.infer<typeof QueryMetadataSchema>} Complete metadata
708
+ */
709
+ export function createQueryMetadata(data) {
710
+ const defaults = {
711
+ receiptId: '0'.repeat(64),
712
+ expectedOutputFormat: 'json',
713
+ determinismLevel: 'strict',
714
+ queryType: 'sparql',
715
+ resultBounds: { minResults: 0, maxResults: 1000 },
716
+ timeout: 5000
717
+ };
718
+ return QueryMetadataSchema.parse({ ...defaults, ...data });
719
+ }
720
+
721
+ /**
722
+ * Create valid extract metadata
723
+ * @param {Partial<z.infer<typeof ExtractMetadataSchema>>} data - Partial metadata
724
+ * @returns {z.infer<typeof ExtractMetadataSchema>} Complete metadata
725
+ */
726
+ export function createExtractMetadata(data) {
727
+ const defaults = {
728
+ receiptId: '0'.repeat(64),
729
+ expectedOutputFormat: 'json',
730
+ determinismLevel: 'lenient',
731
+ extractionType: 'exports',
732
+ fileGlobs: ['src/**/*.mjs'],
733
+ includePrivate: false,
734
+ includeDocstrings: true
735
+ };
736
+ return ExtractMetadataSchema.parse({ ...defaults, ...data });
737
+ }
738
+
739
+ /**
740
+ * Create valid render metadata
741
+ * @param {Partial<z.infer<typeof RenderMetadataSchema>>} data - Partial metadata
742
+ * @returns {z.infer<typeof RenderMetadataSchema>} Complete metadata
743
+ */
744
+ export function createRenderMetadata(data) {
745
+ const defaults = {
746
+ receiptId: '0'.repeat(64),
747
+ expectedOutputFormat: 'markdown',
748
+ determinismLevel: 'strict',
749
+ templateName: 'api-reference',
750
+ sectionTitle: 'API Reference',
751
+ includeTableOfContents: true
752
+ };
753
+ return RenderMetadataSchema.parse({ ...defaults, ...data });
754
+ }
755
+
756
+ /**
757
+ * Create valid proof metadata
758
+ * @param {Partial<z.infer<typeof ProofMetadataSchema>>} data - Partial metadata
759
+ * @returns {z.infer<typeof ProofMetadataSchema>} Complete metadata
760
+ */
761
+ export function createProofMetadata(data) {
762
+ const defaults = {
763
+ receiptId: '0'.repeat(64),
764
+ expectedOutputFormat: 'json',
765
+ determinismLevel: 'strict',
766
+ proofType: 'merkle',
767
+ verifyChain: true,
768
+ validateSignatures: false
769
+ };
770
+ return ProofMetadataSchema.parse({ ...defaults, ...data });
771
+ }
772
+
773
+ /**
774
+ * Create valid receipt object
775
+ * @param {Partial<z.infer<typeof ReceiptSchema>>} data - Partial receipt
776
+ * @returns {z.infer<typeof ReceiptSchema>} Complete receipt
777
+ */
778
+ export function createReceipt(data) {
779
+ const now = new Date().toISOString();
780
+ const defaults = {
781
+ id: '0'.repeat(64),
782
+ timestamp: now,
783
+ o_hash: '0'.repeat(64),
784
+ block_type: 'kgc:query',
785
+ input_hash: '0'.repeat(64),
786
+ output_hash: '0'.repeat(64),
787
+ decision: 'ADMIT'
788
+ };
789
+ return ReceiptSchema.parse({ ...defaults, ...data });
790
+ }
791
+
792
+ /**
793
+ * Create valid merkle proof
794
+ * @param {Partial<z.infer<typeof MerkleProofSchema>>} data - Partial proof
795
+ * @returns {z.infer<typeof MerkleProofSchema>} Complete proof
796
+ */
797
+ export function createMerkleProof(data) {
798
+ const defaults = {
799
+ siblings: [],
800
+ root: '0'.repeat(64),
801
+ index: 0,
802
+ totalLeaves: 1
803
+ };
804
+ return MerkleProofSchema.parse({ ...defaults, ...data });
805
+ }
806
+
807
+ /**
808
+ * Create valid dynamic section
809
+ * @param {Partial<z.infer<typeof DynamicSectionSchema>>} data - Partial section
810
+ * @returns {z.infer<typeof DynamicSectionSchema>} Complete section
811
+ */
812
+ export function createDynamicSection(data) {
813
+ const defaults = {
814
+ name: 'section',
815
+ receiptId: '0'.repeat(64),
816
+ lineStart: 1,
817
+ lineEnd: 100
818
+ };
819
+ return DynamicSectionSchema.parse({ ...defaults, ...data });
820
+ }
821
+
822
+ /**
823
+ * Create valid probe error
824
+ * @param {Partial<z.infer<typeof ProbeErrorSchema>>} data - Partial error
825
+ * @returns {z.infer<typeof ProbeErrorSchema>} Complete error
826
+ */
827
+ export function createProbeError(data) {
828
+ const defaults = {
829
+ type: 'SchemaViolation',
830
+ severity: 'error',
831
+ message: 'Validation failed'
832
+ };
833
+ return ProbeErrorSchema.parse({ ...defaults, ...data });
834
+ }
835
+
836
+ /**
837
+ * Create valid probe warning
838
+ * @param {Partial<z.infer<typeof ProbeWarningSchema>>} data - Partial warning
839
+ * @returns {z.infer<typeof ProbeWarningSchema>} Complete warning
840
+ */
841
+ export function createProbeWarning(data) {
842
+ const defaults = {
843
+ type: 'BoundsExceeded',
844
+ severity: 'warning',
845
+ message: 'Resource utilization high'
846
+ };
847
+ return ProbeWarningSchema.parse({ ...defaults, ...data });
848
+ }
849
+
850
+ /**
851
+ * Create valid domain result
852
+ * @param {Partial<z.infer<typeof DomainResultSchema>>} data - Partial result
853
+ * @returns {z.infer<typeof DomainResultSchema>} Complete result
854
+ */
855
+ export function createDomainResult(data) {
856
+ const defaults = {
857
+ domain: 'frontmatter',
858
+ passed: true,
859
+ checks: 10,
860
+ errors: [],
861
+ warnings: []
862
+ };
863
+ return DomainResultSchema.parse({ ...defaults, ...data });
864
+ }
865
+
866
+ /**
867
+ * Create valid probe report
868
+ * @param {Partial<z.infer<typeof ProbeReportSchema>>} data - Partial report
869
+ * @returns {z.infer<typeof ProbeReportSchema>} Complete report
870
+ */
871
+ export function createProbeReport(data) {
872
+ const now = new Date().toISOString();
873
+ const defaults = {
874
+ version: '1.0',
875
+ documentPath: 'docs/example.kgcmd',
876
+ timestamp: now,
877
+ status: 'PASS',
878
+ domains: [],
879
+ summary: {
880
+ totalChecks: 0,
881
+ totalPassed: 0,
882
+ totalErrors: 0,
883
+ totalWarnings: 0,
884
+ coverage: 1.0
885
+ },
886
+ metadata: {
887
+ probeVersion: '1.0.0',
888
+ executionTimeMs: 0
889
+ }
890
+ };
891
+ return ProbeReportSchema.parse({ ...defaults, ...data });
892
+ }
893
+
894
+ /**
895
+ * Create probe configuration
896
+ * @param {Partial<z.infer<typeof ProbeConfigSchema>>} data - Partial config
897
+ * @returns {z.infer<typeof ProbeConfigSchema>} Complete config
898
+ */
899
+ export function createProbeConfig(data) {
900
+ const defaults = {
901
+ universe_id: 'default-universe',
902
+ distributed: false,
903
+ persist: true,
904
+ timeout_ms: 300000,
905
+ batch_size: 100
906
+ };
907
+ return ProbeConfigSchema.parse({ ...defaults, ...data });
908
+ }
909
+
910
+ /**
911
+ * Create valid observation
912
+ * @param {Partial<z.infer<typeof ObservationSchema>>} data - Partial observation
913
+ * @returns {z.infer<typeof ObservationSchema>} Complete observation
914
+ */
915
+ export function createObservation(data) {
916
+ const now = new Date().toISOString();
917
+ const generateUUID = () => {
918
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
919
+ const r = (Math.random() * 16) | 0;
920
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
921
+ return v.toString(16);
922
+ });
923
+ };
924
+ const defaults = {
925
+ id: generateUUID(),
926
+ agent: 'unknown',
927
+ timestamp: now,
928
+ kind: 'completeness',
929
+ severity: 'info',
930
+ subject: '',
931
+ evidence: {
932
+ query: '',
933
+ result: null,
934
+ witnesses: []
935
+ },
936
+ metrics: {
937
+ confidence: 0.5,
938
+ coverage: 0.5,
939
+ latency_ms: 0
940
+ },
941
+ tags: []
942
+ };
943
+ return ObservationSchema.parse({ ...defaults, ...data });
944
+ }
945
+
946
+ // ============================================================================
947
+ // VALIDATION HELPER FUNCTIONS
948
+ // ============================================================================
949
+
950
+ /**
951
+ * Validate observation against schema
952
+ * @param {unknown} data - Data to validate
953
+ * @returns {z.infer<typeof ObservationSchema>} Validated observation
954
+ * @throws {z.ZodError} If validation fails
955
+ */
956
+ export function validateObservation(data) {
957
+ return ObservationSchema.parse(data);
958
+ }
959
+
960
+ /**
961
+ * Validate artifact against schema
962
+ * @param {unknown} data - Data to validate
963
+ * @returns {z.infer<typeof ArtifactSchema>} Validated artifact
964
+ * @throws {z.ZodError} If validation fails
965
+ */
966
+ export function validateArtifact(data) {
967
+ return ArtifactSchema.parse(data);
968
+ }
969
+
970
+ /**
971
+ * Validate probe configuration
972
+ * @param {unknown} data - Data to validate
973
+ * @returns {z.infer<typeof ProbeConfigSchema>} Validated config
974
+ * @throws {z.ZodError} If validation fails
975
+ */
976
+ export function validateProbeConfig(data) {
977
+ return ProbeConfigSchema.parse(data);
978
+ }
979
+
980
+ /**
981
+ * Validate frontmatter
982
+ * @param {unknown} data - Data to validate
983
+ * @returns {z.infer<typeof FrontmatterSchema>} Validated frontmatter
984
+ * @throws {z.ZodError} If validation fails
985
+ */
986
+ export function validateFrontmatter(data) {
987
+ return FrontmatterSchema.parse(data);
988
+ }
989
+
990
+ /**
991
+ * Validate receipt
992
+ * @param {unknown} data - Data to validate
993
+ * @returns {z.infer<typeof ReceiptSchema>} Validated receipt
994
+ * @throws {z.ZodError} If validation fails
995
+ */
996
+ export function validateReceipt(data) {
997
+ return ReceiptSchema.parse(data);
998
+ }
999
+
1000
+ /**
1001
+ * Safe validation (returns null instead of throwing)
1002
+ * @param {unknown} data - Data to validate
1003
+ * @returns {z.infer<typeof ObservationSchema> | null} Validated observation or null
1004
+ */
1005
+ export function tryValidateObservation(data) {
1006
+ const result = ObservationSchema.safeParse(data);
1007
+ return result.success ? result.data : null;
1008
+ }
1009
+
1010
+ /**
1011
+ * Safe frontmatter validation
1012
+ * @param {unknown} data - Data to validate
1013
+ * @returns {z.infer<typeof FrontmatterSchema> | null} Validated or null
1014
+ */
1015
+ export function tryValidateFrontmatter(data) {
1016
+ const result = FrontmatterSchema.safeParse(data);
1017
+ return result.success ? result.data : null;
1018
+ }
1019
+
1020
+ /**
1021
+ * Safe receipt validation
1022
+ * @param {unknown} data - Data to validate
1023
+ * @returns {z.infer<typeof ReceiptSchema> | null} Validated or null
1024
+ */
1025
+ export function tryValidateReceipt(data) {
1026
+ const result = ReceiptSchema.safeParse(data);
1027
+ return result.success ? result.data : null;
1028
+ }