Skip to content

fix(cli): resolve traces --server through the shared id-or-slug resolver - #10

Merged
andrei-hasna merged 1 commit into
mainfrom
9b7c68db-3ad5-4bd9-9d47-f50d7fa4804b
Aug 7, 2026
Merged

fix(cli): resolve traces --server through the shared id-or-slug resolver#10
andrei-hasna merged 1 commit into
mainfrom
9b7c68db-3ad5-4bd9-9d47-f50d7fa4804b

Conversation

@andrei-hasna

@andrei-hasna andrei-hasna commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes bug row 9b7c68db-3ad5-4bd9-9d47-f50d7fa4804b.

The defect

servers traces --server <value> returned 0 rows at rc=0 for every input except a full UUID.

src/cli/index.ts passed opts.server straight to listTraces, and src/db/traces.ts compares it to traces.server_id with exact equality. That column always holds the full UUID (createTrace writes the already-resolved id), so 'platform-alumia' = 'a2b5e3e0-...' is false and so is 'a2b5e3e0' = 'a2b5e3e0-dbcf-...'. The action had no not-found branch, so it printed an empty table and exited 0.

It fails toward a plausible wrong answer. The traces table renders the truncated 8-char ID in its own SERVER column, so the value a reader copies off the screen is exactly the one that returns nothing, and a real server addressed that way is byte-identical to a server that does not exist:

=== --server alpha-srv (real server, 1 trace) ===   rc=0
ID      EVENT  SERVER  AGENT  CREATED
(none)
Showing 0.

=== --server zzz-not-a-server-zzz (does not exist) ===   rc=0
ID      EVENT  SERVER  AGENT  CREATED
(none)
Showing 0.

The fix

Resolve through resolveServerOrExit, the helper every other --server site in this file already uses (826, 876, 1068, 501/544/581/605/634/1727/1882 — line 1015 was the only one skipping it). No second resolver is introduced.

That buys full UUID, short ID, name and slug, and turns an unknown identifier into stderr + exit 1. The --server help string now matches trace:add's documented contract, "Server ID, partial ID, or slug".

Live, after the fix:

--server aa1793ff  (short id)  rc=0   Showing 1.   only beta.event
--server alpha-srv (slug)      rc=0   Showing 1.   only alpha.event
--server zzz-not-a-server-zzz  rc=1   stdout empty, stderr "Server not found: zzz-not-a-server-zzz"

Regression

test/cli-integration.test.ts covers both directions. It asserts the exact event list rather than a non-empty result, so a fix that merely dropped the filter would fail it too. The full-UUID case is a labelled positive control: it passed before this change, so a failure there means the fixture is wrong, not the filter.

Confirmed failing on the unfixed code for the right reason:

285 |       expect(await events(alphaId.slice(0, 8))).toEqual(["alpha.event"]);
error: expect(received).toEqual(expected)
- [ "alpha.event", ]
+ []
1 fail, 3 expect() calls

and the negative direction pre-fix returned rc=0, which makes runExpectFailure throw.

Gates

gate command result
declared suite bun test 285 pass, 0 fail, rc=0
typecheck bun run typecheck (tsc --noEmit) rc=0, no output

Run separately: test does not invoke tsc in this repo.

Baseline on pristine origin/main with deps installed was 284 pass, 0 fail; this adds one test. One intermediate full-suite run showed an unrelated failure at src/runtime/local-server.test.ts:463 (a spawned child missed a 300 ms readiness budget and never wrote pid.txt). That file passes 3/3 in isolation with this change applied and the subsequent full run was clean; the code path is untouched by this diff.

Scope

Deliberately one defect, one fingerprint.

  • operations --server does NOT share it — measured, not assumed: it resolves at 824-828, and servers operations --server platform-alumia returns Showing 20. at rc=0. The bug row flagged it as unchecked; this is the check.
  • src/mcp/tools/traces.ts:71 has the identical unresolved call and renders the same truncated id at line 80. Same fingerprint, sibling surface — noted on the task for a separate lane, not taken here.
  • Ambiguous prefixes currently report Server not found rather than naming candidates, because resolvePartialId returns null for both ambiguous and absent. Distinguishing them means changing the shared resolver's contract for all eleven call sites, which is wider than this defect justifies. Non-zero exit with no silent pick is preserved either way.

Agent: vespasian


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

`servers traces --server <value>` passed its option straight to the database
layer, which compares it to `traces.server_id` with exact equality. That column
always holds the full UUID, so a slug or a short ID matched nothing and the
command printed an empty table at rc=0.

The traces table renders the truncated 8-char ID in its own SERVER column, so
the value a reader copies off the screen was precisely the one that returned
nothing. A real server addressed that way and a server that does not exist
produced byte-identical output, which reads as "this server has no audit
trail" when the trail exists.

Resolve through the existing resolveServerOrExit helper, the same one every
other --server site in this file already uses. That accepts full UUID, short
ID, name and slug, and turns an unknown identifier into a stderr message at
exit 1 instead of a silent empty result.

Also aligns the --server help string with trace:add's documented contract.

Regression covers both directions: full UUID (positive control), short ID,
slug and name each return only their own server's traces, and an unknown
identifier must exit non-zero. Asserting the exact event list rather than a
non-empty result means a fix that merely dropped the filter would also fail.

Scope: `operations --server` was measured and already resolves, so it does not
share this defect. The identical unresolved call at src/mcp/tools/traces.ts:71
is noted on the task for a separate lane.

Task: 9b7c68db-3ad5-4bd9-9d47-f50d7fa4804b

Agent: vespasian
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #10 @ d0ade6b — lens: correctness, reviewer porcius (1 of 1)

Independent adversarial review, dispatched by vespasian on bug row 9b7c68db-3ad5-4bd9-9d47-f50d7fa4804b. Every claim below was re-measured in a fresh clone, not taken from the PR body. Sha cited resolves on the forge and matches headRefOid.

Attack points, each re-measured

1. Failing-before-fix — REPRODUCED, not trusted. Checked out origin/main code with only the PR's test file applied. Result: 1 fail, 3 expect() calls, failing at line 285 (alphaId.slice(0, 8)) with + [] received, while the full-UUID positive control at 282 passed. The test fails on unfixed code for exactly the claimed reason.

2. Both directions — CONFIRMED. Resolution direction covers full UUID, short id, slug, and name; each asserts the EXACT event list (toEqual(["alpha.event"]) with beta.event present in the DB), so a fix that matched everything would also fail. Negative direction asserts stderr contains Server not found AND code !== 0.

3. Ambiguity — NO SILENT PICK, measured with a real collision. Seeded a DB until a one-char prefix (d) matched 3 servers:

traces --server d   ->  rc=1, stdout 0 bytes, stderr: Server not found: d

Source confirms the mechanism: resolvePartialId returns null on rows.length > 1 before the name/slug fallback, so an ambiguous prefix cannot fall through to a wrong server. The error does not name candidates — disclosed in the PR body as a shared-resolver contract change across eleven call sites, correctly left out of scope. Non-blocking follow-up.

4. Unknown identifier — CONFIRMED live on the head:

traces --server zzz-not-a-server-zzz  ->  rc=1, stdout 0 bytes, stderr: Server not found: zzz-not-a-server-zzz
traces --server da44ee3e (short id)   ->  rc=0, Showing 1. (only alpha.event)
traces --server alpha-srv (slug)      ->  rc=0, Showing 1. (only alpha.event)

5. Typecheck — run by the reviewer, separately: bun run typecheck (tsc --noEmit) on the head tree, rc=0, empty output. Verified test script is bun test and does not invoke tsc, so the separate run was required and was real.

6. Scope — one defect. Diff is +44/−2 across exactly src/cli/index.ts (resolve via the existing resolveServerOrExit, help-string alignment) and test/cli-integration.test.ts. No second resolver, no other verb touched. The disclosed sibling (src/mcp/tools/traces.ts:71 unresolved, short id rendered at :80) verified present and correctly excluded.

7. Base freshness: git rev-parse refs/pull/10/merge^1 = a277db1b5b8e6e77759ab51b6a62692c1ae5e637 = origin/main. The merge ref describes the tree that will land.

One discrepancy, non-blocking, stated so nobody inherits the number

The PR's "285 pass, 0 fail" did not reproduce in this review environment: full suite on the head ran 284 pass, 1 fail, the failure being the pre-existing global --format json is honored by every read subcommand timing out at its 5000 ms budget (5000.54ms). The discriminating check: the same test fails identically on pristine origin/main in the same environment (5004.19ms), so it is environment-dependent and not attributable to this diff. The NEW regression test passed inside that full-suite run and passes standalone with 7 expect() calls. Note for a follow-up, not a blocker: the new test makes ~10 cold CLI spawns and can itself exceed bun's 5000 ms default budget on a slow-spawn box when run standalone — a budget measured in isolation, not a defect in the fix.

Process checks

Commit carries exactly one Agent: vespasian trailer (grep count 1), zero Co-Authored-By. Nothing published: npm latest is 0.1.22, matching the review-before-publish order. Merge must still use --squash --body-file with the trailer as the last line, and publish/install steps remain owed after merge.

Verdict: GO. No reachable P0/P1 defect at head. The two named follow-ups (MCP sibling surface; ambiguity candidates message) belong on the row for separate lanes.

@andrei-hasna
andrei-hasna merged commit 5621396 into main Aug 7, 2026
2 checks passed
andrei-hasna added a commit that referenced this pull request Aug 7, 2026
chore(release): 0.1.23

Ships the traces --server id-or-slug resolution fix (#10) together with the
contracts alignment (#6), reference docs (#5), CI gate (#9) and .editorconfig (#8)
landed on main since 0.1.22.

Gates run separately: bun test 285 pass 0 fail rc=0; tsc --noEmit rc=0. CI SUCCESS.

Agent: vespasian
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