@atlashub/smartstack-cli 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -12,9 +12,48 @@ Safe PR merge with review checklist. Supports GitHub and Azure DevOps.
12
12
 
13
13
  ## EXECUTION SEQUENCE
14
14
 
15
- ### 1. Detect Provider and Load PR Context
15
+ ### 1. Load GitFlow Config (MANDATORY)
16
16
 
17
17
  ```bash
18
+ # Find and verify config exists
19
+ CONFIG_FILE=""
20
+ if [ -f ".claude/gitflow/config.json" ]; then
21
+ CONFIG_FILE=".claude/gitflow/config.json"
22
+ elif [ -f ".gitflow/config.json" ]; then
23
+ CONFIG_FILE=".gitflow/config.json"
24
+ else
25
+ # Try develop worktree
26
+ DEVELOP_PATH=$(git worktree list --porcelain 2>/dev/null | grep -A1 "branch refs/heads/develop" | grep "^worktree " | sed 's/^worktree //')
27
+ if [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.claude/gitflow/config.json" ]; then
28
+ CONFIG_FILE="$DEVELOP_PATH/.claude/gitflow/config.json"
29
+ elif [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.gitflow/config.json" ]; then
30
+ CONFIG_FILE="$DEVELOP_PATH/.gitflow/config.json"
31
+ fi
32
+ fi
33
+
34
+ # CRITICAL: Block if no config
35
+ if [ -z "$CONFIG_FILE" ] || [ ! -f "$CONFIG_FILE" ]; then
36
+ echo "❌ GitFlow config not found"
37
+ echo "→ Run: /gitflow init"
38
+ exit 1
39
+ fi
40
+
41
+ # Extract config values
42
+ GF_PROVIDER=$(grep -oP '"provider":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
43
+ GF_MAIN_BRANCH=$(grep -oP '"main":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
44
+ GF_DEVELOP_BRANCH=$(grep -oP '"develop":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
45
+
46
+ # Validate required values
47
+ if [ -z "$GF_MAIN_BRANCH" ] || [ -z "$GF_DEVELOP_BRANCH" ]; then
48
+ echo "❌ Invalid GitFlow config (missing branches)"
49
+ exit 1
50
+ fi
51
+ ```
52
+
53
+ ### 2. Detect Provider and Load PR Context
54
+
55
+ ```bash
56
+ # Detect provider
18
57
  REMOTE_URL=$(git remote get-url origin 2>/dev/null)
19
58
  if [[ "$REMOTE_URL" == *"dev.azure.com"* ]]; then
20
59
  GIT_PROVIDER="azuredevops"
@@ -25,12 +64,23 @@ fi
25
64
  CURRENT=$(git rev-parse --abbrev-ref HEAD)
26
65
  BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
27
66
 
67
+ # Determine target branch (using config values)
68
+ case "$BRANCH_TYPE" in
69
+ feature) TARGET="$GF_DEVELOP_BRANCH" ;;
70
+ release) TARGET="$GF_MAIN_BRANCH" ;;
71
+ hotfix) TARGET="$GF_MAIN_BRANCH" ;;
72
+ *) TARGET="$GF_DEVELOP_BRANCH" ;;
73
+ esac
74
+
75
+ echo "✓ Config loaded: $CONFIG_FILE"
76
+ echo "✓ Branch: $CURRENT → Target: $TARGET"
77
+
28
78
  # Find PR for current branch
29
79
  # GitHub: gh pr list --head "$CURRENT"
30
80
  # Azure DevOps: az repos pr list --source-branch "$CURRENT"
31
81
  ```
32
82
 
33
- ### 2. Determine Merge Strategy
83
+ ### 3. Determine Merge Strategy
34
84
 
35
85
  | Type | Strategy | Delete Branch |
36
86
  |------|----------|---------------|
@@ -38,7 +88,7 @@ BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
38
88
  | Release | merge --no-ff | yes |
39
89
  | Hotfix | merge --no-ff | yes |
40
90
 
41
- ### 3. Review Checklist
91
+ ### 4. Review Checklist
42
92
 
43
93
  Display review checklist:
44
94
  - Build status (pass/fail)
@@ -47,7 +97,7 @@ Display review checklist:
47
97
  - Conflicts (none/detected)
48
98
  - CI status (if available)
49
99
 
50
- ### 4. Execute Merge
100
+ ### 5. Execute Merge
51
101
 
52
102
  **GitHub:**
53
103
  ```bash
@@ -67,7 +117,7 @@ az repos pr update --id $PR_ID --status completed --squash true --delete-source-
67
117
  az repos pr update --id $PR_ID --status completed --squash false --delete-source-branch true
68
118
  ```
69
119
 
70
- ### 5. Verify Merge
120
+ ### 6. Verify Merge
71
121
 
72
122
  ```bash
73
123
  # Force update tracking refs
@@ -78,7 +128,7 @@ git fetch origin --prune --quiet
78
128
  MERGED=$(git branch -r --merged origin/$TARGET | grep "$CURRENT" | wc -l)
79
129
  ```
80
130
 
81
- ### 6. Summary
131
+ ### 7. Summary
82
132
 
83
133
  ```
84
134
  PR MERGED
@@ -14,21 +14,68 @@ Create PR with auto-generated description, Azure DevOps and GitHub support.
14
14
 
15
15
  ## EXECUTION SEQUENCE
16
16
 
17
- ### 1. Detect Provider and Branch Context
17
+ ### 1. Load GitFlow Config (MANDATORY)
18
18
 
19
19
  ```bash
20
- # Detect provider from remote URL
21
- REMOTE_URL=$(git remote get-url origin 2>/dev/null)
22
- if [[ "$REMOTE_URL" == *"dev.azure.com"* ]] || [[ "$REMOTE_URL" == *"visualstudio.com"* ]]; then
23
- GIT_PROVIDER="azuredevops"
24
- # Extract Azure DevOps details
20
+ # Find and verify config exists
21
+ CONFIG_FILE=""
22
+ if [ -f ".claude/gitflow/config.json" ]; then
23
+ CONFIG_FILE=".claude/gitflow/config.json"
24
+ elif [ -f ".gitflow/config.json" ]; then
25
+ CONFIG_FILE=".gitflow/config.json"
26
+ else
27
+ # Try develop worktree
28
+ DEVELOP_PATH=$(git worktree list --porcelain 2>/dev/null | grep -A1 "branch refs/heads/develop" | grep "^worktree " | sed 's/^worktree //')
29
+ if [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.claude/gitflow/config.json" ]; then
30
+ CONFIG_FILE="$DEVELOP_PATH/.claude/gitflow/config.json"
31
+ elif [ -n "$DEVELOP_PATH" ] && [ -f "$DEVELOP_PATH/.gitflow/config.json" ]; then
32
+ CONFIG_FILE="$DEVELOP_PATH/.gitflow/config.json"
33
+ fi
34
+ fi
35
+
36
+ # CRITICAL: Block if no config
37
+ if [ -z "$CONFIG_FILE" ] || [ ! -f "$CONFIG_FILE" ]; then
38
+ echo "❌ GitFlow config not found"
39
+ echo "→ Run: /gitflow init"
40
+ exit 1
41
+ fi
42
+
43
+ # Extract config values
44
+ GF_PROVIDER=$(grep -oP '"provider":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
45
+ GF_MAIN_BRANCH=$(grep -oP '"main":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
46
+ GF_DEVELOP_BRANCH=$(grep -oP '"develop":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
47
+ GF_REMOTE_URL=$(grep -oP '"remoteUrl":\s*"\K[^"]+' "$CONFIG_FILE" | head -1)
48
+
49
+ # Validate required values
50
+ if [ -z "$GF_MAIN_BRANCH" ] || [ -z "$GF_DEVELOP_BRANCH" ]; then
51
+ echo "❌ Invalid GitFlow config (missing branches)"
52
+ exit 1
53
+ fi
54
+ ```
55
+
56
+ ### 2. Detect Provider and Branch Context
57
+
58
+ ```bash
59
+ # Detect provider from config or remote URL
60
+ if [ -z "$GF_PROVIDER" ]; then
61
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
62
+ if [[ "$REMOTE_URL" == *"dev.azure.com"* ]] || [[ "$REMOTE_URL" == *"visualstudio.com"* ]]; then
63
+ GIT_PROVIDER="azuredevops"
64
+ else
65
+ GIT_PROVIDER="github"
66
+ fi
67
+ else
68
+ GIT_PROVIDER="$GF_PROVIDER"
69
+ fi
70
+
71
+ # Extract Azure DevOps details if needed
72
+ if [ "$GIT_PROVIDER" = "azuredevops" ]; then
73
+ REMOTE_URL="${GF_REMOTE_URL:-$(git remote get-url origin 2>/dev/null)}"
25
74
  [[ "$REMOTE_URL" =~ dev\.azure\.com/([^/]+)/([^/]+)/_git/([^/]+) ]] && {
26
75
  AZURE_ORG="${BASH_REMATCH[1]}"
27
76
  AZURE_PROJECT="${BASH_REMATCH[2]}"
28
77
  AZURE_REPO="${BASH_REMATCH[3]}"
29
78
  }
30
- else
31
- GIT_PROVIDER="github"
32
79
  fi
33
80
 
34
81
  # Get current branch (worktree-compatible)
@@ -37,7 +84,21 @@ BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
37
84
  BRANCH_NAME=$(echo $CURRENT | sed 's/.*\///')
38
85
  ```
39
86
 
40
- ### 2. Determine Target Branch
87
+ ### 3. Determine Target Branch
88
+
89
+ ```bash
90
+ # Map branch type to target branch (using config values)
91
+ case "$BRANCH_TYPE" in
92
+ feature) TARGET="$GF_DEVELOP_BRANCH" ;;
93
+ release) TARGET="$GF_MAIN_BRANCH" ;;
94
+ hotfix) TARGET="$GF_MAIN_BRANCH" ;;
95
+ *) TARGET="$GF_DEVELOP_BRANCH" ;;
96
+ esac
97
+
98
+ echo "✓ Config loaded: $CONFIG_FILE"
99
+ echo "✓ Branch: $CURRENT (type: $BRANCH_TYPE)"
100
+ echo "✓ Target: $TARGET"
101
+ ```
41
102
 
42
103
  | Type | Target | Reason |
43
104
  |------|--------|--------|
@@ -8,7 +8,6 @@
8
8
  "EnableDevSeeding": false
9
9
  },
10
10
  "Jwt": {
11
- "Secret": "{{GenerateRandomSecret}}",
12
11
  "Issuer": "{{ProjectName}}",
13
12
  "Audience": "{{ProjectName}}",
14
13
  "AccessTokenExpirationMinutes": 60,
@@ -132,6 +131,10 @@
132
131
  "SenderAddress": ""
133
132
  }
134
133
  },
134
+ "Zefix": {
135
+ "Username": "",
136
+ "Password": ""
137
+ },
135
138
  "Entra": {
136
139
  "TenantId": "",
137
140
  "ClientId": "",
@@ -140,10 +143,13 @@
140
143
  "SyncIntervalMinutes": 60,
141
144
  "UseDeltaSync": true,
142
145
  "DefaultConflictResolution": "ManualReview",
143
- "AutoCreateReferences": true
146
+ "AutoCreateReferences": true,
147
+ "BackgroundSyncEnabled": false,
148
+ "BackgroundSyncIntervalMinutes": 60
144
149
  },
145
150
  "MultiTenant": {
146
151
  "Enabled": true,
152
+ "EnableB2B": true,
147
153
  "EnableB2C": true,
148
154
  "SystemTenantSlug": "default",
149
155
  "SystemTenantName": "Default Workspace",
@@ -1406,33 +1406,7 @@
1406
1406
  DATA STORE
1407
1407
  ============================================ */
1408
1408
  const APP_KEY = 'ba-{{APPLICATION_ID}}';
1409
- let data = {
1410
- metadata: {
1411
- applicationName: '{{APPLICATION_NAME}}',
1412
- applicationId: '{{APPLICATION_ID}}',
1413
- version: '{{VERSION}}',
1414
- createdAt: '{{CREATED_AT}}',
1415
- lastModified: new Date().toISOString()
1416
- },
1417
- cadrage: {
1418
- problem: {},
1419
- current: {},
1420
- vision: {},
1421
- stakeholders: [],
1422
- scope: { vital: [], important: [], optional: [], excluded: [] },
1423
- risks: [],
1424
- assumptions: '',
1425
- success: {}
1426
- },
1427
- modules: [],
1428
- dependencies: [],
1429
- moduleSpecs: {},
1430
- consolidation: {
1431
- interactions: [],
1432
- e2eFlows: []
1433
- },
1434
- handoff: {}
1435
- };
1409
+ let data = {{FEATURE_DATA}};
1436
1410
 
1437
1411
  /* ============================================
1438
1412
  INITIALIZATION
@@ -1263,6 +1263,62 @@ ba-writer.updateStatus({module_feature_id}, "specified")
1263
1263
  ba-writer.updateModuleStatus({feature_id}, {currentModule.code}, "specified")
1264
1264
  ```
1265
1265
 
1266
+ ### 11-bis. Deploy Incremental Interactive HTML (MANDATORY)
1267
+
1268
+ > **After each module is specified, deploy/update the interactive HTML document with all available data.**
1269
+ > This allows the client to review completed modules while the next module is being specified.
1270
+ > The HTML is incrementally enriched: after module 1, only module 1 specs appear; after module 2, both appear; etc.
1271
+
1272
+ **Source:** `html/ba-interactive.html` (relative to skill root = `~/.claude/skills/business-analyse/html/`)
1273
+
1274
+ **Destination:** `docs/business/{app}/business-analyse/v{version}/ba-interactive.html`
1275
+
1276
+ **Steps:**
1277
+
1278
+ 1. **Read sources:**
1279
+ - Read the HTML template from skill directory
1280
+ - Read the master feature.json (application level — now updated with current module status)
1281
+ - Read EACH completed module's feature.json (including the one just specified)
1282
+
1283
+ 2. **Build FEATURE_DATA object:**
1284
+
1285
+ > **Use the EXACT SAME mapping defined in step-05-handoff.md section 9d.**
1286
+ > The mapping is identical — the only difference is that `moduleSpecs` only includes completed modules.
1287
+ > Modules not yet specified will NOT appear in `moduleSpecs` (their entry in `modules[]` will show `status: "pending"`).
1288
+
1289
+ Follow step-05 section 9d "Step 2: Build FEATURE_DATA object" for the complete mapping pseudocode:
1290
+ - `metadata`, `cadrage`, `modules[]`, `dependencies[]` → from master feature.json
1291
+ - `moduleSpecs[moduleCode]` → only for modules with status "specified" (completed so far)
1292
+ - `consolidation` → empty `{ interactions: [], e2eFlows: [] }` (not yet consolidated)
1293
+ - `handoff` → empty `{}` (not yet handed off)
1294
+
1295
+ 3. **Replace placeholders in template:**
1296
+ - Serialize the FEATURE_DATA object as JSON (2-space indentation)
1297
+ - Replace `{{FEATURE_DATA}}` with the serialized JSON
1298
+ - Replace `{{APPLICATION_NAME}}` → `{application_name}`
1299
+ - Replace `{{APPLICATION_ID}}` → `{feature_id}`
1300
+ - Replace `{{VERSION}}` → `{version}`
1301
+ - Replace `{{CREATED_AT}}` → `{ISO timestamp}`
1302
+
1303
+ 4. **Write and confirm:**
1304
+
1305
+ ```
1306
+ ✓ Interactive HTML updated (incremental):
1307
+ Path: docs/business/{app}/business-analyse/v{version}/ba-interactive.html
1308
+ Modules included: {completedModules.length}/{totalModules} specified
1309
+ - {completedModule1}: {uc_count} UCs, {br_count} BRs, {entity_count} entities
1310
+ - {completedModule2}: ...
1311
+ Remaining: {pendingModules.join(', ')} (will be added after specification)
1312
+ → Client can open in browser to review completed modules now.
1313
+ ```
1314
+
1315
+ > **WHY incremental?** The client doesn't have to wait until handoff to start reviewing.
1316
+ > While module 2 is being specified, the client can already give feedback on module 1.
1317
+ > Each incremental deployment OVERWRITES the previous HTML (latest state always).
1318
+ > The FINAL deployment at step-05 (handoff) will include consolidation and handoff data.
1319
+
1320
+ ---
1321
+
1266
1322
  ### 12. Loop Decision
1267
1323
 
1268
1324
  ```
@@ -1338,6 +1394,7 @@ Read metadata.workflow.currentModule
1338
1394
  - Client confirmed module specification
1339
1395
  - Module feature.json written
1340
1396
  - Master updated with module status
1397
+ - **Interactive HTML deployed/updated with completed module data (incremental)**
1341
1398
  - Loop advanced correctly
1342
1399
 
1343
1400
  ## FAILURE MODES
@@ -917,8 +917,10 @@ Status journey: analyze → consolidate → **handed-off**
917
917
 
918
918
  ### 9d. Deploy Interactive HTML Document (MANDATORY)
919
919
 
920
- > **The interactive HTML document is deployed to the project for client-facing review.**
921
- > It allows clients to review, edit, and enrich the business analysis directly in their browser.
920
+ > **The interactive HTML document is deployed to the project PRE-POPULATED with ALL analysis data.**
921
+ > The client opens it in a browser and sees the complete analysis (cadrage, modules, entities, UCs, BRs, wireframes, permissions, consolidation).
922
+ > The client can then review, edit, enrich, and export modifications as JSON.
923
+ > That JSON can be re-imported via `/business-analyse -x` to update the feature.json.
922
924
 
923
925
  **Source:** `html/ba-interactive.html` (relative to skill root = `~/.claude/skills/business-analyse/html/`)
924
926
 
@@ -926,27 +928,223 @@ Status journey: analyze → consolidate → **handed-off**
926
928
 
927
929
  **Deployment steps:**
928
930
 
931
+ #### Step 1: Read source data
932
+
929
933
  1. Read the HTML template from skill directory
930
- 2. Replace template placeholders with actual values:
931
- - `{{APPLICATION_NAME}}` `{application_name}`
932
- - `{{APPLICATION_ID}}` → `{feature_id}`
933
- - `{{VERSION}}` `{version}`
934
- - `{{CREATED_AT}}` → `{ISO timestamp}`
935
- 3. Write the populated HTML to the output directory
936
- 4. Display deployment confirmation:
934
+ 2. Read the master feature.json (application level)
935
+ 3. Read EACH module feature.json (module level)
936
+
937
+ #### Step 2: Build FEATURE_DATA object
938
+
939
+ > **CRITICAL:** The HTML `data` object MUST be pre-populated with ALL analysis data.
940
+ > An empty data object is a **BUG** — the client would see a blank page.
941
+
942
+ Build a JSON object following this **exact mapping** from feature.json to the HTML data model:
943
+
944
+ ```javascript
945
+ {
946
+ metadata: {
947
+ applicationName: master.metadata.application, // e.g. "RH"
948
+ applicationId: master.id, // e.g. "FEAT-001"
949
+ version: master.version, // e.g. "1.0"
950
+ createdAt: master.metadata.createdAt,
951
+ lastModified: master.metadata.updatedAt
952
+ },
953
+ cadrage: {
954
+ problem: {
955
+ description: master.cadrage.problem, // string → problem.description
956
+ trigger: master.cadrage.trigger, // string → problem.trigger
957
+ impactedPeople: "", // not in feature.json, client fills
958
+ history: "",
959
+ consequences: ""
960
+ },
961
+ current: {
962
+ tools: master.cadrage.asIs, // string → current.tools
963
+ steps: [], // client can add process steps
964
+ painPoints: master.cadrage.stakeholders
965
+ .flatMap(s => s.painPoints || []).join("\n"), // aggregate all painPoints
966
+ errors: ""
967
+ },
968
+ vision: {
969
+ changes: master.cadrage.toBe, // string → vision.changes
970
+ results: master.cadrage.acceptanceCriteria
971
+ .map(ac => ac.criterion).join("\n"), // AC → results (one per line)
972
+ successSign: ""
973
+ },
974
+ stakeholders: master.cadrage.stakeholders.map(s => ({
975
+ role: s.role,
976
+ function: s.function || "",
977
+ tasks: s.tasks || [],
978
+ frequency: mapFrequency(s.frequency), // "Quotidien"→"daily", etc.
979
+ access: mapAccess(s.involvement), // "decision-maker"→"admin", "end-user"→"contributor"
980
+ frustrations: (s.painPoints || []).join("\n")
981
+ })),
982
+ scope: {
983
+ vital: (master.cadrage.globalScope.mustHave || [])
984
+ .map(item => ({ name: item, description: "" })), // string[] → {name,description}[]
985
+ important: (master.cadrage.globalScope.shouldHave || [])
986
+ .map(item => ({ name: item, description: "" })),
987
+ optional: (master.cadrage.globalScope.couldHave || [])
988
+ .map(item => ({ name: item, description: "" })),
989
+ excluded: (master.cadrage.globalScope.outOfScope || [])
990
+ .map(item => ({ name: item, description: "" }))
991
+ },
992
+ risks: (master.cadrage.risks || []).map(r => ({
993
+ description: r.description,
994
+ probability: r.probability, // "high" | "medium" | "low"
995
+ impact: r.impact,
996
+ mitigation: r.mitigation || ""
997
+ })),
998
+ assumptions: "",
999
+ success: {
1000
+ definition: (master.cadrage.acceptanceCriteria || [])
1001
+ .map(ac => ac.criterion).join("\n"),
1002
+ metrics: "",
1003
+ timeline: "",
1004
+ minimumConditions: ""
1005
+ }
1006
+ },
1007
+ modules: master.modules.map(m => ({
1008
+ code: m.code,
1009
+ name: m.code, // module code as name
1010
+ description: m.description || "",
1011
+ featureType: m.featureType || "data-centric",
1012
+ priority: m.priority || "must",
1013
+ entities: m.entities || [],
1014
+ status: m.status || "handed-off"
1015
+ })),
1016
+ dependencies: (master.dependencyGraph?.edges || []).map(e => ({
1017
+ from: e.from,
1018
+ to: e.to,
1019
+ description: e.description || ""
1020
+ })),
1021
+ moduleSpecs: {
1022
+ // FOR EACH module: read module feature.json, then map:
1023
+ // [moduleCode]: { useCases, businessRules, entities, permissions, notes, mockupNotes }
1024
+ },
1025
+ consolidation: {
1026
+ interactions: (master.consolidation?.crossModuleInteractions || []).map(i => ({
1027
+ from: i.fromModule,
1028
+ to: i.toModule,
1029
+ description: i.description || ""
1030
+ })),
1031
+ e2eFlows: (master.consolidation?.e2eFlows || []).map(f => ({
1032
+ name: f.name,
1033
+ steps: (f.steps || []).map(s => ({ module: s.module, action: s.action })),
1034
+ actors: (f.steps || []).map(s => s.permission).join(", ")
1035
+ }))
1036
+ },
1037
+ handoff: master.handoff || {}
1038
+ }
1039
+ ```
1040
+
1041
+ **Module specs mapping** — for EACH module in `master.modules[]`:
1042
+
1043
+ 1. Read the module feature.json at `master.modules[i].featureJsonPath`
1044
+ 2. Map to `moduleSpecs[moduleCode]`:
1045
+
1046
+ ```javascript
1047
+ moduleSpecs[moduleCode] = {
1048
+ useCases: (moduleFeature.specification?.useCases || []).map(uc => ({
1049
+ name: uc.name,
1050
+ actor: uc.primaryActor,
1051
+ steps: (uc.mainScenario || []).join("\n"), // array → newline-separated string
1052
+ alternative: (uc.alternativeScenarios || [])
1053
+ .map(a => a.name + ": " + (a.steps || []).join(", ")).join("\n")
1054
+ })),
1055
+ businessRules: (moduleFeature.analysis?.businessRules || []).map(br => ({
1056
+ name: br.name,
1057
+ category: br.category, // "validation"|"calculation"|"workflow"|"security"|"data"
1058
+ statement: br.statement,
1059
+ example: (br.examples || []).map(e => e.input + " → " + e.expected).join("; ")
1060
+ })),
1061
+ entities: (moduleFeature.analysis?.entities || []).map(ent => ({
1062
+ name: ent.name,
1063
+ description: ent.description || "",
1064
+ attributes: (ent.attributes || []).map(a => ({
1065
+ name: a.name,
1066
+ description: a.description || ""
1067
+ })),
1068
+ relationships: (ent.relationships || []).map(r =>
1069
+ r.target + " (" + r.type + ") - " + (r.description || "")
1070
+ )
1071
+ })),
1072
+ permissions: buildPermissionKeys(moduleFeature), // see below
1073
+ notes: "",
1074
+ mockupNotes: (moduleFeature.specification?.uiWireframes || [])
1075
+ .map(w => "[" + w.screen + "]\n" + w.mockup).join("\n\n")
1076
+ }
1077
+ ```
1078
+
1079
+ **Permission keys** — the HTML uses `"Role|Action"` format (e.g. `"RH Admin|Consulter"`):
1080
+
1081
+ ```javascript
1082
+ function buildPermissionKeys(moduleFeature) {
1083
+ const keys = [];
1084
+ const matrix = moduleFeature.specification?.permissionMatrix;
1085
+ if (!matrix) return keys;
1086
+ const actionMap = { read: "Consulter", create: "Creer", update: "Modifier",
1087
+ delete: "Supprimer", validate: "Valider", export: "Exporter",
1088
+ submit: "Valider", import: "Creer" };
1089
+ (matrix.roleAssignments || []).forEach(ra => {
1090
+ (ra.permissions || []).forEach(permPath => {
1091
+ const action = permPath.split(".").pop(); // last segment = action
1092
+ const uiAction = actionMap[action] || action;
1093
+ keys.push(ra.role + "|" + uiAction);
1094
+ });
1095
+ });
1096
+ return keys;
1097
+ }
1098
+ ```
1099
+
1100
+ **Frequency mapping:**
1101
+ ```
1102
+ "Quotidien" → "daily", "Hebdomadaire" → "weekly", "Mensuel" → "monthly"
1103
+ Default: "daily"
1104
+ ```
1105
+
1106
+ **Access mapping (involvement → access):**
1107
+ ```
1108
+ "decision-maker" → "admin", "end-user" with manager-like tasks → "manager"
1109
+ "end-user" → "contributor", "observer" → "viewer"
1110
+ Default: "contributor"
1111
+ ```
1112
+
1113
+ #### Step 3: Replace placeholders in template
1114
+
1115
+ 1. Serialize the FEATURE_DATA object as JSON (with 2-space indentation for readability)
1116
+ 2. Replace `{{FEATURE_DATA}}` with the serialized JSON
1117
+ 3. Replace `{{APPLICATION_NAME}}` → `{application_name}` (still used in `<title>` and header)
1118
+ 4. Replace `{{APPLICATION_ID}}` → `{feature_id}` (still used in `APP_KEY`)
1119
+ 5. Replace `{{VERSION}}` → `{version}`
1120
+ 6. Replace `{{CREATED_AT}}` → `{ISO timestamp}`
1121
+
1122
+ > **NOTE:** `{{APPLICATION_NAME}}`, `{{APPLICATION_ID}}`, `{{VERSION}}`, `{{CREATED_AT}}` still appear
1123
+ > in the HTML body (`<title>`, header, `APP_KEY`). They MUST be replaced separately from FEATURE_DATA.
1124
+
1125
+ #### Step 4: Write and confirm
1126
+
1127
+ 1. Write the populated HTML to the output directory
1128
+ 2. Display deployment confirmation:
937
1129
 
938
1130
  ```
939
1131
  ✓ Interactive HTML deployed:
940
1132
  Path: docs/business/{app}/business-analyse/v{version}/ba-interactive.html
1133
+ Pre-populated with: {stakeholder_count} stakeholders, {module_count} modules,
1134
+ {total_uc} use cases, {total_br} business rules, {total_entity} entities
941
1135
  Open in browser to review and edit the business analysis.
942
1136
  Export JSON and re-import with: /business-analyse -x <exported-json-path>
943
1137
  ```
944
1138
 
945
- **Why deploy at handoff?**
946
- - The feature.json is complete at this point (all modules specified and consolidated)
947
- - The client can review the full analysis in an interactive format
1139
+ **Why a FINAL deployment at handoff?**
1140
+ - Step 03 already deploys the HTML incrementally after each module (partial data)
1141
+ - This final deployment adds the COMPLETE data: all modules + consolidation + handoff info
1142
+ - The client sees the FULL analysis pre-populated — including cross-module interactions and E2E flows
1143
+ - The client can review, edit, and enrich directly in the browser
948
1144
  - Any client modifications can be re-imported via `-x` extraction mode
949
1145
  - The HTML is standalone (no server required) with localStorage persistence
1146
+ - On first open: pre-populated data displays. After client edits: localStorage overrides
1147
+ - **NOTE:** This overwrites the incremental HTML from step-03 with the complete version
950
1148
 
951
1149
  ---
952
1150
 
@@ -1192,7 +1390,7 @@ Before presenting handoff to user:
1192
1390
  - [ ] feature.json updated: handoff section + status "handed-off"
1193
1391
  - [ ] All paths use project namespace from .smartstack/config.json
1194
1392
  - [ ] No invented requirements (everything traced to feature.json)
1195
- - [ ] ba-interactive.html deployed with placeholders replaced
1393
+ - [ ] ba-interactive.html deployed PRE-POPULATED with all analysis data (not empty)
1196
1394
  - [ ] BA manifest (docs/business/index.json) updated with current analysis entries
1197
1395
  - [ ] User ready for next agent selection
1198
1396
 
@@ -38,13 +38,25 @@ fi
38
38
 
39
39
  ```bash
40
40
  CURRENT=$(git rev-parse --abbrev-ref HEAD)
41
+ BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
42
+
43
+ # CRITICAL: Verify config was loaded
44
+ if [ -z "$GF_MAIN_BRANCH" ] || [ -z "$GF_DEVELOP_BRANCH" ]; then
45
+ echo "❌ GitFlow config not loaded properly"
46
+ echo "→ Variables GF_MAIN_BRANCH and GF_DEVELOP_BRANCH are empty"
47
+ exit 1
48
+ fi
41
49
 
42
- case "$CURRENT" in
43
- ${GF_FEATURE_PREFIX}*) TARGET_BRANCH="$GF_DEVELOP_BRANCH" ;;
44
- ${GF_RELEASE_PREFIX}*) TARGET_BRANCH="$GF_MAIN_BRANCH" ;;
45
- ${GF_HOTFIX_PREFIX}*) TARGET_BRANCH="$GF_MAIN_BRANCH" ;;
46
- *) TARGET_BRANCH="$GF_DEVELOP_BRANCH" ;;
50
+ # Determine target branch based on type (using config values)
51
+ case "$BRANCH_TYPE" in
52
+ feature) TARGET_BRANCH="$GF_DEVELOP_BRANCH" ;;
53
+ release) TARGET_BRANCH="$GF_MAIN_BRANCH" ;;
54
+ hotfix) TARGET_BRANCH="$GF_MAIN_BRANCH" ;;
55
+ *) TARGET_BRANCH="$GF_DEVELOP_BRANCH" ;;
47
56
  esac
57
+
58
+ echo "✓ Branch: $CURRENT (type: $BRANCH_TYPE)"
59
+ echo "✓ Target: $TARGET_BRANCH"
48
60
  ```
49
61
 
50
62
  ### 3. Check Prerequisites