Skip to content

fix(#3087): skill routing fails loud on ambiguity instead of silent first-match#123

Open
Skobeltsyn wants to merge 1 commit into
mainfrom
feat/3087-skill-routing-fail-loud
Open

fix(#3087): skill routing fails loud on ambiguity instead of silent first-match#123
Skobeltsyn wants to merge 1 commit into
mainfrom
feat/3087-skill-routing-fail-loud

Conversation

@Skobeltsyn
Copy link
Copy Markdown
Contributor

Part of de-slop epic #3083 (external review). Reviewer finding #4: resolveSkill() silently routed to candidates.first() by registration order when multiple skills matched and there was no skillSelection { } selector and no model { }. An auditable / explicit-boundaries runtime must not pick a production route implicitly.

Change

  • else -> candidates.first() becomes ambiguousSkillRoutingError(candidates): Nothing throwing SkillRoutingException naming the ambiguous candidates and the two disambiguation paths. Extracted to a helper to stay under detekt ThrowsCount.
  • The existing test that pinned the silent first-match fallback is rewritten to assert the fail-loud contract (RED to GREEN).
  • CHANGELOG [Unreleased] documents the behavior change.

Verification: 278 core tests ran, 276 green incl. the new routing test. The only 2 failures are AgentVisionLiveTest live-cloud-api Claude tests, failing solely because the Claude API is currently down — unrelated to this change. detekt + compile + all other modules green.

CI note: the default :test task includes live-cloud-api (direct Anthropic/OpenAI/DeepSeek), so those will be red until the Claude API returns — tracked in #3089.

Resolves #3087.

Generated with Claude Code

…irst-match

De-slop epic #3083, reviewer finding #4. resolveSkill() had every branch fail
loud except the last: multiple compatible skills + no skillSelection selector +
no model silently routed to candidates.first() by registration order. An
auditable / explicit-boundaries runtime must not pick a production route
implicitly.

Now throws SkillRoutingException naming the ambiguous candidates and the two
ways to disambiguate (skillSelection { } or model { }). Extracted into
ambiguousSkillRoutingError(): Nothing to stay under detekt ThrowsCount.

The existing test that pinned the silent first-match fallback is rewritten to
assert the fail-loud contract (RED -> GREEN). Behavior change: documented in
CHANGELOG [Unreleased]; single-candidate / selector / model-routed paths
unchanged.

Task-UUID: 9e0538e2-e6aa-44db-b1f5-4dc670e0a30c

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant