feat: complete llm sourcegen plan

This commit is contained in:
thanhnv
2026-07-09 00:59:28 +09:00
parent c800f7edf7
commit fafccc47ad
16 changed files with 700 additions and 85 deletions
+64 -22
View File
@@ -40,7 +40,10 @@ const DIAGRAM_STEP = {
'07-dd': 'STEP8',
'08-testkit': 'STEP8b',
'09-tasks': 'STEP9',
'10-implement': 'STEP10',
'10-reviewcode': 'STEP11',
'11-reviewcode': 'STEP11',
'12-runtests': 'STEP12',
};
const FULL_DIAGRAM = [
@@ -168,7 +171,7 @@ function logLoop(fromStep, verdict, toStep, note) {
log('warn', 'boss', line);
}
function runHarness({ id, agent, step, attempt = '1' }) {
function runHarness({ id, agent, step, attempt = '1', extraEnv = {} }) {
const diagram = DIAGRAM_STEP[step] ?? step;
const input = `${casanDir}/${id}-input.txt`;
const output = `${casanDir}/${id}-output.md`;
@@ -192,6 +195,7 @@ function runHarness({ id, agent, step, attempt = '1' }) {
CASAN_AGENT_NAME: agent,
CASAN_STEP_NAME: id,
CASAN_PHASE_REPORT: phaseReport,
...extraEnv,
},
},
);
@@ -321,19 +325,37 @@ const sequence = [
{ id: '01-srs', agent: 'okr.srs', step: '01-srs' },
{ id: '02-bd', agent: 'okr.bd', step: '02-bd' },
{ id: '03-spec', agent: 'speckit.specify', step: '03-spec' },
{ id: '04-reviewspec', agent: 'okr.reviewspec', step: '04-reviewspec' },
{ id: '05-plan-attempt-1', agent: 'speckit.plan', step: '05-plan', attempt: '1' },
];
for (const item of sequence) {
const { verdict } = runHarness(item);
if (item.step === '04-reviewspec' && verdict === 'REJECTED') {
// Diagram loop STEP5 -> STEP3. The current sequence expects APPROVED here;
// if a rejection ever happens we surface the loop instead of hiding it.
logLoop('STEP5', verdict, 'STEP3', 'review-spec rejected; sequence continues but needs attention');
runHarness(item);
}
let reviewSpec = runHarness({ id: '04-reviewspec', agent: 'okr.reviewspec', step: '04-reviewspec' });
if (reviewSpec.verdict === 'REJECTED') {
logLoop('STEP5', reviewSpec.verdict, 'STEP3', 'review-spec rejected; retrying spec with template fallback');
runHarness({
id: '04b-spec-attempt-2',
agent: 'speckit.specify',
step: '03-spec',
attempt: '2',
extraEnv: { CASAN_GEN_MODE: 'template' },
});
reviewSpec = runHarness({
id: '04c-reviewspec-attempt-2',
agent: 'okr.reviewspec',
step: '04-reviewspec',
attempt: '2',
extraEnv: { CASAN_MODEL_PRIMARY: process.env.CASAN_REVIEW_ESCALATE_MODEL || 'openai:gpt-4o-mini' },
});
if (reviewSpec.verdict === 'REJECTED') {
logLoop('STEP5', reviewSpec.verdict, 'STOP', 'review-spec rejected after fallback retry');
throw new Error('review-spec rejected after fallback retry');
}
}
runHarness({ id: '05-plan-attempt-1', agent: 'speckit.plan', step: '05-plan', attempt: '1' });
const reviewPlan1 = runHarness({ id: '06-reviewplan-attempt-1', agent: 'okr.reviewplan', step: '06-reviewplan', attempt: '1' });
if (reviewPlan1.verdict === 'REJECTED') {
logLoop('STEP7', reviewPlan1.verdict, 'STEP6', 'BACK-TO-PLAN: retrying plan with missing criteria fixed');
@@ -345,7 +367,7 @@ runHarness({ id: '07-plan-attempt-2', agent: 'speckit.plan', step: '05-plan', at
const fallbackOut = `${casanDir}/model-fallback-output.txt`;
log('debug', 'boss', `model-fallback invoked (real primary failure) → ${fallbackOut}`);
execFileSync(
`/model-fallback.sh`,
`${HARNESS_BASH}/model-fallback.sh`,
[
fallbackOut,
// Real primary failure: reading a nonexistent path exits non-zero (not a
@@ -366,46 +388,66 @@ appendBoss(`Model fallback invoked; output ${fallbackOut}`);
const driftCandidate = `${casanDir}/drift-plan-candidate.txt`;
copyFileSync(fallbackOut, driftCandidate);
log('debug', 'boss', 'drift-detect: fallback output vs golden baseline');
execFileSync(`/drift-detect.sh`, [
execFileSync(`${HARNESS_BASH}/drift-detect.sh`, [
GOLDEN_PLAN,
driftCandidate,
'.specify/logs/level5/okr-plan-drift-report.json',
], { cwd: root, stdio: 'inherit' });
appendBoss('Drift detection invoked: fallback output vs golden baseline.');
const reviewPlan2 = runHarness({ id: '08-reviewplan-attempt-2', agent: 'okr.reviewplan', step: '06-reviewplan', attempt: '2' });
const reviewPlan2 = runHarness({
id: '08-reviewplan-attempt-2',
agent: 'okr.reviewplan',
step: '06-reviewplan',
attempt: '2',
extraEnv: { CASAN_MODEL_PRIMARY: process.env.CASAN_REVIEW_ESCALATE_MODEL || 'openai:gpt-4o-mini' },
});
if (reviewPlan2.verdict === 'REJECTED') {
logLoop('STEP7', reviewPlan2.verdict, 'STEP6', 'BACK-TO-PLAN attempt 2 still rejected');
throw new Error('review-plan rejected after retry');
}
runHarness({ id: '09-dd', agent: 'okr.dd', step: '07-dd' });
runHarness({ id: '10-testkit', agent: 'okr.testkit', step: '08-testkit' });
runHarness({ id: '11-tasks', agent: 'speckit.tasks', step: '09-tasks' });
const reviewCode = runHarness({ id: '12-reviewcode', agent: 'okr.reviewcode', step: '10-reviewcode' });
runHarness({ id: '12-implement', agent: 'speckit.implement', step: '10-implement' });
const reviewCode = runHarness({ id: '13-reviewcode', agent: 'okr.reviewcode', step: '11-reviewcode' });
if (reviewCode.verdict === 'REJECTED') {
// Diagram loop STEP11 -> STEP10 (implement is not an agent step yet).
logLoop('STEP11', reviewCode.verdict, 'STEP10', 'review-code rejected; implement step must be re-run');
runHarness({ id: '13b-implement-attempt-2', agent: 'speckit.implement', step: '10-implement', attempt: '2' });
const reviewCode2 = runHarness({
id: '13c-reviewcode-attempt-2',
agent: 'okr.reviewcode',
step: '11-reviewcode',
attempt: '2',
extraEnv: { CASAN_MODEL_PRIMARY: process.env.CASAN_REVIEW_ESCALATE_MODEL || 'openai:gpt-4o-mini' },
});
if (reviewCode2.verdict === 'REJECTED') {
logLoop('STEP11', reviewCode2.verdict, 'STOP', 'review-code rejected after implement retry');
throw new Error('review-code rejected after implement retry');
}
}
const runTests = runHarness({ id: '14-runtests', agent: 'okr.testkit run-tests', step: '12-runtests' });
if (runTests.verdict === 'FAIL') {
logLoop('STEP12', runTests.verdict, 'STEP6', 'tests FAIL: re-plan/re-implement before acceptance');
}
const rollbackDir = 'docs/output/casan/app-evidence';
mkdirSync(rollbackDir, { recursive: true });
const rollbackTarget = `${rollbackDir}/rollback-target.txt`;
const rollbackBackup = `${rollbackDir}/rollback-target.backup.txt`;
writeFileSync(rollbackTarget, 'original pipeline rollback content\n', 'utf8');
copyFileSync(rollbackTarget, rollbackBackup);
writeFileSync(`${rollbackDir}/rollback-before.txt`, readFileSync(rollbackTarget, 'utf8'), 'utf8');
writeFileSync(rollbackTarget, 'changed content that must be undone\n', 'utf8');
writeFileSync(`${rollbackDir}/rollback-changed.txt`, readFileSync(rollbackTarget, 'utf8'), 'utf8');
const record = execFileSync(`/rollback-manager.sh`, [
'record',
'restore rollback target evidence file',
`cp ${rollbackBackup} ${rollbackTarget}`,
const record = execFileSync(`${HARNESS_BASH}/rollback-manager.sh`, [
'checkpoint',
rollbackTarget,
], { cwd: root, encoding: 'utf8' });
writeFileSync(`${rollbackDir}/rollback-record.stdout`, record, 'utf8');
const tx = record.match(/transaction_id=([^\s]+)/)?.[1];
if (!tx) {
throw new Error('rollback transaction id not found');
}
const execute = execFileSync(`/rollback-manager.sh`, ['execute', tx], {
writeFileSync(rollbackTarget, 'changed content that must be undone\n', 'utf8');
writeFileSync(`${rollbackDir}/rollback-changed.txt`, readFileSync(rollbackTarget, 'utf8'), 'utf8');
const execute = execFileSync(`${HARNESS_BASH}/rollback-manager.sh`, ['execute', tx], {
cwd: root,
encoding: 'utf8',
});