@fluentcommerce/ai-skills 0.2.0 → 0.3.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.

Potentially problematic release.


This version of @fluentcommerce/ai-skills might be problematic. Click here for more details.

Files changed (169) hide show
  1. package/README.md +866 -616
  2. package/bin/cli.mjs +2112 -1973
  3. package/content/cli/agents/fluent-cli/agent.json +149 -149
  4. package/content/cli/agents/fluent-cli.md +132 -132
  5. package/content/cli/skills/fluent-bootstrap/SKILL.md +214 -190
  6. package/content/cli/skills/fluent-cli-index/SKILL.md +1 -1
  7. package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +117 -1
  8. package/content/cli/skills/fluent-cli-reference/SKILL.md +1040 -623
  9. package/content/cli/skills/fluent-cli-retailer/SKILL.md +27 -2
  10. package/content/cli/skills/fluent-cli-settings/SKILL.md +21 -1
  11. package/content/cli/skills/fluent-connect/SKILL.md +937 -886
  12. package/content/cli/skills/fluent-module-deploy/SKILL.md +181 -17
  13. package/content/cli/skills/fluent-profile/SKILL.md +73 -0
  14. package/content/cli/skills/fluent-workflow/SKILL.md +360 -310
  15. package/content/dev/agents/fluent-backend-dev/AGENT.md +58 -0
  16. package/content/dev/agents/fluent-backend-dev/agent.json +69 -0
  17. package/content/dev/agents/fluent-backend-dev.md +287 -0
  18. package/content/dev/agents/fluent-dev/AGENT.md +98 -76
  19. package/content/dev/agents/fluent-dev/agent.json +24 -2
  20. package/content/dev/agents/fluent-dev.md +194 -524
  21. package/content/dev/agents/fluent-frontend-dev/AGENT.md +63 -0
  22. package/content/dev/agents/fluent-frontend-dev/agent.json +52 -0
  23. package/content/dev/agents/fluent-frontend-dev.md +323 -0
  24. package/content/dev/skills/fluent-archive/SKILL.md +234 -0
  25. package/content/dev/skills/fluent-build/SKILL.md +312 -170
  26. package/content/dev/skills/fluent-connection-analysis/SKILL.md +422 -386
  27. package/content/dev/skills/fluent-custom-code/SKILL.md +15 -9
  28. package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +731 -0
  29. package/content/dev/skills/fluent-e2e-test/SKILL.md +501 -394
  30. package/content/dev/skills/fluent-event-api/SKILL.md +962 -945
  31. package/content/dev/skills/fluent-feature-explain/SKILL.md +680 -603
  32. package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +40 -11
  33. package/content/dev/skills/fluent-feature-plan/SKILL.md +478 -221
  34. package/content/dev/skills/fluent-feature-status/SKILL.md +335 -0
  35. package/content/dev/skills/fluent-feedback/SKILL.md +221 -0
  36. package/content/dev/skills/fluent-implementation-map/SKILL.md +644 -0
  37. package/content/dev/skills/fluent-job-batch/SKILL.md +10 -0
  38. package/content/dev/skills/fluent-module-scaffold/SKILL.md +134 -3
  39. package/content/dev/skills/fluent-module-validate/SKILL.md +778 -775
  40. package/content/dev/skills/fluent-mystique-analyze/SKILL.md +817 -0
  41. package/content/dev/skills/fluent-mystique-builder/COMPONENT_TEMPLATE.md +81 -0
  42. package/content/dev/skills/fluent-mystique-builder/README.md +63 -0
  43. package/content/dev/skills/fluent-mystique-builder/SKILL.md +1294 -0
  44. package/content/dev/skills/fluent-mystique-builder/components/INDEX.md +92 -0
  45. package/content/dev/skills/fluent-mystique-builder/components/fc.accordion.md +48 -0
  46. package/content/dev/skills/fluent-mystique-builder/components/fc.action.field.fulfilmentpack.md +20 -0
  47. package/content/dev/skills/fluent-mystique-builder/components/fc.action.field.multiparcel.md +21 -0
  48. package/content/dev/skills/fluent-mystique-builder/components/fc.action.field.returnitems.md +21 -0
  49. package/content/dev/skills/fluent-mystique-builder/components/fc.action.field.wavepick.md +21 -0
  50. package/content/dev/skills/fluent-mystique-builder/components/fc.action.inline.md +24 -0
  51. package/content/dev/skills/fluent-mystique-builder/components/fc.activity.entity.md +25 -0
  52. package/content/dev/skills/fluent-mystique-builder/components/fc.analytics.viz.md +20 -0
  53. package/content/dev/skills/fluent-mystique-builder/components/fc.attribute.column.md +111 -0
  54. package/content/dev/skills/fluent-mystique-builder/components/fc.attribute.json.md +20 -0
  55. package/content/dev/skills/fluent-mystique-builder/components/fc.attribute.jsoneditor.md +54 -0
  56. package/content/dev/skills/fluent-mystique-builder/components/fc.attribute.locationId.md +51 -0
  57. package/content/dev/skills/fluent-mystique-builder/components/fc.attribute.retailerId.md +52 -0
  58. package/content/dev/skills/fluent-mystique-builder/components/fc.button.bar.md +57 -0
  59. package/content/dev/skills/fluent-mystique-builder/components/fc.button.print.download.md +53 -0
  60. package/content/dev/skills/fluent-mystique-builder/components/fc.button.print.inline.compatibility.md +60 -0
  61. package/content/dev/skills/fluent-mystique-builder/components/fc.button.print.inline.md +53 -0
  62. package/content/dev/skills/fluent-mystique-builder/components/fc.button.print.md +24 -0
  63. package/content/dev/skills/fluent-mystique-builder/components/fc.button.print.pick.md +61 -0
  64. package/content/dev/skills/fluent-mystique-builder/components/fc.buttons.add.reject.md +20 -0
  65. package/content/dev/skills/fluent-mystique-builder/components/fc.card.attribute.md +73 -0
  66. package/content/dev/skills/fluent-mystique-builder/components/fc.card.attributes.grid.md +40 -0
  67. package/content/dev/skills/fluent-mystique-builder/components/fc.card.image.md +37 -0
  68. package/content/dev/skills/fluent-mystique-builder/components/fc.card.map.point.md +24 -0
  69. package/content/dev/skills/fluent-mystique-builder/components/fc.card.multi.md +79 -0
  70. package/content/dev/skills/fluent-mystique-builder/components/fc.card.product.md +27 -0
  71. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.area.md +34 -0
  72. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.area.wrapper.feed.md +98 -0
  73. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.bar.md +52 -0
  74. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.bar.wrapper.source.md +104 -0
  75. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.gauge.md +28 -0
  76. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.gauge.wrapper.threshold.md +118 -0
  77. package/content/dev/skills/fluent-mystique-builder/components/fc.chart.line.md +32 -0
  78. package/content/dev/skills/fluent-mystique-builder/components/fc.conditional.md +62 -0
  79. package/content/dev/skills/fluent-mystique-builder/components/fc.dashboard.threshold.md +65 -0
  80. package/content/dev/skills/fluent-mystique-builder/components/fc.daterange.wrapper.forwarder.md +56 -0
  81. package/content/dev/skills/fluent-mystique-builder/components/fc.drawer.button.md +21 -0
  82. package/content/dev/skills/fluent-mystique-builder/components/fc.event.detail.md +20 -0
  83. package/content/dev/skills/fluent-mystique-builder/components/fc.events.search.md +21 -0
  84. package/content/dev/skills/fluent-mystique-builder/components/fc.field.daterange.md +83 -0
  85. package/content/dev/skills/fluent-mystique-builder/components/fc.field.filterComplex.md +106 -0
  86. package/content/dev/skills/fluent-mystique-builder/components/fc.field.intrange.md +82 -0
  87. package/content/dev/skills/fluent-mystique-builder/components/fc.field.multistring.md +50 -0
  88. package/content/dev/skills/fluent-mystique-builder/components/fc.filterPanel.md +53 -0
  89. package/content/dev/skills/fluent-mystique-builder/components/fc.json.editor.md +22 -0
  90. package/content/dev/skills/fluent-mystique-builder/components/fc.json.viewer.md +21 -0
  91. package/content/dev/skills/fluent-mystique-builder/components/fc.list.customAction.md +79 -0
  92. package/content/dev/skills/fluent-mystique-builder/components/fc.list.md +116 -0
  93. package/content/dev/skills/fluent-mystique-builder/components/fc.list.wrapper.bppmetrics.md +69 -0
  94. package/content/dev/skills/fluent-mystique-builder/components/fc.list.wrapper.feed.md +65 -0
  95. package/content/dev/skills/fluent-mystique-builder/components/fc.list.wrapper.source.md +64 -0
  96. package/content/dev/skills/fluent-mystique-builder/components/fc.modal.button.addItem.md +60 -0
  97. package/content/dev/skills/fluent-mystique-builder/components/fc.modal.button.md +21 -0
  98. package/content/dev/skills/fluent-mystique-builder/components/fc.mutation.inline.md +88 -0
  99. package/content/dev/skills/fluent-mystique-builder/components/fc.mystique.collapsible.attributes.md +83 -0
  100. package/content/dev/skills/fluent-mystique-builder/components/fc.mystique.collapsible.text.md +33 -0
  101. package/content/dev/skills/fluent-mystique-builder/components/fc.mystique.link.md +30 -0
  102. package/content/dev/skills/fluent-mystique-builder/components/fc.order.itemDetails.md +20 -0
  103. package/content/dev/skills/fluent-mystique-builder/components/fc.order.shipmentDetails.md +20 -0
  104. package/content/dev/skills/fluent-mystique-builder/components/fc.page.filter.select.md +87 -0
  105. package/content/dev/skills/fluent-mystique-builder/components/fc.page.md +64 -0
  106. package/content/dev/skills/fluent-mystique-builder/components/fc.page.refresh.md +48 -0
  107. package/content/dev/skills/fluent-mystique-builder/components/fc.page.section.column.md +71 -0
  108. package/content/dev/skills/fluent-mystique-builder/components/fc.page.section.header.md +61 -0
  109. package/content/dev/skills/fluent-mystique-builder/components/fc.page.section.md +59 -0
  110. package/content/dev/skills/fluent-mystique-builder/components/fc.page.wizard.md +45 -0
  111. package/content/dev/skills/fluent-mystique-builder/components/fc.page.wizard.summary.md +56 -0
  112. package/content/dev/skills/fluent-mystique-builder/components/fc.progress.circular.md +20 -0
  113. package/content/dev/skills/fluent-mystique-builder/components/fc.provider.graphql.md +71 -0
  114. package/content/dev/skills/fluent-mystique-builder/components/fc.quantity.list.md +87 -0
  115. package/content/dev/skills/fluent-mystique-builder/components/fc.repeater.md +56 -0
  116. package/content/dev/skills/fluent-mystique-builder/components/fc.reports.ipuipc.md +54 -0
  117. package/content/dev/skills/fluent-mystique-builder/components/fc.return.rowExpansion.md +19 -0
  118. package/content/dev/skills/fluent-mystique-builder/components/fc.scanner.barcode.md +21 -0
  119. package/content/dev/skills/fluent-mystique-builder/components/fc.scanner.barcodeFilter.md +72 -0
  120. package/content/dev/skills/fluent-mystique-builder/components/fc.scanner.camera.md +20 -0
  121. package/content/dev/skills/fluent-mystique-builder/components/fc.settingForm.md +64 -0
  122. package/content/dev/skills/fluent-mystique-builder/components/fc.sourcing.profile.drawer.button.md +19 -0
  123. package/content/dev/skills/fluent-mystique-builder/components/fc.sourcing.profile.modal.button.md +64 -0
  124. package/content/dev/skills/fluent-mystique-builder/components/fc.sourcing.strategy.modal.button.md +20 -0
  125. package/content/dev/skills/fluent-mystique-builder/components/fc.stepper.md +20 -0
  126. package/content/dev/skills/fluent-mystique-builder/components/fc.tab.content.md +56 -0
  127. package/content/dev/skills/fluent-mystique-builder/components/fc.tabs.card.md +64 -0
  128. package/content/dev/skills/fluent-mystique-builder/components/fc.tabs.md +69 -0
  129. package/content/dev/skills/fluent-mystique-builder/components/fc.tile.metric.md +73 -0
  130. package/content/dev/skills/fluent-mystique-builder/components/fc.workflow.provider.md +77 -0
  131. package/content/dev/skills/fluent-mystique-builder/validate-docs.ps1 +260 -0
  132. package/content/dev/skills/fluent-mystique-scaffold/SKILL.md +1830 -0
  133. package/content/dev/skills/fluent-mystique-validate/SKILL.md +646 -0
  134. package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1144 -1090
  135. package/content/dev/skills/fluent-retailer-config/SKILL.md +1162 -1120
  136. package/content/dev/skills/fluent-rollback/SKILL.md +387 -0
  137. package/content/dev/skills/fluent-rule-scaffold/SKILL.md +515 -394
  138. package/content/dev/skills/fluent-scope-decompose/SKILL.md +1123 -1021
  139. package/content/dev/skills/fluent-session-audit-export/SKILL.md +880 -632
  140. package/content/dev/skills/fluent-session-summary/SKILL.md +320 -195
  141. package/content/dev/skills/fluent-settings/SKILL.md +151 -2
  142. package/content/dev/skills/fluent-source-onboard/SKILL.md +23 -4
  143. package/content/dev/skills/fluent-sourcing/SKILL.md +14 -0
  144. package/content/dev/skills/fluent-system-monitoring/SKILL.md +771 -767
  145. package/content/dev/skills/fluent-test-data/SKILL.md +514 -513
  146. package/content/dev/skills/fluent-trace/SKILL.md +1169 -1143
  147. package/content/dev/skills/fluent-transition-api/SKILL.md +364 -346
  148. package/content/dev/skills/fluent-use-case-discover/SKILL.md +593 -471
  149. package/content/dev/skills/fluent-use-case-discover/SPEC_TEMPLATE.md +22 -1
  150. package/content/dev/skills/fluent-version-manage/SKILL.md +44 -3
  151. package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +995 -959
  152. package/content/dev/skills/fluent-workflow-builder/SKILL.md +668 -326
  153. package/content/dev/skills/fluent-workflow-deploy/SKILL.md +480 -0
  154. package/content/dev/skills/fluent-workspace-tree/SKILL.md +281 -0
  155. package/content/mcp-extn/agents/fluent-mcp.md +133 -132
  156. package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +812 -800
  157. package/content/mcp-official/agents/fluent-mcp-core.md +91 -91
  158. package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -94
  159. package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -172
  160. package/docs/CAPABILITY_MAP.md +106 -73
  161. package/docs/DEPLOYMENT_PROMOTION_RUNBOOK.md +218 -0
  162. package/docs/DESIGN-implementation-map.md +698 -0
  163. package/docs/DEV_WORKFLOW.md +814 -802
  164. package/docs/FLOW_RUN.md +142 -142
  165. package/docs/GETTING_STARTED.md +427 -0
  166. package/docs/USE_CASES.md +906 -50
  167. package/metadata.json +184 -155
  168. package/package.json +7 -2
  169. package/docs/USE_CASES.pdf +0 -0
@@ -0,0 +1,1294 @@
1
+ ---
2
+ name: fluent-mystique-builder
3
+ description: Build and edit Mystique manifest JSON definitions. Create pages, routes, component trees, data queries, and fragment compositions using the v2.0 schema. Triggers on "build manifest", "create mystique page", "add route", "mystique component", "edit manifest", "build ui", "add page to manifest".
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
+ argument-hint: <operation> [manifest-file] [--page <path>] [--section <name>] [--fragment]
7
+ ---
8
+
9
+ # Mystique Manifest Builder
10
+
11
+ Create, edit, and validate Fluent Commerce Mystique manifest JSON definitions. Mystique is the declarative, manifest-driven UI framework that powers the OMX frontend. Manifests are JSON documents that define the entire application structure: routes, pages, component trees, GraphQL data queries, role-based visibility, template strings, and plugin references. This skill understands the v2.0 manifest schema end-to-end and generates structurally correct JSON that passes `/fluent-mystique-validate`.
12
+
13
+ ## Platform Fundamentals
14
+
15
+ **OMX vs Mystique naming:** "OMX" (Order Management Experience) is the umbrella framework covering UX + Workflows + Connect. "Mystique" is the internal/historical code name for the UX Framework layer specifically. All import paths and code references use `mystique/` (e.g., `mystique/hooks/useAuth`, `mystique/registry`), while documentation uses "OMX" or "UX Framework" externally. Both refer to the same thing for frontend purposes.
16
+
17
+ **Three registries:**
18
+ - **ComponentRegistry** — standard display components (`fc.list`, `fc.card.attribute`, custom components)
19
+ - **FieldRegistry** — form input components for user action attributes (auto-selected by type name)
20
+ - **TemplateRegistry** — Handlebars helper functions for template string expressions
21
+
22
+ **Configuration over code:** Most UI should be built via manifest JSON, not custom components. Only build custom components when: novel visualizations, drag-and-drop, external service integrations, real-time features, or interaction patterns not covered by user action forms. The framework is **versionless with guaranteed backward compatibility** — old configurations never break.
23
+
24
+ **Multi-tenancy context:** Account → Retailer → Location. Manifests can be scoped at ACCOUNT level. User context (`activeRetailer`, `activeLocation`) is available in template strings for query variables and conditional rendering.
25
+
26
+ **Workflow-UI integration:** User action buttons and forms appear automatically based on the entity's current workflow state. No manifest changes needed when workflows evolve — the UI adapts. Custom form field components are selected by matching the workflow `eventAttributes[].type` to FieldRegistry entries.
27
+
28
+ ## Planning Gate
29
+
30
+ ### Pre-flight: Plan Verification
31
+
32
+ Before proceeding, check for an existing approved plan:
33
+
34
+ 1. **Search** `accounts/<PROFILE>/features/*/status.json` for an entry with `plan: "APPROVED"` that covers this UI work, OR check `accounts/<PROFILE>/tasks/` for a task plan with `Status: APPROVED`
35
+ 2. **If multi-artifact work** (manifest + SDK component + settings + workflows): STOP. Invoke `/fluent-feature-plan` first — this skill cannot be used for multi-artifact work without a feature plan
36
+ 3. **If approved plan found:** Skip to implementation, referencing the plan's UI/manifest sections
37
+ 4. **If manifest-only work with no plan:** Continue to the Planning Gate below to write a plan for this skill
38
+
39
+ **Before creating or modifying any manifest JSON, write a plan using the template from `PLAN_TEMPLATE.md` in the `fluent-feature-plan` skill.** Every table row must carry a Source column (NEW/EXISTING/MODIFIED/REUSED/OOTB).
40
+
41
+ **If this manifest change is part of a larger feature** (rules + workflows + UI), use `/fluent-feature-plan` first. Then reference the approved plan during implementation.
42
+
43
+ **Manifest-builder specific emphasis — ensure these are covered:**
44
+
45
+ 1. **Wireframe (ASCII/Mermaid)** — layout sketch showing page structure and component hierarchy
46
+ 2. **Route table** — all routes with path, component, section, nav label, roles
47
+ 3. **Component tree** — hierarchical component layout per page (nested list or tree diagram)
48
+ 4. **Data requirements** — GraphQL queries needed per page with entity types and key fields
49
+ 5. **Settings dependencies** — fragment references (`settingName`), feature flags, plugin settings
50
+ 6. **i18n keys** — translatable strings requiring `i18n:` prefixed keys
51
+ 7. **Plugin references** — custom SDK components needed and their bundle URLs
52
+
53
+ **Write the plan to:** `accounts/<PROFILE>/tasks/<YYYY-MM-DD>-mystique-builder-<slug>.md`. Set `Status: PENDING`.
54
+
55
+ Present the full plan content to the user and wait for approval before writing any manifest JSON. On approval, update the file to `Status: APPROVED`. If the user says "just do it", proceed directly (still write the file for audit trail).
56
+
57
+ ## Post-Build: Manifest Validation
58
+
59
+ After building or modifying any manifest JSON, **always recommend running `/fluent-mystique-validate`** to check for:
60
+ - Schema compliance (manifestVersion, required fields, route types)
61
+ - Component alias resolution (unknown `fc.*` aliases or custom components)
62
+ - Data query syntax and dataSource path validity
63
+ - Template string syntax (balanced `{{`/`}}`, valid helper names)
64
+ - Cross-reference issues (homePath resolves, fragment settingName conventions)
65
+ - Component prop completeness (fc.list needs attributes, fc.conditional needs value/matches)
66
+
67
+ If the user skips this step, `/fluent-pre-deploy-check` will enforce it before deployment. But catching issues early avoids broken UI in production.
68
+
69
+ ## Ownership Boundary
70
+
71
+ This skill owns manifest JSON creation/editing and component tree composition.
72
+
73
+ | This skill OWNS | Other skills OWN |
74
+ |-----------------|------------------|
75
+ | Manifest JSON creation/editing | Manifest validation -> `/fluent-mystique-validate` |
76
+ | Page and route structure | SDK component code -> `/fluent-mystique-scaffold` |
77
+ | Component tree composition | Deployed manifest analysis -> `/fluent-mystique-analyze` |
78
+ | GraphQL query scaffolding in pages | Live GraphQL execution -> MCP tools |
79
+ | i18n key generation in manifest | Settings management -> `/fluent-settings` |
80
+ | Fragment composition | Workflow actions -> `/fluent-workflow-builder` |
81
+
82
+ ## Pre-flight: Plugin Availability
83
+
84
+ Before building a manifest that references custom SDK components (non-`fc.*` aliases), verify the plugin bundle is accessible:
85
+
86
+ 1. **Identify custom components** in the plan — any component alias that does NOT start with `fc.`
87
+ 2. **For each custom component**, check if an SDK project exists in `accounts/<PROFILE>/SOURCE/*/src/index.tsx` with matching `ComponentRegistry.register` calls
88
+ 3. **If ALL custom components found locally:** Ensure the `plugins[]` array references the correct bundle URL (localhost for dev, CDN for production)
89
+ 4. **If ANY custom component is MISSING:** **STOP.** Inform the user: *"Custom component `<alias>` is not registered in any local SDK project. Scaffold it first via `/fluent-mystique-scaffold`, then resume manifest building."*
90
+ 5. **Exception:** If the manifest is being built as part of a feature plan where scaffolding comes later, note the dependency and proceed — but add a `DEPLOYMENT_ORDER_WARNING` comment
91
+
92
+ ## Frontend Development and Deployment Model
93
+
94
+ Understanding how manifests and custom components reach users is critical for correct builder output.
95
+
96
+ ### Manifests are JSON Configuration
97
+
98
+ Manifests are pure JSON — they are NOT compiled or bundled. They are deployed to Fluent UX Apps through one of:
99
+ - **Admin Console** — upload via the UX App configuration UI
100
+ - **Fluent API** — create/update as a Fluent Setting (key convention: `fc.mystique.manifest.<app-name>`)
101
+ - **Fragment references** — deploy manifest fragments as separate Settings, referenced via `type: "reference"` routes
102
+
103
+ ### Custom Components are Externally Hosted Bundles
104
+
105
+ Custom SDK components (anything not `fc.*`) are React code compiled to a `bundle.js` file. Fluent does NOT host these bundles. The deployment model is:
106
+
107
+ 1. **Development:** `yarn start` serves the bundle on `http://localhost:3001/bundle.[hash].js`
108
+ 2. **Production:** `yarn build` produces `dist/bundle.[hash].js` — deploy to a CDN (Vercel, S3, CloudFront, etc.)
109
+ 3. **Manifest references the bundle URL** via the `plugins[]` array
110
+
111
+ ### Plugin Configuration
112
+
113
+ The `plugins[]` array in the manifest tells Mystique where to load external component bundles:
114
+
115
+ ```json
116
+ {
117
+ "plugins": [
118
+ {
119
+ "type": "url",
120
+ "src": "http://localhost:3001/bundle.46a734bac1cb3a54ba80.js"
121
+ }
122
+ ]
123
+ }
124
+ ```
125
+
126
+ For production deployment, replace with the CDN URL:
127
+
128
+ ```json
129
+ {
130
+ "plugins": [
131
+ {
132
+ "type": "url",
133
+ "src": "https://my-components.vercel.app/bundle.abc123.js"
134
+ }
135
+ ]
136
+ }
137
+ ```
138
+
139
+ Plugins can also be loaded from a Fluent Setting:
140
+
141
+ ```json
142
+ {
143
+ "plugins": [
144
+ {
145
+ "type": "setting",
146
+ "setting": "fc.mystique.plugin.custom-components"
147
+ }
148
+ ]
149
+ }
150
+ ```
151
+
152
+ ### Fragment Deployment
153
+
154
+ Manifest fragments are deployed as Fluent Settings with the naming convention:
155
+
156
+ ```
157
+ fc.mystique.manifest.<fragment-name>
158
+ ```
159
+
160
+ The main manifest references them via:
161
+
162
+ ```json
163
+ {
164
+ "type": "reference",
165
+ "settingName": "fc.mystique.manifest.inventory-section"
166
+ }
167
+ ```
168
+
169
+ This allows sections to be independently deployed and versioned without editing the main manifest.
170
+
171
+ ### Manifest Deployment via Settings API
172
+
173
+ Manifests are stored as **Fluent Settings**. To deploy a manifest or fragment, you create or update its corresponding setting.
174
+
175
+ **Storage model — setting names are app-scoped:**
176
+
177
+ | App | Root Manifest Setting | Example Fragment Setting |
178
+ |-----|----------------------|------------------------|
179
+ | OMS | `fc.mystique.manifest.oms` | `fc.mystique.manifest.oms.fragment.dashboard` |
180
+ | Store | `fc.mystique.manifest.store` | `fc.mystique.manifest.store.fragment.picking` |
181
+ | Service Point | `fc.mystique.manifest.servicepoint` | `fc.mystique.manifest.servicepoint.fragment.tasks` |
182
+ | Custom App | `fc.mystique.manifest.<app>` | `fc.mystique.manifest.<app>.fragment.<name>` |
183
+
184
+ All manifest settings use scope `ACCOUNT`, type `JSON`, and the body is stored in `lobValue`.
185
+
186
+ Each app serves at its own URL path: `/_plugins/<app>/manifests/...`
187
+
188
+ **CRITICAL: Always use `lobValue`, not `value`.** Manifests exceed the regular `value` field's size limit. Using `value` will **silently truncate** the JSON — this causes data loss with no error.
189
+
190
+ **Fetch an existing manifest or fragment:**
191
+
192
+ ```graphql
193
+ query Manifest {
194
+ settings(name: "fc.mystique.manifest.oms") {
195
+ edges { node { id name lobValue } }
196
+ }
197
+ }
198
+ ```
199
+
200
+ Use the same query pattern for fragments — just change the `name` parameter:
201
+ ```graphql
202
+ settings(name: "fc.mystique.manifest.oms.fragment.dashboard")
203
+ ```
204
+
205
+ If a fragment has no ACCOUNT-level override, the platform loads the **global default** via HTTP:
206
+ ```
207
+ https://<host>/_plugins/<app>/manifests/fc.mystique.manifest.<app>.fragment.<name>.json?v=<cachebuster>
208
+ ```
209
+
210
+ **Create a new fragment override** (first-time deployment):
211
+
212
+ ```graphql
213
+ mutation CreateFragment {
214
+ createSetting(input: {
215
+ name: "fc.mystique.manifest.oms.fragment.dashboard"
216
+ context: "ACCOUNT"
217
+ contextId: 0
218
+ valueType: "JSON"
219
+ lobValue: { "manifestVersion": "2.0", "routes": [...] }
220
+ }) { id }
221
+ }
222
+ ```
223
+
224
+ **Update an existing manifest** (requires setting ID — fetch first if unknown):
225
+
226
+ ```graphql
227
+ mutation UpdateManifest {
228
+ updateSetting(input: {
229
+ id: 242
230
+ lobValue: { "manifestVersion": "2.0", "routes": [...] }
231
+ }) { id }
232
+ }
233
+ ```
234
+
235
+ `lobValue` must be **raw JSON** (an object), not a stringified JSON string.
236
+
237
+ **Deployment workflow:**
238
+ 1. Fetch current deployed manifest/fragment via `graphql.query` → save locally as `.json`
239
+ 2. Make changes to the local copy (user reviews diff)
240
+ 3. Upload via `createSetting` (new) or `updateSetting` (existing) — or use MCP `setting.upsert`
241
+ 4. Verify in browser — Settings changes are immediate, no cache delay
242
+
243
+ ## When to Use
244
+
245
+ - Creating a new Mystique app manifest from scratch
246
+ - Adding pages or sections to an existing manifest
247
+ - Composing component trees for entity detail or list pages
248
+ - Building manifest fragments for modular deployment
249
+ - Generating page-level GraphQL data queries for Fluent entities
250
+ - Editing existing manifest JSON (add/remove/update routes, components, props)
251
+ - Wiring custom SDK components into manifests via `plugins[]`
252
+
253
+ ## Progress
254
+
255
+ Emit this block at each phase transition to show progress:
256
+
257
+ ```
258
+ > /fluent-mystique-builder [1/6]
259
+ Done Plan verification -> Collect manifest requirements
260
+ Todo Build route structure Todo Compose component trees
261
+ Todo Generate data queries Todo Validate output
262
+ ```
263
+
264
+ Update the block as each phase completes — mark completed phases with `Done`, the active phase with `->`, and remaining phases with `Todo`. Replace `[1/6]` with the current phase number.
265
+
266
+ ## Manifest v2.0 Schema
267
+
268
+ Every Mystique manifest follows this top-level structure:
269
+
270
+ ```json
271
+ {
272
+ "manifestVersion": "2.0",
273
+ "name": "admin",
274
+ "title": "i18n:fc.admin.title",
275
+ "icon": "settings",
276
+ "context": {
277
+ "level": "retailer",
278
+ "role": "ADMIN"
279
+ },
280
+ "plugins": [
281
+ { "type": "url", "src": "https://cdn.example.com/bundle.js" }
282
+ ],
283
+ "homePath": "dashboard",
284
+ "routes": []
285
+ }
286
+ ```
287
+
288
+ ### Required Fields
289
+
290
+ | Field | Type | Description |
291
+ |-------|------|-------------|
292
+ | `manifestVersion` | `"2.0"` | Always `"2.0"` — the only supported schema version |
293
+ | `name` | `string` | App identifier used in logs and user action filtering. No spaces. |
294
+ | `title` | `string` | User-facing app name. Supports `i18n:` prefix for translation |
295
+ | `homePath` | `string` | Landing route path. Must match a defined route's `path` |
296
+ | `routes` | `MystiqueManifestRoute[]` | Array of sections, pages, and fragment references |
297
+
298
+ ### Optional Fields
299
+
300
+ | Field | Type | Default | Description |
301
+ |-------|------|---------|-------------|
302
+ | `icon` | `string` | none | Material icon name for the app |
303
+ | `context` | `{ level, role? }` | `{ level: "retailer" }` | App context: `account`, `retailer`, or `location`. Optional `role` restricts access |
304
+ | `plugins` | `MystiquePlugin[]` | `[]` | External component bundle URLs or setting references |
305
+ | `orchestrationAlias` | `string` | none | Rename entity types for workflow resolution (e.g., `"store"` -> `"servicepoint"`) |
306
+
307
+ ### Route Types
308
+
309
+ Routes come in three types:
310
+
311
+ **Section** — groups pages under a navigation header:
312
+
313
+ ```json
314
+ {
315
+ "type": "section",
316
+ "path": "orders",
317
+ "nav": { "label": "i18n:fc.orders.nav", "icon": "shopping_cart" },
318
+ "roles": ["ADMIN", "OPERATOR"],
319
+ "pages": []
320
+ }
321
+ ```
322
+
323
+ **Page** — a routable screen with data and components:
324
+
325
+ ```json
326
+ {
327
+ "type": "page",
328
+ "path": "orders",
329
+ "component": "fc.page",
330
+ "nav": { "label": "i18n:fc.orders.list.nav", "icon": "list" },
331
+ "data": {
332
+ "query": "query { orders(first: 25) { edges { node { id ref status type createdOn } } pageInfo { hasNextPage endCursor } } }"
333
+ },
334
+ "props": { "title": "i18n:fc.orders.list.title" },
335
+ "descendants": [],
336
+ "roles": ["ADMIN"],
337
+ "fullScreen": false
338
+ }
339
+ ```
340
+
341
+ **Reference** — includes a manifest fragment stored as a Fluent Setting:
342
+
343
+ ```json
344
+ {
345
+ "type": "reference",
346
+ "settingName": "fc.mystique.manifest.custom-inventory"
347
+ }
348
+ ```
349
+
350
+ ### Component Instance
351
+
352
+ The recursive building block used throughout `descendants[]`:
353
+
354
+ ```json
355
+ {
356
+ "component": "fc.card.attribute",
357
+ "dataSource": "orderById",
358
+ "props": {
359
+ "title": "Order Details",
360
+ "width": "half",
361
+ "attributes": [
362
+ { "label": "Reference", "value": "{{ref}}" },
363
+ { "label": "Status", "value": "{{status}}" },
364
+ { "label": "Type", "value": "{{type}}" },
365
+ { "label": "Created", "value": "{{dateRelative createdOn}}" }
366
+ ]
367
+ },
368
+ "descendants": [],
369
+ "roles": ["ADMIN"]
370
+ }
371
+ ```
372
+
373
+ | Field | Type | Required | Description |
374
+ |-------|------|----------|-------------|
375
+ | `component` | `string` | yes | Registry alias — `fc.*` for core, `custom.*` for SDK components |
376
+ | `dataSource` | `string` | no | JSONPath scoping the data context for this component and its descendants |
377
+ | `props` | `Record<string, any>` | no | Component-specific configuration (varies by component) |
378
+ | `descendants` | `MystiqueComponentInstance[]` | no | Nested child components |
379
+ | `roles` | `string[]` | no | Role-based visibility — component hidden if user lacks listed roles |
380
+
381
+ ### Page Data Query
382
+
383
+ The `data` block on a page defines the GraphQL query executed before rendering:
384
+
385
+ ```json
386
+ {
387
+ "data": {
388
+ "query": "query($id: ID!) { orderById(id: $id) { id ref status type createdOn totalPrice customer { firstName lastName } items { edges { node { id ref quantity productRef } } } } }",
389
+ "variables": {
390
+ "id": "{{id}}"
391
+ }
392
+ }
393
+ }
394
+ ```
395
+
396
+ Variables support template strings (`{{id}}` resolves from URL params) and `ConvertableVariable` for non-string types:
397
+
398
+ ```json
399
+ {
400
+ "variables": {
401
+ "id": { "value": "{{id}}", "parse": true }
402
+ }
403
+ }
404
+ ```
405
+
406
+ ### Manifest Fragment
407
+
408
+ Fragments are cut-down manifests for modular deployment:
409
+
410
+ ```json
411
+ {
412
+ "manifestVersion": "2.0",
413
+ "routes": [
414
+ {
415
+ "type": "section",
416
+ "nav": { "label": "Custom Section", "icon": "extension" },
417
+ "pages": [
418
+ {
419
+ "type": "page",
420
+ "path": "custom-page",
421
+ "component": "fc.page",
422
+ "nav": { "label": "Custom Page", "icon": "build" },
423
+ "props": { "title": "Custom Page" },
424
+ "descendants": []
425
+ }
426
+ ]
427
+ }
428
+ ]
429
+ }
430
+ ```
431
+
432
+ ## Operations
433
+
434
+ ### Operation 1: Create Manifest
435
+
436
+ Interactive wizard collecting:
437
+
438
+ ```
439
+ 1. App name (slug: "admin", "servicepoint", "store-ops")
440
+ 2. Title (user-facing, supports i18n: prefix)
441
+ 3. Context level (account / retailer / location)
442
+ 4. Role restriction (optional)
443
+ 5. Home page path
444
+ 6. Initial sections and pages (from wireframe/plan)
445
+ 7. Plugin references (if custom components needed)
446
+ ```
447
+
448
+ Generates a complete `manifest.json` with:
449
+ - `manifestVersion: "2.0"` and all required top-level fields
450
+ - Route structure with sections and pages
451
+ - Page-level GraphQL data queries for each entity page
452
+ - Component trees with props wired to data sources
453
+ - Plugin references for custom SDK components
454
+
455
+ ### Operation 2: Add Page
456
+
457
+ ```
458
+ Input: manifest file + page definition
459
+ 1. Determine target section (existing or new)
460
+ 2. Define page path (validate uniqueness against existing routes)
461
+ 3. Select page component (fc.page recommended as top-level)
462
+ 4. Define data query (entity type -> auto-generate GraphQL using entity map)
463
+ 5. Build component tree:
464
+ - Start with fc.page as the route component
465
+ - Add layout components (fc.column, fc.tabs.card)
466
+ - Add content components (fc.list, fc.card.attribute, fc.provider.graphql)
467
+ - Wire dataSource paths to connect components to query results
468
+ - Set component props per component documentation
469
+ 6. Add nav config if page should appear in sidebar
470
+ 7. Add role restrictions (optional)
471
+ ```
472
+
473
+ ### Operation 3: Add Component to Page
474
+
475
+ ```
476
+ Input: manifest file + page path + component spec
477
+ 1. Load manifest and locate the target page by path
478
+ 2. Find insertion point in descendants tree (by index or parent component)
479
+ 3. Generate component instance:
480
+ - Select component alias from known registry
481
+ - Set props from component schema (see Component Props Reference below)
482
+ - Wire dataSource if the component consumes query data
483
+ - Add descendants recursively for container components
484
+ 4. Validate resulting tree structure
485
+ ```
486
+
487
+ ### Operation 4: Build Fragment
488
+
489
+ ```
490
+ Input: fragment definition (sections and pages)
491
+ 1. Generate MystiqueManifestFragment JSON structure
492
+ 2. Define fragment routes (sections + pages with full component trees)
493
+ 3. Output as standalone JSON file
494
+ 4. Generate Fluent setting key: fc.mystique.manifest.<fragment-name>
495
+ 5. Generate setting.upsert command for deployment via /fluent-settings
496
+ ```
497
+
498
+ ### Operation 5: Generate Data Query
499
+
500
+ ```
501
+ Input: entity type + required fields + query type (list or detail)
502
+ 1. Map entity type to GraphQL root query using Entity Type Query Map
503
+ 2. Build query with requested fields plus standard fields
504
+ 3. Handle connections/edges/node patterns for nested entities
505
+ 4. Add variables for dynamic filtering (pagination, URL params)
506
+ 5. Output as a page.data block ready to embed in the manifest
507
+ ```
508
+
509
+ ### Operation 6: Edit Existing Manifest
510
+
511
+ ```
512
+ Input: manifest file + edit instructions
513
+ 1. Load and parse the manifest JSON
514
+ 2. Locate target element (route by path, component by tree position)
515
+ 3. Apply modifications (add/remove/update routes, components, props)
516
+ 4. Preserve existing structure — minimal diff approach
517
+ 5. Validate modified manifest against v2.0 schema
518
+ 6. Write back to the same file path
519
+ ```
520
+
521
+ ## Component Composition Patterns
522
+
523
+ ### Pattern 1: Entity Detail Page
524
+
525
+ The standard layout for viewing a single entity with attributes and related data:
526
+
527
+ ```json
528
+ {
529
+ "type": "page",
530
+ "path": "orders/:id",
531
+ "component": "fc.page",
532
+ "data": {
533
+ "query": "query($id: ID!) { orderById(id: $id) { id ref status type totalPrice totalTaxPrice createdOn updatedOn customer { id firstName lastName primaryEmail } items { edges { node { id ref productRef quantity paidPrice currency } } } fulfilments { edges { node { id ref status type fromAddress { name } } } } attributes { name value type } } }",
534
+ "variables": { "id": { "value": "{{id}}", "parse": true } }
535
+ },
536
+ "props": {
537
+ "title": "{{capitalise status}} - Order {{ref}}",
538
+ "backButtons": [{ "label": "Orders", "path": "/orders" }],
539
+ "actions": true
540
+ },
541
+ "descendants": [
542
+ {
543
+ "component": "fc.card.attribute",
544
+ "props": {
545
+ "title": "Order Summary",
546
+ "width": "half",
547
+ "attributes": [
548
+ { "label": "Reference", "value": "{{ref}}" },
549
+ { "label": "Status", "value": "{{status}}", "options": { "styles": [
550
+ { "matches": "COMPLETE", "icon": { "name": "MdCheckCircle", "colour": "success" } },
551
+ { "matches": "CANCELLED", "icon": { "name": "MdCancel", "colour": "error" } }
552
+ ]}},
553
+ { "label": "Type", "value": "{{type}}" },
554
+ { "label": "Created", "value": "{{dateRelative createdOn}}" },
555
+ { "label": "Total", "value": "${{totalPrice}}" }
556
+ ]
557
+ }
558
+ },
559
+ {
560
+ "component": "fc.card.attribute",
561
+ "props": {
562
+ "title": "Customer",
563
+ "width": "half",
564
+ "attributes": [
565
+ { "label": "Name", "value": "{{customer.firstName}} {{customer.lastName}}" },
566
+ { "label": "Email", "value": "{{customer.primaryEmail}}" }
567
+ ]
568
+ }
569
+ },
570
+ {
571
+ "component": "fc.tabs.card",
572
+ "props": {
573
+ "tabs": [
574
+ { "label": "Items" },
575
+ { "label": "Fulfilments" },
576
+ { "label": "Attributes" }
577
+ ]
578
+ },
579
+ "descendants": [
580
+ {
581
+ "component": "fc.list",
582
+ "dataSource": "items",
583
+ "props": {
584
+ "defaultPageSize": 25,
585
+ "attributes": [
586
+ { "label": "Product", "value": "{{node.productRef}}" },
587
+ { "label": "Quantity", "value": "{{node.quantity}}" },
588
+ { "label": "Price", "value": "${{node.paidPrice}}", "align": "right" }
589
+ ]
590
+ }
591
+ },
592
+ {
593
+ "component": "fc.list",
594
+ "dataSource": "fulfilments",
595
+ "props": {
596
+ "defaultPageSize": 10,
597
+ "rowLink": "/fulfilments/{{node.id}}",
598
+ "attributes": [
599
+ { "label": "Reference", "value": "{{node.ref}}" },
600
+ { "label": "Status", "value": "{{node.status}}" },
601
+ { "label": "Type", "value": "{{node.type}}" }
602
+ ]
603
+ }
604
+ },
605
+ {
606
+ "component": "fc.list",
607
+ "dataSource": "attributes",
608
+ "props": {
609
+ "defaultPageSize": 25,
610
+ "attributes": [
611
+ { "label": "Name", "value": "{{node.name}}" },
612
+ { "label": "Value", "value": "{{node.value}}" },
613
+ { "label": "Type", "value": "{{node.type}}" }
614
+ ]
615
+ }
616
+ }
617
+ ]
618
+ }
619
+ ]
620
+ }
621
+ ```
622
+
623
+ ### Pattern 2: Entity List Page
624
+
625
+ The standard paginated list page with filtering:
626
+
627
+ ```json
628
+ {
629
+ "type": "page",
630
+ "path": "orders",
631
+ "component": "fc.page",
632
+ "nav": { "label": "i18n:fc.orders.list.nav", "icon": "shopping_cart" },
633
+ "data": {
634
+ "query": "query($retailerId: [Int!], $orders_first: Int, $status: [String], $type: [String], $createdOn: DateRange) { orders(retailerId: $retailerId, first: $orders_first, status: $status, type: $type, createdOn: $createdOn) { edges { node { id ref status type totalPrice createdOn customer { firstName lastName } } } pageInfo { hasNextPage endCursor } } }",
635
+ "variables": {
636
+ "orders_first": 25,
637
+ "retailerId": "{{activeRetailer.id}}"
638
+ }
639
+ },
640
+ "props": { "title": "i18n:fc.orders.list.title" },
641
+ "descendants": [
642
+ {
643
+ "component": "fc.list",
644
+ "dataSource": "orders",
645
+ "props": {
646
+ "defaultPageSize": 25,
647
+ "rowLink": "/orders/{{node.id}}",
648
+ "filter": { "enabled": true },
649
+ "attributes": [
650
+ { "label": "Reference", "value": "{{node.ref}}" },
651
+ { "label": "Customer", "value": "{{node.customer.firstName}} {{node.customer.lastName}}" },
652
+ { "label": "Status", "value": "{{node.status}}" },
653
+ { "label": "Type", "value": "{{node.type}}" },
654
+ { "label": "Total", "value": "${{node.totalPrice}}", "align": "right" },
655
+ { "label": "Created", "value": "{{dateRelative node.createdOn}}", "hideBelow": "sm" }
656
+ ]
657
+ }
658
+ }
659
+ ]
660
+ }
661
+ ```
662
+
663
+ ### Pattern 3: Dashboard with Metrics
664
+
665
+ ```json
666
+ {
667
+ "type": "page",
668
+ "path": "dashboard",
669
+ "component": "fc.page",
670
+ "data": {
671
+ "query": "query($todayStart: DateTime!, $weekStart: DateTime!) { ordersToday: orders(createdOn: {from: $todayStart}) { edges { node { id } } } ordersWeek: orders(createdOn: {from: $weekStart}) { edges { node { id } } } escalations: fulfilments(status: \"ESCALATED\") { edges { node { id } } } }",
672
+ "variables": {
673
+ "todayStart": "{{dateStringFormatter (dateAdd day=-1) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'}}",
674
+ "weekStart": "{{dateStringFormatter (dateAdd day=-7) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'}}"
675
+ }
676
+ },
677
+ "props": { "title": "Overview Dashboard" },
678
+ "descendants": [
679
+ {
680
+ "component": "fc.dashboard.threshold",
681
+ "props": {
682
+ "label": "Orders Today",
683
+ "value": "{{ordersToday.edges.length}}",
684
+ "thresholdLow": 100,
685
+ "thresholdHigh": 500,
686
+ "link": "#/orders"
687
+ }
688
+ },
689
+ {
690
+ "component": "fc.dashboard.threshold",
691
+ "props": {
692
+ "label": "Orders This Week",
693
+ "value": "{{ordersWeek.edges.length}}",
694
+ "thresholdLow": 500,
695
+ "thresholdHigh": 2000,
696
+ "link": "#/orders"
697
+ }
698
+ },
699
+ {
700
+ "component": "fc.dashboard.threshold",
701
+ "props": {
702
+ "label": "Escalations",
703
+ "value": "{{escalations.edges.length}}",
704
+ "thresholdLow": 5,
705
+ "thresholdHigh": 20,
706
+ "link": "#/fulfilments"
707
+ }
708
+ }
709
+ ]
710
+ }
711
+ ```
712
+
713
+ ### Pattern 4: Conditional Content
714
+
715
+ Show different components based on entity data using `fc.conditional`:
716
+
717
+ ```json
718
+ {
719
+ "component": "fc.conditional",
720
+ "props": {
721
+ "value": "{{type}}",
722
+ "matches": "HD"
723
+ },
724
+ "descendants": [
725
+ {
726
+ "component": "fc.card.attribute",
727
+ "props": {
728
+ "title": "Home Delivery Details",
729
+ "attributes": [
730
+ { "label": "Delivery Address", "value": "{{fulfilmentChoice.deliveryAddress.street}}" },
731
+ { "label": "Postcode", "value": "{{fulfilmentChoice.deliveryAddress.postcode}}" }
732
+ ]
733
+ }
734
+ }
735
+ ]
736
+ }
737
+ ```
738
+
739
+ Use multiple `fc.conditional` siblings for if/else-if patterns:
740
+
741
+ ```json
742
+ [
743
+ {
744
+ "component": "fc.conditional",
745
+ "props": { "value": "{{type}}", "matches": "HD" },
746
+ "descendants": [{ "component": "fc.card.attribute", "props": { "title": "Home Delivery" } }]
747
+ },
748
+ {
749
+ "component": "fc.conditional",
750
+ "props": { "value": "{{type}}", "matches": "CC" },
751
+ "descendants": [{ "component": "fc.card.attribute", "props": { "title": "Click & Collect" } }]
752
+ }
753
+ ]
754
+ ```
755
+
756
+ ### Pattern 5: Data Provider Nesting
757
+
758
+ Use `fc.provider.graphql` to fetch additional data within a page:
759
+
760
+ ```json
761
+ {
762
+ "component": "fc.provider.graphql",
763
+ "props": {
764
+ "query": "query($ref: [String]) { locations(ref: $ref) { edges { node { id ref name type status primaryAddress { street city state postcode country } } } } }",
765
+ "variables": { "ref": "{{fromLocation.ref}}" },
766
+ "path": "locations"
767
+ },
768
+ "descendants": [
769
+ {
770
+ "component": "fc.card.attribute",
771
+ "dataSource": "locations",
772
+ "props": {
773
+ "title": "Fulfilment Location",
774
+ "attributes": [
775
+ { "label": "Name", "value": "{{node.name}}" },
776
+ { "label": "Address", "value": "{{node.primaryAddress.street}}, {{node.primaryAddress.city}}" },
777
+ { "label": "Status", "value": "{{node.status}}" }
778
+ ]
779
+ }
780
+ }
781
+ ]
782
+ }
783
+ ```
784
+
785
+ The `path` prop enables auto-pagination for connections. The `loadingBehavior` prop controls rendering during fetch: `"wait"`, `"wait-with-animation"`, or `"pass-immediately"` (default).
786
+
787
+ ### Pattern 6: Tab Card with Multiple Views
788
+
789
+ Use `fc.tabs.card` to organize related content into tabs. Descendants are matched to tabs by index:
790
+
791
+ ```json
792
+ {
793
+ "component": "fc.tabs.card",
794
+ "props": {
795
+ "tabs": [
796
+ { "label": "i18n:fc.order.items.tab" },
797
+ { "label": "i18n:fc.order.fulfilments.tab" },
798
+ { "label": "i18n:fc.order.activity.tab" }
799
+ ],
800
+ "urlKey": "orderTab"
801
+ },
802
+ "descendants": [
803
+ {
804
+ "component": "fc.list",
805
+ "dataSource": "items",
806
+ "props": {
807
+ "defaultPageSize": 25,
808
+ "attributes": [
809
+ { "label": "Product", "value": "{{node.productRef}}" },
810
+ { "label": "Qty", "value": "{{node.quantity}}" }
811
+ ]
812
+ }
813
+ },
814
+ {
815
+ "component": "fc.list",
816
+ "dataSource": "fulfilments",
817
+ "props": {
818
+ "defaultPageSize": 10,
819
+ "rowLink": "/fulfilments/{{node.id}}",
820
+ "attributes": [
821
+ { "label": "Ref", "value": "{{node.ref}}" },
822
+ { "label": "Status", "value": "{{node.status}}" }
823
+ ]
824
+ }
825
+ },
826
+ {
827
+ "component": "fc.list",
828
+ "dataSource": "events",
829
+ "props": {
830
+ "defaultPageSize": 50,
831
+ "attributes": [
832
+ { "label": "Event", "value": "{{node.name}}" },
833
+ { "label": "Time", "value": "{{dateRelative node.createdOn}}" }
834
+ ]
835
+ }
836
+ }
837
+ ]
838
+ }
839
+ ```
840
+
841
+ ### Pattern 7: Page Actions (User Actions, Mutations, Navigation)
842
+
843
+ Configure page-level action buttons via the `actions` prop on `fc.page`:
844
+
845
+ ```json
846
+ {
847
+ "props": {
848
+ "title": "Order {{ref}}",
849
+ "actions": {
850
+ "primary": [
851
+ {
852
+ "type": "userAction",
853
+ "name": "ConfirmShipment",
854
+ "condition": "{{eq status 'PICK_PACK'}}"
855
+ },
856
+ {
857
+ "type": "navigate",
858
+ "label": "View Fulfilments",
859
+ "link": "/orders/{{id}}/fulfilments"
860
+ }
861
+ ],
862
+ "secondary": [
863
+ {
864
+ "type": "mutation",
865
+ "name": "updateOrder",
866
+ "label": "Edit Order",
867
+ "args": { "id": "{{id}}" },
868
+ "filter": { "type": "include", "names": ["status", "attributes"] },
869
+ "extensions": {
870
+ "postSubmit": { "type": "navigate", "link": "/orders/{{id}}" }
871
+ }
872
+ },
873
+ {
874
+ "type": "component",
875
+ "component": "custom.order-export-button",
876
+ "props": { "orderId": "{{id}}" }
877
+ }
878
+ ],
879
+ "userActionFilter": { "type": "exclude", "names": ["CancelOrder"] }
880
+ }
881
+ }
882
+ }
883
+ ```
884
+
885
+ Action types:
886
+ - `userAction` — triggers a workflow user action. Auto-appended from workflow but can be reordered or conditionally shown.
887
+ - `mutation` — executes a GraphQL mutation with auto-generated form. Use `overrides` to customize field labels, sources, and components.
888
+ - `navigate` — navigates to another page. `link` supports template strings.
889
+ - `component` — renders a custom SDK component in the action bar.
890
+
891
+ ## Entity Type to GraphQL Query Map
892
+
893
+ Use this mapping to auto-generate page data queries:
894
+
895
+ | Entity Type | Root Query (list) | Root Query (detail) | Key Fields |
896
+ |-------------|-------------------|---------------------|------------|
897
+ | ORDER | `orders(first, after, status, type, retailerId, createdOn)` | `orderById(id)` | id, ref, status, type, createdOn, totalPrice, customer, items, fulfilments, fulfilmentChoice, attributes |
898
+ | FULFILMENT | `fulfilments(first, after, status, type, retailerId)` | `fulfilmentById(id)` | id, ref, status, type, createdOn, order, items, fromAddress, toAddress, attributes |
899
+ | ARTICLE | `articles(first, after, status, retailerId)` | `articleById(id)` | id, ref, status, type, items, consignmentArticles, attributes |
900
+ | WAVE | `waves(first, after, status, retailerId)` | `waveById(id)` | id, ref, status, type, fulfilments, retailer, attributes |
901
+ | LOCATION | `locations(first, after, status, type, retailerId)` | `locationById(id)` | id, ref, name, type, status, primaryAddress, networks, openingSchedule, attributes |
902
+ | INVENTORY_POSITION | `inventoryPositions(first, after, productRef, locationRef)` | `inventoryPositionById(id)` | id, ref, productRef, locationRef, onHand, status, type, attributes |
903
+ | PRODUCT_CATALOGUE | `productCatalogues(first, after, retailerId)` | `productCatalogueById(id)` | id, ref, name, type, retailer, attributes |
904
+ | VIRTUAL_CATALOGUE | `virtualCatalogues(first, after, retailerId)` | `virtualCatalogueById(id)` | id, ref, name, type, retailer, attributes |
905
+ | CARRIER | `carriers(first, after, retailerId)` | `carrierById(id)` | id, ref, name, type, status, retailer, attributes |
906
+ | RETURN_ORDER | `returnOrders(first, after, status, retailerId)` | `returnOrderById(id)` | id, ref, status, type, order, returnAuthorisation, items, attributes |
907
+ | NETWORK | `networks(first, after, retailerId)` | `networkById(id)` | id, ref, name, type, status, locations, retailers, attributes |
908
+ | SETTING | `settings(first, after, name, context)` | n/a | id, name, value, lobValue, valueType, context, contextId |
909
+
910
+ ### Connection Pattern for Nested Entities
911
+
912
+ All list queries return connections with edges/node pattern. Always include `pageInfo` for pagination:
913
+
914
+ ```graphql
915
+ query($first: Int, $after: String) {
916
+ orders(first: $first, after: $after) {
917
+ edges {
918
+ node {
919
+ id
920
+ ref
921
+ status
922
+ }
923
+ }
924
+ pageInfo {
925
+ hasNextPage
926
+ endCursor
927
+ }
928
+ }
929
+ }
930
+ ```
931
+
932
+ Nested connections follow the same pattern:
933
+
934
+ ```graphql
935
+ {
936
+ orderById(id: $id) {
937
+ items {
938
+ edges {
939
+ node { id ref quantity productRef }
940
+ }
941
+ }
942
+ }
943
+ }
944
+ ```
945
+
946
+ ## Component Knowledge
947
+
948
+ Each OOTB component is documented in its own `.md` file under `components/` (same directory as this skill). `COMPONENT_TEMPLATE.md` defines the standard format — used for both OOTB and client custom component documentation.
949
+
950
+ Use this lookup order for fastest and most reliable component selection:
951
+ 1. Start with `components/INDEX.md` for alias/category/module lookup (AI-friendly table).
952
+ 2. Open matching `components/<alias>.md` files for detailed props, data binding, and composition guidance.
953
+ 3. Use each file's frontmatter (`alias`, `aliases`, `category`, `module`) and `## AI Parse Notes` section for deterministic parsing.
954
+
955
+ **Before building ANY manifest:**
956
+ 1. Grep `components/*.md` for the user's UI need (e.g., "list", "filter", "tabs")
957
+ 2. Read matching component files for props, data binding, and composition patterns
958
+ 3. Propose a composition plan → get user approval → build manifest JSON
959
+ 4. Only if no OOTB combination works → escalate to `/fluent-mystique-scaffold`
960
+
961
+ ### Build vs Configure Decision (use EVERY time)
962
+
963
+ ```
964
+ Step 1: Check Need → Grep components/*.md for matching capability
965
+ ├── Found a match? → Configure via manifest using the documented props
966
+ ├── Close match? → Step 2: Creative composition of OOTB components
967
+ │ ├── Combine fc.conditional + fc.repeater + fc.provider.graphql?
968
+ │ ├── Nest tabs inside providers? Use multiple data providers?
969
+ │ └── Found a working composition? → Build manifest JSON
970
+ ├── Need a custom form field or template? → Step 3: Lightweight SDK extension
971
+ │ └── FieldRegistry.register() or TemplateRegistry.register()
972
+ └── Genuinely no OOTB equivalent? → Step 4: Full custom component
973
+ └── Scaffold via /fluent-mystique-scaffold → ComponentRegistry.register()
974
+ ```
975
+
976
+ **Most requests resolve at Step 1 or 2.** Only proceed to Step 4 for: novel visualizations, drag-and-drop, external service integrations, real-time features, or interaction patterns not covered by user action forms.
977
+
978
+ ### Quick Alias Reference (86 components in `components/`)
979
+
980
+ **Page:** `fc.page`, `fc.page.section`, `fc.page.section.column`, `fc.page.section.header`, `fc.page.wizard`, `fc.page.wizard.summary`, `fc.page.refresh`, `fc.page.filter.select`
981
+
982
+ **Layout:** `fc.conditional`, `fc.repeater`, `fc.provider.graphql`, `fc.workflow.provider`, `fc.tabs`, `fc.tabs.card`, `fc.tab.content`, `fc.accordion`, `fc.filterPanel`
983
+
984
+ **Content:** `fc.list`, `fc.list.customAction`, `fc.card.attribute`, `fc.card.image`, `fc.card.product`, `fc.card.multi`, `fc.card.attributes.grid`, `fc.card.map.point`, `fc.tile.metric`, `fc.dashboard.threshold`, `fc.stepper`, `fc.progress.circular`, `fc.attribute.json`, `fc.attribute.column`, `fc.mystique.collapsible.text`, `fc.mystique.collapsible.attributes`, `fc.mystique.link`, `fc.json.viewer`, `fc.json.editor`, `fc.quantity.list`, `fc.settingForm`
985
+
986
+ **Charts:** `fc.chart.line`, `fc.chart.bar`, `fc.chart.area`, `fc.chart.gauge` (+ wrapper variants)
987
+
988
+ **Actions:** `fc.action.inline`, `fc.mutation.inline`, `fc.button.print` (+ `.pick`, `.inline`, `.download`), `fc.button.bar`, `fc.modal.button`, `fc.modal.button.addItem`, `fc.drawer.button`, `fc.sourcing.strategy.modal.button`, `fc.sourcing.profile.drawer.button`, `fc.sourcing.profile.modal.button`
989
+
990
+ **Forms/Fields:** `select`, `fc.field.daterange`, `fc.field.intrange`, `fc.field.multistring`, `fc.field.filterComplex`, `fc.scanner.barcode`, `fc.scanner.camera`, `fc.scanner.barcodeFilter`
991
+
992
+ **OMS:** `fc.events.search`, `fc.activity.entity`, `fc.event.detail`, `fc.order.itemDetails`, `fc.order.shipmentDetails`, `fc.return.rowExpansion`
993
+
994
+ **Store:** `fc.buttons.add.reject`, `fc.action.field.wavepick`, `fc.action.field.fulfilmentpack`, `fc.action.field.multiparcel`, `fc.action.field.returnitems`
995
+
996
+ **Analytics:** `fc.analytics.viz` (Looker embed)
997
+
998
+ **Legacy aliases** (v1 → v2): `shared.components.material.DynamicPage` → `fc.page`, `shared.components.material.DynamicList` → `fc.list`, `shared.components.material.DynamicCard` → `fc.card.attribute`, `shared.components.material.MaterialTabsSet` → `fc.tabs`
999
+
1000
+ ## Template String Reference
1001
+
1002
+ Mystique uses Handlebars-based template strings throughout manifests. **Only use documented helpers — guessing undocumented helpers produces broken manifests.**
1003
+
1004
+ ### Field Access
1005
+
1006
+ | Expression | Purpose | Example |
1007
+ |-----------|---------|---------|
1008
+ | `{{fieldName}}` | Direct field access | `{{ref}}`, `{{status}}` |
1009
+ | `{{nested.path}}` | Nested field access | `{{customer.firstName}}` |
1010
+ | `{{attributes.byName.X}}` | Attribute by name shorthand | `{{order.attributes.byName.vipStatus}}` |
1011
+ | `{{node.field}}` | List row data access | `{{node.ref}}` in fc.list attributes |
1012
+ | `{{edges.length}}` | Connection count | `{{orders.edges.length}}` |
1013
+
1014
+ ### Context Variables
1015
+
1016
+ | Expression | Purpose |
1017
+ |-----------|---------|
1018
+ | `{{activeUser.username}}` | Current user's username |
1019
+ | `{{activeUser.firstName}}` | Current user's first name |
1020
+ | `{{activeUser.timezone}}` | Current user's timezone |
1021
+ | `{{activeContext.id}}` | Current context ID (retailer or location) |
1022
+ | `{{activeContext.ref}}` | Current context ref |
1023
+ | `{{activeRetailer.id}}` | Current retailer ID |
1024
+ | `{{activeRetailer.ref}}` | Current retailer ref |
1025
+ | `{{activeLocation.id}}` | Current location ID |
1026
+ | `{{activeLocation.ref}}` | Current location ref |
1027
+ | `{{params.id}}` | Route parameter access |
1028
+ | `{{params.filter_status}}` | Query parameter access |
1029
+
1030
+ ### String Helpers
1031
+
1032
+ | Expression | Purpose | Example |
1033
+ |-----------|---------|---------|
1034
+ | `{{capitalise value}}` | Capitalize string | `{{capitalise status}}` |
1035
+ | `{{humanify value}}` | Human-readable (underscore/camel to spaced) | `{{humanify entityType}}` |
1036
+ | `{{toLowerCase value}}` | Lowercase | `{{toLowerCase order.type}}` |
1037
+ | `{{toUpperCase value}}` | Uppercase | `{{toUpperCase lastName}}` |
1038
+ | `{{concat a b separator='X'}}` | Concatenate with separator | `{{concat firstName lastName separator=' '}}` |
1039
+ | `{{stringify value}}` | JSON stringify | `{{stringify order.attributes.byName.payload}}` |
1040
+ | `{{preserveWhitespace text}}` | Preserve whitespace in rendering | `{{preserveWhitespace notes}}` |
1041
+ | `{{partialStringMatch a b}}` | Case-insensitive partial match | `{{partialStringMatch 'item1' 'eM1'}}` returns true |
1042
+ | `{{decodeUrlParams value}}` | URL decode | `{{decodeUrlParams 'TEST%3A%3AL%2FXL'}}` returns `TEST::L/XL` |
1043
+ | `{{{currentUrlParams}}}` | Inject current URL params (triple-brace, unescaped) | `"#/search?{{{currentUrlParams}}}"` |
1044
+ | `{{placeholderText text color='X' defaultValue='X'}}` | Styled fallback text | `{{placeholderText value defaultValue='-'}}` |
1045
+
1046
+ ### Date Helpers
1047
+
1048
+ **CRITICAL: Always use `_locale='en'` for dates in query variables.** Without it, user locale may produce non-ASCII digits (e.g., Arabic numerals) that break backend queries.
1049
+
1050
+ | Expression | Purpose | Example |
1051
+ |-----------|---------|---------|
1052
+ | `{{dateFormat date fmt}}` | Formatted date (display) | `{{dateFormat createdOn 'MMM DD, YYYY'}}` |
1053
+ | `{{dateFormat date fmt true _locale='en'}}` | **Backend-safe** formatted date | `{{dateFormat (dateAdd day=-1) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' true _locale='en'}}` |
1054
+ | `{{dateRelative date}}` | Relative time ("10 minutes ago") | `{{dateRelative order.expiryTime}}` |
1055
+ | `{{dateFormatByLocale date}}` | Locale-aware format (dd/mm or mm/dd) | `{{dateFormatByLocale order.createdOn}}` |
1056
+ | `{{dateAdd date day=N}}` | Date arithmetic (add) | `{{dateAdd order.createdOn day=3}}` |
1057
+ | `{{dateSubtract date day=N}}` | Date arithmetic (subtract) | `{{dateSubtract order.createdOn day=5}}` |
1058
+ | `{{dateStringFormatter (dateAdd day=N) fmt}}` | Date arithmetic + format | `{{dateStringFormatter (dateAdd day=-7) 'YYYY-MM-DD'}}` |
1059
+ | `{{placeholderDate date color='X' defaultValue='X'}}` | Date with styled fallback | `{{placeholderDate date defaultValue='[...]'}}` |
1060
+
1061
+ ### Number and Currency Helpers
1062
+
1063
+ | Expression | Purpose | Example |
1064
+ |-----------|---------|---------|
1065
+ | `{{currency value code}}` | Format as currency | `{{currency totalPrice 'AUD'}}` |
1066
+ | `{{currency value code _locale='X' _decimalPlaces=N}}` | Currency with locale control | `{{currency 123.87 'AUD' _locale='en-AU' _decimalPlaces=3}}` |
1067
+ | `{{add a b}}` | Addition | `{{add 1 2}}` returns 3 |
1068
+ | `{{subtract a b _decimalPlaces=N}}` | Subtraction | `{{subtract 50.32 20.11 _decimalPlaces=2}}` returns 30.21 |
1069
+ | `{{multiply a b}}` | Multiplication | `{{multiply 3 4}}` returns 12 |
1070
+ | `{{divide a b _decimalPlaces=N}}` | Division | `{{divide 12 4 _decimalPlaces=3}}` returns 3.000 |
1071
+ | `{{pow base exp}}` | Exponent | `{{pow 3 2}}` returns 9 |
1072
+
1073
+ ### Array Helpers
1074
+
1075
+ | Expression | Purpose | Example |
1076
+ |-----------|---------|---------|
1077
+ | `{{len array}}` | Array length | `{{len items.edges}}` |
1078
+ | `{{arraySum array 'path'}}` | Sum numeric field across array | `{{arraySum order.items 'node.totalPrice'}}` |
1079
+ | `{{placeholderArray arr separator='X' charCutoff=N}}` | Array display with truncation | `{{placeholderArray tags separator=', ' charCutoff=50}}` |
1080
+
1081
+ ### Logic Helpers
1082
+
1083
+ | Expression | Purpose | Example |
1084
+ |-----------|---------|---------|
1085
+ | `{{eq a b}}` | Equality | `{{eq status 'ACTIVE'}}` |
1086
+ | `{{ne a b}}` | Not equal | `{{ne status 'CANCELLED'}}` |
1087
+ | `{{gt a b}}` | Greater than | `{{gt totalPrice 0}}` |
1088
+ | `{{lt a b}}` | Less than | `{{lt items.count 3}}` |
1089
+ | `{{gte a b}}` | Greater or equal | `{{gte items.count 5}}` |
1090
+ | `{{lte a b}}` | Less or equal | `{{lte items.count 10}}` |
1091
+ | `{{and a b}}` | Logical AND | `{{and (eq status 'BOOKED') (gt items.count 0)}}` |
1092
+ | `{{or a b}}` | Logical OR | `{{or order.status order.items.count}}` |
1093
+ | `{{in value ...options}}` | Inclusion check | `{{in type 'HD' 'CC' 'MULTI'}}` |
1094
+ | `{{switch val k1 v1 k2 v2 default}}` | Switch expression | `{{switch status 'COMPLETE' 'green' 'red'}}` |
1095
+ | `{{hasRole 'ROLE'}}` | Role check | `{{hasRole 'admin'}}` |
1096
+ | `{{firstDefinedValue a b c}}` | First non-null value | `{{firstDefinedValue null undefined 10}}` returns 10 |
1097
+ | `{{anyMatch 'path' 'val' 'op'}}` | Array element match | `{{anyMatch 'node.entity.field' 'delta' 'eq'}}` |
1098
+
1099
+ ### i18n Helpers
1100
+
1101
+ | Expression | Purpose | Example |
1102
+ |-----------|---------|---------|
1103
+ | `{{i18n 'key'}}` | Translate key | `{{i18n 'fc.om.orders.title'}}` |
1104
+ | `{{i18n 'prefix' value _fallback=val}}` | Dynamic key with fallback | `{{i18n 'fc.om.order.type.' node.type _fallback=node.type}}` |
1105
+ | `{{i18n 'a' '.' 'b'}}` | Concatenated key | `{{i18n 'fc.om.order' '.' 'status'}}` |
1106
+
1107
+ ### Special Helpers
1108
+
1109
+ | Expression | Purpose | Example |
1110
+ |-----------|---------|---------|
1111
+ | `{{barcode value format}}` | Generate barcode SVG | `{{barcode '1234 abc' 'CODE128B'}}` |
1112
+ | `{{barcode value}}` | Barcode with auto-detected format | `{{barcode fulfilmentById.ref}}` |
1113
+ | `{{barcode value format false}}` | Barcode without text below | `{{barcode '1234' 'EAN-13' false}}` |
1114
+
1115
+ Supported barcode formats: CODE128, CODE128A, CODE128B, CODE128C, EAN-13, EAN-8, EAN-5, EAN-2, UPC-A, UPC-E, CODE39, ITF, ITF-14, MSI, MSI10, MSI11, MSI1010, MSI1110, Pharmacode, Codabar.
1116
+
1117
+ ## Dynamic Attribute Types
1118
+
1119
+ Dynamic attributes are configurable value displays used in `fc.list` columns, `fc.card.attribute` rows, and similar components. Three types: **standard** (text), **image**, and **component**.
1120
+
1121
+ ### Standard (Text) with Conditional Styling
1122
+
1123
+ ```json
1124
+ {
1125
+ "label": "i18n:fc.om.orders.column.status",
1126
+ "value": "{{status}}",
1127
+ "link": "#/order/{{id}}",
1128
+ "condition": "{{and id}}",
1129
+ "enableCopyIcon": "displayOnHover",
1130
+ "options": {
1131
+ "styles": [
1132
+ {
1133
+ "value": "{{status}}",
1134
+ "matches": ["BOOKED", "CONFIRMED"],
1135
+ "icon": { "name": "MdCheckCircle", "colour": "primary" },
1136
+ "text": { "colour": "green" }
1137
+ },
1138
+ {
1139
+ "matches": ["CANCELLED"],
1140
+ "icon": { "name": "MdCancel", "colour": "error" },
1141
+ "text": { "colour": "red" }
1142
+ },
1143
+ { "icon": { "name": "MdInfo" } }
1144
+ ]
1145
+ }
1146
+ }
1147
+ ```
1148
+
1149
+ Style matching: entries with `matches` checked in order — first match wins. Entry without `matches` is the default. Icon `name` must include library prefix: `Md*` (Material Design) or `Fa*` (Font Awesome).
1150
+
1151
+ ### Image Attribute
1152
+
1153
+ ```json
1154
+ { "type": "image", "label": "Product", "value": "{{node.attributes.byName.imageUrl}}", "options": { "width": 50, "height": 50 } }
1155
+ ```
1156
+
1157
+ ### Component Attribute
1158
+
1159
+ Embed another component inside an attribute slot:
1160
+
1161
+ ```json
1162
+ { "type": "component", "label": "Actions", "options": { "component": "fc.button.print.download", "props": { "label": "Print", "href": "api/v4/consignment/{{id}}/labelStream", "filename": "label.pdf" } } }
1163
+ ```
1164
+
1165
+ ## CardActions Pattern
1166
+
1167
+ CardActions configure action buttons on cards and list rows. Primary actions render as buttons; secondary actions group under "More".
1168
+
1169
+ ```json
1170
+ {
1171
+ "cardActions": {
1172
+ "primary": [{ "type": "userAction", "name": "ConfirmShipment", "label": "i18n:fc.action.confirm" }],
1173
+ "secondary": [{ "type": "userAction", "name": "CancelOrder", "condition": "{{ne status 'SHIPPED'}}" }],
1174
+ "showAtLeast": 1
1175
+ }
1176
+ }
1177
+ ```
1178
+
1179
+ - `type: "userAction"` triggers a workflow user action. `name` matches the ruleset name.
1180
+ - Workflow-defined user actions are **automatically appended** — only add to manifest when reordering or adding conditions.
1181
+ - `showAtLeast` — primary actions to keep visible on mobile (rest collapse into "More").
1182
+ - `condition` — template string; action hidden when evaluates to `"false"`.
1183
+
1184
+ ## Settings vs Manifest Decision Matrix
1185
+
1186
+ | Use **Settings** when | Use **Manifest** when |
1187
+ |---|---|
1188
+ | Config varies by retailer/location | Structure is stable across tenants |
1189
+ | Values change frequently | Routes, pages, component layout |
1190
+ | Non-developers need to update | Developers manage changes |
1191
+ | API keys, external URLs, thresholds | Static labels, i18n keys |
1192
+
1193
+ ## Troubleshooting
1194
+
1195
+ Common manifest issues and fixes:
1196
+
1197
+ 1. **Template not resolving** — Missing `{{ }}` syntax. `"value": "order.ref"` shows literal text. Fix: `"value": "{{order.ref}}"`
1198
+ 2. **Date query returns localized digits** — User locale produces Arabic/Thai numerals that break backend. Fix: Add `_locale='en'`: `{{dateFormat (dateSubtract day=1) 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' true _locale='en'}}`
1199
+ 3. **Condition not working** — String `"false"` is truthy. Fix: Use a template expression: `"condition": "{{and field1 field2}}"`
1200
+ 4. **Link not rendering** — The data value is null/undefined. Fix: Add `"condition": "{{and customer.id}}"` alongside the link
1201
+ 5. **Icon not showing** — Missing library prefix. `"CheckCircle"` is wrong. Fix: `"MdCheckCircle"` (Material Design) or `"FaCheck"` (Font Awesome)
1202
+ 6. **Debugging tips** — Check browser DevTools Network tab for actual GraphQL response shape. Use React DevTools to inspect component props. Validate JSON syntax with `jq .`
1203
+
1204
+ ## Output Locations
1205
+
1206
+ | Artifact | Path |
1207
+ |----------|------|
1208
+ | New manifest | `accounts/<PROFILE>/SOURCE/<sdk-project>/manifests/<name>.json` |
1209
+ | Fragment | `accounts/<PROFILE>/SOURCE/<sdk-project>/manifests/fragments/<name>.json` |
1210
+ | Standalone manifest (no SDK project) | `accounts/<PROFILE>/manifests/<name>.json` |
1211
+ | Build plan | `accounts/<PROFILE>/tasks/<YYYY-MM-DD>-mystique-builder-<slug>.md` |
1212
+
1213
+ ## Handoff Protocol
1214
+
1215
+ ### Signals emitted by this skill
1216
+
1217
+ | Signal | Condition | Example |
1218
+ |--------|-----------|---------|
1219
+ | `-> READY: <path>` | Manifest JSON created/modified | `-> READY: accounts/SAGIRISH/SOURCE/mystique-plugin/manifests/admin.json` |
1220
+ | `-> NEXT: /fluent-<skill>` | Always validate after build | `-> NEXT: /fluent-mystique-validate` |
1221
+ | `-> BLOCKED: <reason>` | Prerequisite missing | `-> BLOCKED: PREREQ_MISSING -- Custom component custom.order-tracker not registered. Run /fluent-mystique-scaffold` |
1222
+ | `-> SKIP: <reason>` | No manifest changes needed | `-> SKIP: No UI changes in this feature plan` |
1223
+
1224
+ ### Error codes
1225
+
1226
+ | Code | Condition | Recovery |
1227
+ |------|-----------|----------|
1228
+ | `PLAN_REQUIRED` | Multi-artifact work attempted without approved plan | Run `/fluent-feature-plan` first |
1229
+ | `PREREQ_MISSING` | Custom SDK components not scaffolded or plugins missing | Scaffold component via `/fluent-mystique-scaffold` |
1230
+ | `VALIDATION_FAILED` | Manifest JSON structure invalid after editing | Fix JSON structure per validation checklist |
1231
+
1232
+ ## Session Tracking
1233
+
1234
+ When invoked, log the following to the session tracking protocol (consumed by `/fluent-session-summary` and `/fluent-session-audit-export`):
1235
+
1236
+ **On entry:**
1237
+ ```json
1238
+ { "skill": "fluent-mystique-builder", "timestamp": "<ISO-8601>", "arguments": { "operation": "<op>", "manifest": "<file>", "page": "<path>" } }
1239
+ ```
1240
+
1241
+ **On exit:**
1242
+ ```json
1243
+ { "skill": "fluent-mystique-builder", "outcome": "<completed|failed|skipped>", "changesProduced": ["<seq-numbers>"], "toolCallsProduced": ["<seq-numbers>"], "nextRecommended": "/fluent-mystique-validate" }
1244
+ ```
1245
+
1246
+ ## Error Reporting
1247
+
1248
+ Errors from this skill follow the standard format:
1249
+
1250
+ | Field | Description |
1251
+ |-------|-------------|
1252
+ | `phase` | Skill phase where error occurred (e.g., `pre-flight`, `route-design`, `component-tree`, `query-generation`, `validation`) |
1253
+ | `severity` | `CRITICAL` (blocks downstream), `HIGH` (needs fix), `MEDIUM` (advisory), `LOW` (informational) |
1254
+ | `message` | Human-readable error description |
1255
+ | `resolution` | Suggested fix or downstream skill to invoke |
1256
+
1257
+ ## Validation Checklist
1258
+
1259
+ Run through this before considering any manifest complete:
1260
+
1261
+ 1. **JSON syntax** — Valid JSON (no trailing commas, correct brackets)
1262
+ 2. **manifestVersion** — Must be `"2.0"`
1263
+ 3. **Required fields** — `name`, `title`, `homePath`, `routes` all present
1264
+ 4. **homePath resolves** — Must match a defined route's `path`
1265
+ 5. **Route types valid** — Every route is `section`, `page`, or `reference`
1266
+ 6. **Page paths unique** — No duplicate `path` values across all pages
1267
+ 7. **Component aliases** — All `fc.*` aliases exist in the known registry; custom aliases have corresponding plugin
1268
+ 8. **dataSource paths** — JSONPath notation valid; references query result fields
1269
+ 9. **Template strings** — Balanced `{{`/`}}`, valid helper names
1270
+ 10. **Plugin URLs** — `src` is a valid URL (http/https for dev, https for production)
1271
+ 11. **Fragment settingName** — Follows `fc.mystique.manifest.*` convention
1272
+ 12. **Roles** — Role strings are valid and consistent across the manifest
1273
+ 13. **i18n keys** — Translatable strings use `i18n:` prefix consistently
1274
+
1275
+ ## Integration with Other Skills
1276
+
1277
+ | Task | Skill |
1278
+ |------|-------|
1279
+ | Validate manifest structure before deploy | `/fluent-mystique-validate` |
1280
+ | Scaffold custom SDK component project | `/fluent-mystique-scaffold` |
1281
+ | Analyze existing manifests for patterns | `/fluent-mystique-analyze` |
1282
+ | Deploy manifest as Fluent Setting | `/fluent-settings` |
1283
+ | Build workflows that power user actions | `/fluent-workflow-builder` |
1284
+ | Create feature plan covering UI work | `/fluent-feature-plan` |
1285
+ | Run pre-deploy checks including manifest validation | `/fluent-pre-deploy-check` |
1286
+
1287
+ ## Handoff
1288
+
1289
+ | Output Artifact | Consumed By | Path |
1290
+ |----------------|-------------|------|
1291
+ | Manifest JSON file | `/fluent-mystique-validate` (validation) | `accounts/<PROFILE>/SOURCE/<project>/manifests/<name>.json` |
1292
+ | Manifest JSON file | `/fluent-settings` (deploy as setting) | `accounts/<PROFILE>/SOURCE/<project>/manifests/<name>.json` |
1293
+ | Fragment JSON file | `/fluent-settings` (deploy as setting) | `accounts/<PROFILE>/SOURCE/<project>/manifests/fragments/<name>.json` |
1294
+ | Build plan | User review | `accounts/<PROFILE>/tasks/<YYYY-MM-DD>-mystique-builder-<slug>.md` |