Recipes

Jarvis CLI change: ambiguous and text-only graph targets answer instead of erroring

Discovered from README cross-links · today

Software & DevOpsWork & Operations

Agent-authored PR makes ix graph lookups answer ambiguous names and source-only

What they did

An agent (jarvisd) opened a draft pull request against the ix-infrastructure/ix repo, changing how the `ix` CLI's graph lookup commands behave when a target is ambiguous or is not a code symbol at all. Previously these cases failed with exit 2 (ambiguous_target) or exit 3 (unresolved_target). The change makes fifteen commands — locate, callers, callees, impact, explain, depends, overrides, imports, imported-by, contains, history, overview, trace, diff and context — answer for the first candidate in the ambiguity's own order (the same one `--pick 1` selects), while always printing an `ambiguous_target` notice: a diagnostic line plus candidate rows and a hint in llm format, a `resolutionNotices` entry with chosen/candidates/total in json, and a stderr notice in text. `--pick <n>` still indexes the same list, and an out-of-range pick answers the first candidate with a `pick_out_of_range` notice. The shared helper is `answerAmbiguous` in resolve.ts, called only from `resolveFileOrReport`, locate's own resolution and trace's `--to` path; internal resolution (pickBest, --from-issue starts, rank, post-edit hook) is untouched. For names that are not code symbols but appear in source files, the existing bounded `rg` miss probe was switched from `-l` to `-n -m1 --null` so it keeps each file's first matching line at the same cost; only source files (via the exported `issueSourcePath` test) are listed, line-setting matches first, capped at 10, rendered as a `text_matches` note in llm and a `{note, textMatches, shown, total}` object in json with no error. Docs-only or test-only mentions still exit 3. `--strict` or `IX_STRICT_TARGETS=1` restores the old errors, `IX_LEGACY_EXIT_CODES` keeps prior semantics, and `context --diff` always re-resolves strictly. The agent also audited the six plugin repos read-only for exit-2/exit-3 consumers, updated outcome.ts, EXIT_CODES_HELP, docs/llm-format.md, flags.md, SKILL.md and CONTRIBUTING, and wrote tests first (39 failing on base) before implementing.

How it went

The PR reached a clean system review after two rounds and one fix session. Typecheck, 248 test files / 4338+ unit tests, parser smoke and doc parity (60 commands, 370 flags) all passed; the new target-answers suite is 63/63. The fix session made the real-backend e2e scenarios assert the new contract and routed reportUnresolved and locate through withTargetCandidates so text-match answers render suggestions in all three formats. It remains a draft: Jarvis never merges, and the perf gate, e2e suite and plugin-repo search were not run because the sandbox lacks docker/ArangoDB and the run rules forbid GitHub calls.

Worth knowing

Exit codes change (2→0 and some 3→0) and JSON shapes change for every graph command, which is a plugin contract break: any wrapper or test pinning exit 2 (e.g. ix-claude-plugin's test_real_ix.sh) will fail unless it sets IX_STRICT_TARGETS=1. A `text_matches` JSON body has a different shape from an entity answer, so plugins rendering `locate --format json` may display it oddly until taught the shape. The rg output buffer grew from 1 MB to 4 MB. New behaviour is verified only with mocks until CI's e2e job runs against a real backend.

Wiring

Work happens in the ix-cli package. The shared helper `answerAmbiguous` lives in resolve.ts and is invoked from `resolveFileOrReport`, locate's own resolution path and trace's `--to` path, so all fifteen commands inherit the behaviour without touching pickBest, --from-issue start resolution, rank or the post-edit hook. The text-match path reuses the existing miss probe's single bounded `rg` call, now with `-n -m1 --null`, filtered through the exported `issueSourcePath` predicate from the issue code, and is gated behind `withTargetCandidates` so suggestions render in llm, json and text. Strictness is read from the `--strict` flag or the `IX_STRICT_TARGETS` env var, with `IX_LEGACY_EXIT_CODES` preserving prior semantics; `context --diff` forces strict re-resolution. Output formatting flows through outcome.ts and EXIT_CODES_HELP, which also surfaces in `explain --help`. Verification order: `npx vitest run src/cli/__tests__/target-answers.test.ts` to reproduce failures on base, then `cd ix-cli && npm run typecheck && npm run test:unit && npm run smoke`, then `npm run build && node scripts/check-doc-parity.mjs` and `node ix-cli/scripts/check-links.mjs`. The e2e scenarios in test/e2e/scenarios.e2e.ts assert the new contract but need docker/ArangoDB, so they run only in CI.

Try it yourself

In this repo, change the `ix` CLI graph lookup commands (locate, callers, callees, impact, explain, depends, overrides, imports, imported-by, contains, history, overview, trace, diff, context) so that an ambiguous target answers for the first candidate with exit 0 instead of exit 2, and a name that is not a code symbol but appears in source files answers with those files and exit 0 instead of exit 3. Always print a visible notice: a `diagnostic code=ambiguous_target` line plus candidate rows and a hint in llm format, a `resolutionNotices` entry with chosen/candidates/total in json, and a stderr notice in text. Keep `--pick <n>` indexing the same candidate list, and add `pick_out_of_range` when the pick is out of range. For text matches, reuse the existing bounded rg miss probe but switch it to `-n -m1 --null` so each file's first matching line is kept, list only source files, put line-setting matches first, and cap at 10 rows; render as `note code=text_matches` in llm and `{note, textMatches, shown, total}` in json with no error. Add `--strict` and `IX_STRICT_TARGETS=1` to restore the old exit 2 / exit 3 behaviour, keep `IX_LEGACY_EXIT_CODES` semantics, and make `context --diff` always re-resolve strictly. Do not touch internal resolution (pickBest, --from-issue starts, rank, post-edit hook). Write failing tests first, then update outcome.ts, EXIT_CODES_HELP, docs/llm-format.md, flags.md, SKILL.md and CONTRIBUTING, and run typecheck, unit tests, smoke and check-doc-parity.

Source

Tools in this stack