// ============================================================================ // Step 06: Critique — Industrial Critic Quality Gate (Claude Opus) // ============================================================================ import { llmJsonRequest } from "../llm-client.js"; import type { EstimationState, StepResult, PipelineConfig } from "../types.js"; import { DEFAULT_MODELS } from "../types.js"; export async function executeCritique( state: EstimationState, config: PipelineConfig, ): Promise { const models = { ...DEFAULT_MODELS, ...config.modelsOverride }; const startTime = Date.now(); const currentState = { facts: state.concept?.auditedFacts, briefingSummary: state.concept?.strategy?.briefingSummary, designVision: state.concept?.strategy?.designVision, sitemap: state.concept?.architecture?.sitemap, positionDescriptions: state.positionDescriptions, siteProfile: state.concept?.siteProfile ? { existingFeatures: state.concept.siteProfile.existingFeatures, services: state.concept.siteProfile.services, externalDomains: state.concept.siteProfile.externalDomains, navigation: state.concept.siteProfile.navigation, totalPages: state.concept.siteProfile.totalPages, } : null, }; const systemPrompt = ` You are the "Industrial Critic" — the final quality gate for a professional B2B estimation. Your job is to find EVERY error, hallucination, and inconsistency before this goes to the client. ### CRITICAL ERROR CHECKLIST (FAIL IF ANY FOUND): 1. HALLUCINATION: FAIL if names, software versions, or details not in the BRIEFING are used. - "Sie", "Ansprechpartner" for personName when an actual name exists = FAIL. 2. LOGIC CONFLICT: FAIL if isRelaunch=true but text claims "no website exists". 3. IMPLEMENTATION FLUFF: FAIL if "React", "Next.js", "TypeScript", "Tailwind" are mentioned. 4. GENERICISM: FAIL if text could apply to ANY company. Must use specific industry terms. 5. NAMEN-VERBOT: FAIL if personal names in briefingSummary or designVision. 6. CMS-LEAKAGE: FAIL if cmsSetup=false but descriptions mention "CMS", "Inhaltsverwaltung". 7. AGB BAN: FAIL if "AGB" or "Geschäftsbedingungen" appear anywhere. 8. LENGTH: briefingSummary ~6 sentences, designVision ~4 sentences. Shorten if too wordy. 9. LEGAL SAFETY: FAIL if "rechtssicher" is used. Use "Standard-konform" instead. 10. BULLSHIT DETECTOR: FAIL if jargon like "SEO-Standards zur Fachkräftesicherung", "B2B-Nutzerströme", "Digitale Konvergenzstrategie" or similar meaningless buzzwords are used. The text must make SENSE to a construction industry CEO. 11. PAGE STRUCTURE: FAIL if the sitemap contains: - Videos as pages (Messefilm, Imagefilm) - Internal functions as pages (Verwaltung) - Entities with their own domains as sub-pages (check externalDomains!) 12. SORGLOS-BETRIEB: FAIL if not mentioned in the summary or position descriptions. 13. TONE: FAIL if "wir/unser" or "Ich/Mein" in position descriptions. FAIL if marketing fluff. 14. MULTILANG: FAIL if Mehrsprachigkeit is described as an API or Schnittstelle. 15. INITIAL-PFLEGE: FAIL if described in terms of "Seiten" instead of "Datensätze". ### MISSION: Return corrected fields ONLY for fields with issues. If everything passes, return empty corrections. ### OUTPUT FORMAT: { "passed": boolean, "errors": [{ "field": string, "issue": string, "severity": "critical" | "warning" }], "corrections": { "briefingSummary"?: string, "designVision"?: string, "positionDescriptions"?: Record, "sitemap"?: array } } `; try { const { data, usage } = await llmJsonRequest({ model: models.opus, systemPrompt, userPrompt: `BRIEFING_TRUTH:\n${state.concept?.briefing}\n\nCURRENT_STATE:\n${JSON.stringify(currentState, null, 2)}`, apiKey: config.openrouterKey, }); return { success: true, data, usage: { step: "06-critique", model: models.opus, promptTokens: usage.promptTokens, completionTokens: usage.completionTokens, cost: usage.cost, durationMs: Date.now() - startTime, }, }; } catch (err) { return { success: false, error: `Critique step failed: ${(err as Error).message}` }; } }