mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-05 05:05:08 +02:00
fix: Codex description limit + wrong-repo bug
Move skill routing table from root SKILL.md.tmpl description (1017/1024 chars) to body where there's no length limit. Add 900-char warning threshold test. Add -C flag to all codex exec calls so Codex always runs in the correct git root directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+162
-18
@@ -152,24 +152,6 @@ describe('gen-skill-docs', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('every Codex SKILL.md description stays under 900-char warning threshold', () => {
|
||||
const WARN_THRESHOLD = 900;
|
||||
const agentsDir = path.join(ROOT, '.agents', 'skills');
|
||||
if (!fs.existsSync(agentsDir)) return;
|
||||
const violations: string[] = [];
|
||||
for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const skillMd = path.join(agentsDir, entry.name, 'SKILL.md');
|
||||
if (!fs.existsSync(skillMd)) continue;
|
||||
const content = fs.readFileSync(skillMd, 'utf-8');
|
||||
const description = extractDescription(content);
|
||||
if (description.length > WARN_THRESHOLD) {
|
||||
violations.push(`${entry.name}: ${description.length} chars (limit ${MAX_SKILL_DESCRIPTION_LENGTH}, ${MAX_SKILL_DESCRIPTION_LENGTH - description.length} remaining)`);
|
||||
}
|
||||
}
|
||||
expect(violations).toEqual([]);
|
||||
});
|
||||
|
||||
test('package.json version matches VERSION file', () => {
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8'));
|
||||
const version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||
@@ -695,6 +677,168 @@ describe('PLAN_FILE_REVIEW_REPORT resolver', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// --- {{PLAN_COMPLETION_AUDIT}} resolver tests ---
|
||||
|
||||
describe('PLAN_COMPLETION_AUDIT placeholders', () => {
|
||||
const shipSkill = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
|
||||
const reviewSkill = fs.readFileSync(path.join(ROOT, 'review', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('ship SKILL.md contains plan completion audit step', () => {
|
||||
expect(shipSkill).toContain('Plan Completion Audit');
|
||||
expect(shipSkill).toContain('Step 3.45');
|
||||
});
|
||||
|
||||
test('review SKILL.md contains plan completion in scope drift', () => {
|
||||
expect(reviewSkill).toContain('Plan File Discovery');
|
||||
expect(reviewSkill).toContain('Actionable Item Extraction');
|
||||
expect(reviewSkill).toContain('Integration with Scope Drift Detection');
|
||||
});
|
||||
|
||||
test('both modes share plan file discovery methodology', () => {
|
||||
expect(shipSkill).toContain('Plan File Discovery');
|
||||
expect(reviewSkill).toContain('Plan File Discovery');
|
||||
// Both should have conversation context first
|
||||
expect(shipSkill).toContain('Conversation context (primary)');
|
||||
expect(reviewSkill).toContain('Conversation context (primary)');
|
||||
// Both should have grep fallback
|
||||
expect(shipSkill).toContain('Content-based search (fallback)');
|
||||
expect(reviewSkill).toContain('Content-based search (fallback)');
|
||||
});
|
||||
|
||||
test('ship mode has gate logic for NOT DONE items', () => {
|
||||
expect(shipSkill).toContain('NOT DONE');
|
||||
expect(shipSkill).toContain('Stop — implement the missing items');
|
||||
expect(shipSkill).toContain('Ship anyway — defer');
|
||||
expect(shipSkill).toContain('intentionally dropped');
|
||||
});
|
||||
|
||||
test('review mode is INFORMATIONAL only', () => {
|
||||
expect(reviewSkill).toContain('INFORMATIONAL');
|
||||
expect(reviewSkill).toContain('MISSING REQUIREMENTS');
|
||||
expect(reviewSkill).toContain('SCOPE CREEP');
|
||||
});
|
||||
|
||||
test('item extraction has 50-item cap', () => {
|
||||
expect(shipSkill).toContain('at most 50 items');
|
||||
});
|
||||
|
||||
test('uses file-level traceability (not commit-level)', () => {
|
||||
expect(shipSkill).toContain('Cite the specific file');
|
||||
expect(shipSkill).not.toContain('commit-level traceability');
|
||||
});
|
||||
});
|
||||
|
||||
// --- {{PLAN_VERIFICATION_EXEC}} resolver tests ---
|
||||
|
||||
describe('PLAN_VERIFICATION_EXEC placeholder', () => {
|
||||
const shipSkill = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('ship SKILL.md contains plan verification step', () => {
|
||||
expect(shipSkill).toContain('Step 3.47');
|
||||
expect(shipSkill).toContain('Plan Verification');
|
||||
});
|
||||
|
||||
test('references /qa-only invocation', () => {
|
||||
expect(shipSkill).toContain('qa-only/SKILL.md');
|
||||
expect(shipSkill).toContain('qa-only');
|
||||
});
|
||||
|
||||
test('contains localhost reachability check', () => {
|
||||
expect(shipSkill).toContain('localhost:3000');
|
||||
expect(shipSkill).toContain('NO_SERVER');
|
||||
});
|
||||
|
||||
test('skips gracefully when no verification section', () => {
|
||||
expect(shipSkill).toContain('No verification steps found in plan');
|
||||
});
|
||||
|
||||
test('skips gracefully when no dev server', () => {
|
||||
expect(shipSkill).toContain('No dev server detected');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Coverage gate tests ---
|
||||
|
||||
describe('Coverage gate in ship', () => {
|
||||
const shipSkill = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
|
||||
const reviewSkill = fs.readFileSync(path.join(ROOT, 'review', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('ship SKILL.md contains coverage gate with thresholds', () => {
|
||||
expect(shipSkill).toContain('Coverage gate');
|
||||
expect(shipSkill).toContain('>= target');
|
||||
expect(shipSkill).toContain('< minimum');
|
||||
});
|
||||
|
||||
test('ship SKILL.md supports configurable thresholds via CLAUDE.md', () => {
|
||||
expect(shipSkill).toContain('## Test Coverage');
|
||||
expect(shipSkill).toContain('Minimum:');
|
||||
expect(shipSkill).toContain('Target:');
|
||||
});
|
||||
|
||||
test('coverage gate skips on parse failure (not block)', () => {
|
||||
expect(shipSkill).toContain('could not determine percentage — skipping');
|
||||
});
|
||||
|
||||
test('review SKILL.md contains coverage WARNING', () => {
|
||||
expect(reviewSkill).toContain('COVERAGE WARNING');
|
||||
expect(reviewSkill).toContain('Consider writing tests before running /ship');
|
||||
});
|
||||
|
||||
test('review coverage warning is INFORMATIONAL', () => {
|
||||
expect(reviewSkill).toContain('INFORMATIONAL');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Ship metrics logging ---
|
||||
|
||||
describe('Ship metrics logging', () => {
|
||||
const shipSkill = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('ship SKILL.md contains metrics persistence step', () => {
|
||||
expect(shipSkill).toContain('Step 8.75');
|
||||
expect(shipSkill).toContain('coverage_pct');
|
||||
expect(shipSkill).toContain('plan_items_total');
|
||||
expect(shipSkill).toContain('plan_items_done');
|
||||
expect(shipSkill).toContain('verification_result');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Plan file discovery shared helper ---
|
||||
|
||||
describe('Plan file discovery shared helper', () => {
|
||||
// The shared helper should appear in ship (via PLAN_COMPLETION_AUDIT_SHIP)
|
||||
// and in review (via PLAN_COMPLETION_AUDIT_REVIEW)
|
||||
const shipSkill = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
|
||||
const reviewSkill = fs.readFileSync(path.join(ROOT, 'review', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('plan file discovery appears in both ship and review', () => {
|
||||
expect(shipSkill).toContain('Plan File Discovery');
|
||||
expect(reviewSkill).toContain('Plan File Discovery');
|
||||
});
|
||||
|
||||
test('both include conversation context first', () => {
|
||||
expect(shipSkill).toContain('Conversation context (primary)');
|
||||
expect(reviewSkill).toContain('Conversation context (primary)');
|
||||
});
|
||||
|
||||
test('both include content-based fallback', () => {
|
||||
expect(shipSkill).toContain('Content-based search (fallback)');
|
||||
expect(reviewSkill).toContain('Content-based search (fallback)');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Retro plan completion ---
|
||||
|
||||
describe('Retro plan completion section', () => {
|
||||
const retroSkill = fs.readFileSync(path.join(ROOT, 'retro', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('retro SKILL.md contains plan completion section', () => {
|
||||
expect(retroSkill).toContain('### Plan Completion');
|
||||
expect(retroSkill).toContain('plan_items_total');
|
||||
expect(retroSkill).toContain('Plan Completion This Period');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Plan status footer in preamble ---
|
||||
|
||||
describe('Plan status footer in preamble', () => {
|
||||
|
||||
@@ -207,7 +207,7 @@ export async function finalizeEvalCollector(evalCollector: EvalCollector | null)
|
||||
if (evalsEnabled) {
|
||||
const gstackDir = path.join(os.homedir(), '.gstack');
|
||||
fs.mkdirSync(gstackDir, { recursive: true });
|
||||
for (const f of ['.completeness-intro-seen', '.telemetry-prompted']) {
|
||||
for (const f of ['.completeness-intro-seen', '.telemetry-prompted', '.proactive-prompted']) {
|
||||
const p = path.join(gstackDir, f);
|
||||
if (!fs.existsSync(p)) fs.writeFileSync(p, '');
|
||||
}
|
||||
|
||||
@@ -107,12 +107,17 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'gemini-review-findings': ['review/**', '.agents/skills/gstack-review/**', 'test/helpers/gemini-session-runner.ts', 'lib/worktree.ts'],
|
||||
|
||||
|
||||
// Coverage audit (shared fixture) + triage
|
||||
// Coverage audit (shared fixture) + triage + gates
|
||||
'ship-coverage-audit': ['ship/**', 'test/fixtures/coverage-audit-fixture.ts', 'bin/gstack-repo-mode'],
|
||||
'review-coverage-audit': ['review/**', 'test/fixtures/coverage-audit-fixture.ts'],
|
||||
'plan-eng-coverage-audit': ['plan-eng-review/**', 'test/fixtures/coverage-audit-fixture.ts'],
|
||||
'ship-triage': ['ship/**', 'bin/gstack-repo-mode'],
|
||||
|
||||
// Plan completion audit + verification
|
||||
'ship-plan-completion': ['ship/**', 'scripts/gen-skill-docs.ts'],
|
||||
'ship-plan-verification': ['ship/**', 'qa-only/**', 'scripts/gen-skill-docs.ts'],
|
||||
'review-plan-completion': ['review/**', 'scripts/gen-skill-docs.ts'],
|
||||
|
||||
// Design
|
||||
'design-consultation-core': ['design-consultation/**', 'scripts/gen-skill-docs.ts', 'test/helpers/llm-judge.ts'],
|
||||
'design-consultation-existing': ['design-consultation/**', 'scripts/gen-skill-docs.ts'],
|
||||
|
||||
Reference in New Issue
Block a user