Skip to content

fix(test): loopback-bind test listeners and land socket instrumentation - #255

Open
cuioss-oliver wants to merge 38 commits into
mainfrom
feature/loopback-stall-fix-and-instrumentation
Open

fix(test): loopback-bind test listeners and land socket instrumentation#255
cuioss-oliver wants to merge 38 commits into
mainfrom
feature/loopback-stall-fix-and-instrumentation

Conversation

@cuioss-oliver

@cuioss-oliver cuioss-oliver commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Intent

Every live-socket fixture in the api-sheriff test tree bound its ephemeral server with the
single-argument listen(0) overload — the dual-stack wildcard — while every client in those same
fixtures dialled 127.0.0.1. That mismatch is the measured cause of the macOS-local 30-second test
stall: a wildcard ephemeral bind can be won by a foreign loopback listener, and the awaiting client
then burns its full connect ceiling.

This PR binds all 20 such sites to loopback through one shared testsupport constant, lands the
socket-state instrumentation that captures the kernel's own view at every timeout, replaces the
refuted surviving-hypothesis section of the build-gate discipline doc with the measured mechanism,
closes out the originating lesson, and adds an ArchUnit fitness function plus a source sweep that
refuse the reintroduction of every wildcard spelling.

The fix is behaviour-preserving: every dial target across all nine affected fixtures was already
127.0.0.1, so binding the listener to loopback changes no test semantics.

Deliverables

  1. Loopback bind — new LoopbackHost.ADDRESS; all 20 bare .listen(0) sites across nine
    fixtures bind explicitly to loopback, and the 24 inline 127.0.0.1 literals route through it.
  2. Socket-state instrumentationSocketSnapshot lands in the test tree; Awaits carries the
    OS socket view, a thread dump, and elapsed time on every timeout.
  3. Documentation — the build-gate discipline doc records the measured mechanism. The refuted
    kqueue-readiness reading is retained and labelled SUPERSEDED, with an account of how it failed.
  4. Lesson close-out2026-08-29-16-002 updated as a record-only, purely additive change.
  5. ArchUnit guard + source sweepLoopbackEphemeralBindArchTest bans both bare bind spellings
    at the bytecode level, and a companion source sweep catches the wildcard host literal the
    bytecode rule structurally cannot see (listen(0, "0.0.0.0") and listen(0, LoopbackHost.ADDRESS)
    are the same call target). Four specimens pin the sweep's reach: single-line, wrapped across
    lines, variable port, and nested port call.

Verification

  • Criteria re-derived, not assumed: bare .listen(0) count 0 across 972 files scanned, no
    unreadable or elided paths; 127.0.0.1 in the nine fixtures 24 → 0; new ServerSocket(0) exactly
    4, all in TlsEdgeProducerTest.
  • Instrumentation legs proven disjoint — each dropped in turn reddened only its own probes.
  • Both guards proven falsifiable — reverting a call site to listen(0) failed the ArchUnit rule
    with one violation naming file and line; wrapping one across three lines failed the sweep the same
    way. Other probes stayed green each time, so both discriminate rather than blanket-fail.
  • Both project gates green on the final head, working tree clean after each.

What CI established that local runs could not

The Linux lane refuted a documented assumption of this plan, and the correction is part of the diff.

The plan recorded that netstat is absent from ubuntu-latest (with lsof present), so the
capture would abstain there. CI showed the opposite: netstat is present, so the availability
probe returned true and the netstat half ran — while its invocation (-anv -p tcp) and port pattern
were macOS-only, so it produced nothing usable on Linux. The netstat half had never worked there.

It stayed invisible because the probe asserting the capture had worked scanned the combined
section, so lsof output satisfied it. Splitting that assertion per tool — itself a CodeRabbit
finding on this PR — is what turned a silent degradation into a red build. Both the invocation and
the port pattern are now platform-selected, and the doc records the episode.

Review

Five review rounds; every finding is fixed, replied to individually, and its thread resolved. One
suggestion was declined with reasoning on the thread: required_bots names the registry
bot_kind (pr-agent), not the reviewer's GitHub author login (cuioss-review-bot) — see the
thread on .plan/marshal.json for why the login form cannot satisfy the participation quorum.

Notably, all four rounds after the first found defects in the guard added during review, not in
the deliverables above, which have been stable since they landed.

Summary by CodeRabbit

  • Bug Fixes

    • Improved test-network reliability by consistently binding local services to the loopback address.
    • Enhanced timeout diagnostics with TCP socket snapshots.
  • Tests

    • Added automated checks to detect unsafe wildcard ephemeral socket bindings.
    • Expanded coverage for loopback binding behavior and socket diagnostics.
  • Documentation

    • Updated development guidance with troubleshooting information and local network test behavior.
    • Documented socket-binding safeguards and platform-specific diagnostic considerations.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @cuioss-oliver, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 4 days and 17 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The test suite now uses explicit loopback bindings, captures socket state in timeout diagnostics, and enforces bind usage with ArchUnit. Documentation records the measured macOS mechanism. Review-plan metadata replaces one required review bot.

Changes

Loopback bind policy

Layer / File(s) Summary
Loopback fixture binding
api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/LoopbackHost.java, api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/*, api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/*
Adds LoopbackHost.ADDRESS and applies it to gateway and TLS test server binds, client connections, and upstream routes. Documents intentional wildcard binds in TlsEdgeProducerTest.
Socket snapshot diagnostics
api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/SocketSnapshot.java, api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/Awaits.java, api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/AwaitsTest.java
Adds bounded lsof and netstat capture, appends snapshots to timeout messages, and tests socket visibility.
ArchUnit bind guard
api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/LoopbackEphemeralBindArchTest.java, api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/*
Detects bare ephemeral ServerSocket(int) and listen(int) calls, scans wildcard host literals, and validates matched controls.
Measured mechanism documentation
doc/development/build-gate-discipline.adoc
Revises the macOS mechanism, evidence labels, controls, unresolved findings, contributor rules, and cross-platform snapshot note.

Review plan metadata

Layer / File(s) Summary
Review bot requirement
.plan/marshal.json
Replaces cuioss-review-bot with pr-agent while retaining coderabbit.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to e3239

The bind fix is otherwise ready, but add coverage for all documented wildcard-host forms and clarify Linux socket output so future guard regressions and timeout investigations remain reliable.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: explicit loopback binding for test listeners and socket instrumentation for diagnostics.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cuioss-review-bot

cuioss-review-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit e32394a)

🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

sourcery-ai[bot]
sourcery-ai Bot previously approved these changes Sep 3, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sourcery assessment

Approved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 73d9a70f-310e-4f4e-932f-0c8af6be0d89

📥 Commits

Reviewing files that changed from the base of the PR and between 1f4fc24 and 81e4952.

📒 Files selected for processing (20)
  • .plan/marshal.json
  • .plan/project-architecture/_project.json
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/LoopbackEphemeralBindArchTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/LoopbackEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/WildcardEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgePipelineTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteBffWiringTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcDispatchStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcStatusMapperTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/ReservedBodyCeilingTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/WebSocketRelayStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/Awaits.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/AwaitsTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/LoopbackHost.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/SocketSnapshot.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/PassthroughRelayTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/SniFrontListenerTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/TlsEdgeProducerTest.java
  • doc/development/build-gate-discipline.adoc

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/AwaitsTest.java Outdated
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cuioss-oliver added a commit that referenced this pull request Sep 3, 2026
`listen(0, "0.0.0.0")` and `listen(0, LoopbackHost.ADDRESS)` are the same call
target — same name, same parameter types — so the ArchUnit rule accepted both
while the first rebuilds exactly the wildcard ephemeral bind the guard exists
to refuse. The discriminator is the argument's text, which lives in the source
rather than the bytecode, so a wider ArchUnit rule cannot reach it.

Adds a source sweep for the three wildcard host spellings (0.0.0.0, ::, empty)
with its own matched controls: a positive control asserting the sweep finds the
specimen's deliberate literal, a negative control asserting it leaves the
loopback-bound spelling alone, and a non-vacuity check on the scanned source
set. Records the bytecode rule's structural limit in its Javadoc so its green
is not read as more than it proves.

Falsifiability demonstrated: reverting one guarded call site to
`listen(0, "0.0.0.0")` failed the sweep with exactly one violation naming file
and line, while the other eight probes stayed green.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
cuioss-oliver added a commit that referenced this pull request Sep 3, 2026
… section

`SocketSnapshot.available()` proves only that both binaries are executable, and
`capture()` renders their output into one section. The probe scanned the whole
section, so an `lsof` address satisfied it even when `netstat` contributed no
matching row — the probe would pass on a half-degraded capture while claiming
the OS snapshot saw the socket.

Splits the assertion per tool. `SocketSnapshot` now owns the two sub-section
banners as constants and renders from them, so a probe that narrows a message
to one tool's rows cannot drift from the rendering it narrows.

No degradation carve-out is needed: the degraded netstat rendering names its
ports as a bare set rather than as address tokens, and the probe matches only
address tokens, so it does not mistake the degradation note for a real row.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

cuioss-oliver and others added 15 commits September 3, 2026 23:49
Replace 20 bare `.listen(0)` wildcard binds across the nine live-socket
fixtures with `.listen(0, LoopbackHost.ADDRESS)`, and route the 24
`127.0.0.1` literals in those same files through that one new shared
constant (`testsupport/LoopbackHost`). A wildcard ephemeral bind that is
later dialled on loopback is the measured exposure this closes; every
dial target in these fixtures was already `127.0.0.1`, so binding the
listener to loopback is behaviour-preserving rather than a semantic
change.

The constant is deliberately not named `Test*` (Surefire's default
includes match `**/Test*.java`) and lives in `testsupport` rather than
inside any one feature package.

TlsEdgeProducerTest keeps its four `new ServerSocket(0)` wildcard binds
and gains comments explaining why. Production `SniFrontListener.start()`
binds the wildcard via `netServer.listen(publicPort)`, so a loopback-only
holder would stop colliding with it and those controls would silently
stop controlling anything. None of the four dials the port it holds.

ReservedBodyCeilingTest converts only its three transport dial targets;
its eight `localhost` occurrences (the Host: header, the redirect_uri and
the Javadoc mentions) are deliberately unchanged, since a drop there is
the signature of the blanket replace this change forbids.

No file under api-sheriff/src/main/** is modified, and no await ceiling
is widened.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
A thread dump says what the JVM was doing; it cannot say whether the
awaited work ever reached the machine. Every Awaits ceiling now also
carries the kernel's own view of this process's TCP sockets, including
the per-socket queue depths that separate "the bytes arrived and nobody
was told" from "nothing was ever sent".

Adds testsupport/SocketSnapshot.java: a diagnostic-only capture over lsof
and netstat that degrades to a stated note on every failure path, bounds
each child process, and can never throw or fail a test. Wires it into
Awaits.timedOut and adds two disjoint matched controls in AwaitsTest —
one asserting the section is present at all, one asserting the capture
names a loopback socket this JVM actually holds, abstaining rather than
failing where the binaries are absent.

The three-leg mutation probe was re-run after applying, not assumed:
dropping the snapshot reddens exactly the two snapshot probes, dropping
the thread dump reddens exactly the three dump-carrying probes, and
zeroing the elapsed measurement reddens exactly the three elapsed
probes. The legs stay disjoint, so a single red names which mechanism
was lost.

The degraded path was exercised directly by making netstat unresolvable:
the suite stays green with one abstention, confirming available() routes
the content probe through its assumption skip while the section header is
still emitted so the presence probe holds.

CONNECT_CEILING_SECONDS and TEARDOWN_CEILING_SECONDS are unchanged.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
…eue reading

The stall section asserted a lost macOS kqueue readiness event as the current
reading. That inference was refuted on 2026-09-03: `.listen(0)` binds the
dual-stack wildcard, macOS lets it coexist with a foreign `127.0.0.1`-specific
listener on the same ephemeral port, and BSD most-specific-match then routes the
fixture's own client to that other process.

Retitle the section around the measured mechanism and carry its four controls
with their scopes: the system-wide `lsof` that named the foreign owner at
`rxbytes=146 / txbytes=0`, the E1 squat-then-wildcard-bind reproduction, the E2
`BindException` negative control, and the 179-listeners / 20 000-allocation rate
pair (1.0925 % predicted, 1.0931 % measured, loopback arm 0).

Keep the superseded reading rather than deleting it, since how it failed is the
useful part: the dumps were read correctly and the step from "no thread had
work" to "a notification was lost" assumed the work had been queued at all. Add
the elimination to the 2026-09-01 rules-out list and state why it belongs there
rather than in the 2026-09-02 list. Record that the TIME_WAIT listener form was
refuted correctly and still left the listener socket looking answered-for.

Restate the limit against the new mechanism, keep both power caveats, and keep
the three unsettled items unsettled: the 5.036 s TEARDOWN occurrence in
`ReservedBodyCeilingTest`, the inference that CI is clean for want of such
listeners, and whether the mechanism accounts for every historical occurrence.

Update the contributor guidance for the landed fix without claiming closure: the
quarter-of-runs figure is pre-fix, no post-fix rate has been measured, and the
`SocketSnapshot` block is now the first thing to read on a recurrence.

The `-T1` section heading and the `@Nested` `Tests run: 0` CI-log trap are
unchanged.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
…itness function

Binding all 20 sites to loopback fixes the tree as it stands; nothing stopped the
next fixture from reintroducing the exposure. `LoopbackEphemeralBindArchTest`
does, over the api-sheriff test tree.

The rule keys on the overload signature, never the port literal — ArchUnit reads
bytecode and cannot see the `0`, but it can see which overload was called, and
the bare single-int forms bind the wildcard whatever the port. Both spellings are
banned together, `ServerSocket(int)` and `listen(int)`, since a guard against one
leaves the other free to reintroduce the identical bind under another name.

Phrased positively — `classes().should(...)` emitting `violated` — because under
the `no...` form ArchUnit inverts event polarity and the rule would pass
vacuously, the same reasoning `NoStoredOptionalArchTest` records.

All four ADR-0030 control legs ship with it:

- Non-vacuity: the guarded selection resolves to more than zero classes, the
  specimen package resolves, and at least one selected class is seen calling the
  host-bound `listen(int, String)` form. That third leg is what makes a
  zero-violation result mean "no bare bind" rather than "no bind calls scanned".
- Negative control: `WildcardEphemeralBindSpecimen` carries both banned spellings
  and must be rejected, proven with `assertThrows` and `allowEmptyShould(true)`
  so an unresolved specimen fails loudly instead of passing quietly.
- Matched positive controls: `LoopbackEphemeralBindSpecimen` is the host-bound
  near-miss and must be accepted; production `SniFrontListener` must stay
  unselected, asserted only after re-establishing that it does still call
  `listen(int)` — "not selected" is trivially true of a class that stopped
  matching at all.
- Carve-outs: the specimen package, and `TlsEdgeProducerTest` with its nested
  classes, whose four sites hold or probe a port for production's own wildcard
  bind and are never dialled. The justification stays recorded at each site.

Demonstrated red before being accepted green: with one real fixture site
temporarily reverted to `listen(0)`, the rule failed with exactly one violation
naming the class, file and line; reverted forward-only, the five tests are green.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
The -Ppre-commit gate reformatted the continuation indentation of the
loopback-bound listen calls and the client request introduced by
deliverable 1. Committed as-authored-by-the-gate so the tree is settled
and the next gate run is a no-op.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
The -Ppre-commit gate widened the snapshot probe's throws clause to
Exception, realigned its try-with-resources continuation, and
de-indented the omitted-rows string concatenation in SocketSnapshot.
Committed so the tree is settled and the next gate run is a no-op.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
The -Ppre-commit gate dropped the redundant test prefix from the rule's
method name, flipped three equals calls to constant-first form, and
adjusted import spacing in the two specimens. Committed so the tree is
settled and the next gate run is a no-op.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
… login

`required_bots` named `cuioss-review-bot`, which is PR-Agent's GitHub author
login. The bot registry keys by `bot_kind`, and that reviewer's declaration in
`automatic-review/standards/pr-agent.md` reads `bot_kind: pr-agent` with
`author_login: cuioss-review-bot`. No `cuioss-review-bot` bot definition
exists, so the token could never be proven a participant: PR-Agent would
review the diff, be credited as `pr-agent`, and the quorum would still report
it absent — failing identically on every pass and every future PR.

Fallout from #249, which correctly renamed the GitHub App identity but carried
the rename into this token as well. CLAUDE.md already documents the intended
value as `coderabbit,pr-agent`.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
`listen(0, "0.0.0.0")` and `listen(0, LoopbackHost.ADDRESS)` are the same call
target — same name, same parameter types — so the ArchUnit rule accepted both
while the first rebuilds exactly the wildcard ephemeral bind the guard exists
to refuse. The discriminator is the argument's text, which lives in the source
rather than the bytecode, so a wider ArchUnit rule cannot reach it.

Adds a source sweep for the three wildcard host spellings (0.0.0.0, ::, empty)
with its own matched controls: a positive control asserting the sweep finds the
specimen's deliberate literal, a negative control asserting it leaves the
loopback-bound spelling alone, and a non-vacuity check on the scanned source
set. Records the bytecode rule's structural limit in its Javadoc so its green
is not read as more than it proves.

Falsifiability demonstrated: reverting one guarded call site to
`listen(0, "0.0.0.0")` failed the sweep with exactly one violation naming file
and line, while the other eight probes stayed green.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
… section

`SocketSnapshot.available()` proves only that both binaries are executable, and
`capture()` renders their output into one section. The probe scanned the whole
section, so an `lsof` address satisfied it even when `netstat` contributed no
matching row — the probe would pass on a half-degraded capture while claiming
the OS snapshot saw the socket.

Splits the assertion per tool. `SocketSnapshot` now owns the two sub-section
banners as constants and renders from them, so a probe that narrows a message
to one tool's rows cannot drift from the rendering it narrows.

No degradation carve-out is needed: the degraded netstat rendering names its
ports as a bare set rather than as address tokens, and the probe matches only
address tokens, so it does not mistake the degradation note for a real row.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
The -Ppre-commit gate widened the four new sweep probes' throws clauses from
IOException to Exception, matching the rewrite it applied to the socket
instrumentation earlier on this branch. Committed so the tree is settled and
the next gate run is a no-op.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
PR #254 added ForwardedTrustFromEnvironment after this branch forked, carrying
two bare `listen(0)` binds and a local LOOPBACK = "127.0.0.1" constant. Both
are the exposure class this branch exists to remove: the listeners bind the
dual-stack wildcard while the fixture dials loopback.

Found by this branch's own ArchUnit guard, which CI ran against the merge
result. That is the guard working as intended on code the branch never saw —
the sites are invisible from the branch tip and appear only once the two
histories are combined.

Routes the local constant through LoopbackHost.ADDRESS rather than leaving a
second spelling of the same address in the tree.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
The netstat half of SocketSnapshot was macOS-only in both halves of its
contract, and neither was platform-guarded:

- `netstat -anv -p tcp` is macOS syntax. Linux reads `-p` as show-program,
  taking no argument, so the invocation never selected TCP there. `-ant` is
  the Linux spelling that does.
- The port pattern matched macOS's dot separator (`127.0.0.1.59120`). Linux
  prints a colon (`127.0.0.1:59120`).

Selected per platform rather than unioned: a dot-matching pattern applied to
Linux output matches the dots inside the IPv4 address, so `127.0.0.1:59120`
would yield a spurious port `1`. Accepting either separator everywhere would
manufacture wrong ports rather than widen coverage.

The section banner no longer names the argv, since a banner carrying one
platform's flags stops matching on the other and would silently turn a probe
that narrows a message to these rows into one that finds nothing. The argv
actually used is rendered inside the section instead.

This refutes a documented assumption of this plan: netstat was believed absent
from ubuntu-latest, with lsof present. CI shows netstat IS present, so
available() returns true and the netstat half runs — it simply produced
nothing usable. The previous section-wide assertion hid that behind lsof's
output; the per-tool assertion added earlier in this branch is what surfaced
it.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
…w CI caught it

The page described the capture as `netstat -anv -p tcp`, which is the macOS
spelling only. Corrects that to name both platforms' invocations, and adds the
finding itself: two beliefs were wrong at once — that netstat is absent from
ubuntu-latest, and that a section-wide assertion demonstrated the capture had
worked — and each hid the other.

Written up because it is the same failure shape this page already documents
elsewhere: a check whose green is satisfiable by a neighbour is not a check on
the thing it names.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
The class doc still named `netstat -anv -p tcp` as the command, which is the
macOS spelling only, and presented the Recv-Q/Send-Q/rxbytes reading as
platform-neutral when those columns are macOS's. Names both invocations and
scopes the column reading to macOS.

Also settles the formatter's array-initialiser rewrite from the gate run.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
@cuioss-oliver
cuioss-oliver force-pushed the feature/loopback-stall-fix-and-instrumentation branch from cf72c95 to 8a01904 Compare September 3, 2026 22:42
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 3 minutes.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 54 minutes.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 17 minutes.

The build-gate page said the fix converted "All 20 such sites across 9 files".
It converted 22: PR #254 landed two more bare listen(0) sites in
GatewayEdgeRouteTest after this branch forked, and rebasing brought them in.
Re-derived from the tree rather than trusted — 22 host-bound listen calls across
the nine fixture files, excluding the specimens this branch added and the two
occurrences that are Javadoc text rather than bind sites.

This one matters more than the earlier stale counts: it is the claim a reader
checks the deliverable against, and it UNDERSTATED the work, so a reader
verifying it would have found more sites than the page admits to and had no way
to tell whether the surplus was intended.

Second, from the simplify re-run: the wrapped-line assertion's message said
joining the specimen "leaves five violations" while the count assertion three
lines above asserts four. A leftover from the round that briefly had five
specimens. No coverage change.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 35 minutes.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 92742a2c-bc2c-4ff9-b733-01bb288b12f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba8879 and 4dee8fe.

📒 Files selected for processing (19)
  • .plan/marshal.json
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/LoopbackEphemeralBindArchTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/LoopbackEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/WildcardEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgePipelineTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteBffWiringTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcDispatchStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcStatusMapperTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/ReservedBodyCeilingTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/WebSocketRelayStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/Awaits.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/AwaitsTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/LoopbackHost.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/SocketSnapshot.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/PassthroughRelayTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/SniFrontListenerTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/TlsEdgeProducerTest.java
  • doc/development/build-gate-discipline.adoc

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread doc/development/build-gate-discipline.adoc Outdated
Comment thread doc/development/build-gate-discipline.adoc Outdated
The section said saturation, deadlock and a blocked handler were "excluded" by
two thread dumps. Two dumps are two instants: they show no sampled thread in
those states AT those moments, and say nothing about work that ran or blocked
between them. On a page whose subject is not drawing conclusions from evidence
that cannot support them, that was the wrong claim to leave standing.

Rewritten to put the weight where it belongs — the live socket capture and the
E1/E2 controls observe the mechanism directly, while the dumps are consistent
with it. Consistency is the right weight for a sample.

Two smaller corrections alongside:

- The arch test's carve-out note still said "three of the four sites" are
  nested. freePort's re-probe made five; the nested three are the collision
  holders, so the claim is now about them rather than about a total. Sixth stale
  count on this branch, all from the same move.

- The sweep description named one wildcard host form and its nesting limit but
  not the full set. It refuses 0.0.0.0, :: and the empty host, and tolerates
  whitespace before listen — a contributor reading only the doc would have
  under-estimated what the guard covers.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: e5b11fe8-64f2-4530-9d1f-738f832166ac

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba8879 and 70e3e95.

📒 Files selected for processing (19)
  • .plan/marshal.json
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/LoopbackEphemeralBindArchTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/LoopbackEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/WildcardEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgePipelineTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteBffWiringTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcDispatchStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcStatusMapperTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/ReservedBodyCeilingTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/WebSocketRelayStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/Awaits.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/AwaitsTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/LoopbackHost.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/SocketSnapshot.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/PassthroughRelayTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/SniFrontListenerTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/TlsEdgeProducerTest.java
  • doc/development/build-gate-discipline.adoc

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

cuioss-oliver and others added 2 commits September 5, 2026 08:33
…sweep

`server./* c */listen(0, "0.0.0.0")` is legal Java and slipped a whitespace-only
separator, while the bytecode rule accepts it for the usual reason — the target
is still listen(int, String). The bypass therefore sat open in both halves of
the guard.

The separator now accepts whitespace, block comments and line comments. The
block-comment branch is written `[^*]|\*(?!/)` so it consumes without
backtracking, and the alternatives cannot overlap, so this does not reintroduce
the S8786 ambiguity fixed earlier on this branch.

Asserted against literal strings rather than source specimens, following the
lesson from the spaced-selector round: a shape the formatter may normalise
cannot be pinned in source. Three assertions — both comment spellings match, and
a comment-interleaved LOOPBACK bind still does NOT match, so tolerating comments
did not cost the discrimination the sweep exists for.

Residual limit recorded in place rather than implied: a comment INSIDE the
argument list is still out of reach, and closing that needs a Java lexer —
disproportionate machinery for the secondary half of a guard whose primary half
is the bytecode rule.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
Introducing SEPARATOR placed its declaration between WILDCARD_HOST_LISTEN's
Javadoc and the field that Javadoc documents, orphaning the doc and leaving the
pattern undocumented. Moved SEPARATOR above it.

java:S8491, and the second time this exact insertion mistake has occurred on
this branch — the first was readBounded landing between loopbackRows' Javadoc
and its method. Both times the symptom read as a formatting nit and was in fact
a detached doc.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: e5c1f028-1cfe-46b7-a469-e109dd1e449b

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba8879 and e32394a.

📒 Files selected for processing (19)
  • .plan/marshal.json
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/LoopbackEphemeralBindArchTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/LoopbackEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/arch/specimen/WildcardEphemeralBindSpecimen.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgePipelineTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteBffWiringTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GatewayEdgeRouteTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcDispatchStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/GrpcStatusMapperTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/ReservedBodyCeilingTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/edge/WebSocketRelayStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/Awaits.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/AwaitsTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/LoopbackHost.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/testsupport/SocketSnapshot.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/PassthroughRelayTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/SniFrontListenerTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/gateway/tls/TlsEdgeProducerTest.java
  • doc/development/build-gate-discipline.adoc

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 52 minutes.

cuioss-oliver and others added 2 commits September 5, 2026 13:45
…nner overclaiming

Every specimen in the source file uses "0.0.0.0", so the `::` and empty-host
alternatives could have been deleted from WILDCARD_HOST_LISTEN and every control
would have stayed green while the guard went on claiming to reject all three.
The controls now assert each form directly, plus a negative on "127.0.0.1" —
without which all three positives would pass for a pattern that matched ANY host
string.

Second: NETSTAT_SUBSECTION promised "Recv-Q/Send-Q, rxbytes/txbytes and
process:pid discriminate" on every platform. Those are macOS `-v` columns; Linux
`-ant` does not produce them, so the header named discriminators the rows below
it did not contain. The banner is now a bare marker and the column note is
rendered per platform, saying plainly which fields are absent on Linux.

That is the third label-overclaims-content correction in this file. The pattern
is consistent enough to be worth naming: a header written against the platform
the author was standing on.

Reported by CodeRabbit on PR #255.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
…s the columns

The class Javadoc lays out three diagnostic readings built on Recv-Q/Send-Q,
rxbytes/txbytes and process:pid, presented as universally available. The commit
before this one established that the byte counters and process:pid are macOS
`-v` columns that Linux's `-ant` does not produce — so a Linux reader following
this guide would attempt the "nothing was ever put on the wire" reading using
columns that are not there, and that reading specifically needs the cumulative
counters to tell "never sent" from "already drained".

Now states that only the first reading is available on Linux, and points the
reader at the rendered capture's own column line before applying any of it.

Found by looking for the sibling rather than waiting for it to be reported:
correcting a header and leaving the prose that depends on it is the same
fix-one-site-miss-the-other pattern this branch has produced repeatedly, and
three instances of it in this file were caught by review rather than by me.

Co-Authored-By: plan-marshall <noreply@cuioss.de>
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Action performed

Full review triggered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant