Skip to content

fix(routing): name what the field leaves out; add --keep-copies-under and all-vs-all estimate - #394

Open
napetrov wants to merge 23 commits into
intel:mainfrom
napetrov:fix/routing-gaps2
Open

napetrov wants to merge 23 commits into
intel:mainfrom
napetrov:fix/routing-gaps2

Conversation

@napetrov

@napetrov napetrov commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Summary

novelty routing
  --catalog DIR                       the field
    same-name copies with different content
      conflict: error names each copy's directory,
                suggests --catalog <subdir> or --keep-copies-under <subdir>
  --keep-copies-under DIR             keeps only the copies under DIR (relative to --catalog)
  --all-vs-all
    --queries-by-skill                queries of a skill the field leaves out: dropped with a
                                      WARNING naming the keep flag; refused only if nothing is
                                      left or a name no rule explains
    --estimate-only                   prices every call, zero model calls, JSON shaped like the
                                      single-artifact estimate
    --queries-by-skill / --out        missing ones reported in one error
  saved routing JSON
    field.keep_copies_under           the rule the run used
    field.dropped_queries             [{skill, reason, keep_flag, queries}]

Evidence

  • Before: --all-vs-all --queries-by-skill naming a spec-error skill aborts with exit 2; a
    ponytail-shaped catalog (skills/ and .openclaw/skills/ with differing copies) gives 0
    competitors and "routing needs competitors" with no cause; --all-vs-all --estimate-only is
    refused.
    After: tests/test_novelty_routing_gaps.py (19 tests) and the all-vs-all and field tests
    pass. The new tests failed before the change (18 failures across the routing test files).
    pytest tests/test_novelty*.py tests/test_skill_repo_shapes.py tests/test_cli_flags.py tests/test_cli_surface.py: 1455 passed. mypy and ruff clean on the changed modules.

Merge Danger

Door: two-way. The flags are additive; --keep-copies-under and the estimate path have not
shipped before. The saved routing JSON gains keys and no existing key is removed.

Blast radius: novelty routing only. Behaviour changes in two places: an --all-vs-all run
whose queries name a skill the field leaves out now exits 0 with a WARNING (was exit 2), and the
conflict error text changed. Runs without a same-name conflict or a left-out skill are unchanged.

Local self-review: 2 fresh-context passes, 0 blocking.

Follow-ups (not in this PR):

  • The stdout summary does not mention dropped queries; the stderr WARNING and
    field.dropped_queries do.
  • --keep-copies-under . is a no-op: it names the root, so every copy stays and nothing resolves.
  • The --keep-copies-under help does not say that names with no copy under DIR are left out
    with a note.
  • Move check_all_vs_all_args and drop_queries_left_out into commands/novelty_routing_inputs.py.

🤖 Generated with Claude Code

…ll-vs-all estimate

Four gaps in `novelty routing`:

- Same-name catalog copies with different content emptied the field with
  no hint. The error now names the conflicting skills, the directory of each
  copy, and the `--catalog <dir>` to route with. `--prefer-path <dir>` keeps
  the one copy under <dir> when the copies there agree.
- `--all-vs-all --queries-by-skill` aborted when the queries named a skill the
  field left out. Those queries are dropped with a WARNING naming the flag
  that keeps the skill; the run is refused only when no query is left. A name
  no rule explains is still refused, as a likely typo.
- `--all-vs-all --estimate-only` was refused. It now prints the same JSON
  shape as the single-artifact estimate, with zero model calls.
- A missing `--queries-by-skill` and `--out` are reported in one error.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ped queries

Self-review fixes to the routing gaps change:

- Rename --prefer-path to --keep-copies-under DIR. It keeps only the copies under DIR,
  relative to --catalog; an empty, absolute, escaping or non-directory value exits 2.
- Record keep_copies_under in the saved field, and list dropped all-vs-all queries as
  field.dropped_queries ({skill, reason, keep_flag, queries}) instead of free-text left_out.
- The "leaves out" list names only skills the field does not have.
- A copy at the catalog root is named as the catalog root, not as <catalog>/.
- A queried catalog skill that failed to load is refused with the load error, not as a
  misspelling.
- The conflict note points to --keep-copies-under.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov
napetrov requested a review from a team as a code owner November 4, 2026 18:46
Copilot AI balanced review requested due to automatic review settings November 4, 2026 18:46
@github-actions github-actions Bot added the cli-interface-change Changes dokimi's user-facing CLI surface label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

CLI interface change detected

This PR changes dokimi's user-facing CLI surface (commands and/or options):

~ `dokimi novelty routing` parameters changed:
    + added option `--keep-copies-under`

Updated on every push; the cli-interface-change label is on while the diff is non-empty.

@exai-bot

exai-bot commented Nov 4, 2026 •

Copy link
Copy Markdown

OpenCodeReview: Commit 027e4d3 was not reviewed. This PR has reached its review limit (6 reviews).

CI at `027e4d3`: 7 still running, 7 passing, 2 skipped
Check Status
CLI interface diff still running
CodeQL (actions) still running
CodeQL (python) still running
Package install smoke test (py3.11) still running
Tests (py3.11) still running
Tests (py3.14) still running
Windows smoke test (py3.13) still running

To review this commit: re-request review from exai-bot in the Reviewers list.

<details><summary>Last review (5d38e30): no findings</summary>

Open threads from the bot: 0

</details>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Estimate-only preflight accepts duplicate skills that measured runs reject, while root-copy diagnostics and documentation incorrectly rule out a valid copy-selection option.

2 open issues
What changed in this PR

2 new findings.

This PR improves novelty routing when catalog copies conflict or queries target excluded skills, and adds cost estimates for all-vs-all runs.

Changes:

  • Adds --keep-copies-under and more specific catalog conflict diagnostics.
  • Drops queries for excluded skills with warnings and saved reasons.
  • Enables all-vs-all estimates and reports missing required flags together.
File Description
dokimi/​tests/​test_skill_repo_shapes.py Updates excluded-skill warning expectations.
dokimi/​tests/​test_novelty_routing_gaps.py Adds routing regression coverage.
dokimi/​tests/​test_novelty_routing_field.py Updates field exclusion checks.
dokimi/​tests/​test_novelty_routing_all_vs_all.py Updates query handling and saved-field tests.
dokimi/​tests/​data/​cli_flags_allowlist.txt Registers the new copy-selection flag.
dokimi/​scripts/​growth_baseline.json Lowers the command module’s size baseline.
dokimi/​dokimi/​novelty/​routing.py Integrates copy selection and exclusion diagnostics.
dokimi/​dokimi/​novelty/​catalog_keep.py Validates and resolves copy-selection rules.
dokimi/​dokimi/​commands/​novelty.py Wires the flag, estimates, and query filtering.
dokimi/​dokimi/​commands/​novelty_routing_report.py Adds diagnostics, estimates, and field metadata.
dokimi/​dokimi/​commands/​novelty_routing_inputs.py Passes copy rules and diagnostic details through loaders.
dokimi/​docs/​skill-novelty.md Documents the updated routing behavior.
dokimi/​docs/​reference/​cli-interface.json Records the new CLI option.
dokimi/​changelog.d/​20261104_120000_napetrov_routing-gaps2.md Summarizes routing additions and fixes.

Review effort: Balanced

@codecov-commenter

codecov-commenter commented Nov 4, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 96.72897% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.80%. Comparing base (7fa98b9) to head (027e4d3).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
dokimi/dokimi/commands/novelty_routing_report.py 96.58% 2 Missing and 2 partials ⚠️
dokimi/dokimi/novelty/catalog_keep.py 93.87% 2 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #394      +/-   ##
==========================================
+ Coverage   94.78%   94.80%   +0.02%     
==========================================
  Files         353      355       +2     
  Lines       46527    46765     +238     
  Branches     7967     8011      +44     
==========================================
+ Hits        44100    44335     +235     
- Misses       1540     1541       +1     
- Partials      887      889       +2     
Files with missing lines Coverage Δ
dokimi/dokimi/commands/novelty.py 98.29% <100.00%> (+0.59%) ⬆️
dokimi/dokimi/commands/novelty_routing_inputs.py 91.35% <100.00%> (+4.40%) ⬆️
dokimi/dokimi/novelty/all_vs_all.py 99.23% <100.00%> (+0.01%) ⬆️
dokimi/dokimi/novelty/routing.py 96.43% <100.00%> (+0.14%) ⬆️
dokimi/dokimi/novelty/catalog_keep.py 93.87% <93.87%> (ø)
dokimi/dokimi/commands/novelty_routing_report.py 98.19% <96.58%> (-1.81%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

A SKILL.md one directory beneath the catalog (review/SKILL.md) was reported as a copy at the
catalog root, so the conflict hint said to dedupe or rename it. --keep-copies-under review
selects it. The hint now names that directory as the keep path and gives the dedupe advice only
for a SKILL.md in the catalog root. The root-copy test wrote a skill directory, not a root
SKILL.md, so it pinned the bug; it now writes the root file, and a test routes the suggested keep.

--all-vs-all --estimate-only priced a field that names a skill twice, which the measured run
refuses. The estimate now runs the same check, check_field_names, before it prices the calls.

Thread: PRRT_kwDOVBg7g86zv_GG
Thread: PRRT_kwDOVBg7g86zwAGs
Thread: PRRT_kwDOVBg7g86zwAH5
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov

napetrov commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

No change here: this review is only the bot marker. Its finding is the thread above, fixed in 9545b30.

No change here: this overview restates the two threads above, fixed in 9545b30 and 9545b30.

No change: this is the automated CLI notice. The flag it lists is registered in the CLI allowlist (dokimi/tests/data/cli_flags_allowlist.txt:96).

No change here: this summary restates the thread above, fixed in 9545b30.

No change: this is a Codecov setup notice, not a code claim. No Codecov check runs on this PR, and the PR changes no Codecov setup.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 631fca0. The routing changes hold up: --keep-copies-under validates its value, the conflict error names each copy's directory, and --all-vs-all --estimate-only prices the same field the run measures. One risk before merge: no test checks which copy --keep-copies-under keeps. Two nits inline, and one more on the empty-query-file message.

What I ran, on a Linux review host at 631fca0:

  • make ci-local: 30 passed, 0 failed, 7 skipped (jobs that need another Python, a conda or semgrep install, or a push event). Changed-line coverage gate passes.
  • Reproductions for the findings, with the LLM transport mocked.
  • A mutation run: 40 mutants of the new source, 36 killed, 4 survive the full routing suite, and so does one more (a duplicated validation call). The two that matter are inline.
  • --help, docs/reference/cli-interface.json regenerated from the head tree (identical), changelog fragment format, growth baseline for novelty.py (1019 lines, equal to the new baseline), all three commits have Signed-off-by.

Nit on the empty-query-file message. routing --all-vs-all --queries-by-skill q.json with q.json = {} fails with no skill in the field has queries to route: the queries name only skills the field leaves out (`off` sets disable-model-invocation; ...; `review` has copies with different content; ...). The file names none of those skills, and with no skill left out the parenthesis is empty. The base printed only the first clause. Suggestion: add the parenthesis only when queries_by_skill is non-empty and names a dropped skill (novelty_routing_report.py:260).

Checked, no issues

  • Validation: an empty, absolute, .., file or missing --keep-copies-under value, and the flag without --catalog, each exit 2 with the documented reason. A trailing slash and a skill directory directly under the catalog work.
  • Saved field: field.keep_copies_under and field.dropped_queries appear; comparing JSON key paths of base and head, nothing is removed. schemas/novelty.v1.json has no routing definition, so the schema-versioning rule does not apply.
  • Estimate: --all-vs-all --estimate-only exits 0 without --out, makes no model call and writes no file; a duplicate name is refused as in the run.
  • Earlier bot and reviewer threads: the root-copy hint and the duplicate-name check in the estimate are both fixed at this head.

Comment thread dokimi/dokimi/novelty/catalog_keep.py Resolved
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
``{"skill", "reason", "keep_flag", "queries"}``.
"""
names = {c.name for c in field}
unexplained = sorted(n for n in queries_by_skill if n not in names and n not in dropped)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
Must-fix: preserve load-error refusal when an excluded copy shares the name. Trigger: a catalog with two enabled skills (cache, queue), malformed YAML at broken/review/SKILL.md, a disabled off/review/SKILL.md, and queries for cache and review. Evidence: catalog scanning records the malformed copy in unloaded['review'] and the disabled copy in dropped['review']; this condition excludes review from validation solely because it is in dropped, so its queries are discarded and the run succeeds instead of reporting the load error promised in the docs. Impact: incomplete query coverage with a misleading disabled-only explanation. Check absent field names in unloaded before applying rule-based dropping, and add a mixed-copy regression test. Workaround: repair the malformed copy before running. Severity is medium because this is a bounded input-validation and saved-result correctness failure.

Comment thread dokimi/dokimi/novelty/catalog_keep.py Outdated
raise ValueError("--keep-copies-under is an absolute path: give a directory relative to "
f"--catalog: {keep}")
base = Path(root).resolve()
target = (base / keep).resolve()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
Must-fix (medium). Impact: invalid copy-selection input escapes the CLI's input-error handling and produces a traceback instead of an exit-2 validation message. Trigger: on supported Python 3.11, create catalog/loop -> loop and pass --catalog catalog --keep-copies-under loop. Evidence: Path.resolve() raises RuntimeError for a symlink loop; routing_field() catches only ValueError here, its loading wrappers catch OSError, ValueError and RecursionError, and the CLI catches InputError, so this exception is not translated. Wrap resolution and translate its symlink-loop RuntimeError to ValueError, with a regression test. Workaround: choose a non-cyclic directory. Severity justification: this is a bounded, concrete invalid-input failure affecting both measured and estimate paths, not normal routing.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit per nit, up to 5b5047c, which fix the three nits from my review:

  • 00f6ebf: dokimi/tests/test_novelty_routing_gaps.py:207: the dropped skill now has two queries; the test asserts queries == 2 in the record and dropped 2 queries of in stderr.
  • 36c27c9: dokimi/tests/test_novelty_routing_gaps.py:139: the second _parse call passes --out under tmp_path, so no results/ is written to the working directory.
  • 5b5047c: dokimi/dokimi/commands/novelty_routing_report.py:259: the left-out clause is added to the error only when the queries file names skills.

make ci-local at 5b5047c: 29 passed, 1 failed (timing flake, passes on rerun), 7 skipped.

Not approving yet. Still open: the risk from my review.

A mutant that keeps the copies outside the directory passed the whole
routing suite: the competitor names are the same for both copies.

Thread: PRRT_kwDOVBg7g860DRpM
Review: PRR_kwDOVBg7g88AAAABWNTUWQ
Review: PRR_kwDOVBg7g88AAAABWMe6eQ
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A name with a malformed copy and a copy a rule left out was exempt from
the unexplained-name refusal, so its queries were dropped and the run
succeeded with a disabled-only explanation. A name in the unloaded map
is refused with its load error.

Thread: PRRT_kwDOVBg7g860Dnta
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
On Python 3.11 and 3.12 Path.resolve() raises RuntimeError for a symlink
loop, which the CLI does not translate, so the run printed a traceback.
The check now reports a loop as a ValueError (exit 2) on every version.

Thread: PRRT_kwDOVBg7g860Dnth

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov

napetrov commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in f0795d4: kept copy pinned. Nit: fixed in 5b5047c.

Fixed in f0795d4: the open risk is pinned. Thanks for the nits.

@vlad-perevezentsev vlad-perevezentsev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 8050502. --keep-copies-under keeps the right copy, the all-vs-all estimate prices the field the run measures, and the fixes for the earlier threads hold. The main gap is the conflict hint: for nested folders and for symlinked copies it suggests flags that change nothing, and the flag then says nothing.

What I ran, at 8050502:

  • make ci-local --offline: 19 passed, 0 failed, 13 skipped (network, Docker and push-only jobs).
  • novelty routing (lexical and --estimate-only, no model call) on scratch catalogs: nested copies (skills/review + skills/legacy/review), a SKILL.md symlinked from outside, two conflicting names in four folders, a disabled + spec-error skill, a broken copy next to a loaded one, a missing --catalog. The results are in the line comments.

Checked, no issues

  • Earlier threads: a broken copy next to a disabled one is refused with its load error (29c1605); a symlink loop in --keep-copies-under exits 2 on 3.11/3.12 (8050502); the test pins which copy is kept (f0795d4).
  • Validation: an empty, absolute, .. or file value, and the flag without --catalog, exit 2 with a clear reason.
  • Saved field and docs: keep_copies_under and dropped_queries only add keys; no schema covers the routing field. skill-novelty.md and the changelog fragment describe the new flag and the dropped-query behaviour.

Comment thread dokimi/dokimi/novelty/catalog_keep.py Resolved
…ng-gaps2

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…r name

The conflict hint now names, for each conflicting name, only the
directories that hold exactly one of its copies, and says that
--keep-copies-under takes one directory.

Thread: PRRT_kwDOVBg7g860Lia8
Thread: PRRT_kwDOVBg7g860LibM
Review: PRR_kwDOVBg7g88AAAABWQyWpQ
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g860LibD
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g860LibN
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…even when one loaded

Thread: PRRT_kwDOVBg7g860LibV
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g860Libd
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g860Libj
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov

napetrov commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks: all seven fixed this round, from 337c641.

…talog copy

A broken catalog copy of a name in --competitors was recorded as unloaded,
so --all-vs-all refused the run as "half loaded", although the explicit
competitor shadows every catalog copy of its name. Main measured it.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
A name whose field copy is under --keep-copies-under was still refused as
"half loaded" when a copy outside the directory failed to load, although the
user named the copy to measure. That broken copy is now left out with a note;
a broken copy under the directory, or one with no path, still refuses.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
The conflict hint placed a copy by its unresolved path, while the keep rule
resolves it, so a copy whose SKILL.md links outside the catalog got a
--keep-copies-under suggestion that the run then answered with "kept no copy".

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Covers a name whose copies agree (left alone) and the "kept no copy" note,
which must not appear once a name was resolved.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
@xaleryb

xaleryb commented Nov 6, 2026

Copy link
Copy Markdown
Contributor
  • Ran on Python 3.13, the head plus the commits below: the full suite, ruff, mypy, bandit, the growth and comment-block checks, the benchmark gate and the CLI snapshot. Every earlier reviewer input was re-run.
  • --all-vs-all --competitors skill:ext/review with a broken catalog review copy exited 2 ("beside one that loaded"). Expected exit 0, as on main: the explicit competitor shadows every catalog copy of its name.
  • --keep-copies-under skills with a valid skills/review and a broken .openclaw/skills/review exited 2. Expected: the broken copy is left out with a note, as the docs say for copies outside the directory.
  • A skills/review/SKILL.md that is a symlink out of the catalog made the hint suggest --keep-copies-under skills, which then keeps no copy. Expected: the hint suggests only keeps that resolve the name.
  • Pushed:
    • b2ca839: an excluded name is never recorded as unloaded.
    • 2e771d8: the keep rule applies to broken copies.
    • d003e5d: the hint uses the keep rule's path test.
    • 5d38e30: pins the "copies agree" and "kept no copy" branches.

skill-novelty.md keeps --keep-copies-under under intel#396's `catalog check` name; the growth
baseline records novelty.py at its 926 merged lines.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
@xaleryb

xaleryb commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

027e4d3: merged main after #396. skill-novelty.md keeps --keep-copies-under under the new catalog check name, and the growth baseline records novelty.py at 926 lines. The CLI snapshot is unchanged.

@vlad-perevezentsev vlad-perevezentsev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I checked 027e4d3. All seven points from my first round are fixed, and the fixes by @xaleryb (explicit competitor beside a broken copy, a broken copy outside --keep-copies-under, the hint for a symlinked SKILL.md) are right.

Merge: safe now: it merges cleanly into current main, 0 commits behind, and its tests pass at this head. Once it is in, #405, #406 and #403 conflict with it (novelty.py, novelty_routing_report.py, novelty_routing_inputs.py, routing.py, skill-novelty.md, the growth baseline) and need a rebase.

What I ran, at 027e4d3:

  • make ci-local --offline: 19 passed, 0 failed, 13 skipped (network, Docker and push-only jobs).
  • novelty routing (lexical and --estimate-only, no model call) on the scratch catalogs from my first round: nested copies, two conflicting names, a symlinked SKILL.md, a broken copy beside a loaded one (with and without --keep-copies-under), a missing --catalog. Each now gives the fixed behaviour.

Checked, no issues

  • Conflict hint: for nested copies it suggests only skills/legacy; for two names it gives the folders per name and says the flag takes one directory; for a symlinked copy it no longer offers the keep that resolves nothing.
  • Broken copies: a queried name with a broken copy beside a loaded one is refused with the load error; --keep-copies-under leaves out a broken copy outside it with a note, and still refuses one under it.
  • Docs and changelog: skill-novelty.md and the fragment describe the per-skill dropped_queries record and the broken-copy rule.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 027e4d3. The fixes since my last review hold up: the three nits I raised are fixed, the earlier bot and reviewer points are fixed, and the new rules for a broken copy beside a loaded one behave as documented. One test gap on the broken-copy gate is worth fixing before merge.

What I ran, on a Linux review host at 027e4d3:

  • make ci-local: 29 passed, 1 failed, 7 skipped; 15,954 tests; the changed-line coverage gate passes. The one failed step is the test run: 4 tests hit time limits (test_the_interval_excludes_zero_exactly_when_p_is_below_005, test_unbalanced_quote_in_hook_completes_quickly at 1.17 s against a 1 s limit, two test_audit_test_quality setups). None of those files is in this PR's diff, and all three files pass together on rerun (701 passed).
  • Reproductions for the code findings, with the LLM transport mocked.
  • A mutation run: 34 mutants of the new source, 28 killed, 6 survive the four routing test files. One matters (inline); the others change only message text, the order of a saved list, or are unreachable.
  • All 19 non-merge commits carry Signed-off-by.

Checked, no issues

  • Broken copy beside a loaded one: a queried name with a malformed copy and a disabled copy (the case in exai-bot's earlier point) is refused with the load error. An explicit --competitors skill:X still shadows a broken catalog copy. A broken copy under --keep-copies-under is refused, and one outside it is left out with a note.
  • Symlink loop: --keep-copies-under loop with catalog/loop -> loop exits 2 with a usage error and no traceback (run on Python 3.13 only; the 3.11/3.12 RuntimeError branch is not exercised in this run).
  • Conflict hint: for three copies, nested copies, a root SKILL.md plus a nested copy, direct skill directories and symlinked SKILL.md copies, every --keep-copies-under value the hint offers keeps exactly one copy.
  • Saved field: dropped_queries has one record per skill; with a disabled skill of 3 queries and another of 1, queries is 3 and 1 and matches the two warnings. docs/skill-novelty.md and the changelog fragment describe reasons and keep_flags. The PR description still shows the older reason/keep_flag shape.
  • Estimate: --all-vs-all --estimate-only gives the call count the measured run makes (4 queries, --repeats 2: 8 calls), needs no --out and writes no file.
Bot points I checked that no longer apply at this head (2)
  • exai-bot's point on a disabled copy hiding a malformed copy of the same name does not apply: a name with a copy that failed to load is now refused with its load error even when another copy was left out by a rule, and when another copy loaded.
  • exai-bot's point on a symlink loop in --keep-copies-under does not apply: it is translated to a usage error and exits 2 without a traceback.

return
prefix = (Path(root) / keep_copies_under).resolve()
for name, paths in sorted(broken.items()):
if (name in unloaded and all(p is not None and not under(p, prefix) for p in paths)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test notices if a broken copy under --keep-copies-under is cleared because another broken copy of the name sits outside it.

Mutation check: changing all(...) to any(...) on this line passes all 192 tests in test_novelty_routing_gaps.py, _field.py, _all_vs_all.py and test_skill_repo_shapes.py. Every existing test has one broken copy per name, so all and any give the same answer.

Case: skills/review loads, skills/legacy/review is broken, and .openclaw/skills/review is broken. With --keep-copies-under skills, the broken copy under skills should still refuse the name ("failed to load beside one that loaded"). With the any mutant the name is cleared, and --all-vs-all measures review and exits 0 with a broken copy inside the directory the user chose.

Suggestion: add this test next to the other broken-copy tests:

def test_keep_copies_under_still_refuses_when_a_broken_copy_is_under_it_and_another_outside(tmp_path):
    root = tmp_path / "catalog"
    _skill(root, "skills", "cache", "cache slow lookups")
    _skill(root, "skills", "review", "review a change for bugs")
    _broken(root, "skills/legacy", "review")
    _broken(root, ".openclaw/skills", "review")
    args = _keep_run(root, tmp_path, "--keep-copies-under", "skills")
    with pytest.raises(InputError, match="failed to load"):
        args.func(args)

It passes on the head and fails on the any mutant.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli-interface-change Changes dokimi's user-facing CLI surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants