Plumb
Reading edge · switch to stable
Reference

Changelog

0.6.0 — the debugger is gone

If you run Plumb on Java, this is the release to take. Grounding was watched by a second process attached to your suite over JDWP. It is now observed inside the process where it happens, by probes woven into the bytecode as your classes load. No port, no handshake, no attach, no second process, no timing.

with the debugger now
a pilot's 1,928-test Spring Boot board 2.3–5.4 hours 41 seconds
that board's overhead over the bare suite +1.2%
a 973-test suite, bare against observed 93.8/93.8, 92.4/92.1, 92.2/91.4
mutation-mode observation ~90× no measurable cost

It is provable rather than merely cheaper. Transformed bytecode is verified by the JVM as it loads, so a malformed probe fails immediately, loudly, and the same way every time — which is how both defects in the weaver were found, one of them by a real project rather than by us.

And it is what makes a Java 8 floor possible at all. com.sun.jdi is not in the Java 8 API — it is tools.jar there, and a module needing --add-modules from 9 on — so a debugger meant a different build and launch arrangement per JVM generation. java.lang.instrument has been standard since Java 5. The instrument compiles at --release 8 and has been run on Temurin 1.8.0_504, lambda-bodied methods included. The adapter around it is still Java 17, so the floor has not moved yet; what was blocking it has.

A board you already have may read differently

Every movement is a correction, and all three run one way — toward saying less where less was known. Nothing here makes a story proven that was not.

you had you may get why
inert not-checked inert is the positive claim no production code ran, and may now only be made where a surface was declared and the instrument actually rewrote something. It used to be read from an absent hit, which an instrument that never attached produces just as well as a test that touched nothing.
ungrounded dispatched work handed to another thread that reaches an entry point is now seen arriving, rather than inferred from a hand-off going out. Measured: 142 hand-offs seen where the debugger saw 137.
dispatched grounded the same, from the other side.

You do not have to remember a version to tell which side of the line a board came from. Every fact carries the instrument that made it: "mechanism": "JDWP breakpoints …" before, "bytecode probes, in process …" after. If a story moved across that line, read the mechanism before reading the story.

observation.failed now means the instrument was declared and never reached your code — there is no observer left to die. On Java the usual cause is a production class sitting under a declared test root.

Mutation was rebuilt on the same instrument

Chapter 4 mutates the code a test drove under a declared entry point. That scope came from the debugger; it now comes from the same weaver that answers grounding, and four things changed with it.

Survivors are named — operator, Class#method, file and line — and printed once each behind --survivors, with the manifest carrying them per story.

--mutation now requires at least one declared entry point and refuses with an explanation, rather than reporting nothing to mutate: with no entry point there is no window, and every story came up clean without having been looked at.

Expect your killed-out-of-total to move on both halves of the fraction. A ratio changing is neither a regression nor an improvement; it is a different denominator. Compare ratios only within one version.

Taking this release asks one thing of you

--json consumers must change. mutation.survivors was an integer — the shortfall — and is now the list of named mutants. The integer is mutation.survived. Anything reading survivors as a number now reads a list. There is also mutation.unreached, counting survivors no citing test evaluated.

And check the order of your Java classpath file. The guard's own remedy used to say to append your build's output directories, which yields deps:classes:test-classes while Maven's runtime order is the reverse. Both directories carry the resources Maven copied into them, so the main application.properties was shadowing the test one — on a pilot that lost a test-only Flyway location, killed a shared Spring context, and made citations read did not pass for tests that are green under Surefire. The guard now checks order as well as membership and will refuse a classpath it used to run; the remedy it prints is the whole classpath, already ordered, from your own project.

Smaller things


0.5.3 — 0.5.2's Java half, which 0.5.2 did not contain

If you took 0.5.2 for the Java fix, take this one instead. 0.5.2 is published, signed and verifiable, and the adapter inside it is the one the fix replaced. The Java source landed; the compiled jar the artifact bundles is gitignored, the release host already had one built two days earlier, and it was packaged without complaint. Everything downstream agreed with itself — the checksum matched, the signature was good, the version was right — over the wrong bytes.

The artifact now refuses to build when the jar is older than the Java it ships, and names the source that is newer. A clean git status could never have caught this: it is silent about a file git was told to ignore.

Everything 0.5.2's notes describe is true of this release, and was not true of that one.

0.5.2 — a Java board finishes

If you run Plumb on Java, take this one. A pilot's 1,928-test Spring Boot suite ran for over seven hours without producing a board. The same suite now boards in 43 seconds, against 40.7 seconds for the tests alone.

The observer was arming one JDWP breakpoint per production method, on and off, around every test — cited or not — and each of those is a synchronous round-trip to the observed JVM. On a 1,924-class surface that is 8,517 round-trips a test, and the cost grew as classes loaded: 375 tests in 18.4 minutes and still slowing down. Two things were wrong.

Two defects found on the way, both of which could report something false:

Taking this release asks nothing of you

inert means exactly what it meant, no condition code changed, and no configuration moves. If your board was correct before it is correct now, and if it was slow it is not.

0.5.1 — a signing key you can actually check

The only reason this release exists is the key. Nothing about the tool changed; if you installed 0.5.0 and it works, it still works and it is still the same code.

The key that signed 0.5.0 and every release before it has been withdrawn and published as revoked. gpg --verify on those releases now reports a revoked key with reason for revocation: Key has been compromised, and git tag -v v0.5.0 says the same. That is the revocation working, not a new fault — but it means the signature on 0.5.0 is no longer evidence of anything.

Worse, the installer served from plumbspec.org/install.sh was published the day before the rotation, so it pinned the withdrawn key. Anyone installing with PLUMB_REQUIRE_SIGNATURE=1 was having the install refused: the keyserver correctly hands back nothing usable for a revoked key, and an installer that pins a key it cannot fetch refuses rather than proceeding. That is the right behaviour and the wrong pin. This release replaces it.

Taking this release asks something of you

0.5.0 — the methodology leaves, and Plumb stops reading specs

The largest change is a position, not a feature: Plumb does not read a spec, verify one, or check against one. Everything that did has moved out or gone. If you adopted Plumb for the board, nothing you rely on changed. If you adopted it for the spec gates, they are still available — as a separate product you install yourself.

Taking this release asks something of you

New

0.4.1

Fixed — a spec written by plumb init --spec could not run.

If you had the error

ModuleNotFoundError: No module named 'flowcheck'
!!!! Interrupted: 1 error during collection !!!!

Run

plumb init --spec

0.4.0 — the series, decisions and parameters

0.3.10 — two defects the suite could not see

0.3.9 — a Java run no longer needs a Python test framework

0.3.8 — a run that observed nothing exits non-zero, and the codes are documented

0.3.7 — the Java adapter runs on a JVM that is not the build host's