The default behavior of yarn install is that the yarn.lock file gets updated if there is any mismatch between package.json and yarn.lock. Weird, right?

I find this wording ambiguous, so let me clarify: Yarn will always use the lockfile information as much as possible. If, however, a range isn't part of the lockfile (for instance because you edited your package.json but forgot to run an install afterwards), then Yarn will have to resolve it dynamically (since we don't have anything available to tell us what to use).

Importantly, it will only do this for this very range (and any other unknown range that could appear as a result), and will leave alone all the other properly locked ranges. Nothing will be upgraded.

There is a long-standing issue for making this a default, but the developers decided to leave it for a new Yarn version.

Indeed, --immutable (new name for --frozen-lockfile) is now the default when running Yarn on CI.