Repository navigation
Conversation
The first rung of the language proxy that anyone can review on its own: a schema, the tests that pin its encoding, and nothing that decides behaviour. No engine, no client, no gRPC server - so the question a reviewer has to answer is "is this a coherent wire contract", not "is a sidecar correct". Taken by path-scoped checkout from feats/proxy-requirements (#293), where the freeze happened, rather than rewritten - the module layout, the schema, the golden fixtures and the bridges are that branch's own. WHAT THE FREEZE MEANS. After it the wire may only GAIN: nothing may be removed, renumbered, retyped or renamed, and capability tokens rather than a version number are how an addition is negotiated. A protocol with eleven independent implementations cannot be revised casually - the cost of a change is paid eleven times - so freezing makes that cost explicit at design time instead of discovering it at implementation six. The banner at the top of proxy.proto is the authority on the rule; the module README carries what it costs. Three tests pin it in this commit, each failing for a different reason on purpose: GoldenSessionBytesTest holds Java to two checked-in length-delimited streams in BOTH directions, because a same-runtime round trip only proves Java agrees with Java and these bytes are the fixed point independently generated parsers must meet; ProxyProtocolRoundTripTest is the semantic half; GeneratedCodePlacementTest catches generated sources escaping where the build expects them. REACTOR WIRING. The default reactor, not behind -Dpc.foreignClients. That flag exists for modules needing a toolchain the machine may not have; this one is an ordinary JVM module whose only extra requirement is a protoc the build downloads from Maven Central itself. grpc.version moves to the root pom, where the sidecar and the Java client will inherit it - one pin for every module that speaks the protocol, which is what three separate declarations each saying "must match the protocol module's pin" were asking for on #293, and cheapest to do now while there is one consumer. TWO THINGS MASTER REQUIRES THAT THE SOURCE BRANCH DID NOT, both newer than the branch this was cut from, and neither a defect there: - protobuf-java moves 3.25.5 -> 3.25.8. requireUpperBoundDeps fails a graph that settles on a version lower than something in it asks for, and grpc-protobuf asks for 3.25.8 transitively. The protoc that compiles the schema is selected from the same property, so both halves moved together and the golden bytes are unchanged - which the fixtures assert rather than the build assuming. - error_prone_annotations is managed to 2.47.0, module-locally. gRPC drags it in twice at different versions (grpc-stub asks 2.30.0, the guava it brings asks 2.47.0), which is the same rule again. Deliberately not coupled to the root's ${error-prone.version}, which pins the Error Prone COMPILER for an unrelated reason. AND ONE THING NEITHER SIDE COULD HAVE PREDICTED. The two wire-bridge tests each declared an identical `Row` record. Jabel rewrites every record in this build into a class - it rejects an un-annotated one outright, even here where release.target is 17 - and the toString it synthesises trips Error Prone's UnnecessaryStringBuilder on code with no source positions, which does not report a finding but CRASHES the compiler plugin with "invalid replacement: [0, -1)" and fails the module. @SuppressWarnings does not reach it: there is no user-written node to attach to, measured as a control arm before anything was changed. Switching the check off globally was the alternative and is the wrong trade - the root pom's off-set is documented to only ever shrink. The two duplicated records became one shared WireBridgeRow class, which removes the clone the duplication detectors would rightly flag and the crash at the same time. Its javadoc carries this so the next record in this module is not a rediscovery. Upstream-Issue: confluentinc#154
…guide, beside the schema The freeze's premise is that a client author works from two artifacts alone - the frozen .proto and the specification - and never from the sidecar's Java. Landing the schema without the specification would ship half a contract and leave the other half on a branch nobody merges for months. RELOCATED FROM parallel-consumer-proxy/docs/ TO THIS MODULE'S docs/, and deliberately. On #293 both documents sit under the sidecar module because that module landed first; the sidecar is not in this rung, so the alternative was an orphan directory for a module that does not exist. The protocol module is also the better home on the merits: the specification says what the messages MEAN, the guide says what an implementation must DO with them, and the .proto owns only their encoding - all three are the contract, none of them are the server. This will surface as an ordinary rename conflict when #293 merges master; that is the right place to resolve it and not a reason to leave the documents behind. SpecificationCoverageTest moves with the document it reads. It enforces the specification-to-schema diff empty from the generated descriptors - every message, field, enum, enum value and rpc must appear in the section documenting its owner, written in backticks the way the reference writes a schema name - so a schema addition that forgets its documentation goes red with no list to maintain. It carries its own negative control, which is what makes its green mean anything: a fabricated Heartbeat field named `deadline`, `reason` or `window` must be reported undocumented even though all three words are already in the prose. That control is why the check is anchored twice; the unanchored predicate it replaced passed all three. Its SPECIFICATION path resolves against the module rather than the repository root, which is why the relocation needed no change to the constant - only a javadoc line saying so, since that is now load-bearing. Three citations in the guide named files that do not exist on master - the Go client's generator script, the cross-client divergence review, and the clients that generate from this schema. Each is now reached with the git command that reads it on #293 rather than by a dangling path, and the codegen section says outright that no client generates from this schema on master yet, so a reader does not take a forward-looking instruction for a description of the tree. Upstream-Issue: confluentinc#154
…that runs them A frozen schema with nothing enforcing the freeze is a comment. Three things land together, in the order the job runs them. `bin/check-proto-lint.sh` - buf lint. The .proto earns its own static analysis because it is the one artifact every client is generated FROM: a defect there is not one language's bug, it is eleven, arriving in each as generated code nobody reads. buf catches classes no downstream analyser can - an enum whose zero value is not UNSPECIFIED, a package disagreeing with its directory, shared request and response types. The rule set and its two exceptions live in the module's buf.yaml, never in the script. `bin/check-proto-breaking.sh` - buf breaking, FILE category. FILE and not WIRE because a renamed field is wire-identical, and the promise here covers the generated APIs eleven languages sit on rather than only the bytes. Both sides of the comparison are found by SEARCHING for the schema rather than at a hardcoded path, so a PR that renames the module cannot re-enter the "nothing frozen yet" branch and pass green; two copies, or a layout whose module root cannot be derived, exit 2 as "cannot run" rather than passing. `bin/test-check-proto-breaking.sh` - the gate's self-test, which CI runs AHEAD of the gate. Its cases are a deletion, a renumber, a rename, an addition, the renamed-module baseline and an empty baseline; the rename case is what pins the category choice, because nothing else in the repository would notice buf.yaml being weakened to WIRE. Two of them assert the gate's REASON as well as its exit code, because "passed because the schema only gained" and "passed because there was nothing to compare against" are the same 0. ON THE FREEZE BASELINE, stated rather than implied: it is origin/master, so it MOVES. A break is caught on the PR that introduces it and never again, which protects against accidental drift in a pull request but does not protect the contract. That gap and the two candidate fixes are recorded in docs/inflight/ci-pin-the-proto-freeze-baseline.md, brought over from #293 and retagged - its `next-` prefix names a status, which this directory's rules no longer allow, and its pin candidate had to be re-stated because the freeze commit it named is on that branch and will never be reachable from master. ONE PORTABILITY DEFECT FIXED ON ARRIVAL, and its class closed. The gate read its two arrays with `mapfile`, a bash 4 builtin; macOS ships bash 3.2, where the line dies with "mapfile: command not found" and the gate exits 127 - a gate that has NOT run, reported as neither verdict, which is the exact failure its own header argues about. It failed bin/check-all.sh on the first sweep here while passing every ubuntu runner. ShellCheck has no bash-version awareness at all, so nothing static caught it. Swept for other instances: the only other `mapfile` in the tree is the deliberate fixture in bin/test-check-shell-lint.sh asserting shellcheck's blindness to it, and bin/check-branch-self-reference.sh already carries a comment saying not to use it - knowledge that survived only as prose. So the class now has a bash-4-builtin row in bin/check-shell-hazards.sh with four self-test arms (both spellings fire; the while-read replacement and a word merely containing the name stay clean), and the lint self-test's claim that the macOS lane is the only detector is narrowed to what is still true. The protoc cache warm in maven.yml's prepare-deps job comes with the module it warms for: go-offline does not fetch the platform protoc and protoc-gen-grpc-java EXECUTABLES that protobuf-maven-plugin resolves for itself at generate time, and cold they inherit the Azure West US CDN route lottery - measured on #293 as a 272ms fetch in one job and a 240s hang on the identical URL in another, failing 2 of 6 consecutive runs. It reads both versions out of the module's pom and refuses a non-version-shaped answer, because warming the wrong coordinate is indistinguishable from a cache hit until it fails four jobs downstream. `proto: breaking` is a new job name and is NOT a required status check yet; adding it to the master ruleset is a separate, deliberate act, and docs/ci.md says so beside the rest of what the job does. Upstream-Issue: confluentinc#154
…ows, and a module-table entry A module in the reactor and in no release-documentation record is a module a release reader cannot place. This one has something real to say, which is what distinguishes it from the eleven client shells #380 deliberately left rowless: the schema is frozen, its encoding is pinned by checked-in bytes, and its specification is enforced against it. The rows say two things that are easy to get wrong in opposite directions. The api_expectation is STRONGER than every other module's - frozen, not pre-1.0 - because the wire may only gain and a gate fails a change that tries. The reliability claim is WEAKER, because no engine and no client has carried a record over this schema on master: the evidence is about a CONTRACT, which is why the evidence row's limitation says so in a sentence rather than leaving a reader to infer it from what is absent. WRITTEN INTO THE TRACKED RECORDS, not as docs/data/*.d/ fragments. The per-module fragment machinery exists on #293 and is not on master; #380 declined to import it and wrote down why. Nothing about this rung changes that argument - the mechanism rides the extraction that needs a merge path, a schema extension and a deferral gate, and one honest row needs none of them. bin/check-docs-data.sh validates these files today, so the rows are checked rather than merely written. The fragments this module carried on #293 were not copied: they predate the freeze commit and still describe the schema as "provisional until the freeze unit completes", with an api_expectation saying fields may be renamed and renumbered without ceremony. That is the opposite of what the module now claims, and it is a small worked example of why a record that nothing checks against the thing it describes goes stale quietly. Upstream-Issue: confluentinc#154
Dependency ReviewThe following issues were found:
License Issues.github/workflows/repo-hygiene.yml
OpenSSF ScorecardScorecard details
Scanned Files
|
✅ Duplicate Code ReportTwo engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check. ✅ PMD CPD
|
| PR | Base | Change | |
|---|---|---|---|
| Clones | 93 | 88 | :face_with_monocle: +5 |
| Duplicated lines | 1313 | 1257 | :face_with_raised_eyebrow: +56 |
| Duplication | 1.01% | 1.00% | 🫤 +0.01% |
| Rule | Limit | Status |
|---|---|---|
| Max duplication | 2% | ✅ Pass (1.01%) |
| Max increase vs base | +0.1% | ✅ Pass (+0.01%) |
⚠️ 5 new clones introduced
- 11 lines:
parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/ProxyProtocolRoundTripTest.java:74<->parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/ProxyProtocolRoundTripTest.java:59 - 8 lines:
parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/GoldenSessionFixture.java:10<->parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/ProxyProtocolRoundTripTest.java:10 - 9 lines:
parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/GoldenSessionFixture.java:21<->parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/ProxyProtocolRoundTripTest.java:20 - 16 lines:
parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/GoldenSessionFixture.java:132<->parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/GoldenSessionFixture.java:101 - 17 lines:
parallel-consumer-proxy-protocol/src/main/proto/parallelconsumer/proxy/v1/proxy.proto:230<->parallel-consumer-proxy-protocol/src/main/proto/parallelconsumer/proxy/v1/proxy.proto:153
Powered by astubbs/duplicate-code-cross-check
| .addTopics("demo-topic") | ||
| .setMaxConcurrency(2) | ||
| // exactly one entry - map serialization order across entries is unspecified | ||
| .putKafkaProperties("bootstrap.servers", "localhost:9092") |
There was a problem hiding this comment.
parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/GoldenSessionFixture.java:132
| .setMaxConcurrency(16) | ||
| .putKafkaProperties("bootstrap.servers", "localhost:9092") | ||
| .addCapabilities("dispatch") | ||
| .setLaunchToken("per-launch-secret") |
There was a problem hiding this comment.
parallel-consumer-proxy-protocol/src/test/java/bz/stub/parallelconsumer/proxy/protocol/ProxyProtocolRoundTripTest.java:74
🧪🔒 Quarantine Lane Report
🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its |
|
…est is tested rather than skipped `shell: macos` went red on the first run of this branch with "CANNOT RUN on macOS (exit 2)" for bin/test-check-proto-breaking.sh: hosted macOS ships no buf, the self-test fails closed without it, and this lane deliberately refuses to count a skip as a pass. Third instance of the same environment gap, after PyYAML and ShellCheck, and fixed the same way the lane's own error message prescribes. It is the instance that lane most needed. bin/check-proto-breaking.sh is the only gate in bin/ that reads its inputs into ARRAYS, which is exactly where the bash-3.2 divergence this job exists for lives - it shipped with a `mapfile` (bash 4; exit 127 on 3.2, a gate reporting neither verdict), every ubuntu runner passed it, and a developer box caught it. Without buf here, the one suite that exercises that code on 3.2 was the one suite the lane could not run. bufbuild/buf-action rather than `brew install buf`: the version is already pinned by that action in maven.yml's `proto: breaking` job, so the two lanes cannot drift onto different buf versions - and bin/check-action-versions.sh enforces the single-version rule across the directory. Installed BEFORE the bash-3.2 pin, so the action's own setup runs on the runner's default bash rather than under the pin the job applies to what is under test. Upstream-Issue: confluentinc#154
…s engine back Merges `feats/proxy-dispatch-clients` (#390, the top of the Wagon A stack) into this branch, so that #293 can be retargeted onto that rung and its displayed diff collapse to the engine residue. This is the plan's "retarget move" - `docs/plans/2026-08-31-001-process-god-branch-decomposition-plan.md` - and it is an ordinary merge: no history was rewritten and nothing was force-pushed. The pre-merge tip is preserved as `origin/backup/pre-stack-merge-293`. The merge also brings `master` forward, because the stack is current with it and this branch was not. That is most of the volume and most of the conflicts. ## Which side won, and why The rule was: the god branch's SEMANTIC content, expressed on the stack's refined STRUCTURE. - **Engine-only files stayed** - `ProxyProcessor`, `ConfigureHandler`, the dispatch waves, epochs, leases, reconnect and the produce path. They are the residue this campaign exists to expose. - **Stack-only files were taken** - the ten foreign clients, the runner registry, the sidecar shim, the self-retiring guards, `Main` and `ParentDeathWatchdog`. - **Records lost, everywhere.** The stack converted five value types from Java records to plain final classes because Jabel rewrites a record into a class with no source positions and Error Prone 2.42.0 crashes rather than reporting on it. This branch never met that only because its root pom had no Error Prone; master's does now, so the conversion is load-bearing here. - **Dependency pins are the stack's**: protobuf 3.25.8 (`requireUpperBoundDeps` refuses 3.25.5 against grpc-protobuf's transitive ask), gson 2.14.0, `error_prone_annotations` 2.48.0 in the proxy and the Java client aggregator and 2.47.0 in the protocol module - deliberately unequal, because their highest requesters differ. - **`clients.yml` is this branch's**, not the stack's. The stack's copy is a smaller earlier file whose own header says it has no conformance lane, no per-language static analysis and no dependency audit; only its `--component clippy` fix and its workflow-level `PC_FOREIGN_CLIENTS_STRICT` were folded in. - **Master's copy won for everything master owns** - the gate scripts, the copyright checker and its self-test, `repo-hygiene.yml`, `docs/ci.md`, `docs/copyright.md`. This branch's only delta in most of those was a copyright header in a pre-canonical form, written before #338 landed the canonical one on master. - **The two ledgers were merged as unions**, not resolved to a side. `docs/inflight/bug-857-family.md` keeps both sides' sightings, this branch's renumbered per the file's own stated convention; `docs/quarantined-tests.md` drops three entries whose owning PRs retired the annotation on master, because a registry entry without a `@Quarantined` test is a hard gate failure. ## The verdict-free work return, re-expressed on master's shape Unit U4 is still on this branch as `WorkManager.onAbandonedResult` and its call sites, and `WorkManager.java` auto-merged keeping them - so resolving `WorkContainer`, `ProcessingShard` and `ShardManager` to master alone would have compiled to nothing. Master moved underneath it: #335 collapsed `inFlight` and `maybeUserFunctionSucceeded` into one atomic `ExecutionState`, and #336 and #373 replaced the shard's raw `availableWorkContainerCnt` with a compare-and-set claim. `ProcessingShard.onAbandoned` and `ShardManager.onAbandoned` are therefore taken verbatim from `feats/proxy-verdict-free-return` (#295), which is cut on current master and is the authoritative re-expression - it routes the restore through `includeInSelection` rather than incrementing a counter that no longer exists. `WorkContainer` is NOT #295's, and the difference is deliberate: that branch marks abandonment with an `AtomicBoolean` cleared by the claim winner, while this branch keys it by delivery (`markAbandoned(long)`, `isAbandonedForCurrentDelivery`, `isReturnForSupersededDelivery`). The delivery-keyed form is strictly more: it tells a late return for delivery n from a live return for delivery n+1, which the boolean cannot, and acting on that confusion ends a running flight and decrements `numberRecordsOutForProcessing` twice. It also needs no clearing on redelivery, so it does not race the claim. Master's own `deliveryCount` - already an `AtomicLong` incremented only by a won claim - supplies the identity, so the field is the one thing added. ## The reconciliation duties #387 and #390 recorded Both are discharged, and the guards they left behind now pass because the gap closed rather than because anything was edited. **`ProxyHarness` and `ConformanceHarness` are one class again.** #387 cut the latter out of the former with the engine lane removed, and wrote into the class header, the module pom and a deferred inflight note that whoever landed the engine must merge them - leaving open which module it ends up in, on the grounds that "the engine lane decides". It decides the sidecar module: the lane needs `ConfigureHandler` and `ProxyProcessor`, and `TestModeMain` - the process a spawned foreign runner actually starts - is in that same test tree and would otherwise have to import the conformance module and close a cycle. So the NAME is the extracted rung's and the MODULE is this branch's, and `parallel-consumer-proxy-conformance` reaches it through the proxy test-jar its pom already declared. The stack's refinements to the class survive: `Delivery` stays a plain final class, and the `startEmbeddedClient` lane stays beside the restored `startEngine`. **`ConformanceDriver.spawnAgainst` is back**, with `LanguageRunner` a `ConformanceBinding` again - it stopped being one on #390 for exactly as long as there was no engine to spawn against - and the ten foreign bindings are registered in `ConformanceBindings.selectable()`. `ConformanceBindings.deferredUntilTheEngineArrives()` is kept and now returns an empty list, which is the honest reading: nothing is deferred. It is not deleted because `TheEngineArrivingMustBringTheForeignCellsTest` reads it, and because the next capability to arrive without a cell needs that list rather than a new one. ## Two duplications the merge surfaced, resolved rather than left - **The protocol documents existed twice.** #383 moved `protocol-specification.md` and `client-authoring-guide.md` into `parallel-consumer-proxy-protocol/docs/` and predicted the rename conflict here. The protocol module wins on the merits - all three artifacts are the contract rather than the server - but the CONTENT is this branch's, because the stack's copies carry "no client generates from this schema yet" and a `git show` fallback for a Go generator script that exists here. `SpecificationCoverageTest` moved with them; every citation was re-pointed. - **`HarnessScenario` existed twice**, once per harness. The stack's plain-class version won, in the sidecar module beside the class it serves. ## Five Java records had to become plain final classes, or nothing compiled `master` gained Error Prone after this branch forked, and Error Prone 2.42.0 does not report on a Jabel-desugared record - it crashes, failing the whole compilation with a message attributed to line 1 of an unrelated file. #387 hit this one rung down and converted its five value types; the same thing was waiting here for `ProxyProcessor.ManifestOutcome`, `ManifestReconciler.Reconciliation`, `InFlightRegistry.InFlight`, `LivenessSettings` and `TestModeMainTest.Run`. Neither term can move - the root pom pins Error Prone at 2.42.0 because 2.43.0 needs a JVM this build cannot use, and Jabel serves the release 8 target - so the records lost, which is what the rest of the repository already does. `grep -rn "record "` now finds none. ## Two defects this merge introduced and then fixed - **The default lane ran a test that needs a profile.** Keeping BOTH sidecar-spawning tests - this branch's engine-backed `OneRecordThroughTheSidecarTest` and the stack's engine-less `SidecarHandshakeTest` - left the Kotlin and Scala exclusion property naming only the first, so the second ran in an ordinary build with no `sidecar-classpath.txt` and failed. Both are now behind one regex, which is also the arrangement that makes the pairing legible. - **Every proxy module's data record existed twice.** This branch keeps module-maturity and testing-evidence records in per-module `docs/data/*.d/` fragments; the stack, which never imported that mechanism, wrote them into the monolithic files. The merge kept both and `bin/check-docs-data.sh` went from green to 37 structural problems. The fragments win, and deleting the monolithic copies also deleted eleven rows asserting "NO RECORD HAS CROSSED THE WIRE" - true on a stack whose sidecar hosts no engine, false here. ## How it was verified JDK 17, macOS. Full default reactor `./mvnw --fail-at-end test`: **BUILD SUCCESS**, every module green including the conformance suite - so the `java-grpc` cell runs against a live engine for the first time, and `GrpcSpikeConformanceTest` answers all five scenarios over a real gRPC stream. The four self-retiring guards #387 and #390 left behind all pass, and **none of them was edited**: `TheEngineArrivingMustBringTheGrpcBindingTest`, `TheEngineArrivingMustBringTheGrpcCellTest`, `TheEngineArrivingMustBringTheForeignCellsTest` (which asserts about every registered language, not one) and `SelectorMatchingNothingFailsTest`. `bin/check-all.sh` real exit code 1, with three gates failing and all three inherited, established against the pre-merge tip rather than assumed: `check-inflight-tags` reports the same 53 problems before and after; `check-file-refs` was already red for the `@`-prefixed bridge imports that #378 fixes; and `check-branch-self-reference` did not exist on this branch before the merge - its findings are on notes inherited from `master` byte-identical. `check-proto-lint` and `check-proto-breaking` report CANNOT RUN for want of `buf`. **Not run locally: `-Dpc.foreignClients`.** It builds C++ and Swift inside containers, and this box is under a low-disk warning that names a container build as what would tip it over. CI's per-language rows are where that lane runs. ## Two findings recorded rather than fixed - `ParallelEoSStreamProcessorTest.processInKeyOrder` failed its own preamble sanity check once here - and the control arm reddened `master` HARDER: unmodified `master`, uncontended, failed all three parameterisations where this branch under concurrent load failed one. That refutes the existing ledger's "the base branch was green" row, and is the first evidence the flake is master-state. Recorded in `docs/inflight/test-processinkeyorder-sanity-check-races-the-first-poll.md`. - One `java-grpc` conformance run left its last record unsettled, 1 of 2 full-reactor runs and green 3 of 3 in isolation. `core` and `java-direct` were green in the same run, so the suspect is the engine's settle path under a full ceiling rather than the scenario. The scenario was NOT weakened - `docs/inflight/proxy-a-java-grpc-ceiling-run-left-its-last-record-unsettled.md`. ## What is deliberately NOT done here `Main#sessionServiceFactory` still returns `NoEngineSessionService`, so the production entry point hosts no engine. That is unit U10's, which #384 named as still having no PR, and it owns the drain that must land with the engine - plus eight cross-language handshake tests assert the `UNIMPLEMENTED` refusal by status code specifically. Recorded rather than faked, in `docs/inflight/proxy-the-production-entry-point-still-hosts-no-engine.md`. The engine is exercised end to end through `TestModeMain` regardless.
Part of #242.
depends on #380
Description
The proxy protocol's frozen v1 wire contract, and the gates that keep it frozen, as a rung
nobody has to read an engine to review. A schema, its specification, the guide a client author works
from, checked-in golden bytes, and
buf lint/buf breaking. No sidecar, no client, no gRPCserver.
The claim this PR makes, and what to check it against: the contract is coherent and complete on
its own terms - the specification documents every element of the schema, and the schema encodes to
bytes that are pinned rather than regenerated - and the compatibility gate genuinely detects an
incompatible change rather than merely being present. Both halves were verified by making them
fail; the evidence is below.
What "frozen" means here
After the freeze the wire may only gain. Nothing may be removed, renumbered, retyped or renamed,
and an addition is negotiated through a capability token rather than a version number - there is
deliberately no version field, because named tokens compare by set intersection and every language
spells that identically. A protocol with eleven independent implementations cannot be revised
casually: the cost of a change is paid eleven times, so freezing makes that cost explicit at design
time instead of discovering it at implementation six.
What is in it
parallel-consumer-proxy-protocol-proxy.protoand its generated Java, plus the twohand-written well-known-type bridges (
WireDurations,WireTimestamps) that are the singleimplementation each for every JVM speaker of the protocol. No other production Java.
docs/. The freeze'spremise is that a client author works from the schema and the specification alone and never from
the sidecar's Java; the guide beside them says what an implementation must actually do.
GoldenSessionBytesTestholds Java totwo committed length-delimited streams in both directions - parse to exact values, serialize to
exact bytes - because a same-runtime round trip only proves Java agrees with Java, and these bytes
are the fixed point independently generated parsers must meet.
ProxyProtocolRoundTripTestis thesemantic half.
SpecificationCoverageTestenforces the specification-to-schema diff empty from thegenerated descriptors, so there is no list to rot.
GeneratedCodePlacementTestcatches generatedsources escaping where the build expects them.
bin/check-proto-lint.sh-buf lint. The.protoearns static analysis of its own becauseit is the one artifact every client is generated from: a defect there is not one language's bug,
it is eleven, arriving in each as generated code nobody reads.
bin/check-proto-breaking.sh-buf breaking, FILE category, plusbin/test-check-proto-breaking.sh, its self-test.proto: breakinginmaven.yml, running lint, then the self-test, then the gate. It is a newjob name and is not a required status check; adding it to the master ruleset is a separate,
deliberate act, and
docs/ci.mdsays so.Where this came from
Extracted from #293, the language-proxy god PR, as extraction A3 of
docs/plans/2026-08-31-001-process-god-branch-decomposition-plan.md- the rung above#380, whose "left for later rungs" list names exactly this content. The
schema, the fixtures, the bridges and the gates are that branch's own, taken by path-scoped checkout
rather than rewritten.
Both red arms, reproduced on this branch
#293's body claims the gate was "proven able to fail on a field deletion and
on a renumber". That was a one-off demonstration on one machine, so it was redone here - and the
baseline matters: against
origin/masterthe gate takes its grace branch ("nothing frozen tocompare against") and passes whatever you do to the schema, so a sabotage run against the default
baseline would have proved nothing. Both arms therefore ran with
PROTO_BREAKING_BASELINE_REFpointed at this branch's own committed schema, with a control arm oneither side.
The schema was restored from a pristine copy after each arm and
git statuswas clean afterwards.bin/test-check-proto-breaking.shruns the same two arms plus a rename (wire-compatible, soonly
FILEcatches it - this is what pins the category, since nothing else in the repo would noticebuf.yamlbeing weakened toWIRE), an addition, a renamed-module baseline and an empty baseline.It was run twice, the second time under
GIT_AUTHOR_NAME='' GIT_COMMITTER_NAME=''- thehosted-runner condition that
1c4101780fixed, and stricter than the runner, because an emptyenvironment identity outranks every config level. All eight cases pass on both arms.
And the golden bytes, which a green gate says nothing about
The byte fixtures are a separate mechanism and got their own arm: one digit changed in the fixture's
beyond-int32 epoch constant, committed resources untouched.
Restored, and the same four tests pass.
Reactor wiring, and why
The default reactor, not behind
-Dpc.foreignClients. That flag exists for modules needing atoolchain the machine may not have; this one is an ordinary JVM module whose only extra requirement
is a
protocthe build downloads from Maven Central itself, so there is nothing for the flag toguard and nothing to skip loudly.
grpc.versionmoves to the root pom. #293 declares it in three modules,each with a comment saying "must match the protocol module's pin" - three places to bump, where a
bump that updated two looked green, and a CVE fix on that branch had to find all three by hand.
Maven properties do not flow between siblings but do flow down, so one property is what those three
comments were asking for. It is cheapest to do now, with one consumer and nothing to reconcile.
Deliberately left for the rungs above
Named so neither side reads as an oversight:
generation work", not "read the generated lines". Nothing under
parallel-consumer-proxy-clientsgenerates from this schema yet, and the client-authoring guide'scodegen section says so outright rather than describing a tree that does not exist.
reference client (A7); the conformance framework (A8); the eleven dispatch clients (A9).
src/test/CLAUDE.mdwrite-time bridge. Its target,docs/testing-at-write-time.md, ships with docs(testing): make prove-it-red arrive when a test is written, not when it is looked up #378, which is still open -and so does the file-reference gate's parser fix that these cross-directory bridges need. It
arrives with the modules it belongs to, as that PR's own comment on feat(proxy) astubbs#242: a sidecar giving non-JVM runtimes key-ordered concurrency, in eleven languages #293
says.
docs/data/*.d/fragment mechanism. build(polyglot) astubbs#242: eleven language client modules, each proving its toolchain reaches a running program #380 declined it and wrotedown why; nothing here changes the argument. This module's maturity and evidence rows go straight
into the tracked records, which
bin/check-docs-data.shalready validates.Four places this differs from #293 on purpose
parallel-consumer-proxy/docs/tothis module's
docs/. The sidecar is not in this rung, so the alternative was an orphandirectory for a module that does not exist - and the protocol module is the better home anyway:
the specification says what the messages mean, the guide says what an implementation must do, the
.protoowns only their encoding, and all three are the contract rather than the server. Thiswill surface as an ordinary rename conflict when feat(proxy) astubbs#242: a sidecar giving non-JVM runtimes key-ordered concurrency, in eleven languages #293 merges
master.SpecificationCoverageTestmoves with the document it reads; its path constant already resolvedagainst the module, so nothing about it changed but a javadoc line saying so.
master'srequireUpperBoundDepspostdates the branch thiswas cut from and fails a graph that settles on a version lower than something in it asks for -
grpc-protobufasks for 3.25.8 transitively. Theprotocthat compiles the schema is selectedfrom the same property, so both halves moved together; the golden bytes are unchanged, which the
fixtures assert rather than the build assuming.
error_prone_annotationsmanaged to 2.47.0, module-locally. Same rule again: gRPC drags it intwice at different versions. Deliberately not coupled to the root's
${error-prone.version},which pins the Error Prone compiler for an unrelated and temporary reason.
WireBridgeRowclass instead of each declaring an identicalRowrecord. See below - this one is not cosmetic.Three things this PR found
A record in this build crashes the compiler. Jabel rewrites every record into a class - it
rejects an un-annotated one outright, even in this module where
release.targetis 17 - and thetoStringit synthesises trips Error Prone'sUnnecessaryStringBuilderon code with no sourcepositions. That does not report a finding: it crashes the plugin with
invalid replacement: [0, -1)and fails the module.
@SuppressWarningsdoes not reach it, there being no user-written node toattach to, which was measured as a control arm before anything was changed. Switching the check off
globally was the alternative and is the wrong trade - the root pom's off-set is documented to only
ever shrink. Folding the two duplicated records into one shared class removes the clone the
duplication detectors would rightly flag and the crash at once, and its javadoc carries the reasoning
so the next record here is not a rediscovery.
The freeze gate shipped with a
mapfile, andbin/check-all.shcaught it on the first sweep.mapfileis a bash 4 builtin; macOS ships bash 3.2, where the line dies withmapfile: command not foundand the gate exits 127 - a gate that has not run, reported as neither verdict, which isthe exact failure the gate's own header argues about. Every ubuntu runner would have passed it, and
ShellCheck has no bash-version awareness at all. Swept for the class: the only other
mapfilein thetree is the deliberate fixture in
bin/test-check-shell-lint.shasserting shellcheck's blindness toit, and
bin/check-branch-self-reference.shalready carries a comment warning against it -knowledge that survived only as prose. So the class now has a
bash-4-builtinrow inbin/check-shell-hazards.shwith self-test arms in both directions, and the lint self-test's claimthat the macOS lane is the only detector is narrowed to what is still true.
The module's data fragments on #293 are stale, and were not copied. They
predate the freeze commit and still call the schema "provisional", with an
api_expectationsayingfields may be renamed and renumbered without ceremony - the opposite of what the module now claims.
Fresh rows were written instead, saying the two things that are easy to get wrong in opposite
directions: the API expectation is stronger than any other module's (frozen, not pre-1.0), and the
reliability claim is weaker, because this is evidence about a contract and no engine or client has
carried a record over it.
What CI added that the local runs could not
shell: macoswent red, and it was right to. Hosted macOS ships nobuf, sobin/test-check-proto-breaking.shexited 2 - and that lane deliberately refuses to count a skip as apass. Third instance of the same environment gap after PyYAML and ShellCheck, fixed the way the
lane's own error message prescribes, and the instance that lane most needed:
bin/check-proto-breaking.shis the only gate inbin/that reads its inputs into arrays, which isexactly where the bash-3.2 divergence that job exists for lives. Installed through
bufbuild/buf-actionrather thanbrew, so the two lanes cannot end up on different buf versions.The duplication reports, read
dups: clonespasses both engines - PMD CPD and jscpd are each within their absolute andvs basethresholds.dups: similarityis red on theTestConventionsArchTest.javafamily, andthat duplication is deliberate and required by a different gate:
EveryModuleWiresUpArchUnitTestfails any module with asrc/test/javaand no wired wrapper, andits javadoc records why the wrapper cannot be inherited instead (surefire's
dependenciesToScanpulls the whole test-jar into every module). Nine copies already sit at ~80-86% against each other on
master; this is the tenth, and it only reads as new because the file is. Same finding, same answeras #380.
The clones the CPD engines did name are all one shape:
ConfigureversusConfigured, in thegolden fixture, the round-trip test and the schema itself. They are two distinct generated message
types with an overlapping field set and no common supertype for the setters, so there is no builder
to share - and the fixture's value is that it spells every field literally, which is what makes the
committed bytes inspectable. Collapsing them behind reflection would remove the clone and the point
of the fixture together.
The analysis surfaces, read
bin/check-pr-analysis-surfaces.sh 383reports two findings on lines this diff wrote, both thesame SpotBugs suggestion and both declined with a reason rather than silently:
WireDurations.toJava(Duration)andWireTimestamps.toJava(Timestamp)"could be declared as"DurationOrBuilder/TimestampOrBuilder. They could, and they must not. These two methods are thewire-to-JVM boundary for a frozen protocol, and their parameter type is part of what they
promise: a
Durationis a message somebody could have sent, while a*OrBuilderalso admits amutable, mid-construction builder - so widening them would let a caller obtain a
java.time.Durationfor a value that was never on any wire. In a module whose entire purpose is "what exactly was in the
message", trading that for a widened parameter is the wrong direction. Nothing inherited was
reported.
Known-red, and not a fault
Check PR Dependenciesis red until build(polyglot) astubbs#242: eleven language client modules, each proving its toolchain reaches a running program #380 merges. That is thedependency gate doing its job on a stack rung, not something to fix.
claude-reviewis red until somebody asks for a review; the base rung is in the same state.roadmap-stage: N/A - the
language-proxy-sidecarentry names #293 as its carrier and sits atlimited-pocon the strength of that PR's content. Landing a frozen schema and its gates on master makes no more advanced artifact exist - there is still no sidecar and no client - so its stage is unchanged.Checklist
that owns the contract), the module
README.md,docs/ci.mdfor the new job and its three steps,AGENTS.md's module table, anddocs/inflight/ci-pin-the-proto-freeze-baseline.mdfor the opengap in what the gate asserts.
docs/features/- N/A - nothing here is auser-facing feature. The module publishes nothing to any registry and there is no sidecar to speak
the protocol yet; the release-documentation records it does earn are the maturity and evidence
rows, which are in this PR.
arms and a golden-byte assertion were proven able to fail by sabotage, with control arms, above.
ce-simplifyandce-code-reviewlocally - N/A - neither was run. The content is a frozenschema, two documents and two gate scripts taken from feat(proxy) astubbs#242: a sidecar giving non-JVM runtimes key-ordered concurrency, in eleven languages #293, where they
were already reviewed; the changes this rung made to them are named one by one above, and the
cheaper place to spend a review is
@claude review thison the PR, which is the only route thatopens inline threads.