diff --git a/network-poc/static/index.html b/network-poc/static/index.html
index 7b1e2c7..c7bcbbf 100644
--- a/network-poc/static/index.html
+++ b/network-poc/static/index.html
@@ -2499,17 +2499,70 @@ IMPORTANT: Keep the code SHORT. Max ~50 lines. No comments, no docstrings. Write
.map(([name, code]) => `--- ${name} ---\n${code}`)
.join('\n\n');
- termLog(`\n[${fileList.length + 2}] Testaaja — arviointi`);
+ // Staattinen analyysi ennen LLM-arviointia
+ termLog(`\n[${fileList.length + 2}] Testaaja — staattinen analyysi + arviointi`);
pipelineStep('tester', 'Review', 'active', `${Object.keys(generatedFiles).length} tiedostoa`);
- const reviewPrompt = `Review this project. List bugs or issues. Be brief.
-If the code is correct, say "LGTM".
+
+ // Yksinkertainen staattinen tarkistus selaimessa
+ const staticIssues = [];
+ for (const [name, code] of Object.entries(generatedFiles)) {
+ if (!name.endsWith('.py')) continue;
+ const lines = (code || '').split('\n');
+ // Tarkista importit
+ const imports = lines.filter(l => l.match(/^(from|import)\s/));
+ const usedNames = code.replace(/^(from|import)\s.*/gm, '');
+ for (const imp of imports) {
+ const match = imp.match(/import\s+(\w+)|from\s+\S+\s+import\s+(.+)/);
+ if (match) {
+ const names = (match[1] || match[2]).split(',').map(n => n.trim().split(' as ').pop().trim());
+ for (const n of names) {
+ if (n && !usedNames.includes(n) && n !== '*') {
+ staticIssues.push(`${name}: käyttämätön import '${n}'`);
+ }
+ }
+ }
+ }
+ // Tarkista puuttuvat importit
+ if (code.includes('FastAPI') && !imports.some(i => i.includes('FastAPI'))) {
+ staticIssues.push(`${name}: käyttää FastAPI mutta ei importtaa sitä`);
+ }
+ if (code.includes('Session') && !imports.some(i => i.includes('Session'))) {
+ staticIssues.push(`${name}: käyttää Session mutta ei importtaa sitä`);
+ }
+ // Tarkista tyhjät funktiot
+ const emptyFuncs = code.match(/def \w+\([^)]*\):\s*\n\s*(pass|\.\.\.)/g);
+ if (emptyFuncs) staticIssues.push(`${name}: ${emptyFuncs.length} tyhjää funktiota`);
+ }
+ if (staticIssues.length > 0) {
+ termLog(` Staattinen analyysi (${staticIssues.length} huomautusta):`);
+ for (const issue of staticIssues) {
+ termLog(` ⚠ ${esc(issue)}`);
+ }
+ } else {
+ termLog(' Staattinen analyysi: ei huomautuksia');
+ }
+ const reviewPrompt = `Review this project code. Check EVERY item and report result:
+
+1. Imports: ✓/✗ — are all imports valid and available?
+2. Database: ✓/✗ — is the DB setup correct (engine, session, models)?
+3. Endpoints: ✓/✗ — do all routes have correct parameters and return types?
+4. Error handling: ✓/✗ — are edge cases handled (404, validation)?
+5. Security: ✓/✗ — any SQL injection, missing auth, or data exposure?
+
+EXAMPLE output:
+1. Imports: ✓ — all imports are valid
+2. Database: ✗ — missing Base.metadata.create_all(engine) call
+3. Endpoints: ✓ — GET/POST/DELETE routes are correct
+4. Error handling: ✗ — no 404 when todo not found
+5. Security: ✓ — using ORM, no raw SQL
${allCode}`;
- const review = await kpnRun(agentPrompts.tester.model, reviewPrompt, false, 200);
+ const review = await kpnRun(agentPrompts.tester.model, reviewPrompt, false, 300);
pipelineStep('tester', 'Review', 'done', `${Object.keys(generatedFiles).length} tiedostoa`, review);
// Vaihe 4: Korjausluuppi — jos testaaja löysi ongelmia
- if (review && !review.toLowerCase().includes('lgtm') && !review.toLowerCase().includes('looks good')) {
+ const hasIssues = review && (review.includes('✗') || staticIssues.length > 0);
+ if (hasIssues) {
termLog(`\n[${fileList.length + 3}] Koodari — korjaukset`);
pipelineStep('coder', 'Korjaukset', 'active', review);
const fixPrompt = `Fix the issues found in the review.