fix(skills): drop the last undefined tool reference, guard the class in CI (#493) - #557
Merged
Merged
Conversation
…in CI (#493) `customer_360` declared `tools: ['search_knowledge']` — the survivor of the eleven fictional tools #493 counted, missed by #512 because that skill looked "already correct". The runtime silently drops an unresolved tool, so the skill shipped with its only declared capability resolving to nothing, while its instructions promised an account + cases + opportunities + knowledge roll-up it had no tool to read. Defining the missing tool was never the fix: `ToolSchema` is a read-only Studio projection — no `implementation` field, no executor loads it — so a hand-authored `search_knowledge` would validate, build, and still never run. And the knowledge base here is not a search service: it is `crm_knowledge_article`, a normal object. The skill now reads it with `query_records` alongside the contacts, cases and opportunities the description already promised, and quotes totals from `aggregate_data`. Adds `test/skills-integrity.test.ts` (its own file — metadata-references is a busy merge surface): - every skill tool resolves to a platform built-in or an `action_<name>` tool materialised from a real Action, with wildcard subscriptions resolved by prefix; - every referenced Action is `ai.exposed` with a ≥40-char LLM description AND a headless path (flow with a defined target, or a script body) — the regression #512's follow-up commit had to fix by hand; - stack tool metadata cannot be used to satisfy a dangling reference, since it never executes; - every skill handed off to in instructions exists — the defect that pointed `case_triage` at a `response_drafting` skill that was never defined. Each guard verified against the real defect before landing: reinstating `search_knowledge`, `action_escalate_case` and the `response_drafting` hand-off each fails its own assertion. Also corrects two stale docstrings. `case_triage` and `email_drafting` justified their read-only posture by calling `escalate_case`, `close_case` and `send_email` `type: 'modal'` with no headless path; #515 and the send_email rewrite retyped all three to flow/script. The posture is right, the reason was wrong — none of the three opts in via `ai.exposed` (ADR-0011 is default off), so no tool is materialised and the human review step stays a deliberate governance call. typecheck, validate, build and the full suite pass (146 tests, +6). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y9STuduWbrAwcgviziaV4e
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
yinlianghui
marked this pull request as ready for review
July 31, 2026 02:34
yinlianghui
pushed a commit
that referenced
this pull request
Jul 31, 2026
…real The allowlist added in #557 guessed the platform tool set from what the `@objectstack/mcp@16.1.0` bridge happens to register, and got it wrong in both directions: - It omitted `search_knowledge`, so the guard would have FAILED a legitimate reference. The tool is real — it is in `PLATFORM_PROVIDED_TOOL_NAMES`, and 16.1.0 documented it all along at `spec/src/ai/knowledge-source.zod.ts:114` ("Whether `search_knowledge` may expose this source to AI agents"). One grep for that name would have caught it; the same absent-from-node_modules inference was applied to `visualize_data` and came out the other way. - It reasoned about deliberately excluding `create_record` / `update_record` / `delete_record`. Those are MCP-bridge tools and are not in the platform registry at all, so there was nothing to exclude. The list is now transcribed verbatim from `PLATFORM_PROVIDED_TOOL_NAMES` in `@objectstack/spec@17.0.0-rc.0` — all 30 entries, diffed equal to the upstream set — carrying instructions to delete the literal and import it on the 17.0 upgrade. A hand-copied registry is the drift risk this file exists to catch, one level up; it is a stopgap for 16.1.0, which exposes no such export. Cross-checked against upstream's own `ai-skill-tool-unresolved` rule, which ships in `@objectstack/lint@17.0.0-rc.0` (objectstack#3820). Both give identical verdicts on seven probes: `search_knowledge`, `query_data`, `todo_write` and `action_convert_lead` accepted; `action_escalate_case` (not `ai.exposed`), `search_knowledgebase` and `triage_case` rejected. Also corrects the "nothing defines it" claim in three places — the `customer_360` docstring, its changeset, and `code_examples.md`. The skill still does not reference the tool, for a narrower and accurate reason: retrieval needs a declared knowledge source, `AIKnowledgeSchema` mounts only on `AgentSchema.knowledge`, and #512 deleted the agents — so a skills-only app has nowhere to declare one and the tool would resolve and return nothing. The skill's instruction line now says the model has no such tool *in this skill*, rather than that none exists. 168 tests pass, typecheck and build clean, validate holds at its 2 pre-existing warnings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y9STuduWbrAwcgviziaV4e
20 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
customer_360declaredtools: ['search_knowledge']— the survivor of the eleven fictional tools #493 counted, missed by #512 because that skill looked "already correct". The runtime silently drops an unresolved tool, so the skill shipped with its only declared capability resolving to nothing, while its instructions promised an account + cases + opportunities + knowledge roll-up it had no tool to read.Took option 2 (remove the reference) from the scoping comment, because option 1 cannot work. Per
ToolSchemain@objectstack/spec@16.1.0:A hand-authored
search_knowledgewould validate, build, and still never run. And the knowledge base here is not a search service — it iscrm_knowledge_article, a normal CRM object, soquery_recordsreaches it exactly like every other object the skill reads.Type of Change
Related Issues
Fixes #493
Related to #512, #515
Changes Made
customer_360—search_knowledge→describe_object/get_record/query_records/aggregate_data. Instructions rewritten to actually deliver what the description promises: contacts, open cases, open opportunities, and publishedcrm_knowledge_articlerows matched on the categories/tags of the cases just read, with totals fromaggregate_datainstead of hand-summed rows.test/skills-integrity.test.ts(new file, per the scoping comment —metadata-references.test.tsis a busy merge surface):action_<name>tool materialised from a real Action, with wildcard subscriptions resolved by prefix;ai.exposedwith a ≥40-char LLM description and a headless path (flow with a defined target, or a script body) — the regression refactor(ai): skills-only AI surface — real Actions instead of 10 fictional tools, retire the two agents #512's follow-up commit had to fix by hand;case_triageat aresponse_draftingskill that was never defined.case_triageandemail_draftingjustified their read-only posture by callingescalate_case,close_caseandsend_emailtype: 'modal'with no headless path. fix(ui): resolve dead references, unresolvable tokens, and unreachable surfaces (#491) #515 and thesend_emailrewrite retyped all three to flow/script. The posture is right, the stated reason was wrong — none of the three opts in viaai.exposed(ADR-0011 is opt-in, default off), so no tool is materialised and the human review step stays a deliberate governance call rather than a mechanical limit.On the built-in allowlist
The AI runtime ships in the closed cloud package (ADR-0025 §2), so open-edition
node_moduleshas no authoritative tool list to resolve against. The allowlist is therefore explicit and annotated with its provenance: the read subset of the tool surface the@objectstack/mcp@16.1.0bridge registers (verifiable locally), plusvisualize_data. The raw write tools (create_record/update_record/delete_record) are deliberately excluded — HotCRM's state changes go through Actions so validation, sharing rules and audit match the UI path, and this list is where a skill reaching past them gets caught.Testing
npx vitest run) — 12 files, 146 tests (+6)npx objectstack lint --skip-i18n) — 1 warning, 9 suggestions, all pre-existingnpx objectstack build), andobjectstack validatepasses with the 2 pre-existingcampaign_enrollmentflow-variable warningssearch_knowledge, addingaction_escalate_case(a non-exposed Action), and repointing the hand-off atresponse_draftingeach fails its own assertion with a readable messageChecklist
.changeset/skills-undefined-tool-references.md, patch)Additional Notes
Two things a reviewer may want to weigh in on:
visualize_datais the one allowlist entry with no local evidence — it appears nowhere in the installed 16.1.0 bundle. It was adopted byrevenue_forecastingin refactor(ai): skills-only AI surface — real Actions instead of 10 fictional tools, retire the two agents #512 against upstream'svalidate-ai-tool-referencesrule (objectstack#3894), which reported 0 findings, so I kept it and documented the basis rather than removing a tool on weaker evidence than the maintainers had. If it turns out not to be served, deleting it fromPLATFORM_TOOLSmakes the guard fail loudly and points straight at the skill.docs/developers/code_examples.mdstill documents ansrc/agents/directory and apermissions: [...]key on skills, both retired by refactor(ai): skills-only AI surface — real Actions instead of 10 fictional tools, retire the two agents #512 / fix(skills): drop the per-skillpermissionskeys — the field does not exist in SkillSchema #511. Left alone as out of scope for this issue, but it is real doc drift worth a follow-up.🤖 Generated with Claude Code
https://claude.ai/code/session_01Y9STuduWbrAwcgviziaV4e