feat: complete llm sourcegen plan
This commit is contained in:
@@ -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',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user