Repository navigation
tests: port 0 is written only in test_support::ports; per-session checkpoint fault count - #155
Merged
Merged
Conversation
…ckpoint fault count The released-port lint tried to tell a held port-0 socket from a released one by following it through bindings, wrappers, closures and helpers in other files, and kept missing new shapes. Replaced with a rule that needs no data flow: a zero port (an address literal ending `:0`, or a zero / zero-valued const used as the port of a bind, a SocketAddr constructor, set_port, or an IP tuple) is written nowhere but `crates/test-support/src/ports.rs`. Tests get ports from its helpers: `listener()`, `tokio_listener()`, `bound_socket()`, `bound_reuseport_socket()`, `DeadPort`, or a child that binds port 0 and reports it. The few sites that really bind port 0 and keep the socket (the MCP OAuth callback, the fixture servers, child command lines, plus one DNS lookup that is not a bind) are annotated `// port-0:` in place and counted in the lint's ALLOWED list. The old lint's bypass cases are pinned as cases the new rule must flag. ~100 test binds moved to the helpers; agent-core gains test-support as a dev-dependency. `BEYOND_AI_AGENT_TEST_FAIL_CHECKPOINT=n` counted writes process-wide, so on a daemon one session's writes shifted which write of another session failed. It now counts per session id (debug builds only), proven with two sessions on one daemon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
…; list what a text rule cannot see
- `format!("127.0.0.1:{}", 0)`, `format!("{}:{}", host, 0)`, `port = 0` named and
`{port}` capturing `let port = 0`: placeholders are mapped to their arguments, so only
the zero that lands in the port position is flagged.
- `port: 0` / `*_port: 0` in a struct literal. It found the gateway harness's `Nats`
carrying `port: 0` until the port was read back; `Nats` is now built only once the
port is known (`NatsProcess` guards the server meanwhile).
- The doc lists what passes a text rule: an env default `unwrap_or(0)` later bound, a
`"0"` string default later formatted into an address, and `n - n`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
jaredLunde
force-pushed
the
jared/port-zero-only-in-ports
branch
from
October 7, 2026 12:58
9207c3d to
79a22d5
Compare
jaredLunde
added a commit
that referenced
this pull request
Oct 7, 2026
…pport::ports (#155) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two follow-ups from #151.
1. Port 0 is written only in
beyond_ai_test_support::portsThis replaces the released-port data-flow lint (
serve_harness_deadlines.rs, about 580 lines removed). That lint had to follow a socket through bindings,Options, closures and helpers in other files, and every audit found a shape it missed.The new rule (
crates/agent/tests/ports_lint.rs) needs no data-flow analysis. It looks at how code is written, not at what happens to the socket. A zero port must not be written anywhere in the workspace exceptcrates/test-support/src/ports.rs. A zero port is any of:an address literal ending in
:0:"127.0.0.1:0","[::1]:0","localhost:0","{host}:0",":0";a zero used as the port of a
bind,SocketAddr{,V4,V6}::new,set_port, or an IP tuple. A zero means0,0u16,0_u16,u16::MIN,0 as u16,Default::default(), or aconst/static/letbound to one.a zero formatted in as an address's port:
format!("127.0.0.1:{}", 0),format!("{}:{}", host, 0),port = 0, or{port}capturinglet port = 0. Placeholders are mapped to their arguments, so only a zero in the port position counts;a zero port field in a struct literal:
port: 0,listen_port: 0. This caught the gateway harness'sNats, which heldport: 0until the port was read back.Natsis now built only once the port is known, and aNatsProcessguard owns the server until then.Comments are skipped, and string literals are only checked for addresses. Whitespace is ignored, so layout cannot hide a case.
Tests get ports from
portshelpers. The new ones arelistener(),tokio_listener(),bound_socket()andbound_reuseport_socket(); the existing ones areDeadPortand children that report their port. About 100 binds moved to these helpers.agent-coregainstest-supportas a dev-dependency.ExecMockand the routed model server now take a held listener.Allowed exceptions. A few sites bind port 0 and keep the socket for real:
agent mcp login's callback listener;CallbackServerunit tests;--listen 127.0.0.1:0).The SSRF resolver's
lookup_host((host, 0))is also allowed: it is a lookup, not a bind. Each exception is annotated// port-0: <why it is held>in place and counted in the lint'sALLOWED, so adding one shows up as a reviewed change to the lint file.Tests.
Option,const ANY: u16 = 0, cross-file helper, socket2, and the rest. New spellings are added too: IPv6,localhost,format!, raw strings, theSocketAddrconstructors,set_port,.into(), and a multi-line tuple.v1:0, JSON"index":0, URLs,assert_eq!(n, 0), and annotated lines.mcp_http_exit.rs); it is migrated.The SO_REUSEPORT Linux caveat now lives on
ports::bound_reuseport_socket;RestartableNatspoints to it.Not caught, and listed in the lint's doc: a zero that only becomes a port through a value the rule cannot follow. Examples:
unwrap_or(0)later passed to a bind;"0"string default later formatted into an address;n - n.The first two can happen by accident, so ports that come from configuration still need review. The last would be deliberate evasion.
2.
BEYOND_AI_AGENT_TEST_FAIL_CHECKPOINT=ncounts per sessionThe counter was process-wide. On a daemon, one session's writes shifted which write of another session failed. It now counts per session id, in debug builds only, and release builds still carry none of the seam names (
release_seamspasses).Test:
serve_session_isolation::an_injected_checkpoint_failure_counts_each_sessions_writesruns two sessions on one daemon. Each must fail closed at its own second write, so neither tool runs. With the counter put back to a single shared count, the test fails.Verification
fmt, dprint and
clippy --workspace --all-targets -D warningsare clean. Workspace nextest passes 4871/4871. ARCHITECTURE.md is updated (the harness row and the env table).🤖 Generated with Claude Code
https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk