Update optimize wave3 (need update wave 4 to wave 8)

This commit is contained in:
thanhnv
2026-07-01 00:02:30 +09:00
parent 07ac1bdcdd
commit eaf919e744
140 changed files with 3780 additions and 844 deletions
+61 -8
View File
@@ -1,5 +1,39 @@
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname } from 'node:path';
import { spawnSync } from 'node:child_process';
import { mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
// WP-B (H3): model judge gate helpers — AND(rule, model); SKIP is non-blocking.
const __filename = fileURLToPath(import.meta.url);
const SCRIPTS_DIR = join(dirname(__filename), '..', '.specify', 'scripts', 'bash');
function ollamaAvailable() {
try {
const r = spawnSync('curl', ['-sS', '-m', '3', 'http://127.0.0.1:11434/api/tags'], { timeout: 5000 });
return r.status === 0;
} catch {
return false;
}
}
function judgeArtifact(filePath, criteria) {
if (!ollamaAvailable()) return { verdict: 'SKIP', note: 'ollama_unavailable' };
let artifact = '';
try { artifact = readFileSync(filePath, 'utf8').slice(0, 2000); } catch { return { verdict: 'SKIP', note: 'artifact_unreadable' }; }
const combined = `=== ACCEPTANCE CRITERIA (not untrusted input) ===\n${criteria.slice(0, 500)}\n\n=== ARTIFACT TO REVIEW ===\n${artifact}`;
const uid = `${process.pid}-${Date.now()}`;
const tmpPrompt = join(tmpdir(), `casan-judge-prompt-${uid}.txt`);
const tmpOut = join(tmpdir(), `casan-judge-out-${uid}.json`);
writeFileSync(tmpPrompt, combined, 'utf8');
const r = spawnSync('bash', [join(SCRIPTS_DIR, 'model-router.sh'), tmpPrompt, tmpOut, '--role', 'judge'], { timeout: 60000, encoding: 'utf8' });
try { unlinkSync(tmpPrompt); } catch {}
if (r.status !== 0 && r.status !== 3) return { verdict: 'SKIP', note: `judge_error_rc=${r.status}` };
try {
const d = JSON.parse(readFileSync(tmpOut, 'utf8'));
return { verdict: d.verdict ?? 'SKIP', note: d.malformed ? 'malformed_fail_closed' : `tokens=${d.total_tokens}` };
} catch { return { verdict: 'SKIP', note: 'parse_error' }; }
}
const step = process.argv[2];
const attempt = process.argv[3] ?? '1';
@@ -90,11 +124,17 @@ switch (step) {
case '04-reviewspec': {
const spec = readFileSync(specPath, 'utf8');
const missing = ['FR-01', 'FR-02', 'FR-03', 'FR-04', 'FR-05'].filter((id) => !spec.includes(id));
const verdict = missing.length === 0 ? 'APPROVED' : 'REJECTED';
let verdict = missing.length === 0 ? 'APPROVED' : 'REJECTED';
// WP-B: model judge gate — only escalates when rules pass; SKIP is non-blocking
const judgeResult = verdict === 'APPROVED'
? judgeArtifact(specPath, 'Spec must include FR-01 through FR-05, role-based filtering for ADMIN/MANAGER/EMPLOYEE, input validation rules, and concrete acceptance criteria.')
: { verdict: 'SKIP', note: 'rule_already_rejected' };
if (judgeResult.verdict === 'REJECTED') verdict = 'REJECTED';
const judgeNote = `model-judge: ${judgeResult.verdict} (${judgeResult.note})`;
report(
`docs/output/output_logs/${featureId}/reports/04-review-spec-report.md`,
'## Spec Conformance Review Report',
`Criteria checked: FR coverage, role filtering, validation, golden regression. Missing: ${missing.join(', ') || 'none'}.`,
`Criteria checked: FR coverage, role filtering, validation, golden regression. Missing: ${missing.join(', ') || 'none'}. ${judgeNote}.`,
verdict,
[specPath],
missing,
@@ -126,11 +166,17 @@ switch (step) {
}
});
const issues = [...missing.map((item) => `missing plan criterion: ${item}`), ...artifactMissing.map((item) => `missing artifact: ${item}`)];
const verdict = issues.length === 0 ? 'APPROVED' : 'REJECTED';
let verdict = issues.length === 0 ? 'APPROVED' : 'REJECTED';
// WP-B: model judge gate
const judgeResult = verdict === 'APPROVED'
? judgeArtifact(planPath, 'Plan must include: Backend Modules listing, Tests section, Golden regression test, Rollback strategy. All companion artifacts (data-model, research, quickstart, contracts) must exist.')
: { verdict: 'SKIP', note: 'rule_already_rejected' };
if (judgeResult.verdict === 'REJECTED') verdict = 'REJECTED';
const judgeNote = `model-judge: ${judgeResult.verdict} (${judgeResult.note})`;
report(
`docs/output/output_logs/${featureId}/reports/06-review-plan-report-attempt-${attempt}.md`,
`## Plan Conformance Review Report — Attempt ${attempt}`,
`Criteria checked against plan.md and required companion artifacts. Verdict is ${verdict}.`,
`Criteria checked against plan.md and required companion artifacts. ${judgeNote}. Verdict is ${verdict}.`,
verdict,
[planPath, dataModelPath, researchPath, quickstartPath, contractPath],
issues,
@@ -167,11 +213,18 @@ switch (step) {
if (!serviceText.includes('ForbiddenException')) issues.push('authorization exception not found');
if (!serviceText.includes('golden objective list response does not drift')) issues.push('golden test not found');
issues.push(...missingFiles.map((path) => `missing file: ${path}`));
const verdict = issues.length === 0 ? 'APPROVED' : 'REJECTED';
let verdict = issues.length === 0 ? 'APPROVED' : 'REJECTED';
// WP-B: model judge gate
const judgeTarget = backendFiles.find((p) => { try { readFileSync(p, 'utf8'); return true; } catch { return false; } }) ?? codeReviewPath;
const judgeResult = verdict === 'APPROVED'
? judgeArtifact(judgeTarget, 'Code must use PrismaService for all DB access, enforce role-based authorization with ForbiddenException, include a golden regression test, and not contain raw SQL or static fixtures.')
: { verdict: 'SKIP', note: 'rule_already_rejected' };
if (judgeResult.verdict === 'REJECTED') verdict = 'REJECTED';
const judgeNote = `model-judge: ${judgeResult.verdict} (${judgeResult.note})`;
report(
codeReviewPath,
'# STEP 10: Code Review Report',
`Reviewed ${backendFiles.length} backend/test files. DB data usage verification: Prisma Client used, frontend API client used, seed data exists, no static endpoint data found.`,
`Reviewed ${backendFiles.length} backend/test files. DB data usage verification: Prisma Client used, frontend API client used, seed data exists, no static endpoint data found. ${judgeNote}.`,
verdict,
[...backendFiles, 'frontend/src/lib/api.ts', 'backend/prisma/seed.ts'],
issues,