Repository navigation
fix(catalog): trigger-probe exits 2 on a set with no labelled query, warns when no skill was listed - #267
Conversation
Settles two points from the review of intel#217 (catalog trigger-probe). A --trigger-set with no labelled query (a questions set without expected_skill, or an empty set) ran nothing, wrote "answers": [], printed "summary": {} and exited 0. _labelled_queries now raises InputError, which the CLI turns into exit 2, before any run and before --out or <out>.partial is written; the message names --target when it was given. When runs happened but no run's init event listed any skill (an older Claude Code, or every run failing before init), the not_listed guard was skipped silently. The command now prints a warning to stderr saying not_listed was not checked; the output JSON and the exit code are unchanged, and nothing is printed when no run happened. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
@exai-bot /review |
exai-bot
left a comment
There was a problem hiding this comment.
Note
AI-generated review of 9ba04bf58615, requested by @antonwolfy, using the default review skill. It can be wrong: verify a finding before acting on it. React with a thumbs up or down on a finding to tell the bot whether it helped.
Model us.anthropic.claude-sonnet-5-5, effort default, 3 of 80 turns, 0.2 of 40 minutes, 46k in / 857 out tokens.
The change makes catalog trigger-probe exit 2 with an InputError when no labelled query is left, before any run or output file. It also prints a stderr warning when runs happened but none listed a skill. I found no defects. cli.main already turns InputError into Error: ... and exit 2, and the check sits before the thread pool and the first write. The tests cover both behaviours and go through cli.main.
No issues found.
For a human reviewer
- Exit 2 instead of an empty probe.json with exit 0 is a behaviour change for any script that relied on the old output.
vchamarthi
left a comment
There was a problem hiding this comment.
No blocking findings. Both behaviours work as the description says. Ready to approve once the remaining GitHub checks on this head (the py3.11 and py3.14 test legs, mypy and CodeQL) finish green.
Checked on 9ba04bf on a Linux host:
make ci-local: 27 passed, 7 skipped, 1 failed. The failure istests/test_process.py::test_sighup_kills_the_live_groups_too, aTimeoutExpiredduring the full-suite run. This PR does not touch that file, and the test passed 3 of 3 alone on this head and 3 of 3 onmain, so it looks like a timing flake under load. Three reruns do not prove that.- The 24 tests in
tests/test_trigger_probe.pypass. - Mutants, run against those tests: replacing the empty-set check with
if False:fails all threetest_cli_exits_2_on_a_set_with_no_labelled_query_before_any_runcases; replacingif runs and not listed:withif False:failstest_cli_warns_when_no_run_listed_any_skill. - The description's table through
cli.py catalog trigger-probewith a stubclaude, head againstmain: a set with noexpected_skilland a{"queries": []}set both exit 2 withError: --trigger-set has no labelled queries: <path>and write no--outfile (main: exit 0 and a file). An init event with"skills": []givesnot_listed: [], exit 0 and the new warning (main: no warning). An init event listing both skills gives no warning on either tree.
The empty-set check in _labelled_queries runs before <out>.partial is written and before any claude run, and cli.main turns the InputError into Error: ... with exit 2 (cli.py:116).
Two notes, neither blocking:
catalog_probe.py:68: therunshalf ofruns and not listedcannot be false once a labelled query exists, as the description says. Removing it leaves all 24 tests passing, so the guard could go.catalog_probe.py:113: an unreadable or malformed--trigger-setstill exits 1 (SystemExit(f"--trigger-set: {exc}")), while the new empty-set case exits 2. That predates this PR; it fits the sharedInputErrorfollow-up if there is one.
Coverage: read the whole diff and ran everything above. A probe against a real Claude Code run was not done: the changed code acts on the parsed transcript, which the stub produces in the same stream-json shape the existing tests use.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #267 +/- ##
==========================================
- Coverage 92.83% 92.81% -0.02%
==========================================
Files 262 262
Lines 33016 33020 +4
Branches 5510 5512 +2
==========================================
Hits 30649 30649
- Misses 1571 1574 +3
- Partials 796 797 +1
🚀 New features to boost your workflow:
|
Summary
Follow-up to the review of #217 (
catalog trigger-probe), which left two Nits:(review);
not_listedguard is skipped without a word when no run reports any listed skill(review).
On
main, with a two-skill catalog (sum-fast,io-read) and a stubclaudethat prints astream-json transcript:
main--trigger-setwhose questions have noexpected_skillprobe.jsonwith"answers": [], prints"summary": {}andwrote probe.json, exit 0Error: --trigger-set has no labelled queries: ts.json, exit 2, noprobe.json, no run--target sum-fast(read with --target 'sum-fast'){"queries": []}(empty skill-reach set)"skills": []in every runnot_listed: [], recall 0.5 scored, exit 0, nothing on stderrwarning: Claude Code listed no skills in any run; not_listed was not checkedon stderrnot_listed: [], exit 0--targetdoes not filter the queries of any format: it labels the rows of a skill-creator set,where every row is labelled. So the "
--targetmatches none" case the review names cannot occurat this head. The error still names
--targetwhen it was given, for a set whose labels aremissing anyway.
Changes
dokimi/dokimi/commands/catalog_probe.py,_labelled_queries: when no labelled query is leftafter the filter, raise
InputErrornaming--trigger-set, its path and--targetwhen given.cli.mainprints it asError: ...and exits 2, as it does the command's other input errors.This happens before
<out>.partialor--outis written and before anyclauderun.cmd_catalog_trigger_probe: when at least one run happened and no run listed anyskill, print one warning line to stderr.
not_listedstays[]in the output and the exit codeis unchanged. Nothing is printed when there were no runs.
dokimi/docs/evaluating-treatments.md, thecatalog trigger-probeparagraph: one sentence foreach behaviour.
dokimi/tests/test_trigger_probe.py:test_cli_exits_2_on_a_set_with_no_labelled_query_before_any_run(three cases: unlabelled questions, the same with
--target, an empty skill-reach set). It goesthrough
cli.mainand checks exit 2, the message, that noprobe.jsonor.partialfile waswritten and that
probe_trigger_setwas never called.test_cli_warns_when_no_run_listed_any_skillchecks that the warning appears for"skills": []with
not_listed == [], and that no warning appears for a run that lists skills.Risk
A trigger set with no labelled query was accepted and produced an empty output with exit 0. It
now exits 2 and writes nothing. A script that relied on that empty
probe.jsonwill now see theerror. The new stderr line appears only when runs happened and none listed any skill. The output
JSON (no schema change) and stdout are unchanged in every case.
Testing
On a Linux review host, at
9ba04bf(onmain3a1f287):make ci-local: 28 passed, 0 failed, 7 skipped (agent-quality.yml:external-catalog-smoke,which needs docker and network;
ci.yml:test-latest,docs.yml:deployandpip-audit.yml:report, whose job conditions are false on a PR;cli-interface.yml"Package theresult", whose expression
ci-localcannot evaluate;conda-forge.yml:conda-forge-deps, with nomicromamba; semgrep secrets, which runs on the PR). 9302 tests passed, 4 skipped; changed-line
coverage 100% (4 of 4 lines). An earlier run on a busy host failed one wall-clock test,
tests/test_reference_checks.py::test_the_regex_limit_is_shared_by_every_pattern_of_one_check(2.02 s against a 1.5 s bound). That module is not touched here, and the test passed alone and
in the rerun above.
tests/test_trigger_probe.py,tests/test_docs_code_refs.py,tests/test_growth_ratchet.py,tests/test_audit_test_quality.py,tests/test_import_architecture.py: 311 passed.ruff checkpasses on the changed files, and so does
mypyoncatalog_probe.py.main's code the new test file fails 4 of its 24 tests (the three empty-set cases do notraise
SystemExit, and the warning is missing). At this head all 24 pass.if not queries:→if False:) fails all threetest_cli_exits_2_on_a_set_with_no_labelled_query_before_any_runcases;if runs and not listed:→if False:) failstest_cli_warns_when_no_run_listed_any_skill.cli.mainwith a stubclaudescript, at this head and onmain.command always has at least one run, so the no-run case cannot be reached from the CLI.
Revert
git revertof the one commit. It leaves no stored records and no schema versions behind.Checklist
pytestpassesruff,mypy, and schema checks passAGENTS.md.github/.licenserc.yaml)