fix: resolve merge conflicts with origin/main (v0.6.1 qa-design-review → design-review rename)

Conflicts resolved:
- README.md: kept install section + office-hours/debug skills, adopted
  main's design-review rename and restructured footer
- design-review/SKILL.md: took main's version (renamed from qa-design-review)
- plan-design-review/SKILL.md: took main's version with base branch detect
- Updated install instructions to use /design-review (not /qa-design-review)
- Updated skill count to 15 in footer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-17 22:59:16 -07:00
36 changed files with 2552 additions and 864 deletions
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env bun
/**
* Show which E2E and LLM-judge tests would run based on the current git diff.
*
* Usage:
* bun run eval:select # human-readable output
* bun run eval:select --json # machine-readable JSON
* bun run eval:select --base main # override base branch
*/
import * as path from 'path';
import {
selectTests,
detectBaseBranch,
getChangedFiles,
E2E_TOUCHFILES,
LLM_JUDGE_TOUCHFILES,
GLOBAL_TOUCHFILES,
} from '../test/helpers/touchfiles';
const ROOT = path.resolve(import.meta.dir, '..');
const args = process.argv.slice(2);
const jsonMode = args.includes('--json');
const baseIdx = args.indexOf('--base');
const baseOverride = baseIdx >= 0 ? args[baseIdx + 1] : undefined;
// Detect base branch
const baseBranch = baseOverride || detectBaseBranch(ROOT) || 'main';
const changedFiles = getChangedFiles(baseBranch, ROOT);
if (changedFiles.length === 0) {
if (jsonMode) {
console.log(JSON.stringify({ base: baseBranch, changed_files: 0, e2e: 'all', llm_judge: 'all', reason: 'no diff — would run all tests' }));
} else {
console.log(`Base: ${baseBranch}`);
console.log('No changed files detected — all tests would run.');
}
process.exit(0);
}
const e2eSelection = selectTests(changedFiles, E2E_TOUCHFILES, GLOBAL_TOUCHFILES);
const llmSelection = selectTests(changedFiles, LLM_JUDGE_TOUCHFILES, GLOBAL_TOUCHFILES);
if (jsonMode) {
console.log(JSON.stringify({
base: baseBranch,
changed_files: changedFiles,
e2e: {
selected: e2eSelection.selected,
skipped: e2eSelection.skipped,
reason: e2eSelection.reason,
count: `${e2eSelection.selected.length}/${Object.keys(E2E_TOUCHFILES).length}`,
},
llm_judge: {
selected: llmSelection.selected,
skipped: llmSelection.skipped,
reason: llmSelection.reason,
count: `${llmSelection.selected.length}/${Object.keys(LLM_JUDGE_TOUCHFILES).length}`,
},
}, null, 2));
} else {
console.log(`Base: ${baseBranch}`);
console.log(`Changed files: ${changedFiles.length}`);
console.log();
console.log(`E2E (${e2eSelection.reason}): ${e2eSelection.selected.length}/${Object.keys(E2E_TOUCHFILES).length} tests`);
if (e2eSelection.selected.length > 0 && e2eSelection.selected.length < Object.keys(E2E_TOUCHFILES).length) {
console.log(` Selected: ${e2eSelection.selected.join(', ')}`);
console.log(` Skipped: ${e2eSelection.skipped.join(', ')}`);
} else if (e2eSelection.selected.length === 0) {
console.log(' No E2E tests affected.');
} else {
console.log(' All E2E tests selected.');
}
console.log();
console.log(`LLM-judge (${llmSelection.reason}): ${llmSelection.selected.length}/${Object.keys(LLM_JUDGE_TOUCHFILES).length} tests`);
if (llmSelection.selected.length > 0 && llmSelection.selected.length < Object.keys(LLM_JUDGE_TOUCHFILES).length) {
console.log(` Selected: ${llmSelection.selected.join(', ')}`);
console.log(` Skipped: ${llmSelection.skipped.join(', ')}`);
} else if (llmSelection.selected.length === 0) {
console.log(' No LLM-judge tests affected.');
} else {
console.log(' All LLM-judge tests selected.');
}
}
+42 -2
View File
@@ -544,6 +544,45 @@ Minimum 0 per category.
11. **Show screenshots to the user.** After every \`$B screenshot\`, \`$B snapshot -a -o\`, or \`$B responsive\` command, use the Read tool on the output file(s) so the user can see them inline. For \`responsive\` (3 files), Read all three. This is critical — without it, screenshots are invisible to the user.`;
}
function generateDesignReviewLite(): string {
return `## Design Review (conditional, diff-scoped)
Check if the diff touches frontend files using \`gstack-diff-scope\`:
\`\`\`bash
eval $(~/.claude/skills/gstack/bin/gstack-diff-scope <base> 2>/dev/null)
\`\`\`
**If \`SCOPE_FRONTEND=false\`:** Skip design review silently. No output.
**If \`SCOPE_FRONTEND=true\`:**
1. **Check for DESIGN.md.** If \`DESIGN.md\` or \`design-system.md\` exists in the repo root, read it. All design findings are calibrated against it — patterns blessed in DESIGN.md are not flagged. If not found, use universal design principles.
2. **Read \`.claude/skills/review/design-checklist.md\`.** If the file cannot be read, skip design review with a note: "Design checklist not found — skipping design review."
3. **Read each changed frontend file** (full file, not just diff hunks). Frontend files are identified by the patterns listed in the checklist.
4. **Apply the design checklist** against the changed files. For each item:
- **[HIGH] mechanical CSS fix** (\`outline: none\`, \`!important\`, \`font-size < 16px\`): classify as AUTO-FIX
- **[HIGH/MEDIUM] design judgment needed**: classify as ASK
- **[LOW] intent-based detection**: present as "Possible — verify visually or run /design-review"
5. **Include findings** in the review output under a "Design Review" header, following the output format in the checklist. Design findings merge with code review findings into the same Fix-First flow.
6. **Log the result** for the Review Readiness Dashboard:
\`\`\`bash
eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
mkdir -p ~/.gstack/projects/$SLUG
echo '{"skill":"design-review-lite","timestamp":"TIMESTAMP","status":"STATUS","findings":N,"auto_fixed":M}' >> ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl
\`\`\`
Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "issues_found", N = total findings, M = auto-fixed count.`;
}
// NOTE: design-checklist.md is a subset of this methodology for code-level detection.
// When adding items here, also update review/design-checklist.md, and vice versa.
function generateDesignMethodology(): string {
return `## Modes
@@ -890,7 +929,7 @@ echo "---CONFIG---"
~/.claude/skills/gstack/bin/gstack-config get skip_eng_review 2>/dev/null || echo "false"
\`\`\`
Parse the output. Find the most recent entry for each skill (plan-ceo-review, plan-eng-review, plan-design-review). Ignore entries with timestamps older than 7 days. Display:
Parse the output. Find the most recent entry for each skill (plan-ceo-review, plan-eng-review, plan-design-review, design-review-lite). Ignore entries with timestamps older than 7 days. For Design Review, show whichever is more recent between \`plan-design-review\` (full visual audit) and \`design-review-lite\` (code-level check). Append "(FULL)" or "(LITE)" to the status to distinguish. Display:
\`\`\`
+====================================================================+
@@ -1081,6 +1120,7 @@ const RESOLVERS: Record<string, () => string> = {
BASE_BRANCH_DETECT: generateBaseBranchDetect,
QA_METHODOLOGY: generateQAMethodology,
DESIGN_METHODOLOGY: generateDesignMethodology,
DESIGN_REVIEW_LITE: generateDesignReviewLite,
REVIEW_DASHBOARD: generateReviewDashboard,
TEST_BOOTSTRAP: generateTestBootstrap,
};
@@ -1139,7 +1179,7 @@ function findTemplates(): string[] {
path.join(ROOT, 'debug', 'SKILL.md.tmpl'),
path.join(ROOT, 'gstack-upgrade', 'SKILL.md.tmpl'),
path.join(ROOT, 'plan-design-review', 'SKILL.md.tmpl'),
path.join(ROOT, 'qa-design-review', 'SKILL.md.tmpl'),
path.join(ROOT, 'design-review', 'SKILL.md.tmpl'),
path.join(ROOT, 'design-consultation', 'SKILL.md.tmpl'),
path.join(ROOT, 'document-release', 'SKILL.md.tmpl'),
];
+1 -1
View File
@@ -28,7 +28,7 @@ const SKILL_FILES = [
'plan-eng-review/SKILL.md',
'setup-browser-cookies/SKILL.md',
'plan-design-review/SKILL.md',
'qa-design-review/SKILL.md',
'design-review/SKILL.md',
'gstack-upgrade/SKILL.md',
'document-release/SKILL.md',
].filter(f => fs.existsSync(path.join(ROOT, f)));