@fluentcommerce/ai-skills 0.1.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +622 -0
  3. package/bin/cli.mjs +1973 -0
  4. package/content/cli/agents/fluent-cli/agent.json +149 -0
  5. package/content/cli/agents/fluent-cli.md +132 -0
  6. package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
  7. package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
  8. package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
  9. package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
  10. package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
  11. package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
  12. package/content/cli/skills/fluent-connect/SKILL.md +886 -0
  13. package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
  14. package/content/cli/skills/fluent-profile/SKILL.md +180 -0
  15. package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
  16. package/content/dev/agents/fluent-dev/agent.json +88 -0
  17. package/content/dev/agents/fluent-dev.md +525 -0
  18. package/content/dev/reference-modules/catalog.json +4754 -0
  19. package/content/dev/skills/fluent-build/SKILL.md +192 -0
  20. package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
  21. package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
  22. package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
  23. package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
  24. package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
  25. package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
  26. package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
  27. package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
  28. package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
  29. package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
  30. package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
  31. package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
  32. package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
  33. package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
  34. package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
  35. package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
  36. package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
  37. package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
  38. package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
  39. package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
  40. package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
  41. package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
  42. package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
  43. package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
  44. package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
  45. package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
  46. package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
  47. package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
  48. package/content/mcp-extn/agents/fluent-mcp.md +69 -0
  49. package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
  50. package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
  51. package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
  52. package/content/rfl/agents/fluent-rfl.md +56 -0
  53. package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
  54. package/docs/CAPABILITY_MAP.md +77 -0
  55. package/docs/CLI_COVERAGE.md +47 -0
  56. package/docs/DEV_WORKFLOW.md +802 -0
  57. package/docs/FLOW_RUN.md +142 -0
  58. package/docs/USE_CASES.md +404 -0
  59. package/metadata.json +156 -0
  60. package/package.json +51 -0
@@ -0,0 +1,775 @@
1
+ ---
2
+ name: fluent-module-validate
3
+ description: Discover, classify, and validate Fluent Commerce modules. Inventories all deployed modules (reference vs custom), validates extension module structure, and maps registered rules to their source. Produces cached JSON artifacts with content hashing. Triggers on "validate module", "check module structure", "module health", "what modules are deployed", "list modules", "is my module correct".
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
+ argument-hint: [module-root-path | inventory] [--fix] [--force]
7
+ ---
8
+
9
+ # Module Inventory and Structure Validator
10
+
11
+ Two capabilities in one skill:
12
+
13
+ 1. **Module Inventory** — discover all deployed modules on an account, classify them (reference / custom extension / custom workflow / custom data), and map registered rules to their source modules.
14
+ 2. **Structure Validation** — validate a specific custom extension module for structural correctness, version consistency, rule wiring, test coverage, and deployment readiness.
15
+
16
+ Both produce cached JSON artifacts. Inventory uses TTL-based caching (re-query if older than 1 hour). Structure validation uses content-hash caching (skip if source files unchanged).
17
+
18
+ ## Ownership Boundary
19
+
20
+ This skill owns:
21
+ - Module inventory and classification for an account
22
+ - Structural validation and health reporting for individual modules
23
+ - The `module-inventory.json` and per-module `report.json` artifacts
24
+
25
+ Other skills own:
26
+ - Build execution → `/fluent-build`
27
+ - Rule scaffolding and OOTB-vs-custom decision → `/fluent-rule-scaffold`
28
+ - Module deployment → `/fluent-module-deploy`
29
+ - Source code behavior analysis → `/fluent-custom-code`
30
+
31
+ ### Cross-Skill Integration
32
+
33
+ **`/fluent-module-validate inventory` output is consumed by:**
34
+ - `/fluent-custom-code` — Uses `module-inventory.json` to identify custom modules, distinguish `hasSource` cases, and skip reference modules during source analysis
35
+ - `/fluent-workflow-analyzer` — Uses deployed module list to annotate workflow rules with module origins
36
+ - `/fluent-build` — Uses inventory to determine what needs packaging
37
+
38
+ **Recommended sequencing:**
39
+ 1. Run `/fluent-module-validate inventory` first (lists what's deployed, classifies modules)
40
+ 2. Then `/fluent-custom-code` (analyzes local source against deployed inventory)
41
+ 3. Then `/fluent-workflow-analyzer` (maps workflows to deployed rules)
42
+
43
+ ## When to Use
44
+
45
+ - "What modules are deployed on this account?"
46
+ - "Which modules are custom vs reference?"
47
+ - "What rules are available?" (→ inventory + `plugin.list`)
48
+ - Before a build to catch structural issues early
49
+ - After scaffolding a new rule to verify wiring
50
+ - Before a version bump to ensure nothing is missing
51
+ - When inheriting an unfamiliar account to map the landscape
52
+ - As a pre-deployment gate in CI/CD pipelines
53
+
54
+ ## Required Inputs
55
+
56
+ - **For inventory**: `PROFILE` name (to run `fluent module list -p <PROFILE>`)
57
+ - **For structure validation**: module root path (directory containing `resources/module.json` and `plugins/`)
58
+ - If not provided, search recursively under `accounts/<PROFILE>/SOURCE/` for `resources/module.json`
59
+
60
+ ## Cross-Platform Command Notes
61
+
62
+ - Fluent CLI syntax is the same across macOS, Linux, and Windows.
63
+ - Where shell snippets differ, this skill provides both Bash and PowerShell variants.
64
+ - Use the variant that matches the active shell to avoid quoting/redirection issues.
65
+
66
+ ---
67
+
68
+ # Part 1: Module Inventory
69
+
70
+ ## Classification Algorithm
71
+
72
+ Every deployed module has a symbolic name. Classification uses this decision tree:
73
+
74
+ ```
75
+ Has "fluent-commerce/" prefix?
76
+ ├── YES → strip prefix to get <baseName>
77
+ │ ├── <baseName> in REFERENCE_NAMES set?
78
+ │ │ ├── YES → REFERENCE
79
+ │ │ └── NO
80
+ │ │ ├── starts with "fc-module-" → CUSTOM_EXTENSION
81
+ │ │ ├── starts with "fc-workflow-" → CUSTOM_WORKFLOW
82
+ │ │ └── else → CUSTOM_DATA
83
+ │ │
84
+ └── NO (no "fluent-commerce/" prefix)
85
+ ├── starts with "fc-module-" → CUSTOM_EXTENSION
86
+ └── else → CUSTOM_DATA
87
+ ```
88
+
89
+ ### Reference Module Names
90
+
91
+ These are the known Fluent Commerce standard modules (OOTB). This list should be updated when Fluent releases new reference modules:
92
+
93
+ ```
94
+ core, order, fulfilment, inventory, location, globalinventory,
95
+ servicepoint, wave, returns, b2c-sample-data, b2c-sample-data-inventory
96
+ ```
97
+
98
+ ### Module Types Explained
99
+
100
+ | Type | Description | Has Rules? | Has Source? | Examples |
101
+ |------|-------------|-----------|-------------|---------|
102
+ | **REFERENCE** | Standard Fluent Commerce modules from repository. Deployed via `fluent module install <name>`. | Yes (OOTB rules) | No (closed-source) | `fluent-commerce/core`, `fluent-commerce/order` |
103
+ | **CUSTOM_EXTENSION** | Account-specific modules with custom Rubix rules. Built from Java source. | Yes (custom rules) | Yes (under `accounts/<PROFILE>/SOURCE/`) | `fluent-commerce/fc-module-custom-extension` |
104
+ | **CUSTOM_WORKFLOW** | Workflow definitions packaged as deployable modules. | No | Workflow JSON in module ZIP | `fluent-commerce/fc-workflow-order-hm-updated` |
105
+ | **CUSTOM_DATA** | Configuration, settings, sample data, access control. No executable rules. | No | JSON/config files in module ZIP | `HM`, `hm-inventory-data`, `hm/global-access` |
106
+
107
+ **All types must be deployed** — even reference modules are deployed per-retailer via `fluent module install`.
108
+
109
+ ### Rule Key Format (from `plugin.list`)
110
+
111
+ Registered rules follow the pattern: `ACCOUNT.context.RuleName`
112
+
113
+ | Prefix | Meaning |
114
+ |--------|---------|
115
+ | `FLUENTRETAIL.<context>.*` | Ships with the Fluent platform. Always available regardless of which modules are deployed. |
116
+ | `<ACCOUNT>.<context>.*` | Registered by a module deployed to this account. Context maps to the module's rule namespace. |
117
+
118
+ Known context-to-module mappings:
119
+
120
+ | Context | Module |
121
+ |---------|--------|
122
+ | `base` | `fluent-commerce/core` |
123
+ | `order`, `commonv2` | `fluent-commerce/order` |
124
+ | `fulfilment` | `fluent-commerce/fulfilment` |
125
+ | `globalinventory`, `commongi` | `fluent-commerce/inventory` |
126
+ | `core` | `fluent-commerce/core` (account-scoped copy) |
127
+ | Custom context names | Custom extension modules |
128
+
129
+ ## Inventory Artifact
130
+
131
+ ### Location
132
+
133
+ ```
134
+ accounts/<PROFILE>/analysis/module-validate/
135
+ ├── module-inventory.json ← full inventory with classification + rule mapping
136
+ ├── <module-name>.report.json ← per-module structure validation
137
+ └── <module-name>.hash ← per-module content hash
138
+ ```
139
+
140
+ ### Cache Strategy
141
+
142
+ Inventory data comes from a live API call (`fluent module list`). Use TTL-based caching:
143
+
144
+ - If `module-inventory.json` exists and is **less than 1 hour old** → read cached, skip re-query
145
+ - If older than 1 hour or missing → re-query live, overwrite
146
+ - `--force` → always re-query
147
+
148
+ Check timestamp:
149
+ ```bash
150
+ # Get file age in seconds (cross-platform via Node.js)
151
+ node -e "
152
+ const fs = require('fs');
153
+ const f = process.argv[1];
154
+ if (!fs.existsSync(f)) { console.log('missing'); process.exit(0); }
155
+ const age = (Date.now() - fs.statSync(f).mtimeMs) / 1000;
156
+ console.log(age > 3600 ? 'stale' : 'fresh');
157
+ " "accounts/<PROFILE>/analysis/module-validate/module-inventory.json"
158
+ ```
159
+
160
+ ### Inventory Schema (`module-inventory.json`)
161
+
162
+ ```json
163
+ {
164
+ "schema": "module-inventory-v1",
165
+ "timestamp": "2026-02-22T10:30:00Z",
166
+ "profile": "<PROFILE>",
167
+ "account": "<account-name>",
168
+ "environment": "sandbox",
169
+ "totalModules": 15,
170
+ "modules": {
171
+ "reference": [
172
+ { "name": "fluent-commerce/core", "symbolicName": "fluent-commerce/core", "version": "2.2.1", "deployedAt": "2026-02-19T05:13:25Z", "deployedBy": "<deployer>" },
173
+ { "name": "fluent-commerce/order", "symbolicName": "fluent-commerce/order", "version": "2.1.0", "deployedAt": "2026-02-04T06:03:30Z", "deployedBy": "<deployer>" }
174
+ ],
175
+ "customExtension": [
176
+ {
177
+ "name": "fluent-commerce/fc-module-custom-extension",
178
+ "symbolicName": "fluent-commerce/fc-module-custom-extension",
179
+ "version": "0.0.29",
180
+ "versionSource": "module.json",
181
+ "sourceVersion": "0.0.29",
182
+ "sourceVersionSource": "module.json",
183
+ "versionDrift": false,
184
+ "artifactCoordinates": { "groupId": "com.fluentcommerce", "artifactId": "fc-module-custom-extension", "version": "0.0.29" },
185
+ "osgiSymbolicName": "_.hmextensions",
186
+ "deployedAt": "2026-02-20T14:58:20Z",
187
+ "deployedBy": "<deployer>",
188
+ "sourceDir": "accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-custom-extension",
189
+ "hasSource": true
190
+ },
191
+ {
192
+ "name": "fc-module-upsert-location",
193
+ "symbolicName": "com.example/fc-module-upsert-location",
194
+ "version": "1.2.2",
195
+ "versionSource": "pom.project.version",
196
+ "hasSource": false
197
+ }
198
+ ],
199
+ "customWorkflow": [
200
+ { "name": "fluent-commerce/fc-workflow-order-hm-updated", "version": "1.0.0" }
201
+ ],
202
+ "customData": [
203
+ { "name": "HM", "version": "1.0.13" }
204
+ ]
205
+ },
206
+ "referenceArtifacts": [
207
+ {
208
+ "name": "fluent-commerce/core",
209
+ "version": "2.2.1",
210
+ "sourcePath": "accounts/<PROFILE>/SOURCE/referece modules/fc-module-core-2.2.1",
211
+ "jarFile": "assets/rules/fc-core-plugin-2.2.1.jar",
212
+ "workflowFragments": [],
213
+ "settingsFiles": []
214
+ }
215
+ ],
216
+ "undeployedModules": [
217
+ {
218
+ "name": "fluent-commerce/fc-module-hm-return",
219
+ "version": "0.0.6",
220
+ "moduleType": "configuration_only",
221
+ "sourcePath": "accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-hm-return",
222
+ "deploymentStatus": "not_deployed"
223
+ }
224
+ ],
225
+ "ruleStats": {
226
+ "totalRegistered": 587,
227
+ "byPrefix": {
228
+ "FLUENTRETAIL": 232,
229
+ "<ACCOUNT>": 355
230
+ },
231
+ "byContext": {
232
+ "FLUENTRETAIL.base": 174,
233
+ "<ACCOUNT>.order": 97,
234
+ "<ACCOUNT>.commonv2": 97,
235
+ "<ACCOUNT>.globalinventory": 71
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ ### Generating the Inventory
242
+
243
+ ```
244
+ 1. Run: fluent module list -p <PROFILE>
245
+ 2. Parse output into module objects (name, symbolicName, version, deployedAt, deployedBy)
246
+ 3. Classify each module using the decision tree above
247
+ 4. For CUSTOM_EXTENSION modules, check source availability:
248
+ - Search accounts/<PROFILE>/SOURCE/ recursively for matching module.json name
249
+ - If source repo found (has src/main/java/) → hasSource = true, sourceType = "full_source"
250
+ - If reference module package found (module.json + JAR under assets/rules/, no src/) → hasSource = false, sourceType = "reference_artifact" (read metadata from module.json, no decompilation needed for rule names)
251
+ - If standalone JAR/ZIP found (no module.json alongside) → hasSource = false, sourceType = "standalone_jar" (needs decompilation)
252
+ - If nothing found → hasSource = false, sourceType = "missing"
253
+ **Escalation:** When sourceType is "missing", include actionable guidance in the report:
254
+ - Option A: Clone the source repo → `git clone <repo-url> accounts/<PROFILE>/SOURCE/<repo-name>/`
255
+ - Option B: Provide the compiled JAR → copy to `accounts/<PROFILE>/SOURCE/<jar-name>.jar`, then run `/fluent-custom-code <PROFILE>` to decompile and analyze
256
+ - Option C: Run `/fluent-connect` to trigger full workspace preparation including JAR decompilation
257
+ - Note: Without source or JAR, analysis is limited to deployed metadata from `fluent module list` and `plugin.list` (rule descriptions and parameters only)
258
+ - Handle double-nested git clone dirs (repo-name/repo-name/) by resolving to inner root
259
+ - Set hasSource, sourceType, and sourceDir accordingly
260
+ 5. For modules with hasSource = true, extract identity from source:
261
+ a. Read resources/module.json → symbolicName (name), sourceVersion (version)
262
+ b. Read plugins/rules/*/pom.xml → artifactCoordinates (groupId, artifactId, version)
263
+ c. Read pom.xml property rubix.osgi.symbolic.name → osgiSymbolicName
264
+ d. Compare sourceVersion with deployed version → set versionDrift flag
265
+ 6. Scan accounts/<PROFILE>/SOURCE/ for undeployed modules:
266
+ - Find module.json files not matching any deployed module name
267
+ - Classify as config-only (no rules[], no src/main/java/) or extension
268
+ - Add to inventory with deploymentStatus: "not_deployed"
269
+ 7. Scan for reference module artifacts in SOURCE/:
270
+ - Detect module.json names matching REFERENCE_NAMES set
271
+ - Record version, included workflow fragments, settings, and JAR sizes
272
+ - Add to referenceArtifacts section (separate from deployed reference modules)
273
+ 8. Optionally query plugin.list (MCP tool) for rule stats
274
+ - Verify MCP is connected to the correct account before using results
275
+ - If MCP points at a different account, skip this step
276
+ 9. Extract account name and environment from profile base URL:
277
+ - Pattern: https://<account>[.<env>].api.fluentretail.com
278
+ - .test. = test, .sandbox. = sandbox, no qualifier = production
279
+ 10. Write module-inventory.json
280
+ ```
281
+
282
+ ### Identity Extraction from Source
283
+
284
+ Precedence rules (first available wins):
285
+
286
+ **`symbolicName`:**
287
+ 1. `resources/module.json` → `name` field (preferred — this is the canonical module identifier)
288
+ 2. JAR `META-INF/MANIFEST.MF` → `Bundle-SymbolicName` header
289
+ 3. Fallback: `<groupId>/<artifactId>` from pom.xml
290
+
291
+ **`version`:**
292
+ 1. `resources/module.json` → `version` field (preferred — must match what gets deployed)
293
+ 2. `pom.xml` → `<project><version>` (or `<project><parent><version>` if module inherits)
294
+ 3. JAR manifest → `Implementation-Version` or `Bundle-Version`
295
+
296
+ **Maven coordinates** (from pom.xml):
297
+ ```bash
298
+ # Find the rules child POM (has rubix.osgi.symbolic.name property)
299
+ # Typically at: plugins/rules/<module-alias>/pom.xml
300
+
301
+ # Extract groupId, artifactId, version, osgiSymbolicName
302
+ node -e "
303
+ const fs = require('fs');
304
+ const xml = fs.readFileSync(process.argv[1], 'utf8');
305
+ const gid = xml.match(/<groupId>([^<]+)<\/groupId>/);
306
+ const aid = xml.match(/<artifactId>([^<]+)<\/artifactId>/);
307
+ const ver = xml.match(/<version>([^<]+)<\/version>/);
308
+ const sym = xml.match(/<rubix\.osgi\.symbolic\.name>([^<]+)<\/rubix/);
309
+ console.log(JSON.stringify({
310
+ groupId: gid?.[1], artifactId: aid?.[1],
311
+ version: ver?.[1], osgiSymbolicName: sym?.[1]
312
+ }));
313
+ " path/to/pom.xml
314
+ ```
315
+
316
+ **POM parent version inheritance:** If the rules child POM has no `<version>` element but has a `<parent>` block, read the parent POM's `<version>`. In multi-module Maven projects, the parent POM at `plugins/pom.xml` defines the version and children inherit it.
317
+
318
+ ### Version Drift Detection
319
+
320
+ After extracting both deployed and source versions, flag mismatches:
321
+
322
+ ```
323
+ versionDrift = (deployedVersion !== sourceVersion)
324
+ ```
325
+
326
+ | Scenario | Meaning | Action |
327
+ |----------|---------|--------|
328
+ | `0.0.29` == `0.0.29` | In sync | No action needed |
329
+ | `0.0.28` != `0.0.29` | Source ahead of deployed | Deploy pending — run `/fluent-build` + `/fluent-module-deploy` |
330
+ | `0.0.30` != `0.0.29` | Deployed ahead of source | Source may be stale — `git pull` or check branch |
331
+
332
+ Version drift is a WARN, not a FAIL — it's informational for the developer.
333
+
334
+ ---
335
+
336
+ # Part 2: Structure Validation
337
+
338
+ Validate a specific custom extension module for correctness.
339
+
340
+ ---
341
+
342
+ ## Artifact: Persistent Report with Content Hash
343
+
344
+ ### Location
345
+
346
+ ```
347
+ accounts/<PROFILE>/analysis/module-validate/
348
+ ├── <module-name>.report.json ← full validation report
349
+ └── <module-name>.hash ← content hash (single hex string)
350
+ ```
351
+
352
+ `<module-name>` is derived from `module.json` `name` field with `/` replaced by `--` (e.g., `fluent-commerce--fc-module-custom-extension`).
353
+
354
+ ### Change Detection Protocol
355
+
356
+ Before running any checks, compute the module's content hash and compare against the stored hash.
357
+
358
+ **Step 1: Compute current hash**
359
+
360
+ Use Node.js (always available) to hash all source files:
361
+
362
+ ```bash
363
+ node -e "
364
+ const crypto = require('crypto');
365
+ const fs = require('fs');
366
+ const path = require('path');
367
+ const root = process.argv[1];
368
+ const h = crypto.createHash('sha256');
369
+ const exts = new Set(['.java','.json','.xml','.sh','.ps1']);
370
+ const skip = new Set(['dist','target','.git','node_modules','.idea']);
371
+ function walk(d) {
372
+ for (const e of fs.readdirSync(d,{withFileTypes:true})) {
373
+ if (skip.has(e.name)) continue;
374
+ const f = path.join(d, e.name);
375
+ if (e.isDirectory()) walk(f);
376
+ else if (exts.has(path.extname(e.name).toLowerCase())) {
377
+ h.update(f.replace(/\\\\/g,'/') + ':');
378
+ h.update(fs.readFileSync(f));
379
+ }
380
+ }
381
+ }
382
+ walk(root);
383
+ console.log(h.digest('hex'));
384
+ " "<MODULE_ROOT>"
385
+ ```
386
+
387
+ This hashes every `.java`, `.json`, `.xml`, `.sh`, `.ps1` file content (excluding `dist/`, `target/`, `.git/`, `node_modules/`). Any file change — content, rename, add, or delete — produces a different hash.
388
+
389
+ **Step 2: Compare with stored hash**
390
+
391
+ ```bash
392
+ # Read stored hash (empty string if file doesn't exist) — cross-platform via Node.js
393
+ node -e "try{console.log(require('fs').readFileSync(process.argv[1],'utf8').trim())}catch{console.log('')}" "accounts/<PROFILE>/analysis/module-validate/<module-name>.hash"
394
+ ```
395
+
396
+ **Step 3: Decide**
397
+
398
+ | Condition | Action |
399
+ |-----------|--------|
400
+ | Stored hash matches current hash | Print "Module unchanged since last validation — report is current." and read the existing `.report.json`. STOP. |
401
+ | Stored hash is missing or different | Run full validation. Write new `.report.json` and `.hash`. |
402
+ | `--force` flag provided | Always run full validation regardless of hash. |
403
+
404
+ ### Report JSON Schema
405
+
406
+ ```json
407
+ {
408
+ "schema": "module-validate-v1",
409
+ "timestamp": "2026-02-22T10:30:00Z",
410
+ "contentHash": "a1b2c3d4...",
411
+ "module": {
412
+ "name": "fluent-commerce/fc-module-custom-extension",
413
+ "version": "0.0.29",
414
+ "root": "accounts/<PROFILE>/SOURCE/.../",
415
+ "rulesCount": 8
416
+ },
417
+ "summary": {
418
+ "pass": 8,
419
+ "warn": 2,
420
+ "fail": 0,
421
+ "info": 1,
422
+ "status": "READY_FOR_BUILD"
423
+ },
424
+ "checks": [
425
+ {
426
+ "id": "manifest",
427
+ "name": "Module Manifest",
428
+ "result": "PASS",
429
+ "message": "Valid JSON with all required fields",
430
+ "details": {}
431
+ },
432
+ {
433
+ "id": "maven",
434
+ "name": "Maven Structure",
435
+ "result": "PASS",
436
+ "message": "Parent POM + 3 child modules",
437
+ "details": { "modules": ["types", "util", "rules"] }
438
+ },
439
+ {
440
+ "id": "versions",
441
+ "name": "Version Consistency",
442
+ "result": "PASS",
443
+ "message": "0.0.29 across all 4 POMs + module.json",
444
+ "details": { "expected": "0.0.29", "found": { "module.json": "0.0.29", "plugins/pom.xml": "0.0.29" } }
445
+ },
446
+ {
447
+ "id": "rule_wiring",
448
+ "name": "Rule Wiring",
449
+ "result": "PASS",
450
+ "message": "8/8 rules have matching classes with @RuleInfo",
451
+ "details": {
452
+ "wired": ["UpdateStatusHistory", "UpsertAttribute"],
453
+ "orphaned": []
454
+ }
455
+ },
456
+ {
457
+ "id": "test_coverage",
458
+ "name": "Test Coverage",
459
+ "result": "WARN",
460
+ "message": "7/8 rules have tests",
461
+ "details": { "missing": ["CreateMissingVariantProducts"] }
462
+ },
463
+ {
464
+ "id": "schema",
465
+ "name": "GraphQL Schema",
466
+ "result": "PASS",
467
+ "message": "global/schema.json present (1.2MB)"
468
+ },
469
+ {
470
+ "id": "settings",
471
+ "name": "Settings",
472
+ "result": "PASS",
473
+ "message": "25 valid JSON files"
474
+ },
475
+ {
476
+ "id": "workflows",
477
+ "name": "Workflow Fragments",
478
+ "result": "PASS",
479
+ "message": "3 valid JSON files"
480
+ },
481
+ {
482
+ "id": "module_config",
483
+ "name": "Module Config",
484
+ "result": "PASS",
485
+ "message": "3 config tokens defined"
486
+ },
487
+ {
488
+ "id": "scripts",
489
+ "name": "Build Scripts",
490
+ "result": "WARN",
491
+ "message": "build-module.sh present, fetch-schema.sh present"
492
+ },
493
+ {
494
+ "id": "dist",
495
+ "name": "Distribution",
496
+ "result": "INFO",
497
+ "message": "dist/ not present (no build artifacts)"
498
+ }
499
+ ]
500
+ }
501
+ ```
502
+
503
+ ### Writing the Artifact
504
+
505
+ After validation completes:
506
+
507
+ ```bash
508
+ # Ensure directory exists (cross-platform via Node.js)
509
+ node -e "require('fs').mkdirSync(process.argv[1],{recursive:true})" "accounts/<PROFILE>/analysis/module-validate"
510
+
511
+ # Write report (use Write tool, not bash echo)
512
+ # Write hash (cross-platform via Node.js)
513
+ node -e "require('fs').writeFileSync(process.argv[1],process.argv[2]+'\n')" "accounts/<PROFILE>/analysis/module-validate/<module-name>.hash" "<computed-hash>"
514
+ ```
515
+
516
+ Use the **Write** tool for the `.report.json` file to preserve JSON formatting.
517
+
518
+ ---
519
+
520
+ ## Validation Checklist
521
+
522
+ Run checks in this order. Report each as PASS, WARN, or FAIL.
523
+
524
+ ### 1. Module Manifest (`resources/module.json`)
525
+
526
+ | Check | Severity | Detail |
527
+ |-------|----------|--------|
528
+ | File exists | FAIL | `resources/module.json` must exist |
529
+ | Valid JSON | FAIL | Must parse without errors |
530
+ | Has `name` | FAIL | Module name (e.g., `"fluent-commerce/fc-module-custom-extension"`) |
531
+ | Has `version` | FAIL | Semantic version string |
532
+ | Has `rules` array | FAIL | Non-empty array of rule name strings |
533
+ | Has `_schema` | WARN | Schema version (e.g., `"1.0.0"`) |
534
+ | Has `title` | WARN | Human-readable title |
535
+ | Has `description` | WARN | Module description |
536
+ | Has `authors` | WARN | At least one author entry |
537
+ | Has `dependencies` | WARN | Module dependencies (e.g., `fluent-commerce/core`) |
538
+ | Has `compatibility` | WARN | `minRubixVersion`, `rubixPluginSdk`, `fluentApiClient` |
539
+ | No duplicate rules | FAIL | Every entry in `rules[]` must be unique |
540
+
541
+ ### 2. Maven Build Structure (`plugins/`)
542
+
543
+ | Check | Severity | Detail |
544
+ |-------|----------|--------|
545
+ | `plugins/pom.xml` exists | FAIL | Parent POM must exist |
546
+ | Valid XML | FAIL | Must parse without errors |
547
+ | Has `<modules>` | FAIL | Must declare child modules |
548
+ | Expected children exist | FAIL | Each `<module>` entry must have a matching subdirectory |
549
+ | `<packaging>pom</packaging>` | WARN | Parent should be POM packaging |
550
+
551
+ Standard child module structure:
552
+ ```
553
+ plugins/
554
+ ├── pom.xml ← parent POM
555
+ ├── types/ ← generated types (Apollo codegen)
556
+ │ └── <name>/pom.xml
557
+ ├── util/ ← shared utilities
558
+ │ └── <name>/pom.xml
559
+ └── rules/ ← rule classes
560
+ └── <name>/pom.xml
561
+ ```
562
+
563
+ ### 3. Version Consistency
564
+
565
+ All versions must match the version in `resources/module.json`.
566
+
567
+ | File | Field to check |
568
+ |------|----------------|
569
+ | `resources/module.json` | `"version"` (source of truth) |
570
+ | `plugins/pom.xml` | `<version>` on the project element |
571
+ | `plugins/rules/<name>/pom.xml` | `<version>` on parent and/or project |
572
+ | `plugins/types/<name>/pom.xml` | `<version>` on parent and/or project |
573
+ | `plugins/util/<name>/pom.xml` | `<version>` on parent and/or project |
574
+
575
+ Report any mismatch as FAIL with expected vs actual values.
576
+
577
+ ### 4. Rule Class Wiring
578
+
579
+ For every rule name in `module.json` `rules[]`, verify:
580
+
581
+ | Check | Severity | Detail |
582
+ |-------|----------|--------|
583
+ | Java class exists | FAIL | File at `plugins/rules/**/src/main/java/**/<RuleName>.java` |
584
+ | `@RuleInfo` present | FAIL | Class has `@RuleInfo` annotation |
585
+ | `@RuleInfo.name` matches | FAIL | `name` attribute matches `module.json` entry exactly (case-sensitive) |
586
+ | Extends BaseRule or Rule | WARN | Should extend `BaseRule` or `Rule` from Rubix SDK |
587
+
588
+ Also check for **orphaned rules** — Java classes with `@RuleInfo` that are NOT listed in `module.json.rules[]`. Report as WARN.
589
+
590
+ ### 5. Test Coverage
591
+
592
+ For every rule class found:
593
+
594
+ | Check | Severity | Detail |
595
+ |-------|----------|--------|
596
+ | Test class exists | WARN | Matching `<RuleName>Test.java` under `src/test/java/` |
597
+ | Test has at least one `@Test` | WARN | At least one test method |
598
+
599
+ ### 6. GraphQL Schema
600
+
601
+ | Check | Severity | Detail |
602
+ |-------|----------|--------|
603
+ | `global/schema.json` exists | WARN | Required for Apollo codegen |
604
+ | File is non-empty | WARN | Must be a valid JSON schema dump |
605
+ | POM references schema | WARN | `<introspectionFile>` in parent POM points to schema |
606
+
607
+ ### 7. Resources
608
+
609
+ #### Settings (`resources/settings/`)
610
+
611
+ | Check | Severity | Detail |
612
+ |-------|----------|--------|
613
+ | Directory exists | WARN | Optional but common |
614
+ | Each `.json` file is valid JSON | FAIL | Every file must parse |
615
+ | Each setting has `name` field | WARN | Standard setting structure |
616
+
617
+ #### Workflows (`resources/workflows/`)
618
+
619
+ | Check | Severity | Detail |
620
+ |-------|----------|--------|
621
+ | Directory exists | WARN | Optional — module may not ship workflows |
622
+ | Each `.json` file is valid JSON | FAIL | Every file must parse |
623
+ | Each workflow has `name` and `type` | WARN | Standard workflow fragment structure |
624
+
625
+ #### Module Config (`resources/module.config.json`)
626
+
627
+ | Check | Severity | Detail |
628
+ |-------|----------|--------|
629
+ | File exists | WARN | Optional — only needed if module uses config tokens |
630
+ | Valid JSON | FAIL | Must parse if present |
631
+ | Tokens have placeholder values | WARN | Each key should have a `"default:<key>"` pattern or empty string |
632
+
633
+ ### 8. Build Scripts (`scripts/`)
634
+
635
+ | Check | Severity | Detail |
636
+ |-------|----------|--------|
637
+ | Directory exists | WARN | Optional but expected |
638
+ | Build script exists | WARN | At least one of: `build-module.sh`, `build-module.ps1` |
639
+ | Fetch schema script exists | WARN | At least one of: `fetch-schema.sh`, `fetch-schema.ps1` |
640
+
641
+ ### 9. Distribution (`dist/`)
642
+
643
+ | Check | Severity | Detail |
644
+ |-------|----------|--------|
645
+ | Directory exists | INFO | Only present after a build |
646
+ | ZIP artifact exists | INFO | `dist/*-<version>.zip` |
647
+ | ZIP version matches module.json | WARN | If ZIP exists, its version suffix should match |
648
+
649
+ ---
650
+
651
+ ## Execution Protocol
652
+
653
+ ```
654
+ 1. Locate module root
655
+ └─ find resources/module.json (user path or recursive search)
656
+
657
+ 2. Read module.json
658
+ └─ extract name, version, rules[]
659
+
660
+ 3. Compute content hash (Node.js script above)
661
+
662
+ 4. Check for existing report
663
+ ├─ Hash matches and no --force? → Print "unchanged", read report, STOP
664
+ └─ Hash differs or --force? → Continue to step 5
665
+
666
+ 5. Run all 9 validation checks (sections 1–9 above)
667
+
668
+ 6. Build report JSON (schema above)
669
+
670
+ 7. Write artifacts
671
+ ├─ accounts/<PROFILE>/analysis/module-validate/<name>.report.json
672
+ └─ accounts/<PROFILE>/analysis/module-validate/<name>.hash
673
+
674
+ 8. Print human-readable summary + status
675
+ ```
676
+
677
+ ## Console Output Format
678
+
679
+ ```
680
+ MODULE VALIDATION REPORT
681
+ ========================
682
+ Module: fluent-commerce/fc-module-custom-extension
683
+ Version: 0.0.29
684
+ Root: accounts/<PROFILE>/SOURCE/.../
685
+ Hash: a1b2c3d4...
686
+
687
+ RESULTS
688
+ -------
689
+ [PASS] Module manifest — valid JSON with all required fields
690
+ [PASS] Maven structure — parent POM + 3 child modules
691
+ [PASS] Version consistency — 0.0.29 across all 4 POMs + module.json
692
+ [PASS] Rule wiring — 8/8 rules have matching classes with @RuleInfo
693
+ [WARN] Test coverage — 7/8 rules have tests (missing: CreateMissingVariantProducts)
694
+ [PASS] GraphQL schema — global/schema.json present
695
+ [PASS] Settings — 25 valid JSON files
696
+ [PASS] Workflow fragments — 3 valid JSON files
697
+ [PASS] Module config — 3 config tokens defined
698
+ [WARN] Build scripts — build-module.sh present, fetch-schema.sh present
699
+ [INFO] Distribution — dist/ not present (no build artifacts)
700
+
701
+ SUMMARY: 8 PASS, 2 WARN, 0 FAIL
702
+ Status: READY FOR BUILD
703
+
704
+ Report saved: accounts/<PROFILE>/analysis/module-validate/fluent-commerce--fc-module-custom-extension.report.json
705
+ ```
706
+
707
+ Status thresholds:
708
+ - **READY FOR BUILD** — 0 FAIL
709
+ - **NEEDS FIXES** — 1+ FAIL
710
+ - Always list WARN items as improvement suggestions
711
+
712
+ ## Consuming the Report from Other Skills
713
+
714
+ Other skills can check validation state without re-running:
715
+
716
+ ```bash
717
+ # Quick status check — cross-platform via Node.js
718
+ node -e "
719
+ const fs = require('fs');
720
+ const hashFile = process.argv[1];
721
+ const reportFile = process.argv[2];
722
+ let stored = '';
723
+ try { stored = fs.readFileSync(hashFile,'utf8').trim(); } catch {}
724
+ const current = process.argv[3]; // pass computed hash as argument
725
+ if (stored === current) {
726
+ console.log('Validation report is current');
727
+ console.log(fs.readFileSync(reportFile,'utf8'));
728
+ } else {
729
+ console.log('Report is stale - re-run validation');
730
+ }
731
+ " "accounts/<PROFILE>/analysis/module-validate/<name>.hash" \
732
+ "accounts/<PROFILE>/analysis/module-validate/<name>.report.json" \
733
+ "<CURRENT_HASH>"
734
+ ```
735
+
736
+ The report's `summary.status` field gives a machine-readable verdict:
737
+ - `READY_FOR_BUILD` — no FAILs, safe to proceed
738
+ - `NEEDS_FIXES` — has FAILs, block build/deploy
739
+
740
+ ## Auto-Fix Mode (`--fix`)
741
+
742
+ When invoked with `--fix`, attempt to resolve common issues:
743
+
744
+ | Issue | Auto-fix action |
745
+ |-------|----------------|
746
+ | Missing rule in `module.json` | Add orphaned `@RuleInfo` rule names to `rules[]` |
747
+ | Version mismatch in child POM | Update `<version>` to match `module.json` |
748
+ | Missing test class | Scaffold via `/fluent-rule-scaffold` test template |
749
+ | Invalid JSON in settings | Report parse error location (cannot auto-fix) |
750
+
751
+ Always preview changes before applying. Never auto-fix without confirmation.
752
+
753
+ After auto-fix, re-compute the hash and re-run validation to produce a fresh report.
754
+
755
+ ## Cross-Skill Integration
756
+
757
+ | Scenario | Delegate to |
758
+ |----------|-------------|
759
+ | Rule class missing entirely | `/fluent-rule-scaffold` to generate |
760
+ | Build fails after fix | `/fluent-build` to diagnose |
761
+ | Workflow fragments have issues | `/fluent-workflow-analyzer` to validate |
762
+ | Need to understand rule behavior | `/fluent-custom-code` to analyze |
763
+ | Pre-deploy gate | Read `.report.json` `summary.status` before `/fluent-module-deploy` |
764
+
765
+ ## Troubleshooting
766
+
767
+ | Issue | Likely Cause | Fix |
768
+ |-------|-------------|-----|
769
+ | Can't find module root | Non-standard nesting | Search recursively for `resources/module.json` |
770
+ | POM version extraction fails | Non-standard POM structure | Read POM manually and locate `<version>` tags |
771
+ | Rule class not found | Different package structure | Search by filename across entire `plugins/` tree |
772
+ | @RuleInfo mismatch | Typo in annotation vs module.json | Compare exact strings (case-sensitive) |
773
+ | Schema file missing | Not downloaded yet | Run `fetch-schema` script or download via API |
774
+ | Hash always differs | Line endings (CRLF vs LF) | Hash uses raw bytes; consistent across runs on same OS |
775
+ | Report stale after git pull | New files change hash | Normal — validation re-runs automatically |