Modernize (Brownfield Dependency Upgrade)
Runs after Gear 6 for brownfield projects with modernize: true in .stackshift-state.json.
Prerequisites: Gears 1-6 complete, 100% spec coverage established Output: Modern dependency versions, updated tests, synchronized specs, upgrade report Scope: Node.js/TypeScript projects only
Constraints
- Do NOT upgrade to pre-release, beta, or canary versions. Use only stable releases.
- Do NOT delete lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml).
- Do NOT run
npm audit fix --force. Usenpm audit fixonly. - Do NOT proceed to the next phase until the current phase passes its gate check.
- Execute phases sequentially. No phases run in parallel.
- If
/speckit.analyzeis unavailable, manually compare spec acceptance criteria against test results as a fallback.
Trigger Conditions
Read .stackshift-state.json in the project root. Verify ALL of:
pathis"brownfield"modernizeistrue- Gear 6 (implement) is complete
If any condition is false, stop and report which condition failed.
If path is "greenfield", stop. This skill is for brownfield projects only.
State Management
State file: .stackshift-state.json (project root)
Working directory: .modernize/
On start, update .stackshift-state.json:
{ "modernize": true, "modernizeStatus": "in-progress", "modernizeStarted": "<ISO timestamp>" }
On success, update:
{ "modernizeStatus": "complete", "modernizeCompleted": "<ISO timestamp>" }
On failure, update:
{ "modernizeStatus": "failed", "modernizeError": "<summary of failure>" }
Rollback Procedure
If any phase fails and cannot be recovered:
- Copy
.modernize/baseline-package.jsonback topackage.json. - Copy
.modernize/baseline-lockfileback to the original lockfile location (if saved). - Run
npm install. - Run
npm testto verify baseline is restored. - If baseline tests pass, log "Rolled back to baseline successfully."
- If baseline tests fail, stop and ask the user for guidance.
- Update
.stackshift-state.jsonwithmodernizeStatus: "failed".
Phase 1: Pre-Upgrade Baseline
Create the working directory and capture baseline state.
mkdir -p .modernize
cp package.json .modernize/baseline-package.json
# Save lockfile if present
if [ -f package-lock.json ]; then cp package-lock.json .modernize/baseline-lockfile; fi
if [ -f yarn.lock ]; then cp yarn.lock .modernize/baseline-lockfile; fi
if [ -f pnpm-lock.yaml ]; then cp pnpm-lock.yaml .modernize/baseline-lockfile; fi
npm test 2>&1 | tee .modernize/baseline-test-results.txt
npm run test:coverage 2>&1 | tee .modernize/baseline-coverage.txt
npm outdated 2>&1 | tee .modernize/upgrade-plan.txt
If npm test fails at baseline: Stop. The project has pre-existing test failures. Ask the user whether to proceed or fix tests first.
Gate check: .modernize/baseline-package.json exists AND .modernize/baseline-test-results.txt exists.
Log: "Phase 1 complete -- baseline captured. X packages have available updates."
Phase 2: Dependency Upgrade
Upgrade all dependencies to latest stable versions.
npx npm-check-updates -u --target latest --reject '*alpha*,*beta*,*canary*,*rc*'
npm install 2>&1 | tee .modernize/install-output.txt
If npm install fails after ncu -u:
- Read the error output from
.modernize/install-output.txt. - Identify the failing package(s) from peer dependency or resolution errors.
- Revert those specific packages to their previous versions in
package.json(read versions from.modernize/baseline-package.json). - Run
npm installagain. - Document skipped packages in
.modernize/skipped-packages.txt. - If install still fails after 3 attempts, execute the Rollback Procedure and ask the user.
Run security fixes:
npm audit fix 2>&1 | tee .modernize/audit-fix-output.txt
Gate check: node_modules directory exists AND npm install exited with code 0.
Log: "Phase 2 complete -- dependencies upgraded. X packages updated, Y skipped."
Phase 3: Breaking Change Detection
Run the full test suite against upgraded dependencies.
npm test 2>&1 | tee .modernize/post-upgrade-test-results.txt
Compare results to baseline:
diff .modernize/baseline-test-results.txt .modernize/post-upgrade-test-results.txt > .modernize/test-diff.txt 2>&1 || true
Classify failures:
- Breaking change failures: Tests that were passing now fail with different assertion values or changed API signatures. These are expected and get fixed in Phase 4.
- Infrastructure failures: Tests fail with import errors, module-not-found, syntax errors, or timeout errors unrelated to behavior changes. These indicate install problems.
If more than 50% of tests fail: Execute the Rollback Procedure. The upgrade scope is too broad. Ask the user whether to attempt incremental upgrades (major versions one at a time).
If infrastructure failures exist: Fix import/module resolution issues before proceeding. These are not breaking changes -- they indicate incomplete installation or missing peer dependencies.
Gate check: Test results captured AND failure rate is below 50%.
Log: "Phase 3 complete -- X tests passing, Y tests failing (Z breaking changes, W infrastructure issues)."
Phase 4: Fix Breaking Changes (Spec-Guided)
For each breaking change identified in Phase 3:
- Match the failing test to its feature spec in
specs/. - Read the spec's acceptance criteria to understand required behavior.
- Update the code to work with the new dependency version while preserving spec compliance.
- Update the test if the test itself used deprecated APIs.
- Run the specific test to verify the fix.
After fixing all breaking changes, run the full test suite:
npm test 2>&1 | tee .modernize/post-fix-test-results.txt
If tests still fail after fixing all identified breaking changes: Compare against baseline. If failures exist that were not in the baseline, investigate. If after 3 fix iterations tests still fail, ask the user for guidance.
Gate check: All tests that passed at baseline now pass. No regressions.
Log: "Phase 4 complete -- X breaking changes fixed. All baseline tests restored."
Phase 5: Spec Synchronization
Determine whether dependency upgrades changed observable behavior (not just implementation).
Detection method: Compare test output diffs from Phase 3. If tests that were passing now produce different output values (not errors), behavior changed. If tests only failed with import/syntax/API signature errors, only implementation changed.
If behavior changed:
- Update the relevant feature spec to document the new behavior.
- Update acceptance criteria if outputs or interfaces changed.
- Update technical requirements with new dependency versions.
- Run
/speckit.analyzeto validate spec-implementation alignment. If/speckit.analyzeis unavailable, manually verify each spec's acceptance criteria against test results.
If only implementation changed:
- Update version numbers in spec technical details only.
- No functional spec changes needed.
Gate check: All specs in specs/ reflect current dependency versions AND /speckit.analyze (or manual check) shows no drift.
Log: "Phase 5 complete -- X specs updated. No spec drift detected."
Phase 6: Test Coverage Improvement
Run coverage and assess against the 85% threshold.
npm run test:coverage 2>&1 | tee .modernize/post-upgrade-coverage.txt
If all modules are already at 85%+: Skip to Phase 7. Log: "Phase 6 skipped -- all modules already at 85%+ coverage."
If any module is below 85%:
- Identify modules below threshold from coverage output.
- For each module, read its spec's acceptance criteria.
- Write tests covering each acceptance criterion that lacks a test.
- Re-run coverage after adding tests.
Gate check: npm run test:coverage shows all modules at 85%+.
Log: "Phase 6 complete -- coverage improved from X% to Y%. Z new tests added."
Phase 7: Final Validation
Run the complete validation suite. Every check must pass.
npm run build 2>&1 | tee .modernize/build-output.txt
If build fails: Read error output. Fix compilation errors. Re-run build. If build fails after 3 attempts, ask the user.
npm test 2>&1 | tee .modernize/final-test-results.txt
If any test fails: This is a regression. Do not proceed. Fix the failing test or execute Rollback Procedure.
npm run test:coverage 2>&1 | tee .modernize/final-coverage.txt
If any module is below 85%: Return to Phase 6.
Run /speckit.analyze (or manual spec check).
If spec drift detected: Return to Phase 5.
npm audit 2>&1 | tee .modernize/final-audit.txt
If high or critical vulnerabilities remain: Run npm audit fix (not --force). If vulnerabilities persist, document them in the upgrade report and inform the user.
Gate check: Build passes AND all tests pass AND coverage >= 85% AND no spec drift AND no high/critical vulnerabilities.
Log: "Phase 7 complete -- all validations passed. Modernization successful."
Output: Upgrade Report
Write .modernize/UPGRADE_REPORT.md with the following structure:
# Dependency Modernization Report
**Date**: <ISO date>
**Project**: <project name from package.json>
## Summary
- **Dependencies upgraded**: X packages
- **Major version bumps**: X packages
- **Breaking changes fixed**: X
- **Tests fixed/added**: X tests
- **Coverage**: X% -> Y%
- **Specs updated**: X specs
- **Skipped packages**: X (see details below)
## Upgraded Dependencies
| Package | Old Version | New Version | Breaking? |
|---------|-------------|-------------|-----------|
| <name> | <old> | <new> | Yes/No |
## Skipped Packages
<List any packages that could not be upgraded and why>
## Breaking Changes Fixed
For each breaking change:
- **Package**: <name>
- **Affected feature**: <feature name>
- **Fix**: <what was changed>
- **Spec impact**: <behavior change or implementation only>
## Spec Updates
<List specs that were modified and why>
## Test Coverage
- Before: X%
- After: Y%
- New tests: Z
## Validation Results
- Build: pass/fail
- Tests: X passing, Y failing
- Coverage: X% (target: 85%+)
- Spec drift: none/details
- Vulnerabilities: X high, Y critical (or none)
After writing the report, update .stackshift-state.json with modernizeStatus: "complete".
Success Criteria
Modernization is complete when ALL of:
- All dependencies updated to latest stable versions (or documented as skipped)
- All tests passing (zero failures)
- Test coverage >= 85% on all modules
- Build successful
- No spec drift (verified by
/speckit.analyzeor manual check) - No high/critical security vulnerabilities (or documented with user acknowledgment)
- Specs updated where behavior changed
.modernize/UPGRADE_REPORT.mdgenerated.stackshift-state.jsonupdated withmodernizeStatus: "complete"