Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Failing after 35s
Monorepo Pipeline / 🧪 Test (push) Failing after 35s
Monorepo Pipeline / 🏗️ Build (push) Failing after 12s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Image Processor (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
100 lines
4.4 KiB
TypeScript
100 lines
4.4 KiB
TypeScript
// ============================================================================
|
|
// 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<StepResult> {
|
|
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<string, string>,
|
|
"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}` };
|
|
}
|
|
}
|