@orderful/droid 0.54.0 → 0.55.0

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.
@@ -33,6 +33,7 @@
33
33
  "./src/tools/share/skills/share/SKILL.md",
34
34
  "./src/tools/status-update/skills/status-update/SKILL.md",
35
35
  "./src/tools/tech-design/skills/tech-design/SKILL.md",
36
+ "./src/tools/test-blueprints/skills/test-blueprints/SKILL.md",
36
37
  "./src/tools/webform/skills/webform/SKILL.md",
37
38
  "./src/tools/wrapup/skills/wrapup/SKILL.md"
38
39
  ],
@@ -53,6 +54,7 @@
53
54
  "./src/tools/share/commands/share.md",
54
55
  "./src/tools/status-update/commands/status-update.md",
55
56
  "./src/tools/tech-design/commands/tech-design.md",
57
+ "./src/tools/test-blueprints/commands/test-blueprints.md",
56
58
  "./src/tools/webform/commands/webform.md",
57
59
  "./src/tools/wrapup/commands/wrapup.md"
58
60
  ],
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @orderful/droid
2
2
 
3
+ ## 0.55.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#330](https://github.com/Orderful/droid/pull/330) [`490323a`](https://github.com/Orderful/droid/commit/490323a23b49a420ebbbc5a02cb021afb1387d29) Thanks [@DenisSa](https://github.com/DenisSa)! - Add test-blueprints tool for writing and verifying EDI test blueprint HBS templates
8
+
3
9
  ## 0.54.0
4
10
 
5
11
  ### Minor Changes
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "droid-test-blueprints",
3
+ "version": "0.1.0",
4
+ "description": "Write and verify test blueprint HBS templates for EDI transaction types. Automates scenario design, template creation, registration, and Zod schema validation.",
5
+ "author": {
6
+ "name": "Orderful",
7
+ "url": "https://github.com/orderful"
8
+ },
9
+ "repository": "https://github.com/orderful/droid",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "droid",
13
+ "ai",
14
+ "test-blueprints"
15
+ ],
16
+ "skills": [
17
+ "./skills/test-blueprints/SKILL.md"
18
+ ],
19
+ "commands": [
20
+ "./commands/test-blueprints.md"
21
+ ]
22
+ }
@@ -0,0 +1,22 @@
1
+ name: test-blueprints
2
+ description: "Write and verify test blueprint HBS templates for EDI transaction types. Automates scenario design, template creation, registration, and Zod schema validation."
3
+ version: 0.1.0
4
+ status: beta
5
+ audience:
6
+ - engineering
7
+
8
+ includes:
9
+ skills:
10
+ - name: test-blueprints
11
+ required: true
12
+ commands:
13
+ - name: test-blueprints
14
+ is_alias: false
15
+ agents: []
16
+
17
+ dependencies: []
18
+
19
+ prerequisites:
20
+ - "ts-node and tsconfig-paths must be available in the target workspace (used by the validate script)"
21
+
22
+ config_schema: {}
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: test-blueprints
3
+ description: "Write and verify test blueprint HBS templates for EDI transaction types."
4
+ argument-hint: "[write {type} | verify {type}]"
5
+ ---
6
+
7
+ # /test-blueprints
8
+
9
+ **User invoked:** `/test-blueprints $ARGUMENTS`
10
+
11
+ **Your task:** Invoke the **test-blueprints skill** with these arguments.
12
+
13
+ ## Examples
14
+
15
+ - `/test-blueprints write 943`
16
+ - `/test-blueprints verify 943`
17
+ - `/test-blueprints 944`
18
+
19
+ ## Quick Reference
20
+
21
+ ```bash
22
+ /test-blueprints write {type} # Full create flow: schema → scenarios → templates → register → validate
23
+ /test-blueprints verify {type} # Validate existing blueprints against Zod schema
24
+ /test-blueprints {type} # Alias for write {type}
25
+ ```
26
+
27
+ See the **test-blueprints skill** for full behaviour and validation rules.
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: test-blueprints
3
+ description: "Write and verify test blueprint HBS templates for EDI transaction types. Use when creating test scenarios for a new transaction type (e.g., 'create blueprints for 943'), verifying existing blueprints against their Zod schema (e.g., 'verify 943 blueprints', 'validate the 855 templates'), or adding test blueprints to the testTransformation module. User prompts like 'create test blueprints for 940', 'generate 944 test templates', 'verify 943 blueprints', 'validate the 855 templates', 'check if 944 blueprints are valid'."
4
+ argument-hint: "[write {type} | verify {type}]"
5
+ allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, Agent]
6
+ ---
7
+
8
+ # Test Blueprints Skill
9
+
10
+ Write and verify test blueprint HBS templates for EDI transaction types in the Orderful platform. Each blueprint is a JSON file conforming to the transaction type's simplified Zod schema, representing a distinct business scenario.
11
+
12
+ ## Commands
13
+
14
+ | Command | Action |
15
+ |---------|--------|
16
+ | `/test-blueprints write {type}` | Full workflow: discover schema, design scenarios, create templates, register, validate |
17
+ | `/test-blueprints verify {type}` | Validate existing blueprints against the Zod schema and report results |
18
+ | `/test-blueprints {type}` | Alias for `write {type}` |
19
+
20
+ ---
21
+
22
+ ## Command: verify
23
+
24
+ Validate existing blueprint templates against their Zod schema. Use this to check that all templates for a transaction type are still valid after schema changes, or to spot-check templates at any time.
25
+
26
+ ### Steps
27
+
28
+ 1. **Locate files.** Read `references/codebase-paths.md` for exact paths.
29
+ - Find the Zod schema for the transaction type
30
+ - Find the blueprint directory (`testBlueprints/{type}s/`)
31
+ - If no blueprints exist for the type, report that and stop
32
+
33
+ 2. **Read the Zod schema** to determine the schema export name and import path.
34
+
35
+ 3. **Run the validation script:**
36
+ ```bash
37
+ droid exec test-blueprints validate \
38
+ --workspace {workspace-root} \
39
+ --schema-import "{import-path}" \
40
+ --schema-name "{SchemaExportName}" \
41
+ --blueprints-dir "{path-to-blueprints-dir}"
42
+ ```
43
+
44
+ The script automatically:
45
+ - Discovers all `.json.hbs` files in the blueprints directory
46
+ - Skips templates containing Handlebars expressions (validated at render time, not statically)
47
+ - Validates remaining templates against the Zod schema via ts-node
48
+ - Returns structured JSON with per-file pass/fail/skip results
49
+
50
+ 4. **Report results.** Parse the JSON output and show a summary table:
51
+ - PASS / FAIL / SKIP status per template
52
+ - For failures: the exact field path and error message
53
+ - Suggest fixes for common mistakes (see Common Validation Pitfalls below)
54
+
55
+ ---
56
+
57
+ ## Command: write
58
+
59
+ Full workflow to create test blueprint templates for a transaction type.
60
+
61
+ ### Phase 1: Discovery
62
+
63
+ Identify the transaction type and locate the required files. Read `references/codebase-paths.md` for exact paths.
64
+
65
+ 1. **Find the Zod schema** for the transaction type. This is the source of truth for the template shape. Read it fully - every field, every enum value, every `metaEnum` definition. Also read `shared.zod.ts` for all imported shared types and their enum values.
66
+
67
+ 2. **Find the example JSON** (if one exists) in the simplified schema examples directory. This shows realistic field values.
68
+
69
+ 3. **Check TransactionTypeName** to confirm the enum key exists (e.g., `T943`).
70
+
71
+ 4. **Check existing blueprints** to see if templates already exist for this type. If they do, ask the user whether to replace or augment.
72
+
73
+ 5. **Determine if the template is static or templated.** This is an EDI domain question, not a codebase config lookup — the config may not exist yet for the transaction type.
74
+
75
+ Ask: **"Is this transaction type a direct response that echoes back data from a specific inbound document?"**
76
+
77
+ - **Static** — The transaction originates from the sender's own system data. The sender generates it from their own records (WMS, ERP, inventory system) without referencing a specific inbound EDI document. Templates are pure JSON with no Handlebars expressions.
78
+ - Examples: 846 (inventory report from seller's system), 943 (warehouse ships from WMS data), 944 (warehouse reports what it received), 940 (depositor issues a shipping order)
79
+ - **Templated** — The transaction is a direct response to a specific inbound document and structurally echoes back fields from it (line items, PO numbers, product IDs). Templates use Handlebars expressions to pull from the upstream document's data.
80
+ - Examples: 855 (acknowledges an 850's line items), 856 (ships against an 850), 810 (invoices against an 856)
81
+
82
+ If unsure, check existing templates for similar transaction types in the same document family.
83
+
84
+ ### Phase 2: Scenario Design
85
+
86
+ Analyse the Zod schema to identify the key variability dimensions, then design scenarios.
87
+
88
+ **Identify dimensions of variability:**
89
+ - Enum fields (especially top-level ones like reporting codes, purpose codes, status codes) - each distinct value is a potential scenario axis
90
+ - Optional object blocks (parties, dates, notes, references, carrier details) - presence/absence is a scenario axis
91
+ - Array fields (line items, pallets) - single vs. multiple is a scenario axis
92
+ - Nested optional structures (exception details, physical details, lot tracking) - exercising these is important
93
+
94
+ **Select 8-12 scenarios** ranked by business impact. Always include:
95
+ 1. **Standard/baseline** - the happy path with typical fields populated
96
+ 2. **Comprehensive** - every field populated (mirrors `810_comprehensive` / `846_comprehensive` pattern)
97
+ 3. **Minimal** - only required fields, everything optional absent
98
+
99
+ Then select the remaining scenarios based on what the schema uniquely supports. Prioritise:
100
+ - Each distinct enum value for top-level enums (e.g., different reporting codes)
101
+ - Key structural variations (pallets vs. loose items, single vs. multi-line)
102
+ - Industry-specific scenarios (cold chain, lot tracking, ocean shipment, returns)
103
+ - Correction/amendment flows (if the schema has a purpose/action code)
104
+
105
+ **Present the scenario list to the user** with a brief rationale for each before creating files. Wait for approval.
106
+
107
+ ### Phase 3: Template Creation
108
+
109
+ Create the blueprint files.
110
+
111
+ 1. **Create the directory** at:
112
+ ```
113
+ apps/platform-api/src/modules/testTransformation/data/testBlueprints/{type}s/
114
+ ```
115
+ where `{type}` is the three-digit transaction number (e.g., `943s`, `944s`).
116
+
117
+ 2. **Write each template** as a `.json.hbs` file. Follow these rules:
118
+ - File naming: `{type}_{scenarioName}.json.hbs` (e.g., `943_standard.json.hbs`)
119
+ - If static: pure JSON, no Handlebars expressions
120
+ - If templated: use Handlebars expressions to reference upstream context (e.g., `{{purchaseOrder.*}}`, `{{#each purchaseOrder.lineItems}}`). See existing 855 templates for the PO-response pattern.
121
+ - Use realistic but clearly fictional data (company names, addresses, product descriptions)
122
+ - Use distinct data across templates - don't copy-paste the same parties/products everywhere
123
+ - Every enum value must be a valid value from the Zod schema's `metaEnum` definition - use the simplified name (e.g., `"commonCarrier"`), not the X12 code (e.g., not `"M"`)
124
+
125
+ ### Phase 4: Registration
126
+
127
+ Register the blueprints in the repository.
128
+
129
+ 1. **Edit `testBlueprint.repository.ts`**:
130
+ - Add imports for each template file (follow the existing naming convention)
131
+ - Add a new entry in `blueprintsByTransactionType` under the appropriate `TransactionTypeName` key
132
+ - Place it in alphabetical/numerical order relative to existing entries
133
+
134
+ ### Phase 5: Validation
135
+
136
+ Run the **verify** command for the newly created templates. Fix any errors and re-run until all pass.
137
+
138
+ ### Phase 6: Summary
139
+
140
+ Report to the user:
141
+ - How many templates were created
142
+ - A table of template names and the scenario each covers
143
+ - The file edited for registration
144
+ - Validation result (all pass)
145
+
146
+ ---
147
+
148
+ ## Common Validation Pitfalls
149
+
150
+ These are the most frequent errors when writing blueprints. Check `shared.zod.ts` to verify exact enum values.
151
+
152
+ | Mistake | Fix |
153
+ |---------|-----|
154
+ | `"motor"` for transportation method | Use `"commonCarrier"` |
155
+ | `"truck"` for transportation method | Use `"commonCarrier"` or `"privateCarrier"` |
156
+ | `"inches"` for dimension UOM | Use `"inch"` |
157
+ | `"assignedByBuyer"` for identification code type | Use `"receiversId"` or a value from the enum |
158
+ | X12 codes like `"M"`, `"F"`, `"01"` | Use simplified names like `"commonCarrier"`, `"fullDetail"`, `"damaged"` |
159
+ | `"lbs"` or `"kg"` for weight UOM | Use `"pound"` or `"kilogram"` |
160
+ | `"EA"` or `"CS"` for unit of measure | Use `"each"` or `"case"` (lowercase simplified names) |
@@ -0,0 +1,79 @@
1
+ # Codebase Paths Reference
2
+
3
+ All paths are relative to the orderful-workspace root.
4
+
5
+ ## Zod Schemas (source of truth for template shape)
6
+
7
+ ```
8
+ libs/schemas/src/lib/data/simplifiedSchemas/
9
+ ```
10
+
11
+ To find the Zod schema for a transaction type, search the index:
12
+ ```bash
13
+ grep -r "TransactionTypeName.T<number>" libs/schemas/src/lib/data/simplifiedSchemas/index.ts
14
+ ```
15
+
16
+ Then find the schema file and export name from the import.
17
+
18
+ ## Example JSON (realistic field values)
19
+
20
+ ```
21
+ libs/schemas/src/lib/data/simplifiedSchemas/examples/
22
+ ```
23
+
24
+ Pattern: `simplified-<schemaName>.json`
25
+
26
+ ## Shared Zod Schemas (enum values, shared types)
27
+
28
+ ```
29
+ libs/schemas/src/lib/data/simplifiedSchemas/shared.zod.ts
30
+ ```
31
+
32
+ This file defines all shared types used across transaction schemas:
33
+ - `PartySchema` - party identification with contacts
34
+ - `QuantitySchema` - value + unit of measure
35
+ - `PhysicalDetailsSchema` - pack, weight, dimensions
36
+ - `LineItemIdentifiersSchema` - product IDs (buyer, vendor, GTIN)
37
+ - `ProductAttributeDetailsSchema` - description, color, size, material
38
+ - `RoutingDetailsSchema` - carrier and transportation method
39
+ - `EquipmentDetailsSchema` - trailer, container, seal
40
+ - `FOBDetailsSchema` - freight payment terms
41
+ - `SpecialHandlingDetailsSchema` - handling codes
42
+ - `CarrierWeightDetailsSchema` - shipment weight totals
43
+ - `NoteDetailsSchema` - notes with text array
44
+ - `PalletExchangeSchema` - pallet exchange/return enum
45
+ - `ContactSchema` - contact person details
46
+
47
+ ## TransactionTypeName Enum
48
+
49
+ ```
50
+ libs/platform-types/src/lib/transactionTypeNames.ts
51
+ ```
52
+
53
+ Check this file for the exact enum key (e.g., `T943`, `T944`).
54
+
55
+ ## Blueprint Templates
56
+
57
+ ```
58
+ apps/platform-api/src/modules/testTransformation/data/testBlueprints/
59
+ ```
60
+
61
+ Organised by type: `810s/`, `846s/`, `855s/`, `856s/`, `943s/`, etc.
62
+
63
+ ## Blueprint Repository (registration)
64
+
65
+ ```
66
+ apps/platform-api/src/modules/testTransformation/repositories/testBlueprint.repository.ts
67
+ ```
68
+
69
+ This file:
70
+ 1. Imports all `.json.hbs` template files
71
+ 2. Registers them in `blueprintsByTransactionType` under the `TransactionTypeName` key
72
+ 3. Optionally registers upstream blueprints in `upstreamBlueprints`
73
+
74
+ ## Validation Script Execution
75
+
76
+ ```bash
77
+ cd <workspace-root> && npx ts-node -r tsconfig-paths/register \
78
+ --project apps/platform-api/tsconfig.json <path-to-script>
79
+ ```
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * test-blueprints validate
4
+ *
5
+ * Validate blueprint .json.hbs templates against their Zod schema.
6
+ * Generates a temporary validation script in the workspace and runs it
7
+ * via ts-node so that tsconfig paths resolve correctly.
8
+ *
9
+ * Usage:
10
+ * droid exec test-blueprints validate \
11
+ * --workspace /path/to/orderful-workspace \
12
+ * --schema-import "simplifiedSchemas/schemaFile" \
13
+ * --schema-name "Simplified943Schema" \
14
+ * --blueprints-dir "apps/platform-api/src/modules/testTransformation/data/testBlueprints/943s"
15
+ *
16
+ * Output (JSON):
17
+ * {
18
+ * "success": true,
19
+ * "results": [
20
+ * { "file": "943_standard.json.hbs", "status": "pass" },
21
+ * { "file": "943_templated.json.hbs", "status": "skip", "reason": "contains Handlebars expressions" },
22
+ * { "file": "943_bad.json.hbs", "status": "fail", "errors": [{ "path": "header.type", "message": "Invalid enum value" }] }
23
+ * ],
24
+ * "summary": { "pass": 8, "fail": 1, "skip": 2, "total": 11 }
25
+ * }
26
+ */
27
+ export {};
28
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../../../src/tools/test-blueprints/skills/test-blueprints/scripts/validate.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * test-blueprints validate
4
+ *
5
+ * Validate blueprint .json.hbs templates against their Zod schema.
6
+ * Generates a temporary validation script in the workspace and runs it
7
+ * via ts-node so that tsconfig paths resolve correctly.
8
+ *
9
+ * Usage:
10
+ * droid exec test-blueprints validate \
11
+ * --workspace /path/to/orderful-workspace \
12
+ * --schema-import "simplifiedSchemas/schemaFile" \
13
+ * --schema-name "Simplified943Schema" \
14
+ * --blueprints-dir "apps/platform-api/src/modules/testTransformation/data/testBlueprints/943s"
15
+ *
16
+ * Output (JSON):
17
+ * {
18
+ * "success": true,
19
+ * "results": [
20
+ * { "file": "943_standard.json.hbs", "status": "pass" },
21
+ * { "file": "943_templated.json.hbs", "status": "skip", "reason": "contains Handlebars expressions" },
22
+ * { "file": "943_bad.json.hbs", "status": "fail", "errors": [{ "path": "header.type", "message": "Invalid enum value" }] }
23
+ * ],
24
+ * "summary": { "pass": 8, "fail": 1, "skip": 2, "total": 11 }
25
+ * }
26
+ */
27
+
28
+ import { execSync } from 'child_process';
29
+ import { existsSync, writeFileSync, unlinkSync, readdirSync, readFileSync } from 'fs';
30
+ import { join } from 'path';
31
+ import { tmpdir } from 'os';
32
+
33
+ interface ValidationError {
34
+ path: string;
35
+ message: string;
36
+ }
37
+
38
+ interface FileResult {
39
+ file: string;
40
+ status: 'pass' | 'fail' | 'skip';
41
+ reason?: string;
42
+ errors?: ValidationError[];
43
+ }
44
+
45
+ interface ValidateResult {
46
+ success: boolean;
47
+ results?: FileResult[];
48
+ summary?: { pass: number; fail: number; skip: number; total: number };
49
+ error?: string;
50
+ }
51
+
52
+ interface ParsedArgs {
53
+ workspace?: string;
54
+ schemaImport?: string;
55
+ schemaName?: string;
56
+ blueprintsDir?: string;
57
+ }
58
+
59
+ function parseArgs(args: string[]): ParsedArgs {
60
+ const result: ParsedArgs = {};
61
+
62
+ for (let i = 0; i < args.length; i++) {
63
+ const arg = args[i];
64
+ if (arg === '--workspace' && args[i + 1]) {
65
+ result.workspace = args[++i];
66
+ } else if (arg === '--schema-import' && args[i + 1]) {
67
+ result.schemaImport = args[++i];
68
+ } else if (arg === '--schema-name' && args[i + 1]) {
69
+ result.schemaName = args[++i];
70
+ } else if (arg === '--blueprints-dir' && args[i + 1]) {
71
+ result.blueprintsDir = args[++i];
72
+ }
73
+ }
74
+
75
+ return result;
76
+ }
77
+
78
+ function expandPath(p: string): string {
79
+ if (p.startsWith('~/')) {
80
+ return p.replace('~', process.env.HOME || '');
81
+ }
82
+ if (p.startsWith('$HOME/')) {
83
+ return p.replace('$HOME', process.env.HOME || '');
84
+ }
85
+ return p;
86
+ }
87
+
88
+ function validate(parsed: ParsedArgs): ValidateResult {
89
+ if (!parsed.workspace) {
90
+ return { success: false, error: 'Missing --workspace' };
91
+ }
92
+ if (!parsed.schemaImport) {
93
+ return { success: false, error: 'Missing --schema-import' };
94
+ }
95
+ if (!parsed.schemaName) {
96
+ return { success: false, error: 'Missing --schema-name' };
97
+ }
98
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(parsed.schemaName)) {
99
+ return { success: false, error: 'Invalid --schema-name: must be a valid identifier' };
100
+ }
101
+ if (/['"\n\r]/.test(parsed.schemaImport)) {
102
+ return { success: false, error: 'Invalid --schema-import: must not contain quotes or newlines' };
103
+ }
104
+ if (!parsed.blueprintsDir) {
105
+ return { success: false, error: 'Missing --blueprints-dir' };
106
+ }
107
+
108
+ const workspace = expandPath(parsed.workspace);
109
+ if (!existsSync(workspace)) {
110
+ return { success: false, error: `Workspace not found: ${workspace}` };
111
+ }
112
+
113
+ const blueprintsDir = parsed.blueprintsDir.startsWith('/')
114
+ ? parsed.blueprintsDir
115
+ : join(workspace, parsed.blueprintsDir);
116
+
117
+ if (!existsSync(blueprintsDir)) {
118
+ return { success: false, error: `Blueprints directory not found: ${blueprintsDir}` };
119
+ }
120
+
121
+ // Find .json.hbs files
122
+ const files = readdirSync(blueprintsDir).filter(f => f.endsWith('.json.hbs'));
123
+ if (files.length === 0) {
124
+ return { success: false, error: `No .json.hbs files found in ${blueprintsDir}` };
125
+ }
126
+
127
+ // Pre-check for Handlebars templates (skip these from Zod validation)
128
+ const results: FileResult[] = [];
129
+ const filesToValidate: string[] = [];
130
+
131
+ for (const file of files) {
132
+ const content = readFileSync(join(blueprintsDir, file), 'utf-8');
133
+ if (content.includes('{{')) {
134
+ results.push({ file, status: 'skip', reason: 'contains Handlebars expressions' });
135
+ } else {
136
+ // Check JSON parsability before sending to ts-node
137
+ try {
138
+ JSON.parse(content);
139
+ filesToValidate.push(file);
140
+ } catch (e) {
141
+ const err = e as Error;
142
+ results.push({ file, status: 'fail', errors: [{ path: '(root)', message: `Invalid JSON: ${err.message}` }] });
143
+ }
144
+ }
145
+ }
146
+
147
+ // If all files are skipped or failed JSON parse, return early
148
+ if (filesToValidate.length === 0) {
149
+ const summary = {
150
+ pass: 0,
151
+ fail: results.filter(r => r.status === 'fail').length,
152
+ skip: results.filter(r => r.status === 'skip').length,
153
+ total: results.length,
154
+ };
155
+ return { success: summary.fail === 0, results, summary };
156
+ }
157
+
158
+ // Generate validation script
159
+ const validationScript = `
160
+ import { ${parsed.schemaName} } from '${parsed.schemaImport}';
161
+ import * as fs from 'fs';
162
+ import * as path from 'path';
163
+
164
+ const dir = ${JSON.stringify(blueprintsDir)};
165
+ const filesToValidate: string[] = ${JSON.stringify(filesToValidate)};
166
+ const results: Array<{ file: string; status: string; errors?: Array<{ path: string; message: string }> }> = [];
167
+
168
+ for (const file of filesToValidate) {
169
+ const content = fs.readFileSync(path.join(dir, file), 'utf-8');
170
+ const parsed = JSON.parse(content);
171
+ const result = ${parsed.schemaName}.safeParse(parsed);
172
+
173
+ if (result.success) {
174
+ results.push({ file, status: 'pass' });
175
+ } else {
176
+ results.push({
177
+ file,
178
+ status: 'fail',
179
+ errors: result.error.issues.map((issue: { path: (string | number)[]; message: string }) => ({
180
+ path: issue.path.join('.'),
181
+ message: issue.message,
182
+ })),
183
+ });
184
+ }
185
+ }
186
+
187
+ console.log(JSON.stringify(results));
188
+ `.trim();
189
+
190
+ const scriptPath = join(tmpdir(), `_validate-${Date.now()}.ts`);
191
+
192
+ try {
193
+ writeFileSync(scriptPath, validationScript, 'utf-8');
194
+
195
+ const output = execSync(
196
+ `npx ts-node -r tsconfig-paths/register --project apps/platform-api/tsconfig.json ${JSON.stringify(scriptPath)}`,
197
+ {
198
+ cwd: workspace,
199
+ encoding: 'utf-8',
200
+ stdio: ['pipe', 'pipe', 'pipe'],
201
+ timeout: 60_000,
202
+ }
203
+ );
204
+
205
+ let zodResults: Array<{ file: string; status: string; errors?: ValidationError[] }>;
206
+ try {
207
+ zodResults = JSON.parse(output.trim());
208
+ } catch {
209
+ return { success: false, error: `Failed to parse validation output: ${output}` };
210
+ }
211
+
212
+ for (const r of zodResults) {
213
+ results.push({
214
+ file: r.file,
215
+ status: r.status as 'pass' | 'fail',
216
+ errors: r.errors,
217
+ });
218
+ }
219
+ } catch (err: unknown) {
220
+ const error = err as { stderr?: string; message?: string };
221
+ return {
222
+ success: false,
223
+ error: `Validation script failed: ${error.stderr || error.message}`,
224
+ };
225
+ } finally {
226
+ try { unlinkSync(scriptPath); } catch { /* ignore cleanup errors */ }
227
+ }
228
+
229
+ // Sort results to match original file order
230
+ results.sort((a, b) => files.indexOf(a.file) - files.indexOf(b.file));
231
+
232
+ const summary = {
233
+ pass: results.filter(r => r.status === 'pass').length,
234
+ fail: results.filter(r => r.status === 'fail').length,
235
+ skip: results.filter(r => r.status === 'skip').length,
236
+ total: results.length,
237
+ };
238
+
239
+ return { success: summary.fail === 0, results, summary };
240
+ }
241
+
242
+ // Main
243
+ const args = process.argv.slice(2);
244
+ const parsed = parseArgs(args);
245
+ const result = validate(parsed);
246
+ console.log(JSON.stringify(result, null, 2));
247
+
248
+ if (!result.success) {
249
+ process.exit(1);
250
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.54.0",
3
+ "version": "0.55.0",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "droid-test-blueprints",
3
+ "version": "0.1.0",
4
+ "description": "Write and verify test blueprint HBS templates for EDI transaction types. Automates scenario design, template creation, registration, and Zod schema validation.",
5
+ "author": {
6
+ "name": "Orderful",
7
+ "url": "https://github.com/orderful"
8
+ },
9
+ "repository": "https://github.com/orderful/droid",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "droid",
13
+ "ai",
14
+ "test-blueprints"
15
+ ],
16
+ "skills": [
17
+ "./skills/test-blueprints/SKILL.md"
18
+ ],
19
+ "commands": [
20
+ "./commands/test-blueprints.md"
21
+ ]
22
+ }
@@ -0,0 +1,22 @@
1
+ name: test-blueprints
2
+ description: "Write and verify test blueprint HBS templates for EDI transaction types. Automates scenario design, template creation, registration, and Zod schema validation."
3
+ version: 0.1.0
4
+ status: beta
5
+ audience:
6
+ - engineering
7
+
8
+ includes:
9
+ skills:
10
+ - name: test-blueprints
11
+ required: true
12
+ commands:
13
+ - name: test-blueprints
14
+ is_alias: false
15
+ agents: []
16
+
17
+ dependencies: []
18
+
19
+ prerequisites:
20
+ - "ts-node and tsconfig-paths must be available in the target workspace (used by the validate script)"
21
+
22
+ config_schema: {}
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: test-blueprints
3
+ description: "Write and verify test blueprint HBS templates for EDI transaction types."
4
+ argument-hint: "[write {type} | verify {type}]"
5
+ ---
6
+
7
+ # /test-blueprints
8
+
9
+ **User invoked:** `/test-blueprints $ARGUMENTS`
10
+
11
+ **Your task:** Invoke the **test-blueprints skill** with these arguments.
12
+
13
+ ## Examples
14
+
15
+ - `/test-blueprints write 943`
16
+ - `/test-blueprints verify 943`
17
+ - `/test-blueprints 944`
18
+
19
+ ## Quick Reference
20
+
21
+ ```bash
22
+ /test-blueprints write {type} # Full create flow: schema → scenarios → templates → register → validate
23
+ /test-blueprints verify {type} # Validate existing blueprints against Zod schema
24
+ /test-blueprints {type} # Alias for write {type}
25
+ ```
26
+
27
+ See the **test-blueprints skill** for full behaviour and validation rules.
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: test-blueprints
3
+ description: "Write and verify test blueprint HBS templates for EDI transaction types. Use when creating test scenarios for a new transaction type (e.g., 'create blueprints for 943'), verifying existing blueprints against their Zod schema (e.g., 'verify 943 blueprints', 'validate the 855 templates'), or adding test blueprints to the testTransformation module. User prompts like 'create test blueprints for 940', 'generate 944 test templates', 'verify 943 blueprints', 'validate the 855 templates', 'check if 944 blueprints are valid'."
4
+ argument-hint: "[write {type} | verify {type}]"
5
+ allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, Agent]
6
+ ---
7
+
8
+ # Test Blueprints Skill
9
+
10
+ Write and verify test blueprint HBS templates for EDI transaction types in the Orderful platform. Each blueprint is a JSON file conforming to the transaction type's simplified Zod schema, representing a distinct business scenario.
11
+
12
+ ## Commands
13
+
14
+ | Command | Action |
15
+ |---------|--------|
16
+ | `/test-blueprints write {type}` | Full workflow: discover schema, design scenarios, create templates, register, validate |
17
+ | `/test-blueprints verify {type}` | Validate existing blueprints against the Zod schema and report results |
18
+ | `/test-blueprints {type}` | Alias for `write {type}` |
19
+
20
+ ---
21
+
22
+ ## Command: verify
23
+
24
+ Validate existing blueprint templates against their Zod schema. Use this to check that all templates for a transaction type are still valid after schema changes, or to spot-check templates at any time.
25
+
26
+ ### Steps
27
+
28
+ 1. **Locate files.** Read `references/codebase-paths.md` for exact paths.
29
+ - Find the Zod schema for the transaction type
30
+ - Find the blueprint directory (`testBlueprints/{type}s/`)
31
+ - If no blueprints exist for the type, report that and stop
32
+
33
+ 2. **Read the Zod schema** to determine the schema export name and import path.
34
+
35
+ 3. **Run the validation script:**
36
+ ```bash
37
+ droid exec test-blueprints validate \
38
+ --workspace {workspace-root} \
39
+ --schema-import "{import-path}" \
40
+ --schema-name "{SchemaExportName}" \
41
+ --blueprints-dir "{path-to-blueprints-dir}"
42
+ ```
43
+
44
+ The script automatically:
45
+ - Discovers all `.json.hbs` files in the blueprints directory
46
+ - Skips templates containing Handlebars expressions (validated at render time, not statically)
47
+ - Validates remaining templates against the Zod schema via ts-node
48
+ - Returns structured JSON with per-file pass/fail/skip results
49
+
50
+ 4. **Report results.** Parse the JSON output and show a summary table:
51
+ - PASS / FAIL / SKIP status per template
52
+ - For failures: the exact field path and error message
53
+ - Suggest fixes for common mistakes (see Common Validation Pitfalls below)
54
+
55
+ ---
56
+
57
+ ## Command: write
58
+
59
+ Full workflow to create test blueprint templates for a transaction type.
60
+
61
+ ### Phase 1: Discovery
62
+
63
+ Identify the transaction type and locate the required files. Read `references/codebase-paths.md` for exact paths.
64
+
65
+ 1. **Find the Zod schema** for the transaction type. This is the source of truth for the template shape. Read it fully - every field, every enum value, every `metaEnum` definition. Also read `shared.zod.ts` for all imported shared types and their enum values.
66
+
67
+ 2. **Find the example JSON** (if one exists) in the simplified schema examples directory. This shows realistic field values.
68
+
69
+ 3. **Check TransactionTypeName** to confirm the enum key exists (e.g., `T943`).
70
+
71
+ 4. **Check existing blueprints** to see if templates already exist for this type. If they do, ask the user whether to replace or augment.
72
+
73
+ 5. **Determine if the template is static or templated.** This is an EDI domain question, not a codebase config lookup — the config may not exist yet for the transaction type.
74
+
75
+ Ask: **"Is this transaction type a direct response that echoes back data from a specific inbound document?"**
76
+
77
+ - **Static** — The transaction originates from the sender's own system data. The sender generates it from their own records (WMS, ERP, inventory system) without referencing a specific inbound EDI document. Templates are pure JSON with no Handlebars expressions.
78
+ - Examples: 846 (inventory report from seller's system), 943 (warehouse ships from WMS data), 944 (warehouse reports what it received), 940 (depositor issues a shipping order)
79
+ - **Templated** — The transaction is a direct response to a specific inbound document and structurally echoes back fields from it (line items, PO numbers, product IDs). Templates use Handlebars expressions to pull from the upstream document's data.
80
+ - Examples: 855 (acknowledges an 850's line items), 856 (ships against an 850), 810 (invoices against an 856)
81
+
82
+ If unsure, check existing templates for similar transaction types in the same document family.
83
+
84
+ ### Phase 2: Scenario Design
85
+
86
+ Analyse the Zod schema to identify the key variability dimensions, then design scenarios.
87
+
88
+ **Identify dimensions of variability:**
89
+ - Enum fields (especially top-level ones like reporting codes, purpose codes, status codes) - each distinct value is a potential scenario axis
90
+ - Optional object blocks (parties, dates, notes, references, carrier details) - presence/absence is a scenario axis
91
+ - Array fields (line items, pallets) - single vs. multiple is a scenario axis
92
+ - Nested optional structures (exception details, physical details, lot tracking) - exercising these is important
93
+
94
+ **Select 8-12 scenarios** ranked by business impact. Always include:
95
+ 1. **Standard/baseline** - the happy path with typical fields populated
96
+ 2. **Comprehensive** - every field populated (mirrors `810_comprehensive` / `846_comprehensive` pattern)
97
+ 3. **Minimal** - only required fields, everything optional absent
98
+
99
+ Then select the remaining scenarios based on what the schema uniquely supports. Prioritise:
100
+ - Each distinct enum value for top-level enums (e.g., different reporting codes)
101
+ - Key structural variations (pallets vs. loose items, single vs. multi-line)
102
+ - Industry-specific scenarios (cold chain, lot tracking, ocean shipment, returns)
103
+ - Correction/amendment flows (if the schema has a purpose/action code)
104
+
105
+ **Present the scenario list to the user** with a brief rationale for each before creating files. Wait for approval.
106
+
107
+ ### Phase 3: Template Creation
108
+
109
+ Create the blueprint files.
110
+
111
+ 1. **Create the directory** at:
112
+ ```
113
+ apps/platform-api/src/modules/testTransformation/data/testBlueprints/{type}s/
114
+ ```
115
+ where `{type}` is the three-digit transaction number (e.g., `943s`, `944s`).
116
+
117
+ 2. **Write each template** as a `.json.hbs` file. Follow these rules:
118
+ - File naming: `{type}_{scenarioName}.json.hbs` (e.g., `943_standard.json.hbs`)
119
+ - If static: pure JSON, no Handlebars expressions
120
+ - If templated: use Handlebars expressions to reference upstream context (e.g., `{{purchaseOrder.*}}`, `{{#each purchaseOrder.lineItems}}`). See existing 855 templates for the PO-response pattern.
121
+ - Use realistic but clearly fictional data (company names, addresses, product descriptions)
122
+ - Use distinct data across templates - don't copy-paste the same parties/products everywhere
123
+ - Every enum value must be a valid value from the Zod schema's `metaEnum` definition - use the simplified name (e.g., `"commonCarrier"`), not the X12 code (e.g., not `"M"`)
124
+
125
+ ### Phase 4: Registration
126
+
127
+ Register the blueprints in the repository.
128
+
129
+ 1. **Edit `testBlueprint.repository.ts`**:
130
+ - Add imports for each template file (follow the existing naming convention)
131
+ - Add a new entry in `blueprintsByTransactionType` under the appropriate `TransactionTypeName` key
132
+ - Place it in alphabetical/numerical order relative to existing entries
133
+
134
+ ### Phase 5: Validation
135
+
136
+ Run the **verify** command for the newly created templates. Fix any errors and re-run until all pass.
137
+
138
+ ### Phase 6: Summary
139
+
140
+ Report to the user:
141
+ - How many templates were created
142
+ - A table of template names and the scenario each covers
143
+ - The file edited for registration
144
+ - Validation result (all pass)
145
+
146
+ ---
147
+
148
+ ## Common Validation Pitfalls
149
+
150
+ These are the most frequent errors when writing blueprints. Check `shared.zod.ts` to verify exact enum values.
151
+
152
+ | Mistake | Fix |
153
+ |---------|-----|
154
+ | `"motor"` for transportation method | Use `"commonCarrier"` |
155
+ | `"truck"` for transportation method | Use `"commonCarrier"` or `"privateCarrier"` |
156
+ | `"inches"` for dimension UOM | Use `"inch"` |
157
+ | `"assignedByBuyer"` for identification code type | Use `"receiversId"` or a value from the enum |
158
+ | X12 codes like `"M"`, `"F"`, `"01"` | Use simplified names like `"commonCarrier"`, `"fullDetail"`, `"damaged"` |
159
+ | `"lbs"` or `"kg"` for weight UOM | Use `"pound"` or `"kilogram"` |
160
+ | `"EA"` or `"CS"` for unit of measure | Use `"each"` or `"case"` (lowercase simplified names) |
@@ -0,0 +1,79 @@
1
+ # Codebase Paths Reference
2
+
3
+ All paths are relative to the orderful-workspace root.
4
+
5
+ ## Zod Schemas (source of truth for template shape)
6
+
7
+ ```
8
+ libs/schemas/src/lib/data/simplifiedSchemas/
9
+ ```
10
+
11
+ To find the Zod schema for a transaction type, search the index:
12
+ ```bash
13
+ grep -r "TransactionTypeName.T<number>" libs/schemas/src/lib/data/simplifiedSchemas/index.ts
14
+ ```
15
+
16
+ Then find the schema file and export name from the import.
17
+
18
+ ## Example JSON (realistic field values)
19
+
20
+ ```
21
+ libs/schemas/src/lib/data/simplifiedSchemas/examples/
22
+ ```
23
+
24
+ Pattern: `simplified-<schemaName>.json`
25
+
26
+ ## Shared Zod Schemas (enum values, shared types)
27
+
28
+ ```
29
+ libs/schemas/src/lib/data/simplifiedSchemas/shared.zod.ts
30
+ ```
31
+
32
+ This file defines all shared types used across transaction schemas:
33
+ - `PartySchema` - party identification with contacts
34
+ - `QuantitySchema` - value + unit of measure
35
+ - `PhysicalDetailsSchema` - pack, weight, dimensions
36
+ - `LineItemIdentifiersSchema` - product IDs (buyer, vendor, GTIN)
37
+ - `ProductAttributeDetailsSchema` - description, color, size, material
38
+ - `RoutingDetailsSchema` - carrier and transportation method
39
+ - `EquipmentDetailsSchema` - trailer, container, seal
40
+ - `FOBDetailsSchema` - freight payment terms
41
+ - `SpecialHandlingDetailsSchema` - handling codes
42
+ - `CarrierWeightDetailsSchema` - shipment weight totals
43
+ - `NoteDetailsSchema` - notes with text array
44
+ - `PalletExchangeSchema` - pallet exchange/return enum
45
+ - `ContactSchema` - contact person details
46
+
47
+ ## TransactionTypeName Enum
48
+
49
+ ```
50
+ libs/platform-types/src/lib/transactionTypeNames.ts
51
+ ```
52
+
53
+ Check this file for the exact enum key (e.g., `T943`, `T944`).
54
+
55
+ ## Blueprint Templates
56
+
57
+ ```
58
+ apps/platform-api/src/modules/testTransformation/data/testBlueprints/
59
+ ```
60
+
61
+ Organised by type: `810s/`, `846s/`, `855s/`, `856s/`, `943s/`, etc.
62
+
63
+ ## Blueprint Repository (registration)
64
+
65
+ ```
66
+ apps/platform-api/src/modules/testTransformation/repositories/testBlueprint.repository.ts
67
+ ```
68
+
69
+ This file:
70
+ 1. Imports all `.json.hbs` template files
71
+ 2. Registers them in `blueprintsByTransactionType` under the `TransactionTypeName` key
72
+ 3. Optionally registers upstream blueprints in `upstreamBlueprints`
73
+
74
+ ## Validation Script Execution
75
+
76
+ ```bash
77
+ cd <workspace-root> && npx ts-node -r tsconfig-paths/register \
78
+ --project apps/platform-api/tsconfig.json <path-to-script>
79
+ ```
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * test-blueprints validate
4
+ *
5
+ * Validate blueprint .json.hbs templates against their Zod schema.
6
+ * Generates a temporary validation script in the workspace and runs it
7
+ * via ts-node so that tsconfig paths resolve correctly.
8
+ *
9
+ * Usage:
10
+ * droid exec test-blueprints validate \
11
+ * --workspace /path/to/orderful-workspace \
12
+ * --schema-import "simplifiedSchemas/schemaFile" \
13
+ * --schema-name "Simplified943Schema" \
14
+ * --blueprints-dir "apps/platform-api/src/modules/testTransformation/data/testBlueprints/943s"
15
+ *
16
+ * Output (JSON):
17
+ * {
18
+ * "success": true,
19
+ * "results": [
20
+ * { "file": "943_standard.json.hbs", "status": "pass" },
21
+ * { "file": "943_templated.json.hbs", "status": "skip", "reason": "contains Handlebars expressions" },
22
+ * { "file": "943_bad.json.hbs", "status": "fail", "errors": [{ "path": "header.type", "message": "Invalid enum value" }] }
23
+ * ],
24
+ * "summary": { "pass": 8, "fail": 1, "skip": 2, "total": 11 }
25
+ * }
26
+ */
27
+
28
+ import { execSync } from 'child_process';
29
+ import { existsSync, writeFileSync, unlinkSync, readdirSync, readFileSync } from 'fs';
30
+ import { join } from 'path';
31
+ import { tmpdir } from 'os';
32
+
33
+ interface ValidationError {
34
+ path: string;
35
+ message: string;
36
+ }
37
+
38
+ interface FileResult {
39
+ file: string;
40
+ status: 'pass' | 'fail' | 'skip';
41
+ reason?: string;
42
+ errors?: ValidationError[];
43
+ }
44
+
45
+ interface ValidateResult {
46
+ success: boolean;
47
+ results?: FileResult[];
48
+ summary?: { pass: number; fail: number; skip: number; total: number };
49
+ error?: string;
50
+ }
51
+
52
+ interface ParsedArgs {
53
+ workspace?: string;
54
+ schemaImport?: string;
55
+ schemaName?: string;
56
+ blueprintsDir?: string;
57
+ }
58
+
59
+ function parseArgs(args: string[]): ParsedArgs {
60
+ const result: ParsedArgs = {};
61
+
62
+ for (let i = 0; i < args.length; i++) {
63
+ const arg = args[i];
64
+ if (arg === '--workspace' && args[i + 1]) {
65
+ result.workspace = args[++i];
66
+ } else if (arg === '--schema-import' && args[i + 1]) {
67
+ result.schemaImport = args[++i];
68
+ } else if (arg === '--schema-name' && args[i + 1]) {
69
+ result.schemaName = args[++i];
70
+ } else if (arg === '--blueprints-dir' && args[i + 1]) {
71
+ result.blueprintsDir = args[++i];
72
+ }
73
+ }
74
+
75
+ return result;
76
+ }
77
+
78
+ function expandPath(p: string): string {
79
+ if (p.startsWith('~/')) {
80
+ return p.replace('~', process.env.HOME || '');
81
+ }
82
+ if (p.startsWith('$HOME/')) {
83
+ return p.replace('$HOME', process.env.HOME || '');
84
+ }
85
+ return p;
86
+ }
87
+
88
+ function validate(parsed: ParsedArgs): ValidateResult {
89
+ if (!parsed.workspace) {
90
+ return { success: false, error: 'Missing --workspace' };
91
+ }
92
+ if (!parsed.schemaImport) {
93
+ return { success: false, error: 'Missing --schema-import' };
94
+ }
95
+ if (!parsed.schemaName) {
96
+ return { success: false, error: 'Missing --schema-name' };
97
+ }
98
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(parsed.schemaName)) {
99
+ return { success: false, error: 'Invalid --schema-name: must be a valid identifier' };
100
+ }
101
+ if (/['"\n\r]/.test(parsed.schemaImport)) {
102
+ return { success: false, error: 'Invalid --schema-import: must not contain quotes or newlines' };
103
+ }
104
+ if (!parsed.blueprintsDir) {
105
+ return { success: false, error: 'Missing --blueprints-dir' };
106
+ }
107
+
108
+ const workspace = expandPath(parsed.workspace);
109
+ if (!existsSync(workspace)) {
110
+ return { success: false, error: `Workspace not found: ${workspace}` };
111
+ }
112
+
113
+ const blueprintsDir = parsed.blueprintsDir.startsWith('/')
114
+ ? parsed.blueprintsDir
115
+ : join(workspace, parsed.blueprintsDir);
116
+
117
+ if (!existsSync(blueprintsDir)) {
118
+ return { success: false, error: `Blueprints directory not found: ${blueprintsDir}` };
119
+ }
120
+
121
+ // Find .json.hbs files
122
+ const files = readdirSync(blueprintsDir).filter(f => f.endsWith('.json.hbs'));
123
+ if (files.length === 0) {
124
+ return { success: false, error: `No .json.hbs files found in ${blueprintsDir}` };
125
+ }
126
+
127
+ // Pre-check for Handlebars templates (skip these from Zod validation)
128
+ const results: FileResult[] = [];
129
+ const filesToValidate: string[] = [];
130
+
131
+ for (const file of files) {
132
+ const content = readFileSync(join(blueprintsDir, file), 'utf-8');
133
+ if (content.includes('{{')) {
134
+ results.push({ file, status: 'skip', reason: 'contains Handlebars expressions' });
135
+ } else {
136
+ // Check JSON parsability before sending to ts-node
137
+ try {
138
+ JSON.parse(content);
139
+ filesToValidate.push(file);
140
+ } catch (e) {
141
+ const err = e as Error;
142
+ results.push({ file, status: 'fail', errors: [{ path: '(root)', message: `Invalid JSON: ${err.message}` }] });
143
+ }
144
+ }
145
+ }
146
+
147
+ // If all files are skipped or failed JSON parse, return early
148
+ if (filesToValidate.length === 0) {
149
+ const summary = {
150
+ pass: 0,
151
+ fail: results.filter(r => r.status === 'fail').length,
152
+ skip: results.filter(r => r.status === 'skip').length,
153
+ total: results.length,
154
+ };
155
+ return { success: summary.fail === 0, results, summary };
156
+ }
157
+
158
+ // Generate validation script
159
+ const validationScript = `
160
+ import { ${parsed.schemaName} } from '${parsed.schemaImport}';
161
+ import * as fs from 'fs';
162
+ import * as path from 'path';
163
+
164
+ const dir = ${JSON.stringify(blueprintsDir)};
165
+ const filesToValidate: string[] = ${JSON.stringify(filesToValidate)};
166
+ const results: Array<{ file: string; status: string; errors?: Array<{ path: string; message: string }> }> = [];
167
+
168
+ for (const file of filesToValidate) {
169
+ const content = fs.readFileSync(path.join(dir, file), 'utf-8');
170
+ const parsed = JSON.parse(content);
171
+ const result = ${parsed.schemaName}.safeParse(parsed);
172
+
173
+ if (result.success) {
174
+ results.push({ file, status: 'pass' });
175
+ } else {
176
+ results.push({
177
+ file,
178
+ status: 'fail',
179
+ errors: result.error.issues.map((issue: { path: (string | number)[]; message: string }) => ({
180
+ path: issue.path.join('.'),
181
+ message: issue.message,
182
+ })),
183
+ });
184
+ }
185
+ }
186
+
187
+ console.log(JSON.stringify(results));
188
+ `.trim();
189
+
190
+ const scriptPath = join(tmpdir(), `_validate-${Date.now()}.ts`);
191
+
192
+ try {
193
+ writeFileSync(scriptPath, validationScript, 'utf-8');
194
+
195
+ const output = execSync(
196
+ `npx ts-node -r tsconfig-paths/register --project apps/platform-api/tsconfig.json ${JSON.stringify(scriptPath)}`,
197
+ {
198
+ cwd: workspace,
199
+ encoding: 'utf-8',
200
+ stdio: ['pipe', 'pipe', 'pipe'],
201
+ timeout: 60_000,
202
+ }
203
+ );
204
+
205
+ let zodResults: Array<{ file: string; status: string; errors?: ValidationError[] }>;
206
+ try {
207
+ zodResults = JSON.parse(output.trim());
208
+ } catch {
209
+ return { success: false, error: `Failed to parse validation output: ${output}` };
210
+ }
211
+
212
+ for (const r of zodResults) {
213
+ results.push({
214
+ file: r.file,
215
+ status: r.status as 'pass' | 'fail',
216
+ errors: r.errors,
217
+ });
218
+ }
219
+ } catch (err: unknown) {
220
+ const error = err as { stderr?: string; message?: string };
221
+ return {
222
+ success: false,
223
+ error: `Validation script failed: ${error.stderr || error.message}`,
224
+ };
225
+ } finally {
226
+ try { unlinkSync(scriptPath); } catch { /* ignore cleanup errors */ }
227
+ }
228
+
229
+ // Sort results to match original file order
230
+ results.sort((a, b) => files.indexOf(a.file) - files.indexOf(b.file));
231
+
232
+ const summary = {
233
+ pass: results.filter(r => r.status === 'pass').length,
234
+ fail: results.filter(r => r.status === 'fail').length,
235
+ skip: results.filter(r => r.status === 'skip').length,
236
+ total: results.length,
237
+ };
238
+
239
+ return { success: summary.fail === 0, results, summary };
240
+ }
241
+
242
+ // Main
243
+ const args = process.argv.slice(2);
244
+ const parsed = parseArgs(args);
245
+ const result = validate(parsed);
246
+ console.log(JSON.stringify(result, null, 2));
247
+
248
+ if (!result.success) {
249
+ process.exit(1);
250
+ }