From e6c6422cec921a79d5133f147b5c6ce18605dc63 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 23 Mar 2026 10:11:19 -0700 Subject: [PATCH] fix: CI report needs checkout + routing needs user-level skill install Two fixes: 1. Report job: add actions/checkout so `gh pr comment` has git context. Also add pull-requests:write permission for comment posting. 2. Routing tests: install skills to BOTH project-level (.claude/skills/) AND user-level (~/.claude/skills/) since Claude Code discovers from both locations. In CI containers, $HOME differs from workdir. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/evals.yml | 7 +++++++ test/skill-routing-e2e.test.ts | 32 +++++++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml index 3ca708d5..b2423017 100644 --- a/.github/workflows/evals.yml +++ b/.github/workflows/evals.yml @@ -130,7 +130,14 @@ jobs: needs: evals if: always() && github.event_name == 'pull_request' timeout-minutes: 5 + permissions: + contents: read + pull-requests: write steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Download all eval artifacts uses: actions/download-artifact@v4 with: diff --git a/test/skill-routing-e2e.test.ts b/test/skill-routing-e2e.test.ts index fae9d1a7..9e75fa97 100644 --- a/test/skill-routing-e2e.test.ts +++ b/test/skill-routing-e2e.test.ts @@ -44,8 +44,11 @@ if (evalsEnabled && !process.env.EVALS_ALL) { // --- Helper functions --- -/** Copy all SKILL.md files into tmpDir/.claude/skills/gstack/ for auto-discovery, - * plus CLAUDE.md so Claude has project context for routing decisions. */ +/** Copy all SKILL.md files for auto-discovery. + * Install to BOTH project-level (.claude/skills/) AND user-level (~/.claude/skills/) + * because Claude Code discovers skills from both locations. In CI containers, + * $HOME may differ from the working directory, so we need both paths to ensure + * the Skill tool appears in Claude's available tools list. */ function installSkills(tmpDir: string) { const skillDirs = [ '', // root gstack SKILL.md @@ -55,24 +58,27 @@ function installSkills(tmpDir: string) { 'gstack-upgrade', 'humanizer', ]; + // Install to both project-level and user-level skill directories + const homeDir = process.env.HOME || os.homedir(); + const installTargets = [ + path.join(tmpDir, '.claude', 'skills'), // project-level + path.join(homeDir, '.claude', 'skills'), // user-level (~/.claude/skills/) + ]; + for (const skill of skillDirs) { const srcPath = path.join(ROOT, skill, 'SKILL.md'); if (!fs.existsSync(srcPath)) continue; - // Install skills at TOP level of .claude/skills/ so Claude Code discovers - // each as a separate invocable skill. Nesting under .claude/skills/gstack/ - // only works for personal skills (~/.claude/skills/) — project-level skills - // need to be top-level for discovery. - const destDir = skill - ? path.join(tmpDir, '.claude', 'skills', skill) - : path.join(tmpDir, '.claude', 'skills', 'gstack'); - fs.mkdirSync(destDir, { recursive: true }); - fs.copyFileSync(srcPath, path.join(destDir, 'SKILL.md')); + const skillName = skill || 'gstack'; + + for (const targetBase of installTargets) { + const destDir = path.join(targetBase, skillName); + fs.mkdirSync(destDir, { recursive: true }); + fs.copyFileSync(srcPath, path.join(destDir, 'SKILL.md')); + } } // Copy CLAUDE.md so Claude has project context for skill routing. - // Without this, Claude in containerized environments lacks the context - // that guides it to invoke specific skills vs answering directly. const claudeMdSrc = path.join(ROOT, 'CLAUDE.md'); if (fs.existsSync(claudeMdSrc)) { fs.copyFileSync(claudeMdSrc, path.join(tmpDir, 'CLAUDE.md'));