Skip to content

Commit fd7fc7f

Browse files
committed
fix: cc-skills now reads label strategy + content types; Claude JSON parsing hardened
Two quality gaps identified by plan review agents: 1. syncCcSkills() was pulling but never reading any files. Now reads: - label-strategy.md — injected into MiniMax label suggestion prompt - content-types.md — cached for issue formatting context Both truncated to 2KB and cached in memory. 2. Claude validation JSON parsing was fragile: - Parse failures silently lost Claude's full response - No schema validation (missing fields caused runtime errors) Now: separate try/catch for JSON.parse, logs full response on failure, validates required fields (confirmed, confidence, analysis) before use. SRED-Type: experimental-development SRED-Claim: yes
1 parent 0ff2c3c commit fd7fc7f

1 file changed

Lines changed: 79 additions & 8 deletions

File tree

src/orchestrator.ts

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,10 @@ function rotateLogIfNeeded(): void {
125125
// Keep last 2MB, discard the rest
126126
const content = fs.readFileSync(LOG_FILE, 'utf-8');
127127
const keepFrom = content.length - 2 * 1024 * 1024;
128-
const newContent = keepFrom > 0
129-
? '... (log rotated)\n' + content.slice(keepFrom)
130-
: content;
128+
const newContent =
129+
keepFrom > 0
130+
? '... (log rotated)\n' + content.slice(keepFrom)
131+
: content;
131132
fs.writeFileSync(LOG_FILE, newContent);
132133
logger.info(
133134
{ oldSize: stats.size, newSize: newContent.length },
@@ -930,7 +931,10 @@ function verifyFindingScript(
930931
{ cmd: cmd.slice(0, 50), title: finding.title },
931932
'Validation command not in allowlist, skipping verification',
932933
);
933-
return { verified: true, output: 'Command not verifiable (not in allowlist)' };
934+
return {
935+
verified: true,
936+
output: 'Command not verifiable (not in allowlist)',
937+
};
934938
}
935939

936940
try {
@@ -954,7 +958,10 @@ function verifyFindingScript(
954958
{ title: finding.title, cmd: cmd.slice(0, 80), err },
955959
'Verification script failed — finding likely hallucinated',
956960
);
957-
return { verified: false, output: `Command failed: ${String(err).slice(0, 200)}` };
961+
return {
962+
verified: false,
963+
output: `Command failed: ${String(err).slice(0, 200)}`,
964+
};
958965
}
959966
}
960967

@@ -1055,7 +1062,37 @@ RULES:
10551062
jsonStr = jsonMatch[0];
10561063
}
10571064

1058-
const validation: ValidationResult = JSON.parse(jsonStr);
1065+
let validation: ValidationResult;
1066+
try {
1067+
validation = JSON.parse(jsonStr);
1068+
} catch (parseErr) {
1069+
// Log full output on parse failure so we don't lose Claude's response
1070+
logger.error(
1071+
{
1072+
title: finding.title,
1073+
output: output.slice(0, 500),
1074+
parseErr,
1075+
},
1076+
'Claude validation JSON parse failed — response logged for debugging',
1077+
);
1078+
return null;
1079+
}
1080+
1081+
// Schema validation: ensure required fields exist
1082+
if (
1083+
typeof validation.confirmed !== 'boolean' ||
1084+
!validation.confidence ||
1085+
!validation.analysis
1086+
) {
1087+
logger.error(
1088+
{
1089+
title: finding.title,
1090+
parsed: JSON.stringify(validation).slice(0, 200),
1091+
},
1092+
'Claude validation missing required fields (confirmed/confidence/analysis)',
1093+
);
1094+
return null;
1095+
}
10591096

10601097
logger.info(
10611098
{
@@ -1134,6 +1171,10 @@ function extractWords(s: string): string[] {
11341171
* Pull latest cc-skills for up-to-date issue templates and patterns.
11351172
* Fire-and-forget — failure is non-fatal.
11361173
*/
1174+
/** Cached cc-skills reference content for issue creation quality */
1175+
let ccSkillsLabelStrategy = '';
1176+
let ccSkillsContentTypes = '';
1177+
11371178
function syncCcSkills(ccSkillsPath: string): void {
11381179
if (!ccSkillsPath || !fs.existsSync(ccSkillsPath)) return;
11391180
try {
@@ -1142,7 +1183,32 @@ function syncCcSkills(ccSkillsPath: string): void {
11421183
timeout: 15_000,
11431184
encoding: 'utf-8',
11441185
});
1145-
logger.info({ path: ccSkillsPath }, 'cc-skills synced');
1186+
1187+
// Read label strategy and content type references for issue creation
1188+
const labelStrategyPath = path.join(
1189+
ccSkillsPath,
1190+
'plugins/gh-tools/skills/issue-create/references/label-strategy.md',
1191+
);
1192+
const contentTypesPath = path.join(
1193+
ccSkillsPath,
1194+
'plugins/gh-tools/skills/issue-create/references/content-types.md',
1195+
);
1196+
1197+
if (fs.existsSync(labelStrategyPath)) {
1198+
ccSkillsLabelStrategy = fs.readFileSync(labelStrategyPath, 'utf-8').slice(0, 2000);
1199+
}
1200+
if (fs.existsSync(contentTypesPath)) {
1201+
ccSkillsContentTypes = fs.readFileSync(contentTypesPath, 'utf-8').slice(0, 2000);
1202+
}
1203+
1204+
logger.info(
1205+
{
1206+
path: ccSkillsPath,
1207+
hasLabelStrategy: !!ccSkillsLabelStrategy,
1208+
hasContentTypes: !!ccSkillsContentTypes,
1209+
},
1210+
'cc-skills synced and references loaded',
1211+
);
11461212
} catch (err) {
11471213
logger.warn({ err }, 'cc-skills sync failed (non-fatal)');
11481214
}
@@ -1209,10 +1275,15 @@ async function suggestLabels(
12091275
};
12101276

12111277
try {
1278+
// Inject cc-skills label strategy if available (read from synced repo)
1279+
const strategyContext = ccSkillsLabelStrategy
1280+
? `\nLABEL STRATEGY GUIDE:\n${ccSkillsLabelStrategy.slice(0, 800)}\n`
1281+
: '';
1282+
12121283
const prompt = `Suggest 2-4 labels from the EXISTING taxonomy only for this code finding.
12131284
Never suggest labels that don't exist in the list below.
12141285
Return ONLY a JSON array of label names, nothing else.
1215-
1286+
${strategyContext}
12161287
AVAILABLE LABELS:
12171288
${repoLabels.map((l) => `- ${l}`).join('\n')}
12181289

0 commit comments

Comments
 (0)