TL;DR: Do not trust the changelog as the only break detector: lock the resolved version with a lockfile and make CI install from the lockfile, so a floating caret cannot resolve differently between runs. When a break appears, bisect the resolved versions to find the culprit minor, then pin or constrain the range past it. The changelog is a hint; the lockfile is the guarantee.

```text
agent allowed a caret range to float past a breaking minor because the package's changelog never mentioned the break
```

1. Find the actual resolved versions: compare the version CI last tested green against the version that broke (lockfile diff or install log).
   Expected: the caret resolved to a newer minor than the green run used.
2. Bisect between the green and red resolved versions to identify the exact minor that introduced the break.
   Expected: one minor version named as the culprit.
3. Constrain the range to exclude the breaking minor (upper bound below it) or pin to the last good version, and record why in a comment next to the constraint.
   Expected: installs resolve to a known-good version again.
4. Make CI install from the lockfile (frozen or ci mode), and commit the lockfile, so the resolved version cannot drift between the agent's test run and the real install.
   Expected: the same versions everywhere, every time.
5. Add a post-install smoke check that fails fast on the broken behavior, so a future float surfaces in minutes instead of in production.
   Expected: the next surprise break gets caught by the smoke test, not by users.

## Use this when
- a caret or tilde range resolved to a newer version than CI tested
- the changelog did not document the breaking change
- fresh installs break while the lockfile-era build was green
- the agent treats "changelog looks clean" as "safe to float"

## Not for this skill when
- the break was documented and the agent ignored it (fix the notes review)
- you have no lockfile at all (create one first; this skill assumes one exists)
- the range is intentionally floating and you accept the risk (then the smoke test is the whole answer)

## Variant phrasings
- "caret range pulled breaking minor"
- "changelog didn't mention breaking change"
- "fresh install broke, lockfile was green"
- "how to stop caret from floating past breaking version"
- "undocumented breaking change in minor bump"

## Why it happens
Caret ranges are a bet that the ecosystem honors semver, and changelogs are a bet that authors document every break. Both bets usually pay off, which is why the failure is surprising. When an author ships a behavioral break in a minor without documenting it, the caret happily floats into it on the next fresh install, while the old lockfile keeps CI green - so the break appears "out of nowhere" on a machine that never saw the tested versions.

## Edge cases
- The culprit minor may be pulled in transitively, not by your direct range; check the full resolved tree, not just your manifest.
- Pinning to dodge one bad minor means missing its security fixes too; revisit the pin when the next minor ships and test it deliberately.
- Some registries serve different tarballs for the same version over time; if the bisect is inconclusive, checksums may have shifted (rare, but real).
- Over-constraining ranges across many packages recreates the unresolvable-tree problem; constrain surgically, one package at a time.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_KyfcXKRD0EsV92kAwz22gg
