diff --git a/network-poc/frontend/src/pages/index.astro b/network-poc/frontend/src/pages/index.astro
index 7e1671a..6fdc109 100644
--- a/network-poc/frontend/src/pages/index.astro
+++ b/network-poc/frontend/src/pages/index.astro
@@ -1418,20 +1418,71 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
let stepN = fileOrder.length + 2;
- // === Vaihe 4: Mekaaninen QA-validointi ===
+ // === Vaihe 4: Mekaaninen QA-validointi + korjaussilmukka ===
const qaAgent = agents.qa || Object.values(agents)[4];
- termLog(`\n[${stepN}] ${esc(qaAgent.name)} — validointi`);
- highlightAgent('qa');
- explainStep('Validointi', `${qaAgent.name} ajaa mekaanisen koodivalidoinnin.`);
+ const MAX_FIX_ROUNDS = 2;
+ let mechIssues = validateProjectCode(files);
+ let fixRound = 0;
- const mechIssues = validateProjectCode(files);
- if (mechIssues.length > 0) {
- termLog(` ⚠ ${mechIssues.length} ongelmaa (template-bugeja — korjattava):`);
+ while (mechIssues.length > 0 && fixRound < MAX_FIX_ROUNDS) {
+ fixRound++;
+ termLog(`\n[${stepN}] ${esc(qaAgent.name)} — validointi (kierros ${fixRound})`);
+ highlightAgent('qa');
+ explainStep('Validointi', `${qaAgent.name} löysi ${mechIssues.length} ongelmaa — delegoidaan korjattavaksi.`);
+ termLog(` ⚠ ${mechIssues.length} ongelmaa:`);
for (const issue of mechIssues) termLog(` ${esc(issue)}`);
- } else {
- termLog(` ✓ Kaikki tiedostot validoitu — 0 ongelmaa`);
+ promptLog.push({ step: promptLog.length, agentKey: 'qa', agentName: qaAgent.name, model: 'mekaaninen', label: `validointi #${fixRound}`, systemPrompt: '(mekaaninen validointi)', userPrompt: 'validateProjectCode(files)', response: mechIssues.join('\n') });
+ stepN++;
+
+ // Ryhmitellään ongelmat tiedostoittain
+ const issuesByFile = {};
+ for (const issue of mechIssues) {
+ const m = issue.match(/^ISSUE:\s*(\S+?):/);
+ const fname = m ? m[1] : 'unknown';
+ if (!issuesByFile[fname]) issuesByFile[fname] = [];
+ issuesByFile[fname].push(issue);
+ }
+
+ // Delegoidaan korjaukset Coder-agentille tiedosto kerrallaan
+ for (const [fname, fIssues] of Object.entries(issuesByFile)) {
+ if (!files[fname]) continue;
+ const fixAgent = agents[agentMap[fname]] || cdr;
+ termLog(`\n[${stepN}] ${esc(fixAgent.name)} — korjaa ${esc(fname)} (${fIssues.length} ongelmaa)`);
+ highlightAgent(agentMap[fname] || 'coder');
+ explainStep(`Korjaus: ${fname}`, `${fixAgent.name} korjaa validoinnin löytämät ongelmat.`);
+
+ const fixPrompt = `Fix the following issues in this Python file. Return ONLY the complete corrected file, no explanations.\n\nISSUES:\n${fIssues.join('\n')}\n\nCURRENT FILE (${fname}):\n\`\`\`python\n${files[fname]}\`\`\``;
+ const fixResult = await kpnRun(fixAgent.model, fixPrompt, false, { ...fixAgent, prompt: 'You are a Python code fixer. Return ONLY the corrected Python file. No markdown fences, no explanations — just valid Python code.' });
+
+ if (fixResult) {
+ // Poistetaan markdown-koodiblokit jos LLM palauttaa ne
+ let cleaned = fixResult.replace(/^```(?:python)?\s*\n?/m, '').replace(/\n?```\s*$/m, '').trim() + '\n';
+ files[fname] = cleaned;
+ termLog(` ✓ ${esc(fname)} korjattu`);
+ } else {
+ termLog(` ✗ ${esc(fname)} korjaus epäonnistui — pidetään alkuperäinen`);
+ }
+ promptLog.push({ step: promptLog.length, agentKey: agentMap[fname] || 'coder', agentName: fixAgent.name, model: fixAgent.model, label: `korjaus: ${fname}`, systemPrompt: '(code fixer)', userPrompt: fixPrompt, response: fixResult || '(epäonnistui)' });
+ stepN++;
+ }
+
+ // Validoidaan uudelleen
+ mechIssues = validateProjectCode(files);
}
- promptLog.push({ step: promptLog.length, agentKey: 'qa', agentName: qaAgent.name, model: 'mekaaninen', label: 'validointi', systemPrompt: '(mekaaninen validointi — ei LLM:ää)', userPrompt: 'validateProjectCode(files)', response: mechIssues.length === 0 ? 'OK — 0 issues' : mechIssues.join('\n') });
+
+ // Lopullinen validointitulos
+ termLog(`\n[${stepN}] ${esc(qaAgent.name)} — lopullinen validointi`);
+ highlightAgent('qa');
+ if (mechIssues.length > 0) {
+ explainStep('Validointi', `${mechIssues.length} ongelmaa jäi korjaamatta ${MAX_FIX_ROUNDS} kierroksen jälkeen.`);
+ termLog(` ✗ ${mechIssues.length} ongelmaa jäljellä ${fixRound} korjauskierroksen jälkeen:`);
+ for (const issue of mechIssues) termLog(` ${esc(issue)}`);
+ } else {
+ const msg = fixRound > 0 ? `✓ Kaikki ongelmat korjattu (${fixRound} kierrosta)` : '✓ Kaikki tiedostot validoitu — 0 ongelmaa';
+ explainStep('Validointi', msg);
+ termLog(` ${msg}`);
+ }
+ promptLog.push({ step: promptLog.length, agentKey: 'qa', agentName: qaAgent.name, model: 'mekaaninen', label: 'lopullinen validointi', systemPrompt: '(mekaaninen validointi)', userPrompt: 'validateProjectCode(files)', response: mechIssues.length === 0 ? `OK — korjattu ${fixRound} kierroksessa` : mechIssues.join('\n') });
stepN++;
// Tarkkailija: yhteenveto + raportti + arvosana