---
name: fluent-module-deploy
description: Deploy Fluent Commerce modules (reference, extension, or data) to target retailers. Use when deploying, installing, or pushing modules to environments. Triggers on "deploy module", "install module", "push module", "module install".
user-invocable: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
argument-hint: <module-path> [--profile name] [--retailer ref]
cursor-tier: auto
planning-gate: mandatory
---

# Module Deployment

Deploy modules to Fluent Commerce environments with validation and verification.

## Pre-flight: Feedback Check

Before starting, check for past learnings from this skill:

1. Check if `accounts/<PROFILE>/feedback/fluent-module-deploy.jsonl` exists
2. If yes, read last 20 records
3. Filter to: FAILURE or USER_CORRECTED outcomes, confidence = CONFIRMED or PROMOTED
4. Extract top 3 relevant learnings — use as internal guidance during execution
5. Do NOT show raw feedback to the user — silently adjust approach based on past issues

## Planning Gate

### Pre-flight Chain (Three-Level)

Before deploying any module, run the three-level pre-flight check:

1. **Spec check:** If `accounts/<PROFILE>/features/<slug>/status.json` exists for this work:
   - Check `spec: "APPROVED"` — If missing, BLOCK: `→ BLOCKED: PREREQ_MISSING — Run /fluent-use-case-discover first`
   - Check `plan: "APPROVED"` — If missing, BLOCK: `→ BLOCKED: PLAN_REQUIRED — Run /fluent-feature-plan first`
   - If both approved, skip to implementation referencing the plan's §17 (Deployment) section
2. **Multi-artifact work** (deploy + rules + workflows + settings): STOP. Invoke `/fluent-feature-plan` first — this skill cannot be used for multi-artifact work without a feature plan
3. **Standalone deploy-only work** (no feature context, no multi-artifact):
   - Check `accounts/<PROFILE>/tasks/` for a task plan with `Status: APPROVED`
   - If approved task plan found, skip to implementation
   - If no plan, continue to the Planning Gate below to write a plan for this skill

**Before deploying any module, write a plan using the template from `PLAN_TEMPLATE.md` in the `fluent-feature-plan` skill.**

**Module-deploy specific emphasis — ensure these are covered:**

1. **Business Context (Section 1)** — what change this deployment delivers
2. **Scope (Section 2)** — module name, version, type (reference/extension/data)
3. **Impacted rulesets (Section 4.3)** — new rules being registered, existing rules being updated
4. **Settings (Section 4.4)** — settings required by new/changed rules
5. **Impacted workflows (Section 4.1)** — workflows that reference rules in this module
6. **Impacted retailers (Section 4.8)** — profile, retailer ref, environment (sandbox/UAT/prod)
7. **Deployment Sequence (Section 9)** — pre-checks (validation, build, tests) → deploy → verify
8. **Rollback Plan (Section 10)** — previous module version to restore if deployment fails

**Write the plan to:** `accounts/<PROFILE>/tasks/<YYYY-MM-DD>-module-deploy-<slug>.md`. Set `Status: PENDING`.

Present the full plan content to the user and wait for approval before running `fluent module install`. On approval, update the file to `Status: APPROVED`. If the user says "just do it", proceed directly (still write the file for audit trail).

## Pre-Flight: Pre-Deploy Check Gate

**This gate is MANDATORY. Do not skip.**

1. Search `accounts/<PROFILE>/reports/pre-deploy/` for `<MODULE>-<VERSION>.checklist.json`.
2. **If found and `overallResult == "BLOCKED"`**: **STOP. Do NOT proceed.** List all blocking gates from the report. Require explicit user override ("deploy anyway") to continue past a BLOCKED verdict.
3. **If found and `overallResult == "READY"`**: Proceed — pre-deploy validation passed.
4. **If no report found**: WARN the user: "No pre-deploy-check report found for this module version. Run `/fluent-pre-deploy-check` first for comprehensive validation." Only proceed after explicit user confirmation ("deploy without validation").

> **New to Fluent CLI?** Start with `/fluent-cli-index` for a complete command reference and routing guide.

## Handoff Protocol

### Signals emitted by this skill

| Signal | Condition | Example |
|--------|-----------|---------|
| `-> READY: <path>` | Module installed successfully | `-> READY: Module my-extensions v0.0.30 installed to MY_RETAILER (retailer 5)` |
| `-> NEXT: /fluent-<skill>` | Ready for workflow deploy or E2E test | `-> NEXT: /fluent-workflow-deploy` |
| `-> BLOCKED: <reason>` | Deployment blocked | `-> BLOCKED: DEPLOYMENT_BLOCKED — Pre-deploy check verdict is BLOCKED` |
| `-> SKIP: <reason>` | Module already at target version | `-> SKIP: Module already at v0.0.30 on retailer MY_RETAILER` |

### Error codes

| Code | Condition | Recovery |
|------|-----------|----------|
| `PLAN_REQUIRED` | Deployment attempted without approved plan | Run `/fluent-feature-plan` first |
| `DEPLOYMENT_BLOCKED` | Pre-deploy check report is BLOCKED | Run `/fluent-pre-deploy-check` and resolve gates |
| `PREREQ_MISSING` | No pre-deploy report found | Run `/fluent-pre-deploy-check` first |
| `ENV_UNREACHABLE` | Cannot connect to Fluent environment | Check credentials via `config.validate` |

## When to Use

- Deploying reference modules from Fluent repository
- Installing custom extension modules
- Promoting modules across environments (dev -> UAT -> prod)
- Installing data modules

## Prerequisites

- [ ] Active profile set (`fluent profile use <name>`)
- [ ] Module source available (name, URL, or local path)
- [ ] Dependencies installed
- [ ] Configuration values ready (if module requires config)
- [ ] Module validation passed (check `accounts/<PROFILE>/reports/module-validate/<name>.report.json` — `summary.status` should be `READY_FOR_BUILD`). Run `/fluent-module-validate` if report is missing or stale.

## Cross-Platform Command Notes

- Fluent CLI syntax is the same on macOS, Linux, and Windows.
- Prefer `rg` for placeholder scans (`rg` works in Bash, zsh, and PowerShell).
- For manifest existence checks, prefer reading `module.json` directly over shell-specific `ls` checks.

## Module Type Detection

Determine the module type before deploying -- each type has a different validation and deployment flow:

| Type | Detection | Deployment Flow |
|------|-----------|----------------|
| **Reference** | Name is `core`, `fulfilment`, `order`, `inventory`, or a URL source | describe -> check deps -> config -> install |
| **Extension** | Local path with `plugins/` directory | validate -> build -> package -> install from local ZIP |
| **Data** | Local path with `resources/assets/` but no `plugins/` | validate module.json -> install from local path |

**Reference module dependency order (mandatory):**

| # | Module | Dependencies |
|---|--------|-------------|
| 1 | `core` | None (install first) |
| 2 | `fulfilment` | core |
| 3 | `order` | core + fulfilment |
| 4 | `inventory` | core |
| 5 | Custom extensions | Varies (check module.json `dependencies`) |

Installing out of order will result in "dependency not installed" errors. Always verify dependencies via `fluent module list` before installing.

## Deployment Workflow

### Step 0: Verify CLI Available

```bash
fluent --version
```

If CLI is not found, **STOP**. The Fluent CLI is required for all module deployment operations (`fluent module install`, `fluent module list`, `fluent module config`). There is no MCP or REST API fallback for module deployment.

### Step 1: Validate Prerequisites

```bash
# Check active context
fluent profile active

# Expected output:
# Active profile: <profile-name>
# Active retailer: <retailer-ref>
```

### Step 1.5: Validate Module Structure (local modules only)

For local module paths (extension or data modules), run structural validation before deploying:

**1. Check validation report:**

Read `accounts/<PROFILE>/reports/module-validate/<module-name>.report.json` if it exists.

- If `summary.status == "READY_FOR_BUILD"` -- proceed
- If `summary.status == "NEEDS_FIXES"` -- **STOP**. List the FAIL items from the report. Run `/fluent-module-validate` to refresh if the report is stale.
- If report is missing -- **WARN** the user and recommend running `/fluent-module-validate` first. Ask for explicit confirmation before proceeding without validation.

**2. Inline minimum checks** (always, regardless of report):

- `resources/module.json` exists and parses as valid JSON
- Has `name` field (non-empty string)
- Has `version` field (matches semver `^\d+\.\d+\.\d+(-SNAPSHOT)?$`)
- For **extension modules**: `plugins/pom.xml` exists, version in module.json matches parent POM version
- For **data modules**: `resources/assets/` directory exists with at least one asset folder

### Step 2: Verify Module

**For reference modules:**
```bash
fluent module describe <module-name>
```

**For local modules:**
- Read `<module-path>/resources/module.json` directly and confirm it parses as JSON.
- Confirm the module type matches expectations (extension has `plugins/`, data has `resources/assets/`).

### Step 3: Check Dependencies (Mandatory Pre-flight)

```bash
# List installed modules — MUST run before any install
fluent module list -p <profile-name>

# Compare with module.json dependencies
```

**FAIL FAST:** If dependencies are missing, STOP immediately. Do not attempt to install the module — it will fail mid-operation. Install dependencies in the correct order first (see dependency order table above).

If dependencies missing: install them first!

**For reference modules**, enforce the dependency order:
- Before `fulfilment`: verify `core` is installed
- Before `order`: verify `core` AND `fulfilment` are installed
- Before `inventory`: verify `core` is installed
- Before custom extensions: verify all declared dependencies from `module.json` are installed

### Step 4: Generate Configuration (if needed)

```bash
fluent module config <module-source> \
  --retailer <retailer-ref> \
  --profile <profile-name>
```

Output: `module.config.<retailer>.<module-name>.json`

**Check for unreplaced tokens:**
```bash
rg '\[\[.*\]\]' module.config.*.json
```

```powershell
# PowerShell
Select-String -Path "module.config.*.json" -Pattern '\[\[.*\]\]'
```

### Step 5: Install Module

**Basic installation:**
```bash
fluent module install <module-source> \
  --retailer <retailer-ref> \
  --profile <profile-name>
```

**With configuration:**
```bash
fluent module install <module-source> \
  --config module.config.<retailer>.<module>.json \
  --retailer <retailer-ref> \
  --profile <profile-name>
```

**With filters:**
```bash
# Exclude workflows (useful for multi-retailer installs to avoid overwriting customizations)
fluent module install <module> \
  --exclude workflows \
  --retailer <retailer-ref> \
  --profile <profile-name>

# Exclude multiple asset types
fluent module install <module> \
  --exclude workflows \
  --exclude settings \
  --retailer <retailer-ref> \
  --profile <profile-name>

# Include only a specific workflow
fluent module install <module> \
  --include workflows/fc-workflow-order-hd.json \
  --retailer <retailer-ref> \
  --profile <profile-name>
```

Filter paths match the module ZIP structure. Common filterable assets:
- `workflows` — all workflow JSON files
- `settings` — all Fluent Settings
- `rules` — plugin JARs
- `workflows/<specific-file>.json` — single workflow

**Force reinstall:**
```bash
fluent module install <module> --force \
  --retailer <retailer-ref> \
  --profile <profile-name>
```

### Step 6: Verify Installation

```bash
# Check module installed
fluent module list -p <profile-name>

# Check workflows deployed
fluent workflow list -p <profile-name> -r <retailer-ref>
```

## Common Module Sources

| Type | Example |
|------|---------|
| Reference | `core`, `order`, `fulfilment`, `inventory` |
| URL | `https://downloads.fluentcommerce.com/v1.0.0/modules/fcx/b2c-sample-data/latest` |
| Local | `dist/my-module-1.0.0/`, `./my-extension.zip` |

## Error Handling

| Error | Solution |
|-------|----------|
| No active profile | `fluent profile use <name> --retailer <ref>` |
| Module not found | Check spelling, verify path exists |
| Dependencies not met | Install required modules first |
| Config missing keys | Edit config file, add missing values |
| Module already installed | Use `--force` to reinstall |
| Workflow conflicts | Use `--exclude workflows` |

## Version Bumping (Custom Modules)

**Any content change requires a version bump** — Java code, workflow JSONs, settings, or resources. Deploying the same version number results in `"already uploaded: Active - nothing more to do"` for the plugin JAR, and makes it impossible to track which version introduced a change.

### When to Bump

| Changed | Bump Required? | Reason |
|---------|---------------|--------|
| Java rule code | **YES** | JAR filename includes version; CLI skips upload if same version exists |
| Workflow JSON in `resources/workflows/` | **YES** | Module version is the audit trail for what's deployed |
| Settings in `resources/assets/settings/` | **YES** | Traceability — which version introduced the setting |
| Any file in `resources/` | **YES** | Everything is packaged into the versioned ZIP |
| Re-deploy unchanged content | NO | Use `--force` flag if re-install is truly needed |

### Files to Keep in Sync

For **extension modules** (with Java plugins), bump ALL of these:

1. `resources/module.json` (`"version"` field)
2. Parent `plugins/pom.xml` (top-level `<version>`)
3. Each child module `pom.xml` (`types/`, `util/`, `rules/`)

**All must match.** The CLI uses `module.json` version for the module-level check, while Maven uses the POM version for the JAR filename (`fc-module-<name>-<VERSION>.jar`). A mismatch causes the JAR upload to be silently skipped while the module version appears bumped.

For **data modules** (no plugins), only `resources/module.json` needs bumping.

### Bump Procedure

```bash
# 1. Bump module.json (edit manually or via script)
# 2. Bump all POM files in one command:
cd plugins/ && mvn versions:set -DnewVersion=<NEW_VERSION> -DgenerateBackupPoms=false

# 3. Verify no files still reference the old version:
grep -r "<OLD_VERSION>" plugins/*/pom.xml resources/module.json
```

After bumping, rebuild with Maven and repackage via the module build script.

## Config Token Reference

Common `[[placeholder]]` tokens in module config files:

| Token | Description | Example |
|-------|-------------|---------|
| `[[retailer.id]]` | Target retailer's numeric ID | `5` |
| `[[productCatalogue.ref]]` | Product catalogue reference | `PC:MASTER:5` |
| `[[network.ref.hd]]` | HD fulfilment network | `B2C_HD_1` |
| `[[network.ref.cc]]` | CC fulfilment network | `B2C_CC_1` |
| `[[virtualCatalogue.ref]]` | Virtual catalogue | `BASE:1` |
| `[[inventoryCatalogue.ref]]` | Inventory catalogue | `DEFAULT:1` |
| `[[order.validation.gracePeriodSeconds]]` | Grace period timeout | `3600` |

**Check for unreplaced tokens before deploying:**
```bash
rg '\[\[.*\]\]' module.config.*.json
```

```powershell
# PowerShell
Select-String -Path "module.config.*.json" -Pattern '\[\[.*\]\]'
```

## Config Prefix System

Module config files use a prefix system to scope variable values:

| Prefix | Applies To | Example |
|--------|-----------|---------|
| `default:` | All assets (workflows, settings, data) | `"default:carrier.ref": "CARRIER_STD"` |
| `workflow:<type>:<subtype>:` | Specific workflow only | `"workflow:order:hd:network.ref": "NET_HD"` |
| `setting:` | Settings only | `"setting:mySettingKey": "value"` |

**Specificity rules:** More colons = higher specificity = wins. For `ORDER::HD`, the key `workflow:order:hd:network.ref` (2 levels) overrides `workflow:order:network.ref` (1 level) which overrides `default:network.ref` (0 levels).

**Built-in auto-injected variables** (no config needed): `account.id`, `retailer.id`, `retailer.ref`, `retailer.name`.

**Unresolved tokens** are left as-is -- the CLI does not error on missing variable values during substitution. Always scan for `[[...]]` tokens before deploying.

## Post-Deploy: Workflow Deployment

After deploying a module that includes new custom rules, workflows referencing those rules can now be deployed:

1. Use `/fluent-workflow-deploy` to upload individual workflow definitions
2. Or include workflows in the module itself (`resources/assets/workflows/`)
3. Verify rules are registered: `plugin.list({ name: "<RuleName>" })`

**Deployment order:** Module (registers rules) -> Workflows (reference rules) -> Settings (referenced by rules) -> E2E test

## Session Tracking

When invoked, log the following to the session tracking protocol (consumed by `/fluent-session`):

**On entry:**
```json
{ "skill": "<this-skill-name>", "timestamp": "<ISO-8601>", "arguments": { "<key>": "<value>", "...": "..." } }
```

**On exit:**
```json
{ "skill": "<this-skill-name>", "outcome": "<completed|failed|skipped>", "changesProduced": ["<seq-numbers>"], "toolCallsProduced": ["<seq-numbers>"], "nextRecommended": "<from-handoff-section>" }
```

All MCP tool calls made during execution should include `"skill": "<this-skill-name>"` in their tracking record for attribution. The `arguments` object should capture the key parameters actually passed (profile, retailer, entity type, etc.) — not a fixed schema. The `nextRecommended` value comes from the Handoff section below.

### Update status.json gates

If a feature context exists (`accounts/<PROFILE>/features/<slug>/status.json`):
- On **successful deploy**: set `deploy: "DEPLOYED"` and advance `status` to `"DEPLOYED"` (if currently `IN_PROGRESS`, `BUILT`, or `APPROVED`)
- On **deploy failure**: set `deploy: "FAILED"` (do NOT advance `status`)
- Always update `updated` timestamp

## Best Practices

1. **Always verify active profile** before installing
2. **Verify CLI is available** (`fluent --version`) before any deployment
3. **Test in dev** before promoting to production
4. **Use `--exclude workflows`** for multi-retailer installations
5. **Generate fresh config** for each environment
6. **Check dependencies** before installation (especially reference module order)
7. **Bump version** for every custom module redeploy
8. **Check for unreplaced `[[...]]` tokens** before deploying config
9. **Run `/fluent-module-validate`** before deploying local modules
10. **Deploy module before workflows** if workflows reference new custom rules

## Rollback

To rollback a module deployment, redeploy the previous version of the module ZIP. The Fluent CLI does not have a built-in rollback command -- keep previous module ZIPs archived for recovery.

## Post-Execution: Feedback Capture

After completing this skill (whether success or failure), write a feedback record:

1. **Classify outcome:** `SUCCESS` (completed cleanly), `PARTIAL_SUCCESS` (completed after fixing issues), `FAILURE` (could not complete), `BLOCKED` (prereq missing), `USER_CORRECTED` (user overrode approach)
2. **Build record:** Create a `feedback-record-v1` JSON object with:
   - `skill`: `"fluent-module-deploy"`
   - `feature`: feature slug if applicable, null otherwise
   - `phases`: trace of each phase with PASS/FAIL/SKIP
   - `learnings`: any gotchas discovered during execution
   - `userCorrections`: any corrections the user provided
3. **Redact secrets:** Strip tokens, passwords, API keys. Keep business identifiers.
4. **Append** one JSON line to `accounts/<PROFILE>/feedback/fluent-module-deploy.jsonl`
5. **Update** `accounts/<PROFILE>/feedback/index.json` counters (create with `mkdir -p` if missing)

**Skip capture if:** The execution was trivially simple (e.g., user asked a question, no actual write operation happened).

## Known Pitfalls
<!-- feedback-promoted: managed section, updated by feedback loop -->

_(No promoted learnings yet.)_

<!-- end feedback-promoted -->
