fix(router): route only to loaded skills, and say when not to invoke (#3018)

The root router listed every skill and ended 'When in doubt, invoke the
skill'. Current Claude models follow that literally: they tried to invoke
skills a user had turned off in Claude Code's skillOverrides (which gstack's
own disabled_skills render cannot see), and borderline requests tipped into
full workflows. The router now routes only to skills in the session's
available-skills list and names when to answer directly; the CLAUDE.md
routing block skill-start offers says the same.

Co-authored-by: Rekin226 <Rekin226@users.noreply.github.com>
This commit is contained in:
garrytanandRekin226 committed 2026-10-06 18:25:25 +00:00
1 parent 23520a93ed
commit e103e577c6
5 files changed
+28 -15

No files matched your search

+7 -5
View File
@@ -227,11 +227,13 @@ Generic “second opinion”, “outside review”, or “cross-model review”
- User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune`
- User asks for code quality dashboard, "health check" → invoke `/health`
**When in doubt, invoke the skill.** A false positive (invoking a skill that wasn't
needed) is cheaper than a false negative (answering ad-hoc when a structured workflow
exists). The skill provides multi-step workflows, checklists, and quality gates that
always produce better results than an ad-hoc answer. If no skill matches, answer
directly as usual.
**Route only to skills in your available-skills list.** A rule above whose skill is
not listed there is turned off on this install; skip it, never try to invoke it.
**When to invoke, and when not to.** Invoke when the request matches a skill's purpose:
the skill's workflow, checklists and gates beat an ad-hoc answer. Answer directly when
no skill matches, for a quick factual question or a small edit the user scoped
themselves, and when the user asks for a direct answer instead of a workflow.
If the user opts out of suggestions, run `gstack-config set proactive false`.
If they opt back in, run `gstack-config set proactive true`.
+7 -5
View File
@@ -98,11 +98,13 @@ quality gates that produce better results than answering inline.
- User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune`
- User asks for code quality dashboard, "health check" → invoke `/health`
**When in doubt, invoke the skill.** A false positive (invoking a skill that wasn't
needed) is cheaper than a false negative (answering ad-hoc when a structured workflow
exists). The skill provides multi-step workflows, checklists, and quality gates that
always produce better results than an ad-hoc answer. If no skill matches, answer
directly as usual.
**Route only to skills in your available-skills list.** A rule above whose skill is
not listed there is turned off on this install; skip it, never try to invoke it.
**When to invoke, and when not to.** Invoke when the request matches a skill's purpose:
the skill's workflow, checklists and gates beat an ad-hoc answer. Answer directly when
no skill matches, for a quick factual question or a small edit the user scoped
themselves, and when the user asks for a direct answer instead of a workflow.
If the user opts out of suggestions, run `gstack-config set proactive false`.
If they opt back in, run `gstack-config set proactive true`.
+1 -1
View File
@@ -495,7 +495,7 @@ If A: append this section to the end of CLAUDE.md, then commit it (`git add CLAU
## Skill routing
When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.
When the user's request matches an available skill, invoke it via the Skill tool. Route only to skills in the session's available-skills list; answer directly for quick questions or small scoped edits.
Key routing rules:
- Product ideas/brainstorming → invoke /office-hours
+2 -2
View File
@@ -169,8 +169,8 @@ function rewriteInstallRoot(content: string, hostConfig: HostConfig, installRoot
}
/**
* C8 (#3018): the router names every skill and ends "When in doubt, invoke the
* skill", so it routed to skills the user disabled. A per-install render that
* C8 (#3018): the router names every skill, so it routed to skills the user
* disabled (it also said "When in doubt, invoke the skill" until v1.91.31). A per-install render that
* knows its disabled skills drops their routing rules (keeping the other side
* of an "A or B" rule) and says which skills are off. The router itself and
* the way back (gstack-upgrade) can never be disabled.
+11 -2
View File
@@ -2116,10 +2116,19 @@ describe('preamble routing injection (bin/gstack-skill-start emission layer)', (
expect(routingBlock).not.toContain('invoke checkpoint');
});
test('routing section uses soft "when in doubt" policy, not hard "ALWAYS invoke"', () => {
expect(routingBlock).toContain('When in doubt, invoke the skill');
test('routing section routes only to available skills, without "when in doubt" over-triggering (#3018)', () => {
expect(routingBlock).toContain("Route only to skills in the session's available-skills list");
expect(routingBlock).not.toContain('When in doubt');
expect(routingBlock).not.toContain('Do NOT answer directly');
});
test('root router skips routes to skills that are not loaded and says when not to invoke (#3018)', () => {
const router = fs.readFileSync(path.join(ROOT, 'SKILL.md'), 'utf-8');
expect(router).toContain('Route only to skills in your available-skills list');
expect(router).toContain('skip it, never try to invoke it');
expect(router).toContain('Answer directly when\nno skill matches');
expect(router).not.toContain('When in doubt, invoke the skill');
});
});
// --- {{DESIGN_OUTSIDE_VOICES}} resolver tests ---