@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.
- package/LICENSE +21 -0
- package/README.md +622 -0
- package/bin/cli.mjs +1973 -0
- package/content/cli/agents/fluent-cli/agent.json +149 -0
- package/content/cli/agents/fluent-cli.md +132 -0
- package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
- package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
- package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
- package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
- package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
- package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
- package/content/cli/skills/fluent-connect/SKILL.md +886 -0
- package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
- package/content/cli/skills/fluent-profile/SKILL.md +180 -0
- package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
- package/content/dev/agents/fluent-dev/agent.json +88 -0
- package/content/dev/agents/fluent-dev.md +525 -0
- package/content/dev/reference-modules/catalog.json +4754 -0
- package/content/dev/skills/fluent-build/SKILL.md +192 -0
- package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
- package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
- package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
- package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
- package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
- package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
- package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
- package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
- package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
- package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
- package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
- package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
- package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
- package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
- package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
- package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
- package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
- package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
- package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
- package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
- package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
- package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
- package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
- package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
- package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
- package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
- package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
- package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
- package/content/mcp-extn/agents/fluent-mcp.md +69 -0
- package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
- package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
- package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
- package/content/rfl/agents/fluent-rfl.md +56 -0
- package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
- package/docs/CAPABILITY_MAP.md +77 -0
- package/docs/CLI_COVERAGE.md +47 -0
- package/docs/DEV_WORKFLOW.md +802 -0
- package/docs/FLOW_RUN.md +142 -0
- package/docs/USE_CASES.md +404 -0
- package/metadata.json +156 -0
- package/package.json +51 -0
|
@@ -0,0 +1,895 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fluent-custom-code
|
|
3
|
+
description: Analyze custom Fluent implementation source code under accounts/<PROFILE>/SOURCE, explain behavior, and prepare safe extension plans even when repo/module layout is non-standard. Triggers on "analyze custom code", "explain this plugin", "extend rule logic", "understand source", "non-standard module structure".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
|
|
6
|
+
argument-hint: <PROFILE> [--retailer <RETAILER_REF>] [--objective "<what to change>"]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Custom Code Intelligence
|
|
10
|
+
|
|
11
|
+
Understand and extend custom Fluent implementation code safely, even when repo layout does not follow standard Fluent module structure.
|
|
12
|
+
|
|
13
|
+
## Ownership Boundary
|
|
14
|
+
|
|
15
|
+
This skill owns source discovery, code mapping, behavior explanation, and extension readiness for custom repos.
|
|
16
|
+
|
|
17
|
+
- Workflow structure analysis is owned by `/fluent-workflow-analyzer`.
|
|
18
|
+
- Runtime failure tracing is owned by `/fluent-trace`.
|
|
19
|
+
- Workflow editing is owned by `/fluent-workflow-builder`.
|
|
20
|
+
|
|
21
|
+
## When to Use
|
|
22
|
+
|
|
23
|
+
- "Explain what this custom code does end-to-end"
|
|
24
|
+
- "Which class/rule handles this behavior?"
|
|
25
|
+
- "How can we extend this logic safely?"
|
|
26
|
+
- Source layout is unclear, nested, or non-standard
|
|
27
|
+
|
|
28
|
+
## Required Inputs
|
|
29
|
+
|
|
30
|
+
1. `PROFILE` (maps to `accounts/<PROFILE>/`)
|
|
31
|
+
2. Optional `RETAILER_REF` (needed to download workflows if missing)
|
|
32
|
+
3. Optional objective (what behavior to explain or extend)
|
|
33
|
+
|
|
34
|
+
## Workspace Convention
|
|
35
|
+
|
|
36
|
+
Primary paths:
|
|
37
|
+
|
|
38
|
+
- Source repos: `accounts/<PROFILE>/SOURCE/`
|
|
39
|
+
- Workflow JSONs: `accounts/<PROFILE>/workflows/<RETAILER_REF>/`
|
|
40
|
+
|
|
41
|
+
If `accounts/<PROFILE>/workflows/<RETAILER_REF>/` is missing or empty, download first:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
fluent workflow list -p <PROFILE> -r <RETAILER_REF>
|
|
45
|
+
fluent workflow download -p <PROFILE> -r <RETAILER_REF> -w all -o accounts/<PROFILE>/workflows/<RETAILER_REF>/
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If the host OS cannot write workflow files containing reserved characters (for example `::` on Windows), export JSON and normalize file names:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
fluent workflow download -p <PROFILE> -r <RETAILER_REF> -w all --json > accounts/<PROFILE>/workflows/<RETAILER_REF>/workflows-raw.json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then split into one file per workflow with sanitized names, and persist `workflow-file-map.json` (original workflow name -> sanitized filename).
|
|
55
|
+
|
|
56
|
+
If `RETAILER_REF` is omitted and multiple retailer folders exist under `accounts/<PROFILE>/workflows/`, require explicit retailer selection before analysis.
|
|
57
|
+
|
|
58
|
+
### Account intake contract
|
|
59
|
+
|
|
60
|
+
Treat `accounts/<PROFILE>/` as the single analysis workspace for that account:
|
|
61
|
+
|
|
62
|
+
- `accounts/<PROFILE>/SOURCE/` may contain:
|
|
63
|
+
- editable source repos (`module.json`, `src/main/java`, tests)
|
|
64
|
+
- read-only deployment artifacts (`*.jar`, `*.zip`)
|
|
65
|
+
- `accounts/<PROFILE>/workflows/<RETAILER_REF>/` contains downloaded workflow JSON baselines.
|
|
66
|
+
- `accounts/<PROFILE>/workflows/<RETAILER_REF>/workflow-context.json` captures the owning profile + retailer and must match analysis target.
|
|
67
|
+
- `accounts/<PROFILE>/analysis/` contains generated analysis artifacts for reuse.
|
|
68
|
+
|
|
69
|
+
The analysis objective is a holistic inventory:
|
|
70
|
+
|
|
71
|
+
1. What custom rules exist (source + decompiled artifacts).
|
|
72
|
+
2. What is deployed/available (installed rules snapshot and workflow references).
|
|
73
|
+
3. What can be safely reused vs what needs new code.
|
|
74
|
+
4. What remains unknown and needs additional source input.
|
|
75
|
+
|
|
76
|
+
### Pre-Check: Context Verification and Module Inventory
|
|
77
|
+
|
|
78
|
+
**Verify MCP target matches analysis profile:** Run `config.validate` via MCP. If the `baseUrl` points at a different account than `<PROFILE>`, the MCP `plugin.list` results will carry the wrong `<ACCOUNT>` prefix. In that case, use the Fluent CLI instead: `fluent module list -p <PROFILE>`.
|
|
79
|
+
|
|
80
|
+
**Discover all retailers:** A single profile may serve multiple retailers with different workflows. Enumerate retailer configs (`~/.fluentcommerce/<PROFILE>/retailer.*.json`) and list workflows per retailer. Download workflows for all retailers that have deployed content.
|
|
81
|
+
|
|
82
|
+
**Check module inventory:** Before scanning source, check if a module inventory exists:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
accounts/<PROFILE>/analysis/module-validate/module-inventory.json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If found, read it to identify:
|
|
89
|
+
- Which modules are `customExtension` (have rules to analyze)
|
|
90
|
+
- Which have `hasSource: true` (source available under SOURCE/)
|
|
91
|
+
- Which have `hasSource: false` (need JAR/ZIP decompilation)
|
|
92
|
+
- The `sourceDir` for each module (exact path to source root)
|
|
93
|
+
|
|
94
|
+
If missing, recommend running `/fluent-module-validate inventory` first.
|
|
95
|
+
|
|
96
|
+
This pre-check avoids scanning for modules that are reference (no custom code) or data-only (no rules).
|
|
97
|
+
|
|
98
|
+
## Artifact Generation Sequence
|
|
99
|
+
|
|
100
|
+
Follow this order to ensure all dependencies are available:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
Phase 1: Input Validation (Steps 1-1B)
|
|
104
|
+
|- Check module-inventory.json (prerequisite from /fluent-module-validate)
|
|
105
|
+
|- Discover repo roots (Step 1) — classify each root
|
|
106
|
+
|- Read reference module metadata directly from module.json (no decompile)
|
|
107
|
+
|- Decompile standalone JARs only (Step 1A)
|
|
108
|
+
+- Validate workflow context (Step 1B)
|
|
109
|
+
|
|
110
|
+
Phase 2: Metadata Extraction (Steps 2-2A)
|
|
111
|
+
|- Build code map (Step 2) — for custom_extension roots only
|
|
112
|
+
|- Extract module identity (Step 2A) — version, symbolicName, Maven coords
|
|
113
|
+
+- Query live installed rules (MCP plugin.list) -> installed-rules.json
|
|
114
|
+
|
|
115
|
+
Phase 3: Mapping & Analysis (Steps 3-5)
|
|
116
|
+
|- source-map.json <- Step 2 output, foundation for everything
|
|
117
|
+
|- workflow-rule-map.json <- Step 3, needs source-map + installed-rules + workflows
|
|
118
|
+
|- constraints.json <- Step 5 input, needs source-map
|
|
119
|
+
|- behavior-map.md <- Step 4, needs source-map + workflow-rule-map
|
|
120
|
+
+- extension-plan.md <- Step 5, needs all above
|
|
121
|
+
|
|
122
|
+
Phase 4: Caching (Step 6)
|
|
123
|
+
+- fingerprint.json <- source input hash + artifact timestamps
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Dependency graph:
|
|
127
|
+
```
|
|
128
|
+
module-inventory.json downloaded workflows
|
|
129
|
+
| |
|
|
130
|
+
source-map.json <---- installed-rules.json
|
|
131
|
+
| \ |
|
|
132
|
+
constraints.json workflow-rule-map.json
|
|
133
|
+
|
|
|
134
|
+
behavior-map.md
|
|
135
|
+
|
|
|
136
|
+
extension-plan.md
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Analysis Workflow
|
|
140
|
+
|
|
141
|
+
### Step 1: Discover repository roots (recursive, no assumptions)
|
|
142
|
+
|
|
143
|
+
Under `accounts/<PROFILE>/SOURCE/`, recursively detect candidate module/repo roots by scanning for any of:
|
|
144
|
+
|
|
145
|
+
- `module.json`
|
|
146
|
+
- `pom.xml`
|
|
147
|
+
- `build.gradle` / `build.gradle.kts`
|
|
148
|
+
- `settings.gradle` / `settings.gradle.kts`
|
|
149
|
+
- `src/main/java/`
|
|
150
|
+
- `*.jar` / `*.zip` module artifacts
|
|
151
|
+
- rule markers (`@RuleInfo`, `extends BaseRule`, `ContextWrapper`)
|
|
152
|
+
|
|
153
|
+
If standard files are missing, continue with marker-based discovery instead of stopping.
|
|
154
|
+
|
|
155
|
+
**Handle double-nested git clone directories:**
|
|
156
|
+
A common pattern is `<repo-name>/<repo-name>/` where git clone creates a wrapper dir. If a directory under SOURCE contains only one subdirectory with a similar name and no direct module markers (no `pom.xml`, `module.json`, or `src/` at the outer level), treat the inner directory as the actual module root. Record `"doubleNested": true` in the source map.
|
|
157
|
+
|
|
158
|
+
**Classify each discovered root by type:**
|
|
159
|
+
|
|
160
|
+
| Classification | Indicators | Editable? | Buildable? |
|
|
161
|
+
|---------------|-----------|-----------|-----------|
|
|
162
|
+
| `custom_extension` | Has `src/main/java/`, `pom.xml`, rules in `module.json` | Yes | Yes (Maven) |
|
|
163
|
+
| `configuration_only` | Has `module.json` with empty `rules[]`, no `pom.xml`, no Java source | Yes (JSON) | No |
|
|
164
|
+
| `reference_artifact` | Has `module.json` + JAR under `assets/rules/`, no `src/main/java/` | No (read-only) | No |
|
|
165
|
+
| `standalone_jar` | Has `.jar`/`.zip` only, no `module.json` alongside | No (read-only) | No |
|
|
166
|
+
|
|
167
|
+
This classification drives downstream decisions: only `custom_extension` modules get full source analysis; `reference_artifact` modules get metadata directly from `module.json` without decompilation; `configuration_only` modules skip rule analysis entirely.
|
|
168
|
+
|
|
169
|
+
### Step 1A: Decompile artifacts found under SOURCE
|
|
170
|
+
|
|
171
|
+
**Pre-check before decompiling:** For each JAR/ZIP artifact, check if a `module.json` exists alongside or inside it (reference module pattern with `assets/rules/` structure). If `module.json` is present, read rule metadata from it directly — decompilation is only needed for understanding rule implementation logic, not for metadata extraction. Mark these as `reference_artifact`.
|
|
172
|
+
|
|
173
|
+
**Locate JARs comprehensively:** Reference modules place JARs in multiple locations:
|
|
174
|
+
- Custom extension dist/: `SOURCE/<repo>/dist/*.jar`
|
|
175
|
+
- Reference module assets: `SOURCE/<module-name>/assets/rules/*.jar`
|
|
176
|
+
- Build intermediates: `SOURCE/<repo>/plugins/rules/*/target/*.jar`
|
|
177
|
+
- Standalone: `SOURCE/<module>.jar`
|
|
178
|
+
|
|
179
|
+
Use the Glob tool with pattern `accounts/<PROFILE>/SOURCE/**/*.jar` instead of shell `find` for cross-platform compatibility. Exclude `target/dependency/`, `.git/`, and `node_modules/` paths from results.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
find accounts/<PROFILE>/SOURCE/ -name "*.jar" \
|
|
183
|
+
-not -path "*/target/dependency/*" \
|
|
184
|
+
-not -path "*/.git/*" \
|
|
185
|
+
-not -path "*/node_modules/*" 2>/dev/null
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```powershell
|
|
189
|
+
# PowerShell
|
|
190
|
+
Get-ChildItem "accounts\<PROFILE>\SOURCE" -Recurse -Filter "*.jar" -ErrorAction SilentlyContinue |
|
|
191
|
+
Where-Object { $_.FullName -notlike "*\target\dependency\*" -and $_.FullName -notlike "*\.git\*" -and $_.FullName -notlike "*\node_modules\*" }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
If JAR/ZIP artifacts are already present under `accounts/<PROFILE>/SOURCE/`, do not wait for additional inputs:
|
|
195
|
+
|
|
196
|
+
1. Create a deterministic workspace:
|
|
197
|
+
- `accounts/<PROFILE>/SOURCE/.decompiled/<artifact-name>/`
|
|
198
|
+
2. For `.zip` module artifacts, extract first and locate nested `.jar`/`.class` payloads before decompiling.
|
|
199
|
+
3. Decompile into that folder (target classes first, then expand only if needed).
|
|
200
|
+
4. Write a marker file `DECOMPILED.md` with:
|
|
201
|
+
- source artifact path
|
|
202
|
+
- decompiler used
|
|
203
|
+
- generated timestamp
|
|
204
|
+
- caveats (synthetic names, missing comments/generics)
|
|
205
|
+
5. Treat decompiled output as analysis input alongside normal source trees.
|
|
206
|
+
6. Mark evidence as `confirmed by decompile` in mappings/notes.
|
|
207
|
+
7. Do not edit JAR/ZIP or decompiled output; treat both as read-only evidence.
|
|
208
|
+
|
|
209
|
+
Example commands:
|
|
210
|
+
|
|
211
|
+
> **Cross-platform note:** The decompile commands below assume a Unix-like shell (bash, Git Bash, WSL). On Windows without bash, use the PowerShell equivalents shown after each block. Alternatively, use the built-in Glob/Grep/Read tools for file discovery and content inspection.
|
|
212
|
+
|
|
213
|
+
**Auto-download CFR if not present:**
|
|
214
|
+
```bash
|
|
215
|
+
# CFR is a single-JAR decompiler (~2MB), no installation needed
|
|
216
|
+
if [ ! -f tools/cfr.jar ]; then
|
|
217
|
+
mkdir -p tools
|
|
218
|
+
curl -L -o tools/cfr.jar "https://github.com/leibnitz27/cfr/releases/download/0.152/cfr-0.152.jar"
|
|
219
|
+
fi
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
mkdir -p accounts/<PROFILE>/SOURCE/.decompiled/<artifact-name>/
|
|
224
|
+
|
|
225
|
+
# Jar class listing (instant, reads ZIP index only — no decompilation)
|
|
226
|
+
jar tf accounts/<PROFILE>/SOURCE/<artifact>.jar
|
|
227
|
+
|
|
228
|
+
# Preferred decompile: CFR with --jarfilter to extract only rule classes
|
|
229
|
+
java -jar tools/cfr.jar "accounts/<PROFILE>/SOURCE/<artifact>.jar" \
|
|
230
|
+
--outputdir "accounts/<PROFILE>/SOURCE/.decompiled/<artifact-name>" \
|
|
231
|
+
--jarfilter "com.fluentcommerce.rule"
|
|
232
|
+
|
|
233
|
+
# Zip module extraction
|
|
234
|
+
unzip -o accounts/<PROFILE>/SOURCE/<artifact>.zip -d accounts/<PROFILE>/SOURCE/.decompiled/<artifact-name>/raw/
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The `--jarfilter` flag restricts decompilation to classes matching the package prefix, keeping output focused on rule logic only. Omit `--jarfilter` to decompile all classes.
|
|
238
|
+
|
|
239
|
+
```powershell
|
|
240
|
+
# PowerShell — auto-download CFR
|
|
241
|
+
if (-not (Test-Path "tools\cfr.jar")) {
|
|
242
|
+
New-Item -ItemType Directory -Force -Path "tools"
|
|
243
|
+
Invoke-WebRequest -Uri "https://github.com/leibnitz27/cfr/releases/download/0.152/cfr-0.152.jar" -OutFile "tools\cfr.jar"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
New-Item -ItemType Directory -Force -Path "accounts\<PROFILE>\SOURCE\.decompiled\<artifact-name>"
|
|
247
|
+
|
|
248
|
+
# Jar class listing
|
|
249
|
+
jar tf "accounts\<PROFILE>\SOURCE\<artifact>.jar"
|
|
250
|
+
|
|
251
|
+
# Preferred decompile: CFR with --jarfilter
|
|
252
|
+
java -jar tools/cfr.jar "accounts\<PROFILE>\SOURCE\<artifact>.jar" --outputdir "accounts\<PROFILE>\SOURCE\.decompiled\<artifact-name>" --jarfilter "com.fluentcommerce.rule"
|
|
253
|
+
|
|
254
|
+
# Zip module extraction
|
|
255
|
+
Expand-Archive -Path "accounts\<PROFILE>\SOURCE\<artifact>.zip" -DestinationPath "accounts\<PROFILE>\SOURCE\.decompiled\<artifact-name>\raw" -Force
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
If no decompiler is available, extract metadata without full decompilation using JDK tools:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Extract manifest to discover registered rule names
|
|
262
|
+
jar xf <artifact>.jar META-INF/MANIFEST.MF
|
|
263
|
+
grep -A5 "Rubix-Rules" META-INF/MANIFEST.MF
|
|
264
|
+
|
|
265
|
+
# Disassemble specific classes for method signatures and annotations
|
|
266
|
+
javap -p "com/fluentcommerce/rule/SomeRule.class"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
```powershell
|
|
270
|
+
# PowerShell
|
|
271
|
+
jar xf <artifact>.jar META-INF/MANIFEST.MF
|
|
272
|
+
Select-String -Path "META-INF\MANIFEST.MF" -Pattern "Rubix-Rules" -Context 0,5
|
|
273
|
+
|
|
274
|
+
javap -p "com/fluentcommerce/rule/SomeRule.class"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
This gives rule names and method signatures without a third-party decompiler. Continue with workflow-only + metadata analysis if even this is not possible (do not block the full run).
|
|
278
|
+
|
|
279
|
+
### Step 1B: Validate workflow context before mapping
|
|
280
|
+
|
|
281
|
+
Before mapping workflow rules, validate `accounts/<PROFILE>/workflows/<RETAILER_REF>/workflow-context.json`:
|
|
282
|
+
|
|
283
|
+
- `profile` must match `<PROFILE>`
|
|
284
|
+
- `retailerRef` (or `retailer`) must match `<RETAILER_REF>`
|
|
285
|
+
- workflow file count should be non-zero for a full mapping run
|
|
286
|
+
|
|
287
|
+
If context is missing or mismatched, recreate it and flag confidence as reduced until workflows are refreshed.
|
|
288
|
+
|
|
289
|
+
### Step 2: Build a Code Map
|
|
290
|
+
|
|
291
|
+
Create a compact map with:
|
|
292
|
+
|
|
293
|
+
- Repo/module root
|
|
294
|
+
- Build system (Maven/Gradle/unknown)
|
|
295
|
+
- Module identity (`symbolicName`, `version`, and extraction source)
|
|
296
|
+
- Rule classes discovered
|
|
297
|
+
- Shared utils/services
|
|
298
|
+
- Suspected entry points and side-effect points
|
|
299
|
+
|
|
300
|
+
Minimum output table:
|
|
301
|
+
|
|
302
|
+
| Repo | Root | Classification | Build | Rule markers | Notes |
|
|
303
|
+
|---|---|---|---|---|---|
|
|
304
|
+
|
|
305
|
+
### Step 2A: Module identity extraction (version + symbolic name)
|
|
306
|
+
|
|
307
|
+
Capture both `symbolicName` and `version` per module using this precedence:
|
|
308
|
+
|
|
309
|
+
`symbolicName`:
|
|
310
|
+
1. `resources/module.json` -> `name` (preferred)
|
|
311
|
+
2. JAR `META-INF/MANIFEST.MF` -> `Bundle-SymbolicName` (if present)
|
|
312
|
+
3. POM fallback -> `<groupId>/<artifactId>` (or `<artifactId>` if `groupId` missing)
|
|
313
|
+
|
|
314
|
+
`version`:
|
|
315
|
+
1. `resources/module.json` -> `version` (preferred)
|
|
316
|
+
2. POM -> `<project><version>` (fallback to `<project><parent><version>` if module version omitted)
|
|
317
|
+
3. JAR manifest -> `Implementation-Version` or `Bundle-Version`
|
|
318
|
+
|
|
319
|
+
Always store where values came from (for traceability), for example `versionSource: "module.json"` or `versionSource: "pom.parent.version"`.
|
|
320
|
+
|
|
321
|
+
**Maven multi-module version resolution:**
|
|
322
|
+
Many custom modules use a parent/child POM pattern:
|
|
323
|
+
```
|
|
324
|
+
plugins/pom.xml (parent, defines version)
|
|
325
|
+
plugins/types/<name>/pom.xml (child, inherits version)
|
|
326
|
+
plugins/util/<name>/pom.xml (child, inherits version)
|
|
327
|
+
plugins/rules/<name>/pom.xml (child, inherits version)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Version extraction algorithm:
|
|
331
|
+
1. Read `plugins/pom.xml` -> `<project><version>` (parent version, source of truth)
|
|
332
|
+
2. If parent has no `<version>`, read `<parent><version>` from parent's parent
|
|
333
|
+
3. For any child POM with no `<version>`: read `<parent><version>` reference
|
|
334
|
+
4. Record inheritance depth: `"pom.project.version"` vs `"pom.parent.version"`
|
|
335
|
+
|
|
336
|
+
### Step 3: Map workflow rules to source classes
|
|
337
|
+
|
|
338
|
+
From workflow JSON(s), collect rule names and map to discovered classes.
|
|
339
|
+
|
|
340
|
+
For each mapped rule, extract:
|
|
341
|
+
|
|
342
|
+
- expected props/inputs
|
|
343
|
+
- query/mutation/event calls
|
|
344
|
+
- status changes
|
|
345
|
+
- branch conditions and guards
|
|
346
|
+
|
|
347
|
+
### Step 4: Explain behavior with confidence labels
|
|
348
|
+
|
|
349
|
+
For each important behavior, report:
|
|
350
|
+
|
|
351
|
+
- trigger (event/status/user action)
|
|
352
|
+
- processing path (ruleset -> class -> key methods)
|
|
353
|
+
- side effects (state/event/attributes/external calls)
|
|
354
|
+
- confidence:
|
|
355
|
+
- **confirmed by source**
|
|
356
|
+
- **inferred from workflow metadata**
|
|
357
|
+
- **inferred from runtime evidence**
|
|
358
|
+
|
|
359
|
+
### Step 5: Extension readiness analysis
|
|
360
|
+
|
|
361
|
+
Return extension options with risk:
|
|
362
|
+
|
|
363
|
+
1. Add a new ruleset + existing rule reuse
|
|
364
|
+
2. Add a new rule class (recommended for isolated behavior)
|
|
365
|
+
3. Modify existing rule (higher regression risk)
|
|
366
|
+
|
|
367
|
+
For each option include:
|
|
368
|
+
|
|
369
|
+
- changed files
|
|
370
|
+
- test impact
|
|
371
|
+
- deployment impact
|
|
372
|
+
- rollback path
|
|
373
|
+
|
|
374
|
+
### Step 6: Write or refresh analysis artifacts
|
|
375
|
+
|
|
376
|
+
At the end of every run, write or refresh the full artifact bundle (or the incremental subset when freshness rules allow):
|
|
377
|
+
|
|
378
|
+
- `accounts/<PROFILE>/analysis/custom-code/source-map.json`
|
|
379
|
+
- `accounts/<PROFILE>/analysis/custom-code/workflow-rule-map.json`
|
|
380
|
+
- `accounts/<PROFILE>/analysis/custom-code/constraints.json`
|
|
381
|
+
- `accounts/<PROFILE>/analysis/custom-code/behavior-map.md`
|
|
382
|
+
- `accounts/<PROFILE>/analysis/custom-code/extension-plan.md`
|
|
383
|
+
- `accounts/<PROFILE>/analysis/custom-code/fingerprint.json`
|
|
384
|
+
|
|
385
|
+
Also persist installed rule evidence:
|
|
386
|
+
|
|
387
|
+
- `accounts/<PROFILE>/analysis/installed-rules.json`
|
|
388
|
+
|
|
389
|
+
If any required file cannot be generated, record the blocker and leave explicit TODO notes in `constraints.json` under `missingSources`/`risks`.
|
|
390
|
+
|
|
391
|
+
## Handling Non-Standard Structures
|
|
392
|
+
|
|
393
|
+
Do not require strict Fluent module conventions.
|
|
394
|
+
|
|
395
|
+
If structure is non-standard:
|
|
396
|
+
|
|
397
|
+
1. Use semantic markers (annotations, base classes, utility calls) to locate logic.
|
|
398
|
+
2. Infer module boundaries from package names and build files.
|
|
399
|
+
3. Validate assumptions against workflow bindings and runtime event evidence.
|
|
400
|
+
4. Call out unknowns explicitly instead of guessing.
|
|
401
|
+
|
|
402
|
+
## Escalation Path
|
|
403
|
+
|
|
404
|
+
If source is incomplete or unavailable:
|
|
405
|
+
|
|
406
|
+
1. Check for existing JAR/ZIP under `accounts/<PROFILE>/SOURCE/` and decompile first.
|
|
407
|
+
2. Ask for missing repos under `accounts/<PROFILE>/SOURCE/` if neither source nor artifacts exist.
|
|
408
|
+
3. Request module ZIP/JAR if still missing.
|
|
409
|
+
4. Decompile only target classes tied to mapped rules.
|
|
410
|
+
5. Re-run mapping and confidence labels.
|
|
411
|
+
6. For implementation requests, recommend source-level change options and ask for source repo onboarding when only artifacts are present.
|
|
412
|
+
|
|
413
|
+
## Reusable Artifact Contract
|
|
414
|
+
|
|
415
|
+
Always write analysis artifacts to:
|
|
416
|
+
|
|
417
|
+
`accounts/<PROFILE>/analysis/custom-code/`
|
|
418
|
+
|
|
419
|
+
Required files:
|
|
420
|
+
|
|
421
|
+
1. `source-map.json`
|
|
422
|
+
- Repo/module roots discovered
|
|
423
|
+
- Build system
|
|
424
|
+
- Rule classes and marker evidence
|
|
425
|
+
2. `workflow-rule-map.json`
|
|
426
|
+
- Workflow ruleset/rule names mapped to source classes
|
|
427
|
+
- Match confidence (`exact`, `probable`, `unresolved`)
|
|
428
|
+
3. `constraints.json`
|
|
429
|
+
- Non-standard structure notes
|
|
430
|
+
- Missing sources/artifacts
|
|
431
|
+
- Known extension constraints and risks
|
|
432
|
+
4. `behavior-map.md`
|
|
433
|
+
- Human-readable behavior flow with evidence labels
|
|
434
|
+
5. `extension-plan.md`
|
|
435
|
+
- Recommended extension option, file-level changes, tests, deploy/rollback notes
|
|
436
|
+
6. `fingerprint.json`
|
|
437
|
+
- Input hashes/fingerprints for change detection
|
|
438
|
+
- Freshness decision (`reuse` vs `regenerate`)
|
|
439
|
+
|
|
440
|
+
Required metadata in each JSON file:
|
|
441
|
+
|
|
442
|
+
- `profile`
|
|
443
|
+
- `generatedAt` (ISO timestamp)
|
|
444
|
+
- `objective`
|
|
445
|
+
- `sourceInputHash`
|
|
446
|
+
- `contentHash`
|
|
447
|
+
- `confidenceSummary`
|
|
448
|
+
- `inputFingerprint`
|
|
449
|
+
|
|
450
|
+
For markdown artifacts (`behavior-map.md`, `extension-plan.md`), include a short header block with at least:
|
|
451
|
+
|
|
452
|
+
- `profile`
|
|
453
|
+
- `generatedAt`
|
|
454
|
+
- `sourceInputHash`
|
|
455
|
+
|
|
456
|
+
### Artifact generation gate (must pass before completion)
|
|
457
|
+
|
|
458
|
+
Do not mark analysis as complete until all checks pass:
|
|
459
|
+
|
|
460
|
+
1. All six required artifacts exist in `accounts/<PROFILE>/analysis/custom-code/`.
|
|
461
|
+
2. `source-map.json`, `workflow-rule-map.json`, `constraints.json`, and `fingerprint.json` include `sourceInputHash` + `contentHash`.
|
|
462
|
+
3. `workflow-rule-map.json` mappings use `source` (`custom` | `reference` | `unresolved`) and avoid legacy `unmapped` buckets.
|
|
463
|
+
4. If decompiled evidence is used, impacted mappings include confidence text that indicates decompile-derived evidence.
|
|
464
|
+
5. `accounts/<PROFILE>/analysis/installed-rules.json` exists (from MCP `plugin.list`, or a documented fallback snapshot if MCP is unavailable).
|
|
465
|
+
|
|
466
|
+
### freshness model
|
|
467
|
+
|
|
468
|
+
Use this decision before re-parsing:
|
|
469
|
+
|
|
470
|
+
1. Build `inputFingerprint` from source/workflow inputs (paths + checksums/hashes or deterministic counts).
|
|
471
|
+
2. Compare with `fingerprint.json` from the last run.
|
|
472
|
+
3. If unchanged and objective is compatible, reuse existing artifacts.
|
|
473
|
+
4. If changed (or objective differs), regenerate all artifacts and overwrite fingerprints.
|
|
474
|
+
|
|
475
|
+
### incremental refresh (default)
|
|
476
|
+
|
|
477
|
+
Do not regenerate everything for every change. Refresh only what changed:
|
|
478
|
+
|
|
479
|
+
| Change detected | Refresh scope |
|
|
480
|
+
|---|---|
|
|
481
|
+
| One or few `src/main/java` rule files changed | Update affected rule entries in `source-map.json`, then recompute only impacted mappings in `workflow-rule-map.json`, plus related risks in `constraints.json` |
|
|
482
|
+
| Only tests changed | Update `hasTest`/test metadata in `source-map.json` and any test-related risk notes in `constraints.json` |
|
|
483
|
+
| Only workflow JSON changed | Refresh `workflow-rule-map.json` + `behavior-map.md`; keep source map if source fingerprint unchanged |
|
|
484
|
+
| `module.json` changed | Refresh `source-map.json` + `workflow-rule-map.json` + `constraints.json` |
|
|
485
|
+
| Folder structure moved/large rename | Full regenerate of all artifacts |
|
|
486
|
+
|
|
487
|
+
Full regenerate is fallback, not default.
|
|
488
|
+
|
|
489
|
+
### source-map.json schema
|
|
490
|
+
|
|
491
|
+
```json
|
|
492
|
+
{
|
|
493
|
+
"profile": "<PROFILE>",
|
|
494
|
+
"generatedAt": "2026-02-22T10:00:00Z",
|
|
495
|
+
"objective": "full analysis",
|
|
496
|
+
"sourceInputHash": "f8e7d6c5b4a39281",
|
|
497
|
+
"contentHash": "a1b2c3d4e5f6a7b8",
|
|
498
|
+
"confidenceSummary": {
|
|
499
|
+
"exact": 10,
|
|
500
|
+
"probable": 2,
|
|
501
|
+
"unresolved": 0
|
|
502
|
+
},
|
|
503
|
+
"inputFingerprint": {
|
|
504
|
+
"moduleJsonCount": 2,
|
|
505
|
+
"javaFileCount": 48,
|
|
506
|
+
"workflowJsonCount": 17
|
|
507
|
+
},
|
|
508
|
+
"modules": [
|
|
509
|
+
{
|
|
510
|
+
"name": "fc-module-custom-extension",
|
|
511
|
+
"classification": "custom_extension",
|
|
512
|
+
"isEditable": true,
|
|
513
|
+
"isBuildable": true,
|
|
514
|
+
"doubleNested": false,
|
|
515
|
+
"symbolicName": "fluent-commerce/fc-module-custom-extension",
|
|
516
|
+
"version": "0.0.29",
|
|
517
|
+
"versionSource": "module.json",
|
|
518
|
+
"identitySource": "module.json",
|
|
519
|
+
"osgiSymbolicName": "_.hmextensions",
|
|
520
|
+
"artifactCoordinates": {
|
|
521
|
+
"groupId": "com.fluentcommerce",
|
|
522
|
+
"artifactId": "fc-module-custom-extension",
|
|
523
|
+
"version": "0.0.29"
|
|
524
|
+
},
|
|
525
|
+
"repoPath": "accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-custom-extension",
|
|
526
|
+
"moduleJsonPath": "resources/module.json",
|
|
527
|
+
"pomPath": "plugins/pom.xml",
|
|
528
|
+
"manifestPath": "META-INF/MANIFEST.MF",
|
|
529
|
+
"buildSystem": "maven",
|
|
530
|
+
"buildRoot": "plugins/",
|
|
531
|
+
"buildCommand": "cd plugins && mvn clean install",
|
|
532
|
+
"packageScript": "scripts/build-module.sh",
|
|
533
|
+
"outputArtifact": "dist/<name>-<version>.zip",
|
|
534
|
+
"dependencies": [
|
|
535
|
+
{ "name": "fc-module-core", "type": "fluent", "version": "1.0.0" }
|
|
536
|
+
],
|
|
537
|
+
"compatibility": {
|
|
538
|
+
"minRubixVersion": "2.0.0",
|
|
539
|
+
"rubixPluginSdk": "2.0.0"
|
|
540
|
+
},
|
|
541
|
+
"patterns": {
|
|
542
|
+
"basePackage": "com.fluentcommerce.rule",
|
|
543
|
+
"subPackages": ["common", "fulfilment", "order", "returnorder", "variantproduct"],
|
|
544
|
+
"baseClass": "BaseRule",
|
|
545
|
+
"contextWrapper": "ContextWrapper",
|
|
546
|
+
"testFramework": "JUnit 5 + Mockito",
|
|
547
|
+
"testMirror": "src/test/java/ mirrors src/main/java/"
|
|
548
|
+
},
|
|
549
|
+
"rules": [
|
|
550
|
+
{
|
|
551
|
+
"name": "SendWebhookWithDynamicAttributes",
|
|
552
|
+
"class": "com.fluentcommerce.rule.common.SendWebhookWithDynamicAttributes",
|
|
553
|
+
"classPath": "plugins/rules/.../SendWebhookWithDynamicAttributes.java",
|
|
554
|
+
"description": "Send webhooks with dynamically built attributes",
|
|
555
|
+
"parameters": [
|
|
556
|
+
{ "name": "setting", "type": "String", "description": "Setting ref for webhook config" }
|
|
557
|
+
],
|
|
558
|
+
"entityTypes": ["ORDER", "FULFILMENT"],
|
|
559
|
+
"graphqlUsage": [],
|
|
560
|
+
"sendsEvents": false,
|
|
561
|
+
"setsState": false,
|
|
562
|
+
"readsSettings": true,
|
|
563
|
+
"hasTest": true,
|
|
564
|
+
"testPath": "plugins/rules/.../SendWebhookWithDynamicAttributesTest.java",
|
|
565
|
+
"testCount": 5
|
|
566
|
+
}
|
|
567
|
+
],
|
|
568
|
+
"graphqlFiles": [
|
|
569
|
+
{ "path": "src/main/resources/graphql/orderQuery.graphql", "type": "query", "usedByRules": ["CreateMissingProductVariant"] }
|
|
570
|
+
],
|
|
571
|
+
"retailerConfigs": [
|
|
572
|
+
{ "file": "module.config.<RETAILER_REF>.fc-module-custom-extension.json", "retailer": "<RETAILER_REF>" }
|
|
573
|
+
]
|
|
574
|
+
}
|
|
575
|
+
],
|
|
576
|
+
"summary": {
|
|
577
|
+
"totalModules": 2,
|
|
578
|
+
"codeModules": 1,
|
|
579
|
+
"configOnlyModules": 1,
|
|
580
|
+
"referenceModuleArtifacts": 4,
|
|
581
|
+
"totalRules": 12,
|
|
582
|
+
"rulesWithTests": 8,
|
|
583
|
+
"rulesWithoutTests": 4,
|
|
584
|
+
"testCoveragePercent": 67,
|
|
585
|
+
"settingsFiles": 28,
|
|
586
|
+
"workflowFragments": 4
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### workflow-rule-map.json schema
|
|
592
|
+
|
|
593
|
+
```json
|
|
594
|
+
{
|
|
595
|
+
"profile": "<PROFILE>",
|
|
596
|
+
"generatedAt": "2026-02-22T10:00:00Z",
|
|
597
|
+
"objective": "workflow to rule mapping",
|
|
598
|
+
"sourceInputHash": "f8e7d6c5b4a39281",
|
|
599
|
+
"contentHash": "99aa88bb77cc66dd",
|
|
600
|
+
"confidenceSummary": {
|
|
601
|
+
"exact": 22,
|
|
602
|
+
"probable": 41,
|
|
603
|
+
"unresolved": 3
|
|
604
|
+
},
|
|
605
|
+
"inputFingerprint": {
|
|
606
|
+
"workflowJsonCount": 17,
|
|
607
|
+
"ruleInvocationCount": 66
|
|
608
|
+
},
|
|
609
|
+
"mappings": [
|
|
610
|
+
{
|
|
611
|
+
"workflow": "ORDER::HD",
|
|
612
|
+
"ruleset": "OnOrderComplete",
|
|
613
|
+
"ruleName": "<ACCOUNT>.custom-extension.SendWebhookWithDynamicAttributes",
|
|
614
|
+
"source": "custom",
|
|
615
|
+
"sourceClass": "com.fluentcommerce.rule.common.SendWebhookWithDynamicAttributes",
|
|
616
|
+
"classPath": "plugins/rules/.../SendWebhookWithDynamicAttributes.java",
|
|
617
|
+
"confidence": "exact",
|
|
618
|
+
"props": {
|
|
619
|
+
"setting": "webhook.order.complete"
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
"workflow": "ORDER::HD",
|
|
624
|
+
"ruleset": "OnOrderCreated",
|
|
625
|
+
"ruleName": "<ACCOUNT>.core.SetState",
|
|
626
|
+
"source": "reference",
|
|
627
|
+
"referenceModule": "core",
|
|
628
|
+
"confidence": "probable",
|
|
629
|
+
"props": {
|
|
630
|
+
"status": "RECEIVED"
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
"workflow": "ORDER::HD",
|
|
635
|
+
"ruleset": "UnexpectedRuleset",
|
|
636
|
+
"ruleName": "<ACCOUNT>.custom.UnknownRule",
|
|
637
|
+
"source": "unresolved",
|
|
638
|
+
"reason": "Not found in custom source, installed-rules snapshot, or reference catalog",
|
|
639
|
+
"confidence": "unresolved"
|
|
640
|
+
}
|
|
641
|
+
]
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### constraints.json schema
|
|
646
|
+
|
|
647
|
+
```json
|
|
648
|
+
{
|
|
649
|
+
"profile": "<PROFILE>",
|
|
650
|
+
"generatedAt": "2026-02-22T10:00:00Z",
|
|
651
|
+
"objective": "extend webhook logic",
|
|
652
|
+
"sourceInputHash": "f8e7d6c5b4a39281",
|
|
653
|
+
"contentHash": "11223344aabbccdd",
|
|
654
|
+
"confidenceSummary": {
|
|
655
|
+
"exact": 22,
|
|
656
|
+
"probable": 41,
|
|
657
|
+
"unresolved": 3
|
|
658
|
+
},
|
|
659
|
+
"inputFingerprint": {
|
|
660
|
+
"moduleJsonCount": 2,
|
|
661
|
+
"javaFileCount": 48,
|
|
662
|
+
"workflowJsonCount": 17
|
|
663
|
+
},
|
|
664
|
+
"structure": {
|
|
665
|
+
"isStandard": true,
|
|
666
|
+
"deviations": []
|
|
667
|
+
},
|
|
668
|
+
"missingSources": [],
|
|
669
|
+
"missingTests": ["CreateMissingProductVariant"],
|
|
670
|
+
"extensionConstraints": [
|
|
671
|
+
{
|
|
672
|
+
"constraint": "BaseRule required",
|
|
673
|
+
"detail": "All rules must extend BaseRule, not Rule directly",
|
|
674
|
+
"impact": "new rules"
|
|
675
|
+
}
|
|
676
|
+
],
|
|
677
|
+
"risks": [
|
|
678
|
+
{
|
|
679
|
+
"area": "webhook rule modification",
|
|
680
|
+
"risk": "medium",
|
|
681
|
+
"riskSeverity": "medium",
|
|
682
|
+
"mitigation": "Add integration tests across ORDER and FULFILMENT flows before deploy",
|
|
683
|
+
"reason": "Used in 3 workflows across ORDER and FULFILMENT entities"
|
|
684
|
+
}
|
|
685
|
+
]
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### fingerprint.json schema
|
|
690
|
+
|
|
691
|
+
```json
|
|
692
|
+
{
|
|
693
|
+
"profile": "<PROFILE>",
|
|
694
|
+
"generatedAt": "2026-02-22T10:00:00Z",
|
|
695
|
+
"objective": "full analysis",
|
|
696
|
+
"freshnessDecision": "regenerate",
|
|
697
|
+
"sourceInputHash": "f8e7d6c5b4a39281",
|
|
698
|
+
"contentHash": "ddee1122ccbb3344",
|
|
699
|
+
"lastRegeneratedAt": "2026-02-22T10:00:00Z",
|
|
700
|
+
"inputFingerprint": {
|
|
701
|
+
"moduleJsonCount": 2,
|
|
702
|
+
"javaFileCount": 48,
|
|
703
|
+
"workflowJsonCount": 17
|
|
704
|
+
},
|
|
705
|
+
"incrementalRefreshMatrix": {
|
|
706
|
+
"javaRuleChange": "refresh source-map + impacted workflow mappings + related constraints",
|
|
707
|
+
"testsOnly": "refresh test metadata and test-risk notes",
|
|
708
|
+
"workflowJsonChange": "refresh workflow-rule-map + behavior-map",
|
|
709
|
+
"moduleJsonChange": "refresh source-map + workflow-rule-map + constraints",
|
|
710
|
+
"structureMove": "full regenerate"
|
|
711
|
+
},
|
|
712
|
+
"incrementalScope": {
|
|
713
|
+
"sourceMapStale": true,
|
|
714
|
+
"workflowRuleMapStale": true,
|
|
715
|
+
"constraintsStale": true,
|
|
716
|
+
"behaviorMapStale": true,
|
|
717
|
+
"extensionPlanStale": true
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### markdown artifact header template
|
|
723
|
+
|
|
724
|
+
Use this header block at the top of both `behavior-map.md` and `extension-plan.md`:
|
|
725
|
+
|
|
726
|
+
```markdown
|
|
727
|
+
# <Artifact Title>
|
|
728
|
+
|
|
729
|
+
Generated at: <ISO-8601>
|
|
730
|
+
Profile: <PROFILE>
|
|
731
|
+
Source input hash: <sourceInputHash>
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
## Reference Module Integration
|
|
735
|
+
|
|
736
|
+
### Rule naming pattern
|
|
737
|
+
|
|
738
|
+
Fluent Commerce rules follow the pattern `<ACCOUNT>.<MODULE>.<RuleName>`:
|
|
739
|
+
- `FLUENTRETAIL.base.*` — Platform-standard rules (always available)
|
|
740
|
+
- `FLUENTRETAIL.globalinventory.*` — Platform inventory rules (always available)
|
|
741
|
+
- `<ACCOUNT>.order.*` — Order module rules installed on the account
|
|
742
|
+
- `<ACCOUNT>.core.*` — Core module rules installed on the account
|
|
743
|
+
- `<ACCOUNT>.custom-extension.*` — Custom module rules
|
|
744
|
+
|
|
745
|
+
### Pre-fed reference catalog
|
|
746
|
+
|
|
747
|
+
A static catalog of platform-standard (`FLUENTRETAIL.*`) rules ships at:
|
|
748
|
+
|
|
749
|
+
```
|
|
750
|
+
fluent-ai-skills/content/dev/reference-modules/catalog.json
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
This contains 232 rules with full metadata (description, parameters, entity types, produced events). Use this for offline analysis or when MCP tools are unavailable.
|
|
754
|
+
|
|
755
|
+
### Live installed rules snapshot
|
|
756
|
+
|
|
757
|
+
During analysis, query `plugin.list` via MCP to get all rules installed on the account:
|
|
758
|
+
|
|
759
|
+
```
|
|
760
|
+
MCP: plugin.list (no filter)
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Save the output as:
|
|
764
|
+
|
|
765
|
+
```
|
|
766
|
+
accounts/<PROFILE>/analysis/installed-rules.json
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
This captures both reference AND custom rules specific to the account, with the account prefix.
|
|
770
|
+
|
|
771
|
+
**Important:** Verify that MCP is connected to the same account as the analysis profile (see Pre-Check). If MCP points at a different account, the `plugin.list` results will have the wrong `<ACCOUNT>` prefix and must NOT be saved for this profile.
|
|
772
|
+
|
|
773
|
+
If MCP is unavailable or connected to a different account, write a fallback snapshot with `"source": "workflow-observed-fallback"` built from all rule names observed in workflow JSONs, and flag reduced confidence in `constraints.json`.
|
|
774
|
+
|
|
775
|
+
### Rule resolution order
|
|
776
|
+
|
|
777
|
+
When resolving a rule name from a workflow:
|
|
778
|
+
|
|
779
|
+
1. **Custom source** — check `source-map.json` → `modules[].rules[]` (has source code)
|
|
780
|
+
2. **Installed rules** — check `installed-rules.json` (has metadata from plugin.list)
|
|
781
|
+
3. **Reference catalog** — check `reference-modules/catalog.json` (offline fallback)
|
|
782
|
+
4. **Unresolved** — mark as unknown, flag for investigation
|
|
783
|
+
|
|
784
|
+
### workflow-rule-map.json: source field
|
|
785
|
+
|
|
786
|
+
Every mapping must include a `source` field:
|
|
787
|
+
|
|
788
|
+
```json
|
|
789
|
+
{
|
|
790
|
+
"ruleName": "<ACCOUNT>.custom-extension.SendWebhookWithDynamicAttributes",
|
|
791
|
+
"source": "custom",
|
|
792
|
+
"sourceClass": "com.fluentcommerce.rule.common.SendWebhookWithDynamicAttributes"
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
```json
|
|
797
|
+
{
|
|
798
|
+
"ruleName": "<ACCOUNT>.core.SetState",
|
|
799
|
+
"source": "reference",
|
|
800
|
+
"referenceModule": "base",
|
|
801
|
+
"description": "Sets entity status to specified value",
|
|
802
|
+
"parameters": [{ "name": "status", "type": "STRING" }]
|
|
803
|
+
}
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
```json
|
|
807
|
+
{
|
|
808
|
+
"ruleName": "<ACCOUNT>.custom.UnknownRule",
|
|
809
|
+
"source": "unresolved",
|
|
810
|
+
"reason": "Not found in custom source, installed rules, or reference catalog"
|
|
811
|
+
}
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
This eliminates the "unmapped" category — every rule is resolved or explicitly unresolved.
|
|
815
|
+
|
|
816
|
+
## Content Hash and Change Detection
|
|
817
|
+
|
|
818
|
+
### Staleness check
|
|
819
|
+
|
|
820
|
+
Every JSON artifact includes a `contentHash` field — a SHA-256 hash of the source inputs used to generate it:
|
|
821
|
+
|
|
822
|
+
```json
|
|
823
|
+
{
|
|
824
|
+
"profile": "<PROFILE>",
|
|
825
|
+
"generatedAt": "2026-02-22T10:00:00Z",
|
|
826
|
+
"contentHash": "a1b2c3d4e5f6g7h8",
|
|
827
|
+
"sourceInputHash": "f8e7d6c5b4a39281"
|
|
828
|
+
}
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
- `contentHash` — hash of the artifact content itself
|
|
832
|
+
- `sourceInputHash` — hash of the source files used to generate it
|
|
833
|
+
|
|
834
|
+
### Computing source input hash
|
|
835
|
+
|
|
836
|
+
Hash these inputs together (SHA-256, first 16 hex chars):
|
|
837
|
+
|
|
838
|
+
1. All `module.json` files under `accounts/<PROFILE>/SOURCE/` (content)
|
|
839
|
+
2. All `*.java` files under `accounts/<PROFILE>/SOURCE/` (file paths + sizes, NOT full content — for speed)
|
|
840
|
+
3. All workflow JSON files under `accounts/<PROFILE>/workflows/<RETAILER_REF>/` (content)
|
|
841
|
+
|
|
842
|
+
### Skip/refresh logic
|
|
843
|
+
|
|
844
|
+
```
|
|
845
|
+
On invocation:
|
|
846
|
+
1. Compute current sourceInputHash from source files
|
|
847
|
+
2. Read existing source-map.json (if present)
|
|
848
|
+
3. Compare sourceInputHash values
|
|
849
|
+
├── Same → artifacts are fresh, skip analysis (print "No changes detected")
|
|
850
|
+
└── Different → source has changed, re-analyze
|
|
851
|
+
4. --refresh flag → always re-analyze regardless of hash
|
|
852
|
+
5. --module <name> → re-analyze only that module
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
### Hash computation pseudo-code
|
|
856
|
+
|
|
857
|
+
```
|
|
858
|
+
inputs = []
|
|
859
|
+
for each module.json in accounts/<PROFILE>/SOURCE/**/:
|
|
860
|
+
inputs.append(file_content)
|
|
861
|
+
for each *.java in accounts/<PROFILE>/SOURCE/**/:
|
|
862
|
+
inputs.append(file_path + ":" + file_size)
|
|
863
|
+
for each *.json in accounts/<PROFILE>/workflows/<RETAILER_REF>/:
|
|
864
|
+
inputs.append(file_content)
|
|
865
|
+
sourceInputHash = sha256(sort(inputs).join("\n"))[:16]
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
Using file paths + sizes for Java files (instead of full content) keeps the hash fast for large codebases while still detecting new/deleted/renamed files. Full content hashing of module.json and workflows is fine because those files are small.
|
|
869
|
+
|
|
870
|
+
## Cross-Skill Reuse Rules
|
|
871
|
+
|
|
872
|
+
Other skills should consume these artifacts before re-scanning:
|
|
873
|
+
|
|
874
|
+
- `/fluent-workflow-builder`: reuse `workflow-rule-map.json` + `constraints.json`
|
|
875
|
+
- `/fluent-rule-scaffold`: reuse `source-map.json` + `extension-plan.md`
|
|
876
|
+
- `/fluent-trace`: reuse `behavior-map.md` + `workflow-rule-map.json`
|
|
877
|
+
- `/fluent-e2e-test`: reuse `constraints.json` for scenario boundaries
|
|
878
|
+
- `/fluent-workflow-analyzer`: reuse `source-map.json` for rule annotations
|
|
879
|
+
|
|
880
|
+
Reference module knowledge is also available to all skills:
|
|
881
|
+
|
|
882
|
+
- `reference-modules/catalog.json` — offline rule lookup (232 FLUENTRETAIL rules)
|
|
883
|
+
- `installed-rules.json` — live snapshot of all rules on the account
|
|
884
|
+
|
|
885
|
+
If artifacts are stale (hash mismatch) or objective changed, regenerate and keep the same file names.
|
|
886
|
+
|
|
887
|
+
## Output Template
|
|
888
|
+
|
|
889
|
+
Use this structure:
|
|
890
|
+
|
|
891
|
+
1. **Source map** (repos/modules/rules found)
|
|
892
|
+
2. **Installed rules** (from plugin.list, reference + custom)
|
|
893
|
+
3. **Behavior map** (what code does, with evidence)
|
|
894
|
+
4. **Extension plan** (recommended option + risks)
|
|
895
|
+
5. **Open gaps** (missing sources/artifacts or ambiguous logic)
|