fix: gen-skill-docs resolver merge + preamble tier gate + plan file discovery

The local RESOLVERS record in gen-skill-docs.ts was shadowing the imported
canonical resolvers, causing stale test coverage and preamble generators
to be used instead of the authoritative versions in resolvers/.

Changes:
- Merge imported RESOLVERS with local overrides (spread + override pattern)
- Fix preamble tier gate: tier 1 skills no longer get AskUserQuestion format
- Make plan file discovery host-agnostic (search multiple plan dirs)
- Add missing E2E tier entries for ship/review plan completion tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan 2026-03-26 10:45:57 -06:00
parent ecb8ae658c
commit b2e1ee9e7b
No known key found for this signature in database
GPG Key ID: C1F69E85C74EFE1D
3 changed files with 25 additions and 38 deletions

View File

@ -16,7 +16,7 @@ import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import type { Host, TemplateContext } from './resolvers/types'; import type { Host, TemplateContext } from './resolvers/types';
import { HOST_PATHS } from './resolvers/types'; import { HOST_PATHS } from './resolvers/types';
import { RESOLVERS } from './resolvers/index'; import { RESOLVERS as IMPORTED_RESOLVERS } from './resolvers/index';
import { codexSkillName, transformFrontmatter, extractHookSafetyProse, extractNameAndDescription, condenseOpenAIShortDescription, generateOpenAIYaml } from './resolvers/codex-helpers'; import { codexSkillName, transformFrontmatter, extractHookSafetyProse, extractNameAndDescription, condenseOpenAIShortDescription, generateOpenAIYaml } from './resolvers/codex-helpers';
import { generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './resolvers/review'; import { generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './resolvers/review';
@ -473,7 +473,7 @@ Hey gstack team — ran into this while using /{skill-name}:
**What I was trying to do:** {what the user/agent was attempting} **What I was trying to do:** {what the user/agent was attempting}
**What happened instead:** {what actually happened} **What happened instead:** {what actually happened}
**My rating:** {0-10} {one sentence on why it wasn't a 10} **My Rating:** {0-10} {one sentence on why it wasn't a 10}
## Steps to reproduce ## Steps to reproduce
1. {step} 1. {step}
@ -584,15 +584,14 @@ plan's living status.`;
} }
function generatePreamble(ctx: TemplateContext): string { function generatePreamble(ctx: TemplateContext): string {
const tier = ctx.preambleTier ?? 4;
return [ return [
generatePreambleBash(ctx), generatePreambleBash(ctx),
generateUpgradeCheck(ctx), generateUpgradeCheck(ctx),
generateLakeIntro(), generateLakeIntro(),
generateTelemetryPrompt(ctx), generateTelemetryPrompt(ctx),
generateAskUserFormat(ctx), ...(tier >= 2 ? [generateAskUserFormat(ctx), generateCompletenessSection()] : []),
generateCompletenessSection(), ...(tier >= 3 ? [generateRepoModeSection(), generateSearchBeforeBuildingSection(ctx)] : []),
generateRepoModeSection(),
generateSearchBeforeBuildingSection(ctx),
generateContributorMode(), generateContributorMode(),
generateCompletionStatus(), generateCompletionStatus(),
].join('\n\n'); ].join('\n\n');
@ -2801,37 +2800,20 @@ function generateSlugSetup(ctx: TemplateContext): string {
return `eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG`; return `eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG`;
} }
// Use the canonical RESOLVERS from resolvers/index.ts, extended with local overrides.
// Local overrides are kept for functions that have hardcoded ~/.claude/ paths in the
// imported versions — the local versions are host-agnostic and safe for Codex output.
const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = { const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
SLUG_EVAL: generateSlugEval, ...IMPORTED_RESOLVERS,
SLUG_SETUP: generateSlugSetup, // Local override — preamble generator with tier-gated sections
COMMAND_REFERENCE: generateCommandReference,
SNAPSHOT_FLAGS: generateSnapshotFlags,
PREAMBLE: generatePreamble, PREAMBLE: generatePreamble,
BROWSE_SETUP: generateBrowseSetup, // Local overrides — these use host-agnostic paths (no ~/.claude/ hardcoding)
BASE_BRANCH_DETECT: generateBaseBranchDetect,
QA_METHODOLOGY: generateQAMethodology,
DESIGN_METHODOLOGY: generateDesignMethodology,
DESIGN_HARD_RULES: generateDesignHardRules,
DESIGN_OUTSIDE_VOICES: generateDesignOutsideVoices,
DESIGN_REVIEW_LITE: generateDesignReviewLite,
REVIEW_DASHBOARD: generateReviewDashboard,
PLAN_FILE_REVIEW_REPORT: generatePlanFileReviewReport, PLAN_FILE_REVIEW_REPORT: generatePlanFileReviewReport,
TEST_BOOTSTRAP: generateTestBootstrap,
TEST_COVERAGE_AUDIT_PLAN: generateTestCoverageAuditPlan,
TEST_COVERAGE_AUDIT_SHIP: generateTestCoverageAuditShip,
TEST_COVERAGE_AUDIT_REVIEW: generateTestCoverageAuditReview,
TEST_FAILURE_TRIAGE: generateTestFailureTriage,
SPEC_REVIEW_LOOP: generateSpecReviewLoop,
DESIGN_SKETCH: generateDesignSketch,
BENEFITS_FROM: generateBenefitsFrom,
CODEX_SECOND_OPINION: generateCodexSecondOpinion,
CODEX_REVIEW_STEP: generateAdversarialStep,
ADVERSARIAL_STEP: generateAdversarialStep,
DEPLOY_BOOTSTRAP: generateDeployBootstrap,
CODEX_PLAN_REVIEW: generateCodexPlanReview,
PLAN_COMPLETION_AUDIT_SHIP: generatePlanCompletionAuditShip, PLAN_COMPLETION_AUDIT_SHIP: generatePlanCompletionAuditShip,
PLAN_COMPLETION_AUDIT_REVIEW: generatePlanCompletionAuditReview, PLAN_COMPLETION_AUDIT_REVIEW: generatePlanCompletionAuditReview,
PLAN_VERIFICATION_EXEC: generatePlanVerificationExec, PLAN_VERIFICATION_EXEC: generatePlanVerificationExec,
// Local-only entry not in resolvers/index.ts
CODEX_REVIEW_STEP: generateAdversarialStep,
}; };
// ─── Codex Helpers ─────────────────────────────────────────── // ─── Codex Helpers ───────────────────────────────────────────

View File

@ -598,19 +598,21 @@ SOURCE = "codex" if Codex ran, "claude" if subagent ran.
function generatePlanFileDiscovery(): string { function generatePlanFileDiscovery(): string {
return `### Plan File Discovery return `### Plan File Discovery
1. **Conversation context (primary):** Check if there is an active plan file in this conversation Claude Code system messages include plan file paths when in plan mode. Look for references like \`~/.claude/plans/*.md\` in system messages. If found, use it directly — this is the most reliable signal. 1. **Conversation context (primary):** Check if there is an active plan file in this conversation. The host agent's system messages include plan file paths when in plan mode. If found, use it directly this is the most reliable signal.
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content: 2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
\`\`\`bash \`\`\`bash
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-') BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)") REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
# Try branch name match first (most specific) # Search common plan file locations
PLAN=$(ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$BRANCH" 2>/dev/null | head -1) for PLAN_DIR in "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
# Fall back to repo name match [ -d "$PLAN_DIR" ] || continue
[ -z "$PLAN" ] && PLAN=$(ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$REPO" 2>/dev/null | head -1) PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$BRANCH" 2>/dev/null | head -1)
# Last resort: most recent plan modified in the last 24 hours [ -z "$PLAN" ] && PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$REPO" 2>/dev/null | head -1)
[ -z "$PLAN" ] && PLAN=$(find ~/.claude/plans -name '*.md' -mmin -1440 -maxdepth 1 2>/dev/null | xargs ls -t 2>/dev/null | head -1) [ -z "$PLAN" ] && PLAN=$(find "$PLAN_DIR" -name '*.md' -mmin -1440 -maxdepth 1 2>/dev/null | xargs ls -t 2>/dev/null | head -1)
[ -n "$PLAN" ] && break
done
[ -n "$PLAN" ] && echo "PLAN_FILE: $PLAN" || echo "NO_PLAN_FILE" [ -n "$PLAN" ] && echo "PLAN_FILE: $PLAN" || echo "NO_PLAN_FILE"
\`\`\` \`\`\`

View File

@ -184,6 +184,7 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
'review-base-branch': 'gate', 'review-base-branch': 'gate',
'review-design-lite': 'periodic', // 4/7 threshold is subjective 'review-design-lite': 'periodic', // 4/7 threshold is subjective
'review-coverage-audit': 'gate', 'review-coverage-audit': 'gate',
'review-plan-completion': 'gate',
// Office Hours // Office Hours
'office-hours-spec-review': 'gate', 'office-hours-spec-review': 'gate',
@ -208,6 +209,8 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
'ship-local-workflow': 'gate', 'ship-local-workflow': 'gate',
'ship-coverage-audit': 'gate', 'ship-coverage-audit': 'gate',
'ship-triage': 'gate', 'ship-triage': 'gate',
'ship-plan-completion': 'gate',
'ship-plan-verification': 'gate',
// Retro — gate for cheap branch detection, periodic for full Opus retro // Retro — gate for cheap branch detection, periodic for full Opus retro
'retro': 'periodic', 'retro': 'periodic',