---
name: step-03-create
description: Create consolidated migration using MCP for naming
next_step: step-04-validate.md
---

# Step 3: Create Consolidated Migration

## YOUR TASK:

Create a single consolidated migration that captures all changes from the branch.

**MANDATORY:** Use MCP `suggest_migration` for compliant naming. NEVER hardcode names.

**Previous step:** `step-02-fetch.md`

---

## EXECUTION SEQUENCE:

### 1. Build Description from Branch Context

```bash
case "$BRANCH_TYPE" in
  feature)
    # Extract feature name: feature/user-auth → UserAuth
    FEATURE_NAME=$(echo "$CURRENT_BRANCH" | sed 's|feature/||' | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1' | tr -d ' ')
    DESCRIPTION="${FEATURE_NAME}Consolidated"
    ;;
  release)
    VERSION=$(echo "$CURRENT_BRANCH" | sed 's|release/v\?||')
    DESCRIPTION="Release${VERSION}Initial"
    ;;
  hotfix)
    VERSION=$(echo "$CURRENT_BRANCH" | sed 's|hotfix/v\?||')
    DESCRIPTION="Hotfix${VERSION}Fix"
    ;;
  develop)
    DESCRIPTION="DevelopConsolidated"
    ;;
  *)
    DESCRIPTION="Consolidated"
    ;;
esac

echo "Description: $DESCRIPTION"
echo "DbContext Type: $DBCONTEXT_TYPE"
```

### 2. Get Migration Name from MCP (MANDATORY)

```javascript
// DBCONTEXT_TYPE = "core" or "extensions" (from detect_dbcontext)
// DESCRIPTION = built above (e.g., "MultitenantConsolidated")

const result = await mcp__smartstack__suggest_migration({
  description: DESCRIPTION,
  context: DBCONTEXT_TYPE    // NEVER hardcode!
});

// Result example: core_v1.9.0_001_MultitenantConsolidated
MIGRATION_NAME = result.migrationName;
```

**FORBIDDEN:**
- Calculating name manually
- Hardcoding context as "core"
- Using underscores instead of dots in version

### 3. Create Migration with EF Core

```bash
# MIGRATION_NAME = result from MCP (e.g., core_v1.9.0_001_MultitenantConsolidated)
# DBCONTEXT = CoreDbContext or ExtensionsDbContext (from detect_dbcontext)

echo ""
echo "Creating migration: $MIGRATION_NAME"
echo "Context: $DBCONTEXT"
echo ""

dotnet ef migrations add "$MIGRATION_NAME" \
  --context "$DBCONTEXT" \
  --project "$INFRA_PROJECT" \
  --startup-project "$STARTUP_PROJECT" \
  -o Persistence/Migrations \
  --verbose
```

### 4. Verify Migration Created

```bash
# Check migration files exist
MIGRATION_FILE="$MIGRATIONS_DIR/${MIGRATION_NAME}.cs"
DESIGNER_FILE="$MIGRATIONS_DIR/${MIGRATION_NAME}.Designer.cs"

if [ ! -f "$MIGRATION_FILE" ]; then
  echo "ERROR: Migration file not created"
  echo "Expected: $MIGRATION_FILE"

  # Restore from backup
  cp "$BACKUP_DIR"/*.cs "$MIGRATIONS_DIR/"
  echo "Restored from backup"
  exit 1
fi

if [ ! -f "$DESIGNER_FILE" ]; then
  echo "ERROR: Designer file not created"
  exit 1
fi

echo ""
echo "Migration created:"
echo "  $MIGRATION_FILE"
echo "  $DESIGNER_FILE"
```

### 5. Check Migration Content

```bash
# Verify migration has content (not empty)
UP_METHODS=$(grep -c "protected override void Up" "$MIGRATION_FILE" || echo "0")
DOWN_METHODS=$(grep -c "protected override void Down" "$MIGRATION_FILE" || echo "0")

if [ "$UP_METHODS" -eq 0 ]; then
  echo "WARNING: Migration may be empty (no Up method found)"
fi

echo ""
echo "Migration content:"
echo "  Up methods: $UP_METHODS"
echo "  Down methods: $DOWN_METHODS"
```

### 6. Inject SQL Objects (Functions, Views, SP)

EF Core ne génère PAS de DDL pour les TVF, vues et procédures stockées.
Les scripts SQL source sont dans `Persistence/SqlObjects/` (Embedded Resources).
Après un squash, il faut les réinjecter dans la migration consolidée.

```bash
# Check if SqlObjects folder exists with .sql files
SQL_OBJECTS_DIR="$INFRA_PROJECT_DIR/Persistence/SqlObjects"
SQL_FILES=$(find "$SQL_OBJECTS_DIR" -name "*.sql" 2>/dev/null | wc -l)

if [ "$SQL_FILES" -gt 0 ]; then
  echo ""
  echo "Found $SQL_FILES SQL object(s) in SqlObjects/"
  echo "Injecting SqlObjectHelper.ApplyAll(migrationBuilder) into migration..."

  # Add using directive if not present
  if ! grep -q "using SmartStack.Infrastructure.Persistence.SqlObjects;" "$MIGRATION_FILE"; then
    sed -i '1s/^/using SmartStack.Infrastructure.Persistence.SqlObjects;\n/' "$MIGRATION_FILE"
  fi

  # Add SqlObjectHelper.ApplyAll at the end of Up() method
  # Find the closing brace of Up() and insert before it
  sed -i '/protected override void Up/,/^        }/ {
    /^        }/ i\
\            // Apply SQL objects (TVF, Views, SP) from embedded resources\
\            SqlObjectHelper.ApplyAll(migrationBuilder);
  }' "$MIGRATION_FILE"

  echo "  ✅ SqlObjectHelper.ApplyAll(migrationBuilder) injected"
  find "$SQL_OBJECTS_DIR" -name "*.sql" -exec basename {} \; | while read f; do
    echo "  📄 $f"
  done
else
  echo ""
  echo "No SQL objects found in SqlObjects/ - skipping injection"
fi
```

---

## EXPECTED RESULT:

```
After step-03-create:
├── {base_migration_1}.cs              ← From parent (preserved)
├── {base_migration_1}.Designer.cs
├── {NEW_MIGRATION_NAME}.cs            ← NEW: Consolidated migration
├── {NEW_MIGRATION_NAME}.Designer.cs   ← NEW: Designer file
└── {DbContext}ModelSnapshot.cs        ← Updated with new migration
```

---

## STATE OUTPUT:

| Variable | Description |
|----------|-------------|
| `{migration_name}` | Name from MCP (e.g., core_v1.9.0_001_...) |
| `{migration_file}` | Full path to .cs file |
| `{designer_file}` | Full path to .Designer.cs file |

---

## ERROR HANDLING:

| Error | Resolution |
|-------|------------|
| MCP fails | Check MCP connectivity, retry |
| Migration creation fails | Restore from backup, check errors |
| Empty migration | Warning only, may be valid |

---

## SUCCESS CRITERIA:

- MCP called for migration name
- Migration file created
- Designer file created
- Snapshot updated

## NEXT STEP:

→ `step-04-validate.md`
