--- description: Execute Brownfield Upgrade Mode - spec-driven dependency modernization workflow. Runs 4-phase process: spec-guided test coverage, baseline analysis, dependency upgrade with spec-guided fixes, and spec validation. Based on thoth-cli dependency upgrade process adapted for spec-driven development. --- # StackShift Modernize: Spec-Driven Dependency Upgrade **Brownfield Upgrade Mode** - Execute after completing Gears 1-6 Run this command to modernize all dependencies while using specs as your guide and safety net. --- ## Quick Status Check ```bash # Check prerequisites echo "=== Prerequisites Check ===" # 1. Specs exist? SPEC_COUNT=$(find .specify/memory/specifications -name "*.md" 2>/dev/null | wc -l) echo "Specs found: $SPEC_COUNT" # 2. /speckit commands available? ls .claude/commands/speckit.*.md 2>/dev/null | wc -l | xargs -I {} echo "Speckit commands: {}" # 3. Tests passing? npm test --silent && echo "Tests: ✅ PASSING" || echo "Tests: ❌ FAILING" # 4. StackShift state? cat .stackshift-state.json 2>/dev/null | jq -r '"\(.path) - Gear \(.currentStep // "complete")"' || echo "No state file" if [ "$SPEC_COUNT" -lt 1 ]; then echo "❌ No specs found. Run Gears 1-6 first." exit 1 fi ``` --- ## Phase 0: Spec-Guided Test Coverage Foundation **Goal:** 85%+ coverage using spec acceptance criteria as test blueprint **Time:** 30-90 minutes | **Mode:** Autonomous test writing ### 0.1: Load Specifications & Baseline Coverage ```bash echo "=== Phase 0: Spec-Guided Test Coverage ===" mkdir -p .upgrade # List all specs find .specify/memory/specifications -name "*.md" | sort | tee .upgrade/all-specs.txt SPEC_COUNT=$(wc -l < .upgrade/all-specs.txt) # Baseline coverage npm test -- --coverage --watchAll=false 2>&1 | tee .upgrade/baseline-coverage.txt COVERAGE=$(grep "All files" .upgrade/baseline-coverage.txt | grep -oE "[0-9]+\.[0-9]+" | head -1 || echo "0") echo "Specs: $SPEC_COUNT" echo "Coverage: ${COVERAGE}%" echo "Target: 85%" ``` ### 0.2: Map Tests to Specs Create `.upgrade/spec-coverage-map.json` mapping each spec to its tests: ```bash # For each spec, find which tests validate it # Track which acceptance criteria are covered vs. missing ``` ### 0.3: Write Tests for Missing Acceptance Criteria **For each spec with missing coverage:** 1. Read spec file 2. Extract acceptance criteria section 3. For each criterion without a test: - Write test directly validating that criterion - Use Given-When-Then from spec - Ensure test actually validates behavior **Example:** ```typescript // From spec: user-authentication.md // AC-3: "Given user logs in, When session expires, Then user redirected to login" describe('User Authentication - AC-3: Session Expiration', () => { it('should redirect to login when session expires', async () => { // Given: User is logged in renderWithAuth(, { authenticated: true }); // When: Session expires await act(async () => { expireSession(); // Helper to expire token }); // Then: User redirected to login await waitFor(() => { expect(mockNavigate).toHaveBeenCalledWith('/login'); }); }); }); ``` ### 0.4: Iterative Coverage Improvement ```bash ITERATION=1 TARGET=85 MIN=80 while (( $(echo "$COVERAGE < $TARGET" | bc -l) )); do echo "Iteration $ITERATION: ${COVERAGE}%" # Stop conditions if (( $(echo "$COVERAGE >= $MIN" | bc -l) )) && [ $ITERATION -gt 5 ]; then echo "✅ Min coverage reached" break fi # Find spec with lowest coverage # Write tests for missing acceptance criteria # Prioritize P0 > P1 > P2 specs npm test -- --coverage --watchAll=false --silent PREV=$COVERAGE COVERAGE=$(jq '.total.lines.pct' coverage/coverage-summary.json) GAIN=$(echo "$COVERAGE - $PREV" | bc) # Diminishing returns? if (( $(echo "$GAIN < 0.5" | bc -l) )) && (( $(echo "$COVERAGE >= $MIN" | bc -l) )); then echo "✅ Diminishing returns (${GAIN}% gain)" break fi ITERATION=$((ITERATION + 1)) [ $ITERATION -gt 10 ] && break done echo "✅ Phase 0 Complete: ${COVERAGE}% coverage" ``` ### 0.5: Completion Marker ```bash cat > .upgrade/stackshift-upgrade.yml < .upgrade/baseline-spec-status.txt ``` ### 1.2: Dependency Analysis ```bash # Current dependencies npm list --depth=0 > .upgrade/dependencies-before.txt # Outdated packages npm outdated --json > .upgrade/outdated.json || echo "{}" > .upgrade/outdated.json # Count major upgrades MAJOR_COUNT=$(cat .upgrade/outdated.json | jq '[.[] | select(.current != .latest)] | length') echo "Major upgrades: $MAJOR_COUNT" ``` ### 1.3: Spec Impact Analysis **For each major dependency upgrade, identify affected specs:** ```bash # Create impact analysis cat > .upgrade/spec-impact-analysis.json <<'EOF' { "react": { "current": "17.0.2", "latest": "19.2.0", "breaking": true, "affectedSpecs": [ "user-interface.md", "form-handling.md" ], "acceptanceCriteria": [ "user-interface.md: AC-1, AC-3", "form-handling.md: AC-2" ], "testFiles": [ "components/UserInterface.test.tsx" ], "risk": "HIGH" } } EOF ``` ### 1.4: Generate Upgrade Plan Create `.upgrade/UPGRADE_PLAN.md`: ```markdown # Upgrade Plan ## Summary - Dependencies to upgrade: ${MAJOR_COUNT} major versions - Specs affected: [count from impact analysis] - Risk level: [HIGH/MEDIUM/LOW] - Estimated effort: 2-4 hours ## Critical Upgrades (Breaking Changes Expected) ### react: 17.0.2 → 19.2.0 - **Breaking Changes:** - Automatic batching - Hydration mismatches - useId for SSR - **Affected Specs:** - user-interface.md (AC-1, AC-3, AC-5) - form-handling.md (AC-2, AC-4) - **Test Files:** - components/UserInterface.test.tsx - components/FormHandler.test.tsx - **Risk:** HIGH [Continue for all major upgrades...] ## Spec Impact Summary ### High-Risk Specs (Validate Carefully) 1. user-interface.md - React changes affect all components 2. form-handling.md - State batching changes ### Low-Risk Specs (Quick Validation) [List specs unlikely to be affected] ## Upgrade Sequence 1. Update package.json versions 2. npm install 3. Fix TypeScript errors 4. Fix test failures (spec-guided) 5. Fix build errors 6. Validate with /speckit.analyze ``` ### 1.5: Update Tracking ```bash cat >> .upgrade/stackshift-upgrade.yml </dev/null && echo "Lint: ✅" || echo "Lint: ⚠️ OK" # Must be green to proceed ``` ### 2.2: Create Upgrade Branch ```bash git checkout -b upgrade/dependencies-to-latest git add .upgrade/ git commit -m "docs: upgrade baseline and plan Phase 0: Coverage ${COVERAGE}% Phase 1: Analysis complete Specs: $SPEC_COUNT validated Ready for Phase 2 " ``` ### 2.3: Upgrade Dependencies ```bash echo "=== Upgrading Dependencies ===" # Upgrade to latest npx npm-check-updates -u npm install # Update Node (optional but recommended) echo "22.21.0" > .nvmrc nvm install 22.21.0 nvm use # Document changes npm list --depth=0 > .upgrade/dependencies-after.txt ``` ### 2.4: Detect Breaking Changes ```bash echo "=== Testing After Upgrade ===" npm test 2>&1 | tee .upgrade/test-results-post-upgrade.txt # Extract failures grep -E "FAIL|✕" .upgrade/test-results-post-upgrade.txt > .upgrade/failures.txt || true FAILURE_COUNT=$(wc -l < .upgrade/failures.txt) echo "Breaking changes: $FAILURE_COUNT test failures" ``` ### 2.5: Spec-Guided Fix Loop **Autonomous iteration until all tests pass:** ```bash ITERATION=1 MAX_ITERATIONS=20 while ! npm test --silent 2>&1; do echo "=== Fix Iteration $ITERATION ===" # Get first failing test FAILING_TEST=$(npm test 2>&1 | grep -m 1 "FAIL" | grep -oE "[^ ]+\.test\.[jt]sx?" || echo "") if [ -z "$FAILING_TEST" ]; then echo "No specific test file found, checking for general errors..." npm test 2>&1 | head -50 break fi echo "Failing test: $FAILING_TEST" # Find spec from coverage map SPEC=$(jq -r "to_entries[] | select(.value.testFiles[] | contains(\"$FAILING_TEST\")) | .key" .upgrade/spec-coverage-map.json || echo "") if [ -n "$SPEC" ]; then echo "Validates spec: $SPEC" echo "Loading spec acceptance criteria..." cat ".specify/memory/specifications/$SPEC" | grep -A 20 "Acceptance Criteria" fi # FIX THE BREAKING CHANGE # - Read spec acceptance criteria # - Understand intended behavior # - Fix code to preserve that behavior # - Run test to verify fix # Log fix echo "[$ITERATION] Fixed: $FAILING_TEST (spec: $SPEC)" >> .upgrade/fixes-applied.log # Commit incremental fix git add -A git commit -m "fix: breaking change in $FAILING_TEST Spec: $SPEC Fixed to preserve acceptance criteria behavior " ITERATION=$((ITERATION + 1)) if [ $ITERATION -gt $MAX_ITERATIONS ]; then echo "⚠️ Max iterations reached - manual review needed" break fi done echo "✅ All tests passing" ``` ### 2.6: Build & Lint ```bash # Fix build npm run build || (echo "Fixing build errors..." && [fix build]) # Fix lint (often ESLint 9 config changes) npm run lint || (echo "Fixing lint..." && [update eslint.config.js]) ``` ### 2.7: Phase 2 Complete ```bash npm test && npm run build && npm run lint FINAL_COVERAGE=$(jq '.total.lines.pct' coverage/coverage-summary.json) cat >> .upgrade/stackshift-upgrade.yml < .upgrade/UPGRADE_REPORT.md <> .upgrade/stackshift-upgrade.yml <&1 | grep FAIL # 2. Find spec jq '.[] | select(.testFiles[] | contains("failing-test.ts"))' .upgrade/spec-coverage-map.json # 3. Read spec acceptance criteria cat .specify/memory/specifications/[spec-name].md | grep -A 10 "Acceptance Criteria" # 4. Fix to match spec # 5. Verify with npm test ``` **"Can't reach 85% coverage"** ```bash # Check which acceptance criteria lack tests cat .upgrade/spec-coverage-map.json | jq '.[] | select(.missingCoverage | length > 0)' # Write tests for those criteria ``` **"/speckit.analyze shows drift"** ```bash # Review what changed /speckit.analyze # Fix code to match spec OR # Update spec if intentional improvement ``` --- **Remember:** Specs are your north star. When breaking changes occur, specs tell you what behavior to preserve.