Release ledger

Release

v3.1.2

threshold

a hardening release. A second adversarial cross-examination of the codebase, plus a new check at the threshold of every run — the versions a coupling would write are proven against the registries before a single fix is planned — so a config typo or a subtle edge case is caught at the door rather than in a customer's build.

Highlights

  • 01

    Coupling versions are checked before the run starts

    Every version a couplings rule could write into a POM is verified against the configured registries at the first step. A typo (a companion floor no registry publishes) now stops the run naming the rule, instead of silently withholding the trigger's own legitimate fix on every run — or, in report-only mode, writing an unbuildable patch and exiting green. Registry errors and empty listings warn and proceed; only a confirmed absence fails.

  • 02

    Runs survive cancels, hung builds, and throttling

    Maven subprocesses run as their own process group, so a CI cancel or timeout kills forked test JVMs and app servers instead of letting them hold the run open. SIGINT/SIGTERM cancel gracefully so cleanup and telemetry flush still happen. The GitLab client honours 429 Retry-After, and permanent registry auth failures are no longer retried as transient or published as unvalidated MRs.

  • 03

    npm never closes a still-wanted merge request

    An npm run that defers plans because of the MR budget, or because a stale registry answer flipped a fix to unfixable for one run, no longer closes the deferred plans' open MRs — matching the guard the Maven pipeline has had since ADR-039. And a dependency pinned to a git fork, local path, or npm: alias is never silently rewritten to a registry version.

  • 04

    Couplings and reactors do the right thing throughout

    Coupling comparisons use the version each library actually resolves to, the final remediation pass applies couplings, and every re-render path rebuilds companion edits so a merge request never advertises a companion its diff omits. In reactors, a fix no longer downgrades a module already on a newer safe version, and a transitive override reaches every module that pins the coordinate in its own dependencyManagement.

Full notes

Added

  • Startup preflight: coupling companion versions are proven against the configured registries before planning; a version no registry publishes fails the run naming the rule, coordinate, and latest available version.
  • Three new end-to-end regression cases covering the couplings feature, the reactor module-level dependencyManagement shape, and the npm MR-budget deferral guard.

Fixed

  • Maven subprocesses run as process-group leaders so a cancel or timeout kills forked grandchildren (test JVMs, app servers) instead of hanging the run past its timeout.
  • SIGINT/SIGTERM cancel the run gracefully so deferred cleanup and the telemetry flush still happen; a second signal exits immediately.
  • Permanent Maven failures (401/403/404 auth, absent artifacts) are no longer retried as transient nor published as an unvalidated inconclusive merge request.
  • The GitLab client retries 429 responses honouring Retry-After and re-sending the body, so a rate-limited fan-out no longer strands half-published runs.
  • npm reconciliation is deferred (not closing still-wanted MRs) when plans were held back by the MR budget or a non-moving staging regeneration.
  • A direct npm dependency pinned to a non-registry specifier (git, file, github, npm: alias, tarball) is never rewritten to a registry version.
  • Coupling companion and trigger comparisons use the version each coordinate actually resolves to rather than a BOM's release-train number, and an unfixable plan no longer blocks a real companion.
  • The final remediation pass applies couplings (single-POM and reactor), and aggregation, final-pass, and reactor re-render paths rebuild companion edits so an MR never advertises a companion its diff omits; a re-rendered governor import keeps its reclaim comment.
  • A reactor Bump no longer downgrades a module already on a newer safe version, and a transitive Override reaches every module that self-manages the coordinate in its own dependencyManagement, not only the root.