The plan
Every run produces a machine-readable plan before it writes anything: whatif stops there, run continues. The plan is the backbone, not a debug flag. The compare job, the dashboard, the merge request text and the estate report read it.
- A plan explains why nothing happens. Every held update carries its reason, when the hold lifts and which rule imposed it.
- Nothing downstream of the plan invents a change. Branches carry byte-range edits and tasks; apply consumes those, never a dependency.
- Format: JSON,
--report <path>,%sfor the project path,schemaVersion1
The fields
Top level
| Field | Meaning |
|---|---|
pinupVersion, generatedAt | which pinup planned, and when |
repo.path | the project |
limits | prHourlyLimit, prConcurrentLimit as resolved; the runner enforces them |
dashboard | whether the dashboard issue is kept, and its title |
branching | prefix and, during a rename, prefixOld; the runner counts and prunes under both |
deps, updates, branches, warnings, stats | see below |
unmanaged | with --coverage only: pinned versions nothing updates, as {file, line, value} |
deps
One entry per dependency per file: manager, file, depName, packageName, currentValue, currentDigest, lockedVersion, datasource, versioning, registryUrls, the values from the pre-lookup rule pass (rangeStrategy, minimumReleaseAge, pinDigests), the locus (byte offsets of value and digest) and, for custom managers, the index and the captures. A dependency that was not looked up says why in skipReason; one disabled by a rule names the rule in disabled.
allowedVersions is the limit a rule set, allowedVersionsBy the rule. When the limit excludes every newer release, skipReason does not say “up to date” but something like held by allowedVersions "/^8\.2\./" (packageRules[12]): 3 newer releases excluded, the newest 8.5.2. When the dependency gets an allowed update and the rule still holds back something newer, the same sentence is in heldBack. The dashboard shows both on the dependency.
updates
One entry per movement: dep, newValue (the bytes written), newVersion, newDigest, updateType (major, minor, patch, digest, pin, pinDigest, rollback, lockFileMaintenance, majorAvailable), stream on a majorAvailable of a series pin (the sibling package of the higher series), declared and effective, analyzer and evidence, releaseTime with timeSource, securityFix. And:
blocks: every reason the update is held for.reason,origin(config,packageRules[N]or a file in the repository such asfile:.npmrcwith apointer),until,note. Reasons:minimumReleaseAge,schedule,dependencyDashboardApproval,disabled,allowedVersions,internalChecksFilter,hourlyLimit,concurrentLimit,rollingMajor,pluginRequired,taskRefused,nothingToRefresh,publishFailed,closedByHand(a person closed the merge request with exactly these edits; a changed update opens again),narrowedRun(a run narrowed to one package, such as the fast lane, holds a group branch that already has an open merge request: it sees only part of the group and would drop the other updates. A group branch with no open request is written.).disabledandallowedVersionsare the configuration's verdicts, not waits: a full run closes a merge request whose branch is held for one.suppressedBy: the first reason, for readers who need one word.notes,compareUrl: the release notes between the versions and the forge's compare page.
branches
One entry per branch: name (Renovate-compatible), plannedName during a rename, title, commitBody, groupName, updateKeys, edits (file, byte range, old and new bytes, manager), tasks (lock refreshes and post-upgrade commands with their scope), automerge, labels, schedule, existing (the open merge request) and suppressedBy with heldWith where the branch is held as a whole.
warnings
stage (config, extract, lookup, analyze, apply, publish, cache), file, msg. An unreachable datasource, a preset that resolves to nothing, a task whose output left its scope, a cache that is too young: warnings, never a failed run for the rest of the repository.
Reading a plan
# every held update with its reason
jq -r '.updates[] | select(.blocks) | "\(.dep.depName) \(.dep.currentValue) -> \(.newValue): \(.blocks[0].reason) (\(.blocks[0].origin.source))"' plan.json
# what allowedVersions holds back, with or without an allowed update
jq -r '.deps[] | (.heldBack // .skipReason // "") as $r | select($r | startswith("held by")) | "\(.depName) \(.currentValue): \($r)"' plan.json
# the branches that would be written now
jq -r '.branches[] | select(.suppressedBy == null) | .name' plan.jsonNext
Effective label
The second label on every update: the Helm chart analyzer, the safety rule, matchEffective and trustEffective.