Skip to content

fix(skills): drop the last undefined tool reference, guard the class in CI (#493) - #557

Merged
yinlianghui merged 1 commit into
mainfrom
claude/copilots-undefined-tools-1n2wn1
Jul 31, 2026
Merged

fix(skills): drop the last undefined tool reference, guard the class in CI (#493)#557
yinlianghui merged 1 commit into
mainfrom
claude/copilots-undefined-tools-1n2wn1

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Description

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.

Took option 2 (remove the reference) from the scoping comment, because option 1 cannot work. Per ToolSchema in @objectstack/spec@16.1.0:

[READ-ONLY PROJECTION — not an execution entry point] Authoring a tool as metadata does NOT make it runnable: this schema has no implementation/handler field and no framework executor loads a metadata-authored tool.

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 CRM object, so query_records reaches it exactly like every other object the skill reads.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • CI/CD update

Related Issues

Fixes #493
Related to #512, #515

Changes Made

  • customer_360search_knowledgedescribe_object / get_record / query_records / aggregate_data. Instructions rewritten to actually deliver what the description promises: contacts, open cases, open opportunities, and published crm_knowledge_article rows matched on the categories/tags of the cases just read, with totals from aggregate_data instead of hand-summed rows.
  • test/skills-integrity.test.ts (new file, per the scoping comment — metadata-references.test.ts 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 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;
    • 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.
  • Two stale docstrings corrected. 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. fix(ui): resolve dead references, unresolvable tokens, and unreachable surfaces (#491) #515 and the send_email rewrite retyped all three to flow/script. The posture is right, the stated reason was wrong — none of the three opts in via ai.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_modules has 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.0 bridge registers (verifiable locally), plus visualize_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

  • Unit tests pass (npx vitest run) — 12 files, 146 tests (+6)
  • Linting passes (npx objectstack lint --skip-i18n) — 1 warning, 9 suggestions, all pre-existing
  • Build succeeds (npx objectstack build), and objectstack validate passes with the 2 pre-existing campaign_enrollment flow-variable warnings
  • Manual testing completed — each guard verified against the real defect before landing: reinstating search_knowledge, adding action_escalate_case (a non-exposed Action), and repointing the hand-off at response_drafting each fails its own assertion with a readable message
  • New tests added (if applicable)

Checklist

  • I have added a changeset (.changeset/skills-undefined-tool-references.md, patch)
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Additional Notes

Two things a reviewer may want to weigh in on:

  1. visualize_data is the one allowlist entry with no local evidence — it appears nowhere in the installed 16.1.0 bundle. It was adopted by revenue_forecasting in refactor(ai): skills-only AI surface — real Actions instead of 10 fictional tools, retire the two agents #512 against upstream's validate-ai-tool-references rule (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 from PLATFORM_TOOLS makes the guard fail loudly and points straight at the skill.
  2. docs/developers/code_examples.md still documents an src/agents/ directory and a permissions: [...] 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-skill permissions keys — 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

…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
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Jul 31, 2026 2:27am

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review July 31, 2026 02:34
@yinlianghui
yinlianghui merged commit 8a097bf into main Jul 31, 2026
9 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI copilots reference undefined tools, skills and knowledge indexes

2 participants