fix(locate): exit non-zero for an unresolved target - #559
Conversation
`ix locate <missing>` reported the miss in every format but always exited 0, so a machine caller could not distinguish it from a successful command that intentionally returned nothing. `context`, `explain`, `read`, `trace` and `subsystems` all exit 1 for the same condition after #526 and #532; locate was the odd one out, and its llm output even emitted an `error` record while exiting 0. The result body is deliberately left alone rather than replaced with `reportUnresolvedTarget`'s `{error, message}` shape. Shipped plugins read `diagnostics` off it, and the point of #539 is that the exit code becomes informative without the payload going away. Ambiguity keeps its own path: it resolves to several candidates, which is an answer rather than a miss, and it returns before this branch. This is step 2 of the sequence in #539, and must land after step 1 -- ix-opencode-plugin#20, ix-openclaw-plugin#33 and ix-claude-plugin#37. Closes #539
Status — reviewed, deliberately left as a draftRe-read this as part of a sweep of every open PR. The change itself is correct and I am not asking for anything: Not converting it to ready. The merge order in #539 still stands and none of it has moved:
Landing this first makes One cross-reference worth having here: the comment in this diff draws the ambiguity distinction explicitly —
whereas #547, open right now, routes both conditions through |
|
Re-checked today (2026-09-01). Still correct, still must merge last, and the queue in front of it has not moved. The change is small and I have no objection to it: exiting 1 on a genuine miss while keeping the The reason it can't go in yet, verified against each repo's current
Merge order is unchanged from 08-30: ix-opencode-plugin #20 → ix-openclaw-plugin #33 → ix-claude-plugin #37, then this. Leaving it as a draft. Nothing to fix on this branch. |
|
Second pass on the code. Re-verified against today's Correction to what I said on 08-31I flagged that this PR and #547 disagree about whether an ambiguous target should report Your comment in
So both PRs already encode the same rule: ambiguity is an answer, zero matches is a failure. No decision needed, and I've withdrawn it from the list of things needing a call. The restKeeping the Still blocked on the same three, in order: ix-opencode-plugin #20 → ix-openclaw-plugin #33 → ix-claude-plugin #37, then this. Leaving it as a draft. |
Closes #539. Draft on purpose — this is step 2 and must land after step 1.
Merge order
#539 is explicit that flipping this exit code first is what breaks things. The three consumers it names are now ready and green:
Once those are in, this is ready to go. Marking it ready before then would degrade all three.
Change
ix locate <missing>reported the miss in every format but always exited 0, so a machine caller could not tell it from a successful command that intentionally returned nothing.context,explain,read,traceandsubsystemsall exit 1 for the same condition after #526/#532 — locate was the odd one out, and its llm output already emitted anerrorrecord while exiting 0.Verified live against backend 1.0.27, all three formats:
Two deliberate choices
The body stays as it is. I did not switch to
reportUnresolvedTarget, whose{error, message}shape is what the sibling commands use — shipped plugins readdiagnosticsoff locate's result, and #539's whole point is that the exit code becomes informative without the payload going away. Worth a second opinion if you would rather have the uniform shape and accept the consumer churn.Ambiguity is untouched. It resolves to several candidates — an answer, not a miss — and returns on its own path before this branch. Making that non-zero too would be a separate policy call.
End-to-end proof of the sequence
Old vs new plugin
safeRun, both against this build:That is exactly the degradation #539 predicted, and exactly what step 1 prevents.
Checks
Both new tests fail without the change. Full gate: 1449 passed / 2 skipped, lint 0 errors, typecheck clean, knip clean.