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) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-23 10:11:19 -07:00
parent 4d91456b3c
commit e6c6422cec
2 changed files with 26 additions and 13 deletions
+7
View File
@@ -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:
+19 -13
View File
@@ -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'));