fix(client): count only served skills in the get_skills withholding warning - #137
Conversation
…arning get_skills passed the full request count to log_withholding_summary, so an absent key, a pin miss, a wrong-version answer, or a store that raised was reported as withheld. When nothing resolved, the warning blamed contentHash: a get_skills call against an empty store at boot raised a false integrity alarm. Only resolutions where the store served an object (ok or integrity_failure) now count toward the summary. all_skills already counted only what the store held. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont
left a comment
There was a problem hiding this comment.
Approving. Reviewed against xie/agent-skills. make test at 770a004: 2169 passed, 11 skipped. With skills.py reverted to the base, all five new tests fail, so they catch the bug. get_skills is the only caller with this miscount (all_skills and write_skills("*") already count what the store holds), and JS #107 has the matching change.
One GA 1.0 question this brings out. It's older than this PR and doesn't block it. With a store whose is_initialized() is false, get_skills now returns [] with no log at any level, and get_skill_result reports absent. That's spec-conformant (TESTING.md §3.25: retrieval before the first payload "may legitimately see an empty store"). But absent is the outcome callers are told to tolerate, so an agent that reads skills before wait_for_skills runs without them and nothing says so. Before this PR it at least logged a (wrong) warning.
- Freezes at 1.0: whether a not-yet-loaded store is
absentorstore_unavailableon the accessors.reasonis a closed public vocabulary, and the write path already treats a not-loaded store as unavailable (§3.21). Worth a spec decision before 1.0, in both SDKs. - Additive, can follow 1.0: a warning when an accessor reads a store that hasn't initialized, naming
wait_for_skills.
get_skillspassed the full request count tolog_withholding_summary, so a skill that was never withheld was still counted as withheld. That covered:When nothing resolved, the warning said every object failed verification and pointed at
contentHash. So aget_skillscall against an empty store at boot raised a false integrity alarm.Cursor Bugbot found this on the TypeScript port, in launchdarkly/js-ai-sdk#107 (comment). The port copied this function from here, so the same fix is pushed to that PR to keep the two SDKs matching.
Change
get_skillsnow counts only the resolutions where the store served an object (okorintegrity_failure) and passes that count to the summary as the requested total. The subject is now "requested skills the store served", so the counts in the message match what they describe.all_skillsand the"*"path inwrite_skillsalready counted only what the store held, so they are unchanged.Tests
New tests in
TestWithholdingSummary:1 of 2All five fail when the fix is reverted.
make lint,make format-check,make typecheckandmake testpass (2169 passed, 11 skipped).🤖 Generated with Claude Code
Note
Overview
get_skillsno longer treats every requested reference as part of the withholding summary. It increments a served count only whenresolve_from_storereportsokorintegrity_failure(the store actually returned an object), then callslog_withholding_summarywith subject "requested skills the store served" instead of the full batch size.That stops false contentHash / verification warnings for absent keys, version pin misses, wrong-version answers, and store outages—cases that were never withheld skills. The docstring now states that verification warnings exclude plain misses.
Tests add five
TestWithholdingSummarycases: silent behavior for missing keys, pin misses, wrong-version stores, and raising stores; and1 of 2when one served skill verifies and one tampered skill does not, with a missing key ignored.Reviewed by Cursor Bugbot for commit 770a004. Bugbot is set up for automated code reviews on this repo. Configure here.