diff --git a/.github/review-prompt.md b/.github/review-prompt.md
index ed511dc1..4f91e5ff 100644
--- a/.github/review-prompt.md
+++ b/.github/review-prompt.md
@@ -56,7 +56,8 @@ Use only the review contract in this file plus the generated scope and packet fi
- Use `approved_with_notes` when only non-blocking follow-ups remain.
- Use `changes_requested` when any blocking issue remains.
- Fill the structured fields only. The renderer owns the markdown layout.
-- Keep `summary` to plain prose only. Do not include the PR title, a separate verdict line, markdown tables, file inventories, or custom section headings there.
+- Keep `summary` to plain prose only, ideally 2-4 sentences. Do not include the PR title, a separate verdict line, markdown tables, file inventories, or custom section headings there.
+- Keep `overallRationale` to 1 sentence.
- Keep `what`, `why`, and `fix` concise plain text. Do not emit headings, tables, or fenced code blocks inside those fields.
- Use `snippets` only when a short literal excerpt materially clarifies a finding. Keep each snippet under 20 lines, and do not include markdown fences in `code`.
- Use `securityChecklist` for concise review rows about security-sensitive checks. Provide at least 1 row, and use 2-5 when possible. `status` = `pass` | `fail` | `na`.
diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml
index 7ce457f0..bb3c2d0f 100644
--- a/.github/workflows/ai-review.yml
+++ b/.github/workflows/ai-review.yml
@@ -196,12 +196,12 @@ jobs:
# GLM API environment for model routing
env:
ANTHROPIC_BASE_URL: https://api.z.ai/api/anthropic
- REVIEW_MODEL: glm-5.1
+ REVIEW_MODEL: glm-5-turbo
ANTHROPIC_AUTH_TOKEN: ${{ secrets.GLM_API_KEY }}
- ANTHROPIC_MODEL: glm-5.1
- ANTHROPIC_DEFAULT_OPUS_MODEL: glm-5.1
- ANTHROPIC_DEFAULT_SONNET_MODEL: glm-5.1
- ANTHROPIC_DEFAULT_HAIKU_MODEL: GLM-4.7-FlashX
+ ANTHROPIC_MODEL: glm-5-turbo
+ ANTHROPIC_DEFAULT_OPUS_MODEL: glm-5-turbo
+ ANTHROPIC_DEFAULT_SONNET_MODEL: glm-5-turbo
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: glm-5-turbo
DISABLE_BUG_COMMAND: '1'
DISABLE_ERROR_REPORTING: '1'
DISABLE_TELEMETRY: '1'
diff --git a/scripts/github/normalize-ai-review-output.mjs b/scripts/github/normalize-ai-review-output.mjs
index 9be73e21..06d2436c 100644
--- a/scripts/github/normalize-ai-review-output.mjs
+++ b/scripts/github/normalize-ai-review-output.mjs
@@ -14,6 +14,11 @@ const SEVERITY_HEADERS = {
medium: '### 🟡 Medium',
low: '### 🟢 Low',
};
+const SEVERITY_SUMMARY_LABELS = {
+ high: '🔴 High',
+ medium: '🟡 Medium',
+ low: '🟢 Low',
+};
const STATUS_LABELS = {
pass: '✅',
@@ -41,6 +46,7 @@ const CODE_BLOCK_LANGUAGE_PATTERN = /^[A-Za-z0-9#+.-]{1,20}$/u;
const MAX_FINDING_SNIPPETS = 2;
const MAX_SNIPPET_LINES = 20;
const MAX_SNIPPET_CHARACTERS = 1200;
+const TOP_FINDINGS_LIMIT = 3;
function cleanText(value) {
return typeof value === 'string' ? value.trim().replace(/\s+/g, ' ') : '';
@@ -224,35 +230,40 @@ function formatReviewContext(rendering) {
const parts = [];
if (rendering.mode) {
- parts.push(`mode ${renderCode(rendering.mode)}`);
- parts.push(REVIEW_MODE_DETAILS[rendering.mode]);
+ parts.push(renderCode(rendering.mode));
}
- const scopeSummary = formatScopeSummary(rendering);
- if (scopeSummary) {
- parts.push(`scope ${scopeSummary}`);
+ if (
+ typeof rendering.selectedFiles === 'number' &&
+ typeof rendering.reviewableFiles === 'number'
+ ) {
+ parts.push(`${rendering.selectedFiles}/${rendering.reviewableFiles} files`);
}
- const packetCoverage = formatPacketCoverage(rendering);
- if (packetCoverage) {
- parts.push(`packet ${packetCoverage}`);
+ if (
+ typeof rendering.selectedChanges === 'number' &&
+ typeof rendering.reviewableChanges === 'number'
+ ) {
+ parts.push(`${rendering.selectedChanges}/${rendering.reviewableChanges} lines`);
}
- const turnBudget = formatTurnBudget(rendering);
- if (turnBudget) {
- parts.push(`turn budget ${turnBudget}`);
+ if (
+ typeof rendering.packetIncludedFiles === 'number' &&
+ typeof rendering.packetTotalFiles === 'number'
+ ) {
+ parts.push(`packet ${rendering.packetIncludedFiles}/${rendering.packetTotalFiles}`);
}
- const timeBudget = formatTimeBudget(rendering);
- if (timeBudget) {
- parts.push(`workflow cap ${timeBudget}`);
+ const runtimeBudget = formatCombinedBudget(rendering) || formatTimeBudget(rendering) || formatTurnBudget(rendering);
+ if (runtimeBudget) {
+ parts.push(runtimeBudget);
}
if (parts.length === 0) {
return null;
}
- return `> 🧭 Review context: ${parts.join('; ')}.`;
+ return `> 🧭 ${parts.join(' • ')}`;
}
function classifyFallbackReason(reason) {
@@ -612,8 +623,8 @@ export function normalizeStructuredOutput(raw) {
return { ok: true, value };
}
-function renderChecklistTable(title, labelHeader, labelKey, rows) {
- const lines = ['', title, '', `| ${labelHeader} | Status | Notes |`, '|---|---|---|'];
+function renderChecklistTable(labelHeader, labelKey, rows) {
+ const lines = [`| ${labelHeader} | Status | Notes |`, '|---|---|---|'];
for (const row of rows) {
lines.push(
`| ${renderInlineText(row[labelKey])} | ${STATUS_LABELS[row.status]} | ${renderInlineText(row.notes)} |`
@@ -622,9 +633,9 @@ function renderChecklistTable(title, labelHeader, labelKey, rows) {
return lines;
}
-function renderBulletSection(title, items) {
+function renderBulletSection(items) {
if (items.length === 0) return [];
- return ['', title, ...items.map((item) => `- ${renderInlineText(item)}`)];
+ return items.map((item) => `- ${renderInlineText(item)}`);
}
function renderFindingSnippets(snippets) {
@@ -646,46 +657,104 @@ function renderFindingSnippets(snippets) {
return lines;
}
+function renderDetailsBlock(summary, bodyLines) {
+ if (!bodyLines.length) {
+ return [];
+ }
+
+ return ['', '', `${escapeMarkdown(summary)}
`, '', ...bodyLines, '', ' '];
+}
+
+function renderFindingReference(finding) {
+ return finding.line ? `${finding.file}:${finding.line}` : finding.file;
+}
+
+function getOrderedFindings(findings) {
+ return SEVERITY_ORDER.flatMap((severity) => findings.filter((finding) => finding.severity === severity));
+}
+
+function renderTopFindings(findings) {
+ if (findings.length === 0) {
+ return ['No confirmed issues found after reviewing the diff and surrounding code.'];
+ }
+
+ const orderedFindings = getOrderedFindings(findings);
+ const lines = orderedFindings
+ .slice(0, TOP_FINDINGS_LIMIT)
+ .map(
+ (finding) =>
+ `- ${SEVERITY_SUMMARY_LABELS[finding.severity]} ${renderCode(renderFindingReference(finding))} — ${renderInlineText(finding.title)}`
+ );
+
+ if (orderedFindings.length > TOP_FINDINGS_LIMIT) {
+ const remaining = orderedFindings.length - TOP_FINDINGS_LIMIT;
+ lines.push(`- ${remaining} more finding${remaining === 1 ? '' : 's'} in the details below.`);
+ }
+
+ return lines;
+}
+
+function renderDetailedFindings(findings) {
+ if (findings.length === 0) {
+ return [];
+ }
+
+ const lines = [];
+ for (const severity of SEVERITY_ORDER) {
+ const scopedFindings = findings.filter((finding) => finding.severity === severity);
+ if (scopedFindings.length === 0) continue;
+
+ lines.push(`**${SEVERITY_SUMMARY_LABELS[severity]} (${scopedFindings.length})**`, '');
+ for (const finding of scopedFindings) {
+ lines.push(`- **${renderCode(renderFindingReference(finding))} — ${renderInlineText(finding.title)}**`);
+ lines.push(` Problem: ${renderInlineText(finding.what)}`);
+ lines.push(` Why it matters: ${renderInlineText(finding.why)}`);
+ lines.push(` Suggested fix: ${renderInlineText(finding.fix)}`);
+ lines.push(...renderFindingSnippets(finding.snippets));
+ lines.push('');
+ }
+ }
+
+ if (lines[lines.length - 1] === '') {
+ lines.pop();
+ }
+
+ return lines;
+}
+
export function renderStructuredReview(review, { model, rendering: renderOptions } = {}) {
const rendering = mergeRenderingMetadata(review?.rendering, renderOptions);
- const lines = ['### 📋 Summary', '', renderInlineText(review.summary), '', '### 🔍 Findings'];
+ const lines = [
+ '### Verdict',
+ '',
+ `**${ASSESSMENTS[review.overallAssessment]}** — ${renderInlineText(review.overallRationale)}`,
+ '',
+ renderInlineText(review.summary),
+ ];
const reviewContext = formatReviewContext(rendering);
if (reviewContext) {
- lines.splice(4, 0, reviewContext, '');
+ lines.push('', reviewContext);
}
- if (review.findings.length === 0) {
- lines.push('No confirmed issues found after reviewing the diff and surrounding code.');
- } else {
- for (const severity of SEVERITY_ORDER) {
- const findings = review.findings.filter((finding) => finding.severity === severity);
- if (findings.length === 0) continue;
-
- lines.push('', SEVERITY_HEADERS[severity], '');
- for (const finding of findings) {
- const location = finding.line ? `${finding.file}:${finding.line}` : finding.file;
- lines.push(`- **${renderCode(location)} — ${renderInlineText(finding.title)}**`);
- lines.push(` Problem: ${renderInlineText(finding.what)}`);
- lines.push(` Why it matters: ${renderInlineText(finding.why)}`);
- lines.push(` Suggested fix: ${renderInlineText(finding.fix)}`);
- lines.push(...renderFindingSnippets(finding.snippets));
- lines.push('');
- }
- }
- if (lines[lines.length - 1] === '') lines.pop();
- }
-
- lines.push(...renderChecklistTable('### 🔒 Security Checklist', 'Check', 'check', review.securityChecklist));
- lines.push(...renderChecklistTable('### 📊 CCS Compliance', 'Rule', 'rule', review.ccsCompliance));
- lines.push(...renderBulletSection('### 💡 Informational', review.informational));
- lines.push(...renderBulletSection("### ✅ What's Done Well", review.strengths));
+ lines.push('', '### Top Findings', '', ...renderTopFindings(review.findings));
+ lines.push(...renderDetailsBlock(`Full Findings (${review.findings.length})`, renderDetailedFindings(review.findings)));
+ lines.push(
+ ...renderDetailsBlock(
+ `Security Checklist (${review.securityChecklist.length})`,
+ renderChecklistTable('Check', 'check', review.securityChecklist)
+ )
+ );
+ lines.push(
+ ...renderDetailsBlock(
+ `CCS Compliance (${review.ccsCompliance.length})`,
+ renderChecklistTable('Rule', 'rule', review.ccsCompliance)
+ )
+ );
+ lines.push(...renderDetailsBlock(`Informational (${review.informational.length})`, renderBulletSection(review.informational)));
+ lines.push(...renderDetailsBlock(`What's Done Well (${review.strengths.length})`, renderBulletSection(review.strengths)));
lines.push(
- '',
- '### 🎯 Overall Assessment',
- '',
- `**${ASSESSMENTS[review.overallAssessment]}** — ${renderInlineText(review.overallRationale)}`,
'',
`> 🤖 Reviewed by \`${model}\``
);
diff --git a/scripts/github/run-ai-review-direct.mjs b/scripts/github/run-ai-review-direct.mjs
index a2f37959..c1604c8a 100644
--- a/scripts/github/run-ai-review-direct.mjs
+++ b/scripts/github/run-ai-review-direct.mjs
@@ -283,7 +283,7 @@ export async function writeDirectReviewFromEnv(env = process.env, fetchImpl = gl
const responseJson = await postReviewRequest({
apiUrl: cleanText(env.ANTHROPIC_BASE_URL),
apiKey: cleanText(env.ANTHROPIC_AUTH_TOKEN),
- model: cleanText(env.REVIEW_MODEL || env.ANTHROPIC_MODEL || 'glm-5.1'),
+ model: cleanText(env.REVIEW_MODEL || env.ANTHROPIC_MODEL || 'glm-5-turbo'),
system,
prompt: attemptPrompt,
timeoutMs: attemptWindow.timeoutMs,
@@ -318,11 +318,11 @@ export async function writeDirectReviewFromEnv(env = process.env, fetchImpl = gl
const markdown = finalValidation
? renderStructuredReview(finalValidation, {
- model: cleanText(env.REVIEW_MODEL || 'glm-5.1'),
+ model: cleanText(env.REVIEW_MODEL || 'glm-5-turbo'),
rendering,
})
: renderIncompleteReview({
- model: cleanText(env.REVIEW_MODEL || 'glm-5.1'),
+ model: cleanText(env.REVIEW_MODEL || 'glm-5-turbo'),
reason: lastReason,
runUrl: cleanText(env.AI_REVIEW_RUN_URL || '#'),
selectedFiles: coveredSelectedFiles,
diff --git a/tests/unit/scripts/github/normalize-ai-review-output.test.ts b/tests/unit/scripts/github/normalize-ai-review-output.test.ts
index afefb07e..c34d45e0 100644
--- a/tests/unit/scripts/github/normalize-ai-review-output.test.ts
+++ b/tests/unit/scripts/github/normalize-ai-review-output.test.ts
@@ -52,21 +52,22 @@ describe('normalize-ai-review-output', () => {
);
expect(validation.ok).toBe(true);
- const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5.1' });
+ const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5-turbo' });
- expect(markdown).toContain('### 📋 Summary');
- expect(markdown).toContain('### 🔴 High');
+ expect(markdown).toContain('### Verdict');
+ expect(markdown).toContain('### Top Findings');
+ expect(markdown).toContain('- 🔴 High `src/cliproxy/accounts/query.ts:61` — Ambiguous account lookup drops valid matches');
+ expect(markdown).toContain('Full Findings (1)');
expect(markdown).toContain('**`src/cliproxy/accounts/query.ts:61` — Ambiguous account lookup drops valid matches**');
- expect(markdown).toContain('### 🔒 Security Checklist');
+ expect(markdown).toContain('Security Checklist (1)');
expect(markdown).toContain('| Injection safety | ✅ | No user-controlled input reaches a shell, SQL, or HTML boundary in this diff. |');
- expect(markdown).toContain('### 📊 CCS Compliance');
+ expect(markdown).toContain('CCS Compliance (1)');
expect(markdown).toContain('| No emojis in CLI | N/A | This change affects GitHub PR comments only, not CLI stdout. |');
- expect(markdown).toContain('### 💡 Informational');
- expect(markdown).toContain('### ✅ What\'s Done Well');
- expect(markdown).toContain('### 🎯 Overall Assessment');
+ expect(markdown).toContain('Informational (1)');
+ expect(markdown).toContain("What's Done Well (1)");
expect(markdown).toContain('**❌ CHANGES REQUESTED**');
expect(markdown).toContain('Why it matters: That breaks normal selection flows for users with multiple Codex sessions.');
- expect(markdown).toContain('> 🤖 Reviewed by `glm-5.1`');
+ expect(markdown).toContain('> 🤖 Reviewed by `glm-5-turbo`');
});
test('renders mode-aware review context metadata without changing the structured review contract', () => {
@@ -97,7 +98,7 @@ describe('normalize-ai-review-output', () => {
expect(validation.ok).toBe(true);
const markdown = reviewOutput.renderStructuredReview(validation.value, {
- model: 'glm-5.1',
+ model: 'glm-5-turbo',
rendering: {
mode: 'triage',
selectedFiles: 8,
@@ -113,7 +114,7 @@ describe('normalize-ai-review-output', () => {
});
expect(markdown).toContain(
- '> 🧭 Review context: mode `triage`; expanded packaged review with broader coverage; scope 8/34 reviewable files; 620/2140 reviewable changed lines; packet 6/8 selected files included in the final review packet; 2 selected files omitted for packet budget; turn budget 6 turns; workflow cap 5 minutes.'
+ '> 🧭 `triage` • 8/34 files • 620/2140 lines • packet 6/8 • 6 turns / 5 minutes'
);
expect(markdown).toContain('**⚠️ APPROVED WITH NOTES**');
});
@@ -157,7 +158,7 @@ describe('normalize-ai-review-output', () => {
);
expect(validation.ok).toBe(true);
- const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5.1' });
+ const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5-turbo' });
expect(markdown).toContain('`buildReviewScope(files, mode)`');
expect(markdown).toContain('`.github/workflows/ai-review.yml`');
@@ -200,7 +201,7 @@ describe('normalize-ai-review-output', () => {
);
expect(validation.ok).toBe(true);
- const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5.1' });
+ const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5-turbo' });
expect(markdown).toContain('Evidence: Current publish branch');
expect(markdown).toContain('```bash');
@@ -242,7 +243,7 @@ describe('normalize-ai-review-output', () => {
expect(validation.ok).toBe(true);
expect(validation.value.findings[0].snippets[0].code).toBe(' if value:\n print(value)');
- const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5.1' });
+ const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5-turbo' });
expect(markdown).toContain(' if value:');
expect(markdown).toContain(' print(value)');
});
@@ -300,7 +301,7 @@ describe('normalize-ai-review-output', () => {
const result = reviewOutput.writeReviewFromEnv({
AI_REVIEW_EXECUTION_FILE: executionFile,
- AI_REVIEW_MODEL: 'glm-5.1',
+ AI_REVIEW_MODEL: 'glm-5-turbo',
AI_REVIEW_MODE: 'triage',
AI_REVIEW_SELECTED_FILES: '10',
AI_REVIEW_REVIEWABLE_FILES: '46',
@@ -363,7 +364,7 @@ describe('normalize-ai-review-output', () => {
const result = reviewOutput.writeReviewFromEnv({
AI_REVIEW_EXECUTION_FILE: executionFile,
- AI_REVIEW_MODEL: 'glm-5.1',
+ AI_REVIEW_MODEL: 'glm-5-turbo',
AI_REVIEW_MODE: 'fast',
AI_REVIEW_SELECTED_FILES: '6',
AI_REVIEW_REVIEWABLE_FILES: '52',
@@ -410,7 +411,7 @@ describe('normalize-ai-review-output', () => {
const result = reviewOutput.writeReviewFromEnv({
AI_REVIEW_EXECUTION_FILE: executionFile,
- AI_REVIEW_MODEL: 'glm-5.1',
+ AI_REVIEW_MODEL: 'glm-5-turbo',
AI_REVIEW_OUTPUT_FILE: outputFile,
AI_REVIEW_RUN_URL: 'https://github.com/kaitranntt/ccs/actions/runs/1',
AI_REVIEW_STRUCTURED_OUTPUT: JSON.stringify({
@@ -507,15 +508,15 @@ describe('normalize-ai-review-output', () => {
);
expect(validation.ok).toBe(true);
- const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5.1' });
+ const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5-turbo' });
- expect(markdown).toContain('### 🔍 Findings');
+ expect(markdown).toContain('### Top Findings');
expect(markdown).toContain('No confirmed issues found after reviewing the diff and surrounding code.');
- expect(markdown).toContain('### 🔒 Security Checklist');
+ expect(markdown).toContain('Security Checklist (1)');
expect(markdown).toContain(
'| Injection safety | ✅ | No user-controlled data crosses a risky boundary in the reviewed diff. |'
);
- expect(markdown).toContain('### 📊 CCS Compliance');
+ expect(markdown).toContain('CCS Compliance (1)');
expect(markdown).toContain('| Help/docs alignment | N/A | No CLI behavior changed, so there was nothing to update. |');
expect(markdown).toContain('**✅ APPROVED** — No confirmed regressions or missing verification remain.');
});
@@ -545,7 +546,7 @@ describe('normalize-ai-review-output', () => {
);
expect(validation.ok).toBe(true);
- const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5.1' });
+ const markdown = reviewOutput.renderStructuredReview(validation.value, { model: 'glm-5-turbo' });
expect(markdown).toContain(
'**`tests/unit/scripts/github/normalize-ai-review-output.test.ts` — Missing empty-state coverage**'
@@ -575,7 +576,7 @@ describe('normalize-ai-review-output', () => {
overallAssessment: 'approved_with_notes',
overallRationale: 'This is a formatting-only follow-up.',
},
- { model: 'glm-5.1' }
+ { model: 'glm-5-turbo' }
);
expect(markdown).toContain('**``src/weird`path.ts`` — Backtick-safe locations stay readable**');
diff --git a/tests/unit/scripts/github/run-ai-review-direct.test.ts b/tests/unit/scripts/github/run-ai-review-direct.test.ts
index 51331f3c..8131f3dd 100644
--- a/tests/unit/scripts/github/run-ai-review-direct.test.ts
+++ b/tests/unit/scripts/github/run-ai-review-direct.test.ts
@@ -60,7 +60,7 @@ describe('run-ai-review-direct', () => {
{
ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
ANTHROPIC_AUTH_TOKEN: 'test-token',
- REVIEW_MODEL: 'glm-5.1',
+ REVIEW_MODEL: 'glm-5-turbo',
GITHUB_REPOSITORY: 'kaitranntt/ccs',
AI_REVIEW_PROMPT: 'You are a reviewer.',
AI_REVIEW_PACKET_FILE: packetFile,
@@ -110,7 +110,7 @@ describe('run-ai-review-direct', () => {
{
ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
ANTHROPIC_AUTH_TOKEN: 'test-token',
- REVIEW_MODEL: 'glm-5.1',
+ REVIEW_MODEL: 'glm-5-turbo',
GITHUB_REPOSITORY: 'kaitranntt/ccs',
AI_REVIEW_PROMPT: 'You are a reviewer.',
AI_REVIEW_PACKET_FILE: packetFile,
@@ -147,11 +147,9 @@ describe('run-ai-review-direct', () => {
);
expect(result.usedFallback).toBe(false);
- expect(fs.readFileSync(outputFile, 'utf8')).toContain('### 📋 Summary');
+ expect(fs.readFileSync(outputFile, 'utf8')).toContain('### Verdict');
expect(fs.readFileSync(outputFile, 'utf8')).toContain('**✅ APPROVED**');
- expect(fs.readFileSync(outputFile, 'utf8')).toContain(
- 'packet 1/1 selected files included in the final review packet'
- );
+ expect(fs.readFileSync(outputFile, 'utf8')).toContain('packet 1/1');
expect(JSON.parse(fs.readFileSync(logFile, 'utf8')).attempts).toHaveLength(1);
});
});
@@ -171,7 +169,7 @@ describe('run-ai-review-direct', () => {
{
ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
ANTHROPIC_AUTH_TOKEN: 'test-token',
- REVIEW_MODEL: 'glm-5.1',
+ REVIEW_MODEL: 'glm-5-turbo',
GITHUB_REPOSITORY: 'kaitranntt/ccs',
AI_REVIEW_PROMPT: 'You are a reviewer.',
AI_REVIEW_PACKET_FILE: packetFile,
@@ -263,7 +261,7 @@ describe('run-ai-review-direct', () => {
{
ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
ANTHROPIC_AUTH_TOKEN: 'test-token',
- REVIEW_MODEL: 'glm-5.1',
+ REVIEW_MODEL: 'glm-5-turbo',
GITHUB_REPOSITORY: 'kaitranntt/ccs',
AI_REVIEW_PROMPT: 'You are a reviewer.',
AI_REVIEW_PACKET_FILE: packetFile,
@@ -289,9 +287,7 @@ describe('run-ai-review-direct', () => {
expect(result.usedFallback).toBe(false);
expect(fs.readFileSync(outputFile, 'utf8')).toContain('**⚠️ APPROVED WITH NOTES**');
- expect(fs.readFileSync(outputFile, 'utf8')).toContain(
- 'packet 2/3 selected files included in the final review packet; 1 selected file omitted for packet budget'
- );
+ expect(fs.readFileSync(outputFile, 'utf8')).toContain('packet 2/3');
expect(JSON.parse(fs.readFileSync(logFile, 'utf8')).attempts).toHaveLength(2);
});
});