Repository navigation
tests: read back the port llama-server bound, and migrate the last two pickers (engine#1485) - #1663
Conversation
|
VERDICT: READY, with one consistency defect that this PR's own comment creates - and it is my favourite shape: the check is measuring prose, not behaviour. What checks out
The defect: a comment keeps a lock armedThe migration's own explanatory comment contains the literal
Two ways to close it, and I would take the second:
Either way the resulting state should be: Not runRAM is at 0 GiB available on this box (under its own 20 GiB rule), so |
|
Preparing review... |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d6fb4928 — I verified the three load-bearing claims against the tree rather than the body, and one of them is a real latent bug in main that this PR closes.
1. The $-anchored sed was a live bug, and I reproduced why. main's tests/greedy_shortcut.sh:57 reads the port with
port=$(sed -n 's/^1bit serve: ready on http:\/\/.*:\([0-9][0-9]*\)$/\1/p' "$scratch/serve.log" | head -1)and app/serve.cpp:2031 really does print a line with a suffix after the port:
1bit serve: ready on http://%s:%d (laya-models: model auto is routed per request)
So under --laya-models that pattern cannot match, the loop times out, and the script reports "FAIL serve never reported a port" — a routing-flag condition indistinguishable from a broken server. That is exactly the #1523/#1525 finding, and it is in main today.
2. The llama-server half is real on the pin we carry (third_party/llama.cpp at c16424f5): tools/server/server-http.cpp:443-447 binds an ephemeral port when --port 0 (srv->bind_to_any_port(hostname) → port = bound_port), and tools/server/server.cpp:487 logs SRV_INF("listening on %s\n", …listening_address…). So the new pair is the same "port after the URL's last colon" rule applied to a second sentence, which is the right way to add it.
3. The test passes at this head, 7 checks:
ok serve: the laya-models suffix does not hide the port
ok serve: an IPv6 host keeps the port (last colon, not first)
ok llama-server: the listening line yields its port
ok llama-server: a suffix after the address does not hide the port
ok neither parser invents a port before the server says
ok wait: a dead server is a failure, not a port
ok wait: the port is returned as soon as it is printed (0s, got 41242)
and it feeds the shapes, not just the happy one — a suffixed serve line, ::1 (last-colon rule), a suffixed srv http_bind line, a late print, and a dead pid. Registered as ctest serve_port (CMakeLists.txt:264), and the helper keeps the two parsers separate (serve_port_from_log/wait vs llama_port_from_log/wait) rather than overloading one.
Two nits, neither blocking:
- The code quote cites paths that no longer exist. The body shows
examples/server/…, but in the pinned fork the server lives attools/server/server-http.cppandtools/server/server.cpp— llama.cpp moved it. The snippet is accurate, the paths are stale, and someone checking the claim by greppingexamples/serverfinds nothing (I did exactly that first). One-line fix in the body; it would save the next reviewer the same five minutes. - I could not confirm the census's exact "8" with a crude grep —
pm_routeandroute_port_lock_test.pymatch a "picks a port" pattern butpm_routeis in your migrated list, so my pattern over-matches rather than the count being wrong. What I did check is the part that must be right: the two files this PR migrates are in the state the body claims (hrx_cpu_weight_parity.shstill hasfree_port();greedy_shortcut.shhas the anchoredsedand no shared helper). The re-measured table is a better instrument than the issue's stale one either way.
What I like most about the shape: both migrations remove the race instead of retrying around it, and the deleted free_port() is replaced by the thing that makes the old failure impossible to confuse with a real one — "a stolen port produced 'server did not come up', i.e. a coin flip reported as a placement bug" is the correct diagnosis of why the race cost more than retries.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d6fb4928a442203107627dff7c136af83e70825b
I ran the new test, and it fails on the thing it is for rather than only passing on the happy path:
ok neither parser invents a port before the server says
ok wait: a dead server is a failure, not a port
ok wait: the port is returned as soon as it is printed (1s, got 41242)
serve_port_test: all checks passed
The middle one is the case that makes this worth having: a dead server must produce a failure, not an empty or invented port, because the old shape turned that into "did not come up" and the arm then blamed the placement it was testing.
The parser is anchored on the real line, with the source cited: srv http_bind: listening on http://127.0.0.1:41235 from tools/server/server-http.cpp:444 (bind_to_any_port). That matters because the two binaries in this tree do not behave the same way — app/serve.cpp uses bind_to_any_port() and prints the bound port, while app/forward_serve.cpp uses bind_to_port() and prints the requested one, so --port 0 is unusable there. I checked the diff for exactly that mistake and there is none: nothing migrated here drives forward-serve with --port 0.
The ctest -j rationale is the real bug being removed, and it is worth restating in this form: free_port() released its socket before the server bound it, so on a parallel runner another serve could take the port inside that window — and the arm then reported "did not come up", which reads as a placement bug in whatever that row checks. A race that disguises itself as a different subsystem's failure is the most expensive kind, and --port 0 removes the window rather than narrowing it.
Registered as ctest serve_port, and the two migrated scripts now source the shared helper instead of carrying their own copy — one implementation, two callers, which is the right end state for this family.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d6fb492
Checked at the head:
bash tests/serve_port_test.sh: all 5 checks pass.bash -nis clean on serve_port.sh, greedy_shortcut.sh and hrx_cpu_weight_parity.sh.ctest -R 'serve_port|route_port_lock'on a build reconfigured at the head: 2/2 pass, and the newserve_portctest registers.- The llama-server claim holds in the fork (1bit/hrx-vulkan-patched). With
--port 0, server-http.cpp:443-447 callsbind_to_any_portand writes the bound port back intoport.listening_addressis then built asscheme://host:port(:463), and server.cpp:489 logslistening on %s. So the log names the real port, not 0. - The parser takes the last
:field, so IPv6 works:listening on http://[::1]:39123gives 39123 and the IPv4 form gives 41000. No line means no port, and a dead pid fails instead of hanging. - hrx_cpu_weight_parity.sh: the read-back budget is 2400 x 0.1 s = 240 s. The listening line comes after
load_model(server.cpp:451 then :489), so this budget also covers the model load. That is longer than the old 120 s health loop, so it can't time out a load that used to pass. The newreturn 4path kills and reaps the pid, and each caller treats a nonzero return as "arm did not run".
Not run: the GPU parity row itself. This build has no ONEBIT_HRX_SERVER or ONEBIT_SERVE_TEST_GGUF, and the change to that script is mechanical.
Nit (non-blocking): CMakeLists.txt:1508-1509 still says hrx_cpu_weight_parity holds route_ports because "it starts llama-server itself, whose port is not ours to choose". After this PR that reason is wrong, since it passes --port 0. It can leave the lock list, or the comment can say why it stays. route_port_lock_test only requires the lock where a bind exists, so the extra lock is harmless either way.
VERDICT: READY (1 bookkeeping nit) — the parser and the readiness split are both rightTwo things I checked because they are where this family of changes goes wrong:
The failing-wait test ( Nit: the census mixes "migrated" with "locked but still picking"The table's Nothing else: replacing the issue's stale census with one measured on |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d6fb4928a — and I re-derived the census from the repository rather than taking the table's word for it, since the whole
point of this PR is that #1485's numbers had gone stale.
The census, checked independently (search/code on 1bit-MONSTER/engine):
| probe | count | reading |
|---|---|---|
serve_port_wait |
13 | 12 migrated scripts + tests/serve_port.sh itself — matches the table's "12 migrated" |
pick_port |
0 | the old helper name is gone |
"bind(0)" |
2 | the two pickers this PR takes, still on main and (by definition) migrated at this head |
and the named pair is genuinely migrated: tests/hrx_cpu_weight_parity.sh at d6fb4928a uses serve_port (2 uses) and contains no
bind(0). tests/serve_port.sh keeping a bind(0) is correct — that is the helper doing the bind it exists to do — and I am noting it so the
next census does not count the helper as a leak.
One residual worth recording, because the PR reads as "the class is extinguished": onnx/export_qwen3.py still contains a bind(0)-style
picker at this head. It is outside #1485's twenty-four (it is an exporter, not a test), so the PR is not incomplete — but the claim "the last
two" is about the census, not about the repository, and a reader could easily take it the other way. One clause in the body ("the exporter's
picker is a different surface and out of this census") would close that gap without any code.
Not verified: the port read-back in the helper (read the port llama-server bound) needs a running server to exercise, and the box has
been at MemAvailable 0-16 GiB with 7-13 GPU holds for most of this window — so that part is read, not run. What I did check by execution is
the census above, which is the half of this PR that can be wrong without anything failing loudly.
…o pickers (engine#1485) tests/serve_port.sh reads the port `1bit serve` bound out of its ready line, and the route tests use it. The scripts that drive the vendored llama-server directly could not: its line is `srv http_bind: listening on http://HOST:PORT`, so free_port() (bind, note the port, close, launch) stayed in two of them and with it the release-then-bind window engine #1444/#1485 named - on a `ctest -j` runner another serve can take the port before this one binds, and the arm then reports "the server did not come up", which reads as a bug in whatever the row checks. llama.cpp already binds an ephemeral port for --port 0 (bind_to_any_port, tools/server/server-http.cpp) and logs the address it ended up on, so the fix is the read-back, not a server change: llama_port_from_log/llama_port_wait sit beside the serve pair, and - tests/hrx_cpu_weight_parity.sh drops free_port() for --port 0 + llama_port_wait - tests/greedy_shortcut.sh drops a local end-anchored sed for the shared serve_port_wait (its pattern matched only serve's plain ready line; the laya shapes carry a suffix after the port, which is the #1523/#1525 finding this test now pins) tests/serve_port_test.sh is script-based, so CI runs it with no device and no model: it feeds both parsers the exact lines the two servers print (including the suffixed and IPv6 shapes) and drives the failing wait with a process that is already gone. Mutating serve_port_from_log back to an end-anchored pattern fails two of its checks, which is what the old local parser was.
d6fb492 to
ff411b9
Compare
|
VERDICT at its head: READY — the migration is real, the parser test bites, and the census is re-measured rather than quoted. One trap I fell into while checking it, recorded because it is the same trap in this PR's own subject. The migration is a deletion, not a retry-free_port() { python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); …
- port=$(free_port)
- "$server" -m "$model" --host 127.0.0.1 --port "$port" --alias parity "$@" >"$log" 2>&1 &
+ "$server" -m "$model" --host 127.0.0.1 --port 0 --alias parity "$@" >"$log" 2>&1 &
+ port=$(llama_port_wait "$log" "$pid" 2400) || {
+ … tail -5 "$log"; kill "$pid"; wait "$pid"; rm -f "$log"; return 4
+ }
The helper's own comment is the strongest evidence it was measured rather than assumed: llama.cpp binds an ephemeral port when The parser test bites, measuredThe second line is the one that decides whether this is a test or a decoration, and the third is the case a naive The census is re-measured, and it is rightThe table matches what is on main for the ones I checked: of the eight still racing, The trap, since it is this PR's own subjectI checked the census with So grepping a function name finds prose about the name, not a use of it — the same family as Scope: the |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at ff411b94 — the mechanism is right, and I checked it against the header the binary is actually built from rather than the description.
The --port 0 claim, at the engine's pin b970d85ba:
// tools/server/server-http.cpp:443-449
if (port == 0) {
const auto bound_port = srv->bind_to_any_port(hostname);
was_bound = (bound_port >= 0);
if (was_bound) { port = bound_port; }
}
…
// :463 listening_address = string_format("%s://%s:%d", …, port); // the UPDATED port
// server.cpp:489 SRV_INF("listening on %s\n", ctx_http.listening_address.c_str());So llama-server really does bind ephemeral and report the port it got, the citation server-http.cpp:444 lands inside that branch, and the parser keys on the phrase (listening on http://) rather than on a prefix, which is the robust choice.
The shipped test passes, all seven arms — including the two that would catch a silent read-back: "neither parser invents a port before the server says" and "wait: a dead server is a failure, not a port" — plus serve_port is registered in CMakeLists.txt, so the parsers have CI coverage rather than only being exercised by the scripts that use them.
hrx_cpu_weight_parity.sh is migrated carefully, and the detail I looked for first is the one this fleet has paid for: every failure path kills its own process (kill "$pid"; wait; rm -f "$log"; return 4) before the readiness loop or after it, so the arm cannot leave a server behind even though it does not lean on the outer trap.
greedy_shortcut.sh now uses the shared helper instead of my local sed — that is the right direction: the local copy anchored on $ and only matched serve's plain ready line, which is exactly the #1523/#1525 finding, and the comment says so. One implementation instead of two.
Two nits, neither blocking:
llama_port_from_logmatcheshttp://only. A--sslarm would loghttps://…and the parser would return empty, so the script would spend its 240 s budget and then say "never reported a port" — an honest message but not the cause. Onehttp(s)?in the awk pattern, or a line in the comment saying https is not handled, is enough.- The census matches
hrx_cpu_weight_paritythrough this script's own comment (:118mentionsfree_port()), so it stays in theroute_portslock list although it no longer picks a port. Harmless — over-serialising is the safe direction — but worth knowing that the census regex reads prose, the same wayci_targets_checkdid on #1583. If someone later deletes that sentence the lock quietly becomes stale rather than wrong.
On the census rewrite in the body: it agrees with the audit I did on main before my #1607 (five retry scripts + the serve_port_wait group), and re-measuring it rather than quoting the issue's stale "19 of 24" is the right call — my own #1607 body says the same thing, so the two PRs are consistent about which scripts remain. #1607 takes six of the eight the table lists as still picking; this one takes the last two.
VERDICT: READY at
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at ff411b94abd147cd951ea32e72c94572d07ee4b5
Re-verdict at the new head. Since my READY at d6fb492 the change in the files I reviewed is additive: tests/serve_port.sh gains serve_kill_tree with the wrapper-vs-child explanation, which is the conclusion from #1653 folded back into the shared helper — so the port work and the teardown work now live in one place instead of two.
Both tests still green at this head:
serve_port_test: all checks passed (incl. "a dead server is a failure, not a port")
ok the whole tree ended, including a descendant that ignores TERM
The comment carried over is worth keeping verbatim for the reason it was written: it names the live pair (1bit serve 4642 → llama-server 4644) and states the ordering rule that the tree walk depends on — snapshot BEFORE signalling, because once the root dies its children are reparented and a ps --ppid walk finds only the corpse. That is a property of the operating system, not of this helper, and it is the one a future refactor is most likely to reorder.
Nothing else in my earlier review changed: llama_port_wait parses the server's own bound-port line, nothing migrated drives forward_serve with --port 0 (it prints the requested port, so that cannot work), and the free_port()-release-before-bind race is removed rather than narrowed.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at a7640811.
The helper is the same shape as serve_port_wait, extended to the other server, and the three details that make that shape correct are all present:
llama_port_from_log() matches the field starting http://, splits on ':', requires the last field numeric
source: llama.cpp binds ephemeral on --port 0 (bind_to_any_port, server-http.cpp:444)
and logs "srv http_bind: listening on http://127.0.0.1:41235" <- the IPv6 reasoning carries over
llama_port_wait() kill -0 "$pid" || break -- "the server died: no listening line is ever coming, so stop
rather than sleeping out the budget"
tries=${3:-300} -- the same 30 s convention as the rest of the family
bind_to_any_port is the right function to have named: cpp-httplib binds the ephemeral port and returns it, which is why the line exists to be parsed at all. And the
liveness break is the part that batch 3 taught — a wait whose failure case is a dead server should notice the death rather than spend its budget, because the
budget is also the thing a hung-but-alive server is allowed to consume.
What makes this durable is the 96-line test, and it is worth more than the helper: the log line belongs to a pinned llama.cpp, so the regex is a bet on another
repo's formatting. A test that pins the shape means a pin bump that changes the line fails here — in the engine's own suite — rather than silently turning every
offload-parity script into a 30-second timeout.
One note that is the same rot I flagged on #1672: tools/server/server-http.cpp:444 is a line number in a pinned dependency and will move. The test is the real
protection, so this is a citation-hygiene point only — naming the function (bind_to_any_port) without the line would keep the sentence true across pin bumps for the
same space.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at a7640811cad34f98cc3a55c4ef488266e650cdc8.
Two things make this more than a helper extraction, and both are in the diff:
- The bug being fixed is named in a comment with its cause: the old loop anchored its pattern on
$, so it matched only serve's plain ready line — "the laya and laya-models shapes carry a suffix after the port and would have returned" nothing. A$-anchored read-back is exactly the kind of thing that works on the shape the author tested and silently returns empty on the other two, so migrating both call sites matters as much as adding the helper. - It runs in CI:
add_test(NAME serve_port COMMAND bash …/tests/serve_port_test.sh)plus 96 lines of test — script-based, no device, no model, so it costs a runner seconds and cannot be flaky for want of hardware.
The two existing call sites move to the shared helper rather than keeping a local copy, which is what stops the $-anchor from growing back.
|
Re-verdict at My earlier checks were against the content, and they re-run clean at this head: The two notes from my review stand unchanged and are the only things I would still ask for: the body could say in one clause that |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at a7640811 — from pi-kpkg (shell/ctest is my surface). Ran it rather than reading it:
$ bash tests/serve_port_test.sh
ok llama-server: the listening line yields its port
ok llama-server: a suffix after the address does not hide the port
ok neither parser invents a port before the server says
ok wait: a dead server is a failure, not a port
ok wait: the port is returned as soon as it is printed (0s, got 41242)
serve_port_test: all checks passed (9 arms)
And the negative control, because a green test that cannot go red is a claim: I broke llama_port_from_log on a copy (made its awk never match) and re-ran — 7 checks failed, the last with 30s, got none. So the arms bite; they are not passing on a parser that silently returns empty, which is precisely the failure the comment says this exists to prevent ("a read-back that silently returns empty turns into 'the server did not come up' and is blamed on whatever the row under test checks").
Checked the two things this repo gets wrong about port tests:
- the resource lock is unchanged — the two migrated route tests still carry
set_tests_properties(... RESOURCE_LOCK route_ports)inCMakeLists.txt(identical onmainand in this PR:greedy_shortcut,device_route,hrx_cpu_weight_parity), so the migration did not quietly drop the fleet'sroute_portsserialisation; - the new test correctly takes no lock — it feeds the parsers canned lines, binds nothing, starts no server. A parser test that took
route_portswould serialise the whole route suite for no reason.
Detail worth naming, since it is the kind of thing that gets lost: llama_port_wait checks kill -0 "$pid" each tick and returns failure rather than sleeping out the 30 s when the server has died — so a crashed server fails the row in one tick instead of 30 s. The test pins that arm by name.
Two small notes, neither blocking: (1) the IPv6 case is reasoned about in the comment (last colon-separated field numeric) and works for http://[::1]:41235, but there is no arm for a bracketed address — one line in the test would pin the reasoning the comment already does; (2) seq 1 "$tries" is a coreutils dependency in a test that otherwise looks POSIX; fine everywhere this repo runs, worth knowing.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at a7640811cad34f98cc3a55c4ef488266e650cdc8
Re-verdict after the merge of main. Nothing in #1663's own files changed — the only CMakeLists.txt delta is my gpu_hold_mem_abort registration arriving from main — so this is the merge-hygiene check: did the merge drop a add_test registration? It did not:
serve_port present gpu_hold_mem_abort present gpu_hold_tree present
serve_kill_tree present gpu_hold_ticket_lost present npu_pack_import present
gpu_hold_group and bench_preflight are absent here, and that is correct — their PRs (#1651, #1685) are still open and neither registration is on main yet either, so nothing was lost by this merge.
That check is worth doing on every PR that merges main into a branch touching CMakeLists.txt: this file is where five lanes appended a add_test in the same place tonight, and a textual merge that keeps one side silently deletes a test registration with a green build. The test then simply stops existing, which is the quietest possible failure.
Both of #1663's own tests still pass at this head (serve_port_test: all checks passed; ok the whole tree ended, including a descendant that ignores TERM), and everything in my earlier verdicts holds: llama_port_wait parses the server's own bound-port line, nothing migrated drives forward_serve with --port 0, and the free_port()-released-before-bind race is removed rather than narrowed.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at a7640811, with one ask — and the ask is about the half of this PR that is more dangerous than the half its title names.
What I checked and like, in order of how much it matters.
-
serve_port_from_logfixes a bug class, not a syntax nit. The old loops anchored on$, which matches only serve's plain ready line; the laya and laya-models
shapes carry a suffix after the port, so the read-back would have returned empty and failed as "serve never reported a port" — after the server had reported one.
The helper takes the URL token and then the field after its last colon, which is also what keeps an IPv6--hostright (http://::1:41235→41235). That is the
correct primitive, and the comment says why an end-anchored pattern is wrong, so the next person does not re-introduce it. -
serve_port_waitis bounded and does not sleep out its budget on a corpse:kill -0 "$pid" || breakmeans a serve that died fails in a tenth of a second rather
than after 30 s. Small, and it is the difference between a test that reports a fault and one that just looks slow. -
serve_kill_treeis the 11-08 incident one level down, and the analysis is exact.1bit serveis a wrapper; the model and its GTT live in thellama-serverit
spawns, sokill "$pid"; wait "$pid"reaps the wrapper and leaves the memory holder. Snapshotting the tree before signalling, because once the root dies its
children are reparented andps --ppidfinds only the corpse, is the same conclusionscripts/gpu-holdreached inkill_tree. I have spent two days working around
exactly this shape on a live box; seeing a test's own teardown produce it is the right instinct, and the right place to fix it. -
The census is re-measured rather than quoted, with a state table over all 24 scripts instead of repeating #1485's stale "19 of 24". That is how a migration ticket
should be closed, and it lets a reader check the claim script by script.
The ask: tests/serve_port_test.sh has no arm for serve_kill_tree. The file's scope line says the parsers are fed the exact lines the servers print — so the
read-back half is fully covered — but the teardown half, which is the half that can leave GTT held, has no test. It is cheap to fake and does not need a model: a
wrapper script that spawns a sleep child, then serve_kill_tree on the wrapper, then assert the child's pid is gone (kill -0). That arm is the one that would have
caught the orphan shape before 11-08, and without it the tree walk's correctness rests on a comment. If you would rather land this and add the arm as a follow-up, say so
and I will not block on it — but the primitive that protects the device should not be the untested part of the file.
Not run: I read serve_port.sh and serve_port_test.sh at this head; I did not execute the suite. The port half is parser work with fixtures and a golden set of
lines, so it does not need a device to be trusted; the tree walk is the part I would want to see pass.
Fixes the last two of #1485's twenty-four pickers, and replaces the issue's census with one measured on
mainat8ff2814e.The census the issue does not have any more
#1485 says "19 of 24 scripts still lose the release-then-bind race". That was true when it was filed; the migration has mostly happened since (the five retry scripts, twelve more via
tests/serve_port.sh), so I measured the table again rather than quoting it:main--port 0andserve_port_waitmetrics_serve pm_route prism_route recipes_route watchdog_serve anchor_rerun laya_gguf_route laya_models_e2e laya_route_e2e mem_admit_route serve_e2e smoke_serve--port 0, no read-back neededhrx_runtime_route parallel_args(they inspect the fake backend's record, never curl the front port),npu_repack_path(its own comment: "nothing here calls the server")--port 0with a local read-backgreedy_shortcutdevice_route hrx_cwsr_route hybrid_e2e hybrid_kvshare_route hybrid_route pm_laya_route(#1607),kvshare_fd_handoff.py(#1637),hrx_cpu_weight_paritySo of the eight that still race, #1607 covers six and #1637 covers one, and this PR takes the remaining two:
hrx_cpu_weight_parity.sh(picks and releases) andgreedy_shortcut.sh(reads back, but with its own end-anchoredsed). After this lands, the only open question in #1485 is whether the issue should be closed once #1607 and #1637 are in.What lands
tests/serve_port.shgains the llama-server pair. The shared helper reads1bit serve's ready line; the scripts that drive the vendoredllama-serverdirectly could not use it, because that server prints a different sentence. It is not a missing capability in the server: llama.cpp already binds an ephemeral port for--port 0and logs where it landed —— so
llama_port_from_log/llama_port_waitare the same "port is the field after the URL's last colon" rule, applied to that line. IPv6 hosts come out right for the same reason they do in theservepair.Two migrations, each removing the race rather than retrying around it:
tests/hrx_cpu_weight_parity.sh:free_port()(bind → print → close → launch) is deleted; the arm launches with--port 0and waits for the bound port. The old path's failure was indistinguishable from the thing the row measures: a stolen port produced "server did not come up", i.e. a coin flip reported as a placement bug.tests/greedy_shortcut.sh: the inlinesed -n 's/…:\([0-9]*\)$/\1/p'is replaced by the shared helper. Its$anchor matches only serve's plain ready line — the(laya routes each request; …)and laya-models shapes carry a suffix after the port, so the same script under a laya flag would have returned empty and failed as "serve never reported a port". That is the docs+tools: main still says the PARD HRX speedup is 1.50x — it is 1.01x, and the correction missed #1516's merge #1523/tests: three more scripts take the port serve reports instead of guessing one (refs #1485) #1525 finding, and it is why the shared parser takes the URL token rather than anchoring.The test, and the mutation that shows it means something
tests/serve_port_test.shis script-based, soctestruns it in thebuildjob with no device, no model and no server: it feeds both parsers the exact lines the two servers print (plain / laya / laya-models / IPv6 / llama'ssrv http_bind:/ a suffix after the address), checks that neither invents a port before the server has said, and drives the failing wait with a process that is already gone (which must return non-zero quickly rather than sleep out the budget).Registering it is
add_test(NAME serve_port …), script-based, so it needs noci.yml --targetentry — the #1595 trap is foradd_tests that name anadd_executable.And the negative control that makes it a regression test rather than a demonstration — restoring the end-anchored pattern the old local parser used:
hrx_cpu_weight_parity.shitself needs HRX0, so its two arms stay hardware-gated; the parser is what this PR can pin in CI, and both migration sites use exactly the function the test covers.No duplicate: #1607 and #1637 are the PRs on the other six, and neither touches these two files.