Repository navigation
npu_serve_smoke --judge: a directory with no replies says so (rc 2), instead of three FAILs - #2168
Conversation
…instead of three FAILs
The 2026-11-11 10:54Z demo smoke was logged `FAIL` in DEMO-SMOKE.md:
| 2026-11-11T10:54Z | fast | The capital of France is Paris. | 100.5 | ERROR | 0 | **FAIL** |
Triaged from the run's own artefacts, the demo was healthy: fast route line in serve.log, 100.5 tok/s against a 60
floor, first token = timings.prompt_ms 279 ms against a 600 limit, content "The capital of France is Paris."
What failed was the reading. The ad-hoc harness wrote its reply as `npu.json`; `--judge` looks for `reply_0.json`, so
it found no replies and reported
rate FAIL 0 of 0 replies carry no decode timing
first-token FAIL a reply carries no prompt_ms
content FAIL no replies
three lines that read as a broken demo. Renaming the same bytes to reply_0.json gives PASS.
"0 of 0 replies" is not a verdict about a run, it is the wrong directory - so rejudge() now refuses it by name:
serve cannot judge: no replies in <dir>
looked for serve.log, reply_0.json …, load.json; the .json files here are: npu.json
if this is another harness's output, name its reply as reply_0.json and re-run --judge
rc 2, no verdict lines. judge() itself still FAILs on an empty reply LIST, which is the different case (a run that
produced no reply); the distinction is the same one #2039 drew between a skip and a failure.
Verified on the real artefacts: the unreadable directory rc 2 with the message above, the renamed one PASS rc 0, and the
self-test gains the arm for a directory holding a `npu.json` but no `reply_*.json` (16 judging cases, 3 stand-in runs,
rc 0).
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 8faab98: READY
"A directory with no replies says so" is the distinction this gate exists to make, applied to the gate itself. npu_serve_smoke is the check that the shipped binary's serve actually runs (#2119) — the thing the private NPU-phase checker could not see, which is why #2102 and #2106 were release blockers. A judge that reads a directory with no replies and exits quietly is the same defect one level up: it would report nothing rather than report that it could not tell.
The diagnostic is the part worth keeping:
if not replies:
print("serve cannot judge: no replies in %s" % d)
print(" looked for serve.log, reply_0.json …, load.json; the .json files here are: %s" …)Naming both what it expected and what is actually there turns "the gate says nothing" into a one-line answer — the difference between an operator re-running the smoke and an operator wondering whether the gate is broken. It is the same shape as #2019's "refuse by file and number" and #2141's suggested "name both commits": a check that cannot answer should say which inputs it looked for.
Non-zero rc on that path is the other half, and the more important one: "cannot judge" must not be exit 0 — a gate that cannot tell and exits successfully is worse than no gate, because it launders the unknown into a pass.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 8faab98: READY
Reviewed head 8faab98 (refs #2129 follow-up). This is the second verdict on this head.
- The change is confined to
rejudge().judge()andrun()are untouched, so a live run that got no reply still goes throughjudge()on an empty list and FAILs (rc 1), as the new comment says it must. Only re-judging a directory with noreply_*.jsonis refused. - By hand:
- a directory with
serve.logand onlynpu.jsonprints "cannot judge: no replies in …", namesnpu.jsonas the .json found, and exits 2; - the same
serve.logwith a realreply_0.jsongivesRESULT: PASS, rc 0.
- a directory with
--self-test: "16 judging cases, 3 runs against a stand-in serve and a TERM mid-request ok". Mutant: with the newif not replies:guard disabled, the self-test fails (rc 1). I restored the file (git status clean).git merge-treeonto main: no conflicts. CIbuildand CodeQL green.
Nit: rc 2 is also what argparse returns for a usage error, so a caller can tell "cannot judge" from "bad arguments" only by the text. The hw-gate phase does not call --judge, so nothing depends on it today; a distinct code would make it unambiguous, should a script ever key on it.
Follow-up to the 10:54Z smoke
FAILin~/briefs/DEMO-SMOKE.md, which I triaged from the run's own artefacts and found to be a reading failure, not a demo failure.The incident, in three commands
The demo was healthy the whole time — fast route line,
100.5 tok/sagainst a 60 floor, first tokentimings.prompt_ms = 279.458517against a 600 limit, content "The capital of France is Paris.", andhold.logrecords "command exited 0 after 4s". The ad-hoc harness wrotenpu.json;--judgelooks forreply_0.json.What this changes
"0 of 0 replies"is not a verdict about a run — it is a directory this reader cannot read. Sorejudge()now refuses it by name:rc 2, no verdict lines.
judge()is untouched and still FAILs on an empty reply list — that is the different case, a run that produced no reply, and it must keep failing. The distinction is the one#2039drew between a skip and a failure, and the oneDEMO-SMOKE.mditself makes in its "Reporting" section: a reading that cannot be trusted is worse than no reading.Verified
The self-test gains the arm: a directory holding
serve.log+ anpu.json(i.e. another harness's name) and noreply_*.jsonmust return 2 — so the next person triaging a smokeFAILis told they are looking at the wrong directory instead of after a broken demo.Scope: one function plus one self-test arm in
tools/npu_serve_smoke.py. No behaviour change on any run whose artefacts this tool wrote.