Skip to content

Install-time summary

With the plugin installed, composer require, composer update and composer install print a block above Composer's operations list. It covers only the packages the transaction installs or updates, not the whole lock. It never fails the install unless you set install-time-strict.

To Use
See why one package is flagged composer lockrot --explain=<package> (explaining one package)
Accept what is flagged A baseline
Never flag one package The allowlist
Turn the block off for a project "install-time": "off" in extra.lockrot
Silence all of lockrot for one command LOCKROT_DISABLE=1: not even the skipped line prints
Give the check more or less time install-time-budget, in seconds
Stop the install on a finding at fail-on "install-time-strict": true

The block has this shape, on stderr; example-run.md shows a recorded run of the full report:

lockrot: dependency rot in <flagged> of <changed> changed packages
  <verdict>   <package> <version>: <evidence>
  <verdict>   <package> <version>: <evidence> (via <direct> > <intermediate>)
  … and <n> more
  note: <note text>
Run composer lockrot for details.

At most ten lines, always

The block is at most ten lines, in this order:

  • the header;
  • one line per flagged package, in the report's order (highest priority first), cut with … and <n> more when they do not fit;
  • up to three of the run's notes, in the order the run wrote them (run notes);
  • the footer, Run composer lockrot for details.

Lines are counted as written, not as terminal rows: a long line can wrap in a narrow terminal.

A transitive package's line ends with the chain that pulls it in, (via a > b), resolved through the whole lock. The block does not show the other direct requirements that reach the package, or, for a direct requirement, the flagged packages it pulls in (S7). composer lockrot shows both (transitive exposure).

An extra.lockrot key lockrot does not read adds one lockrot: unknown key … line above the block, outside its ten, whether or not anything is flagged (unknown keys). It prints whenever the block would run: not under LOCKROT_DISABLE, not with install-time: off, and only for a transaction that installs or updates something.

Never silent about a package it could not check

A package whose metadata never arrived is unknown, which is not flagged. When nothing is flagged but the run had a network failure (a note with sets_network_failures: true), a short block prints instead of nothing:

Header When
lockrot: <n> of <m> changed packages could not be checked <n> packages are unknown
lockrot: <m> changed packages checked, one check incomplete Metadata arrived for every package, and a later lookup failed: advisories, a monorepo parent's metadata, or a repository host

Up to three notes and the footer follow the header. They are the run's first notes, so the one behind the failure can fall past the cut. composer lockrot --format=json lists every note, each with its sets_network_failures (run notes).

Silence means nothing is flagged and no note is a network failure. It does not mean every check ran: skipped repository activity, a skipped advisory check or the anonymous cap (run notes) prints nothing on its own. The unknown-key line still prints.

Time budget

Once install-time-budget seconds have passed, the install-time check starts no new metadata or advisory request, and repository-activity requests are cut to the time left. A request already running finishes first, so an install can take somewhat longer than the budget. composer lockrot has no budget.

{
    "extra": {
        "lockrot": {
            "install-time-budget": 20
        }
    }
}

The default is 5 seconds (range). The install-time keys have no CLI option and no environment override.

When the budget runs out, a verdict that needs repository activity (S3, S4) can read milder than the one composer lockrot gives for the same package.

The budget ran out before or during Effect on the block Note
A package's metadata The package is unknown; when nothing is flagged, the could-not-be-checked header counts it metadata_unavailable, a network failure
The advisory check A priority an advisory would raise stays one step lower advisories_not_checked
A monorepo parent's metadata Split packages whose own tags have no trusted date stay unmeasured and carry S10 undated_releases (dates from the monorepo) None
The repository activity check, before it began S3 and S4 are not read; findings carry S10 install_time_budget repository_activity_not_checked
The repository activity check, part way Requests still running are cut to the time left; those that time out have no activity, and their findings carry S10 fetch_failed repository_activity_unreachable, a network failure

composer require and composer update reuse the metadata Composer has just fetched in the same process, so they rarely run out. A composer install with a cold cache on a large lock, such as a fresh clone or a CI job, can run out before every package is checked. Raise the budget for such a lock, or lower it to spend less time checking.

When the install continues and when it stops

Symptom Cause Install What to do
A note in the block Something the run could not see: an unreachable repository, an exhausted budget, a missing token Continues, unless install-time-strict is on with fail-on: unchecked and a finding carries S10 Look up its code in run notes
One lockrot: install-time check skipped: … line lockrot could not run the check: a malformed extra.lockrot or lockrot environment variable, an unreadable composer.lock, an unreadable baseline file, an extra.lockrot.baseline naming a missing one, or a defect in lockrot Continues, and install-time-strict does not apply Run composer lockrot; for a configuration problem it names the problem and exits 2 (exit codes)
lockrot: findings at or above fail-on=… and Composer exits 1 install-time-strict is on and a finding reaches fail-on Stopped See after a stop

install-time-strict

{
    "extra": {
        "lockrot": {
            "install-time-strict": true,
            "fail-on": "high"
        }
    }
}

install-time-strict needs a fail-on, from LOCKROT_FAIL_ON or extra.lockrot.fail-on; without one, nothing is stopped. When a finding reaches fail-on, lockrot stops the transaction before any package operation runs, and Composer exits 1. Choosing --fail-on covers the values.

  • A finding in the baseline does not stop the install unless its verdict is worse than the baseline recorded. The block lists both kinds.
  • A gap in what the run could see stops the install only through fail-on: unchecked, which matches every finding that carries S10 (what was not checked). A repository-activity gap adds S10 no_token, anonymous_budget, rate_limit, fetch_failed or install_time_budget; a monorepo parent that never arrived adds undated_releases to the split packages it would date. Such a finding can be ok and missing from the block. Advisory gaps and metadata_unavailable do not stop the install on their own, and --strict-network has no install-time counterpart.
  • --dry-run is never stopped, since no operation is about to run.

A stopped composer require or composer update leaves composer.lock updated (and, for require, composer.json) and nothing new in vendor/. composer install then stops on the same finding. Either:

  • undo the change: run composer remove <package>, or restore composer.json and composer.lock from version control;
  • or, for a flagged finding, accept it into the baseline, then run composer install;
  • or allowlist the package (the allowlist), then run composer install.

A baseline covers only flagged findings: a fail-on: unchecked stop on an ok or unknown finding clears through the allowlist, or, for a repository-activity gap, when that check runs (set a token, raise the budget or rerun).

Under --dry-run

Under composer require --dev … --dry-run, a package absent from the lock on disk is treated as production, so its priority can read one step high. composer lockrot on the real lock has the flag.

  • configuration.md — the install-time, install-time-strict and install-time-budget keys
  • notes.md — every note the block can print, and which are network failures
  • baseline.md — how the baseline applies at install time
  • verdicts.md — what the verdicts and priorities mean
  • example-run.md — a recorded run of the full report the footer points to