From 3913372f1050ab2e6993b7f268b019fc54147465 Mon Sep 17 00:00:00 2001 From: Rene Fichtmueller Date: Sun, 5 Apr 2026 23:11:16 +0200 Subject: [PATCH] feat(blog): Title Contract + Technical Sanity Check + Self-Heal + angle-aware LinkedIn generator Pipeline now has 21 steps: - STEP0: Title Contract binds LLM to headline promise - STEP19: Technical Sanity Check (optical engineering accuracy) - STEP20: Self-Heal (auto-fix technical errors preserving tone) - STEP21: Title Contract Verification (final gate check) - LinkedIn generator is now angle-aware (no more default Physical Layer hook) --- packages/api/src/llm/fo-blog-pipeline.ts | 199 +++++++++++++++++++++-- packages/api/src/routes/blog.ts | 93 ++++++++++- 2 files changed, 267 insertions(+), 25 deletions(-) diff --git a/packages/api/src/llm/fo-blog-pipeline.ts b/packages/api/src/llm/fo-blog-pipeline.ts index baf53c8..31f6844 100644 --- a/packages/api/src/llm/fo-blog-pipeline.ts +++ b/packages/api/src/llm/fo-blog-pipeline.ts @@ -299,6 +299,31 @@ CONTENT MODULES (use 2-3 per article): - Vendor bullshit vs reality - When NOT to use this technology`; +// ═══════════════════════════════════════════════════════ +// STEP 0: TITLE CONTRACT — binds the LLM to the headline promise +// ═══════════════════════════════════════════════════════ + +export const STEP0_TITLE_CONTRACT = `You are a senior technical editor. Your job is to analyze a blog title and create a binding contract that the article MUST fulfill. + +Title: {{TITLE}} + +Analyze the title and return EXACTLY this structure: + +TOPIC DOMAIN: (what subject area — e.g., "pricing comparison", "standards maturity", "migration process") +PROMISE TYPE: (what the title promises — e.g., "COST ANALYSIS", "STATUS REPORT", "DECISION FRAMEWORK", "STEP-BY-STEP GUIDE", "MARKET TIMING") +REQUIRED CONTENT: (what MUST appear in the article to fulfill the title — be specific, 3-5 bullet points) +FORBIDDEN CONTENT: (what would VIOLATE the title promise — be specific) +READER TAKEAWAY: (what the reader should know/do after reading — one sentence) +ANGLE TYPE: (A=Economic, B=Decision, C=Market, D=Operational, E=Political, F=Migration) + +CRITICAL RULES: +- If the title says "Cost Difference" → the article MUST be about costs, pricing, TCO. NOT about physical layer failures. +- If the title says "Production-Ready" → the article MUST be a status report on maturity. NOT about what breaks. +- If the title says "Migration Guide" → the article MUST be step-by-step. NOT about failure scenarios. +- If the title says "Comparison" → the article MUST compare specific options. NOT tell a generic story. + +The contract you produce will be injected into every subsequent pipeline step to prevent drift.`; + // ═══════════════════════════════════════════════════════ // STEP 1: TOPIC EXPANSION // ═══════════════════════════════════════════════════════ @@ -1045,6 +1070,135 @@ Return ONLY a JSON object: Article: {{ARTICLE}}`; +// ═══════════════════════════════════════════════════════ +// STEP 11: TECHNICAL SANITY CHECK — expert-level accuracy verification +// ═══════════════════════════════════════════════════════ + +export const STEP_TECHNICAL_SANITY = `You are a senior optical network engineer reviewing a blog post for technical correctness. + +Your job is NOT to rewrite the text. Your job is to find technical inaccuracies, imprecise wording, or misleading statements. + +Be strict. Assume the reader is an expert. + +CHECK THESE AREAS: + +1. MEASUREMENT vs OBSERVATION + - Does the text confuse inspection (visual, scope) with measurement (OPM, OLTS, DOM)? + - "inspection scope measures loss" → WRONG. A scope shows contamination/defects. An OPM/OLTS measures loss. + - "inspection scope reveals insertion loss" → WRONG. It reveals contamination that CAUSES insertion loss. + +2. FIBER & OPTICS ACCURACY + - MMF vs SMF behavior — correct distinctions? + - SR4 vs DR4 vs FR4 — correct fiber counts, connectors, wavelengths? + - DR4 = MPO-12 (8 fibers, parallel, 1310nm). FR4 = LC duplex (CWDM4, 2km). NEVER mix these. + - Connector types correct? (MPO vs LC vs SC) + - Reach claims accurate? + +3. OPTICAL BUDGET LOGIC + - TX_min / RX_sensitivity usage correct? + - Link budget calculations make sense? + - Margin discussion realistic? (DR4 ≈ 4.8 dB budget, not more) + - DR4 fiber attenuation = 0.35 dB/km at 1310nm (NOT 1550nm which is 0.22 dB/km) + +4. FAILURE BEHAVIOR REALISM + - CRC errors vs hard link down — correct description? + - Intermittent vs permanent failure — accurate? + - FEC behavior correct? + - "creeping errors" pattern realistic? + +5. TERMINOLOGY PRECISION + - "contacts" for fiber → WRONG. Use: connector end-faces, ferrules, mating points + - "signal loss" → too generic. Use: insertion loss, return loss, or attenuation + - "worn contacts" → WRONG for fiber. Use: contamination, alignment degradation, connector wear + - "patch panels worn contacts" → WRONG. Use: aging connectors and accumulated contamination + +6. CAUSE vs SYMPTOM + - Does the text correctly separate root cause from symptom? + - Does it avoid blaming optics when the issue is physical layer? + +7. POWER SPECS + - 400G ≈ 10-15W per PORT (not per chassis) + - 800G ≈ 15-25W per PORT + - "1kW per port" → HARD FAIL + +Return ONLY this JSON: +{ + "technical_score": <1-10>, + "critical_issues": [{"issue": "...", "why_wrong": "...", "fix": "..."}], + "precision_fixes": [{"original": "...", "problem": "...", "better": "..."}], + "safe_to_publish": true/false +} + +Article: +{{ARTICLE}}`; + +// ═══════════════════════════════════════════════════════ +// STEP 12: SELF-HEAL — fix technical errors preserving tone +// ═══════════════════════════════════════════════════════ + +export const STEP_SELF_HEAL = `You are a senior optical network engineer and technical editor. + +You have the original article and a technical sanity check report. Your task is to REPAIR the text. + +RULES: +- Fix all critical technical issues identified in the report +- Sharpen imprecise wording (scope vs OPM, contacts vs end-faces, etc.) +- Preserve the original TONE and FLOW — do not make it sound like a whitepaper +- Do NOT add unnecessary content or over-explain +- Do NOT rewrite sentences that are already correct +- Only change what needs fixing — minimal, surgical edits +- Keep it readable for engineers, not academics + +SPECIFIC FIXES TO APPLY: +- "inspection scope reveals/measures insertion loss" → "inspection scope reveals contamination that impacts insertion loss" +- "contacts" (when discussing fiber) → "connector end-faces" or "mating points" +- "MMF is stricter/less tolerant" → be specific: "DR4 links have tighter loss budgets than SR4 deployments" +- "worn contacts on patch panels" → "accumulated contamination on connector interfaces" +- Verify all dBm values against reference: DR4 budget ≈ 4.8 dB, attenuation at 1310nm = 0.35 dB/km + +Return ONLY the repaired article text — no commentary, no changelog. + +TECHNICAL REPORT: +{{SANITY_REPORT}} + +ARTICLE: +{{ARTICLE}}`; + +// ═══════════════════════════════════════════════════════ +// STEP 13: TITLE CONTRACT VERIFICATION — final check +// ═══════════════════════════════════════════════════════ + +export const STEP_TITLE_CONTRACT_CHECK = `You are a senior editor performing a FINAL quality gate check. + +Compare this article against its Title Contract (the promise the headline makes). + +TITLE CONTRACT: +{{TITLE_CONTRACT}} + +ARTICLE: +{{ARTICLE}} + +CHECK: +1. Does the article FULFILL the title's promise? (yes/no with explanation) +2. Does the article DRIFT into a different topic? (identify specific paragraphs) +3. Would a reader who clicked this title get what they expected? (yes/no) +4. Is the ending on-topic or does it drift into generic "validate your setup" advice? + +If the article VIOLATES the title contract, return: +{ + "contract_fulfilled": false, + "violations": ["specific violation 1", "specific violation 2"], + "drift_paragraphs": ["paragraph text that drifts"], + "verdict": "REJECT — article does not match title promise" +} + +If the article FULFILLS the contract: +{ + "contract_fulfilled": true, + "violations": [], + "verdict": "PASS — article delivers on title promise" +}`; + // ═══════════════════════════════════════════════════════ // NEW BLOG TYPES (v0.2.0) // ═══════════════════════════════════════════════════════ @@ -2155,34 +2309,43 @@ ANTI-AI FILTER — make it human: → Symmetric structure → make it slightly uneven ════════════════════════════════════════════════════════ -GOLD REFERENCE (engagement-boosted — this is the target): +ANGLE-AWARE HOOK SELECTION — CRITICAL +════════════════════════════════════════════════════════ -Everything looks fine. Until it doesn't. +The LinkedIn post MUST match the article's angle. Do NOT default to the Physical Layer story. -You bring up a new batch of 400G links. Lab tests were clean. Traffic is stable. +TITLE CONTRACT FOR THIS ARTICLE: +{{TITLE_CONTRACT}} -Then error counters start creeping up. +Match the hook and behavior beats to the article's actual angle: -Not enough to fail. Just enough to waste your time. +IF ECONOMIC/TCO article: + HOOK: "A $350 optic just turned into an $18,000 problem." or "Your 400G budget looks fine. Until you add the numbers nobody showed you." + BEATS: cost surprises, TCO reality, budget impact + CLOSE: cost-based insight, not "clean your connectors" -So optics get blamed. +IF DECISION/COMPARISON article: + HOOK: "Three quotes on your desk. The cheapest one isn't always the most expensive." or "OEM vs compatible. Everyone has an opinion. Here's what the data says." + BEATS: decision criteria, what specs don't tell you + CLOSE: decision framework, not failure story -Swapped. Replaced. Escalated. +IF MARKET/TIMING article: + HOOK: "800G is shipping. Sort of." or "Half the industry says buy now. The other half says wait." + BEATS: what's real vs hype, timing signals + CLOSE: timing advice, not generic -Nothing changes. +IF OPERATIONAL/PROCESS article: + HOOK: "We deployed 200 links in 3 weeks. Zero issues. Here's the boring reason why." + BEATS: process steps, what was done differently + CLOSE: process insight, not failure -Config looks correct. Hardware looks fine. +IF MIGRATION article: + HOOK: "Month 1: everything on schedule. Month 6: nothing was." + BEATS: planning vs reality, specific surprises + CLOSE: what to plan for, based on real experience -That's usually the point where people start looking in the wrong place. +DO NOT USE THE PHYSICAL LAYER HOOK ("Everything looks fine. Until it doesn't.") — it has been used already. Create a FRESH hook that matches THIS article's specific angle. -Same optics. Same setup. Different result. - -At 100G, you get away with it. -At 400G, you don't. - -Full breakdown in the blog — link in first comment. - -#OpticalNetworking #DataCenter #NetworkEngineering #Flexoptix ════════════════════════════════════════════════════════ Return ONLY the post text. No commentary. No "Here is the post:". Start directly with the hook. diff --git a/packages/api/src/routes/blog.ts b/packages/api/src/routes/blog.ts index 2999044..a83d2e0 100644 --- a/packages/api/src/routes/blog.ts +++ b/packages/api/src/routes/blog.ts @@ -1016,11 +1016,15 @@ async function runLlmPipeline( buildFeedbackContext, buildSLLContext, withCalibration, + STEP0_TITLE_CONTRACT, + STEP_TECHNICAL_SANITY, + STEP_SELF_HEAL, + STEP_TITLE_CONTRACT_CHECK, } = await import("../llm/fo-blog-pipeline"); const LLM_OPTS = { temperature: 0.7, maxTokens: 8192, timeoutMs: 480000 }; const LLM_REFINE = { temperature: 0.4, maxTokens: 6144, timeoutMs: 480000 }; - const TOTAL_STEPS = 18; // 16-step pipeline + APM + Viral Signal + LinkedIn + const TOTAL_STEPS = 21; // 17-step pipeline + title contract + technical sanity + self-heal + contract check let stepsCompleted = 0; try { @@ -1120,13 +1124,23 @@ async function runLlmPipeline( } } catch { /* fine if no articles yet */ } + // ═══ STEP 0: Title Contract — bind LLM to headline promise ═══ + console.log(" Step 0: Title Contract (binding headline to content)..."); + setProgress(draftId, 1, "Step 0: Title Contract"); + const step0 = await generate(systemPrompt, + STEP0_TITLE_CONTRACT.replace("{{TITLE}}", title), + { ...LLM_REFINE, maxTokens: 2048 } + ); + const titleContract = step0.text; + console.log(` Title Contract: ${titleContract.split("\n").slice(0, 3).join(" | ").slice(0, 120)}...`); + // ═══ STEP 1: Topic Expansion ═══ - console.log(" Step 1/10: Topic Expansion..."); - setProgress(draftId, 1, "Step 1/10: Topic Expansion"); + console.log(" Step 1: Topic Expansion..."); + setProgress(draftId, 2, "Step 1: Topic Expansion"); const step1 = await generate(systemPrompt, STEP1_TOPIC_EXPANSION .replace("{{TOPIC}}", title) - .replace("{{EXISTING_ANGLES}}", existingAnglesContext), + .replace("{{EXISTING_ANGLES}}", existingAnglesContext + "\n\nTITLE CONTRACT (the article MUST fulfill this):\n" + titleContract), LLM_OPTS ); stepsCompleted = 1; @@ -1136,7 +1150,7 @@ async function runLlmPipeline( setProgress(draftId, 2, "Step 2/10: Angle Selection"); const step2 = await generate(systemPrompt, STEP2_ANGLE_SELECTION - .replace("{{FORBIDDEN_ANGLES}}", forbiddenAnglesContext) + .replace("{{FORBIDDEN_ANGLES}}", forbiddenAnglesContext + "\nTITLE CONTRACT:\n" + titleContract) .replace("{{SCENARIOS}}", step1.text), LLM_REFINE ); @@ -1160,7 +1174,7 @@ async function runLlmPipeline( const step4 = await generate(systemPrompt, STEP4_MASTER_DRAFT .replace("{{OUTLINE}}", step3.text) - .replace("{{CONTEXT_DATA}}", contextData), + .replace("{{CONTEXT_DATA}}", contextData + "\n\nTITLE CONTRACT (you MUST fulfill this — the article must match the headline promise):\n" + titleContract), { ...LLM_OPTS, maxTokens: 8192 } ); stepsCompleted = 4; @@ -1342,7 +1356,9 @@ async function runLlmPipeline( // Fallback: dedicated LinkedIn post generator try { const stepLinkedIn = await generate(systemPrompt, - STEP_LINKEDIN_POST.replace("{{ARTICLE}}", viralArticle), + STEP_LINKEDIN_POST + .replace("{{TITLE_CONTRACT}}", titleContract) + .replace("{{ARTICLE}}", viralArticle), { temperature: 0.6, maxTokens: 1024, timeoutMs: 120000 } ); linkedinPost = stepLinkedIn.text.trim(); @@ -1361,6 +1377,69 @@ async function runLlmPipeline( } stepsCompleted = 18; + // ═══ STEP 19: Technical Sanity Check ═══ + console.log(" Step 19/21: Technical Sanity Check..."); + setProgress(draftId, 19, "Step 19/21: Technical Sanity Check"); + let sanityReport = ""; + try { + const stepSanity = await generate(systemPrompt, + STEP_TECHNICAL_SANITY.replace("{{ARTICLE}}", viralArticle), + { temperature: 0.2, maxTokens: 4096, timeoutMs: 240000 } + ); + sanityReport = stepSanity.text.trim(); + console.log(` Sanity check: ${sanityReport.includes('"safe_to_publish": false') ? "⚠ ISSUES FOUND" : "✓ safe"}`); + } catch { + console.log(" Technical sanity check skipped (error)"); + } + stepsCompleted = 19; + + // ═══ STEP 20: Self-Heal (fix technical errors) ═══ + if (sanityReport && (sanityReport.includes('"safe_to_publish": false') || sanityReport.includes('"critical_issues"'))) { + console.log(" Step 20/21: Self-Heal (fixing technical errors)..."); + setProgress(draftId, 20, "Step 20/21: Self-Heal (technical fixes)"); + try { + const stepHeal = await generate(systemPrompt, + STEP_SELF_HEAL + .replace("{{SANITY_REPORT}}", sanityReport) + .replace("{{ARTICLE}}", viralArticle), + LLM_REFINE + ); + const healedWords = stepHeal.text.split(/\s+/).length; + if (healedWords > 400) { + viralArticle = stepHeal.text; + console.log(` Self-healed: ${healedWords} words`); + } else { + console.log(" Self-heal output too short — keeping original"); + } + } catch { + console.log(" Self-heal skipped (error)"); + } + } else { + console.log(" Step 20/21: Self-Heal skipped (no critical issues)"); + } + stepsCompleted = 20; + + // ═══ STEP 21: Title Contract Verification ═══ + console.log(" Step 21/21: Title Contract Verification..."); + setProgress(draftId, 21, "Step 21/21: Title Contract Check"); + try { + const stepContract = await generate(systemPrompt, + STEP_TITLE_CONTRACT_CHECK + .replace("{{TITLE_CONTRACT}}", titleContract) + .replace("{{ARTICLE}}", viralArticle), + { temperature: 0.2, maxTokens: 2048, timeoutMs: 120000 } + ); + const contractResult = stepContract.text.trim(); + if (contractResult.includes('"contract_fulfilled": false') || contractResult.includes('"REJECT')) { + console.log(" ⚠ TITLE CONTRACT VIOLATION — article may not match headline"); + } else { + console.log(" ✓ Title contract fulfilled"); + } + } catch { + console.log(" Title contract check skipped (error)"); + } + stepsCompleted = 21; + // Extract article from Viral Signal output (or APM fallback) // Fall back to step9.text if output looks too short or empty let finalArticleText = viralArticle.trim().length > 200 ? viralArticle : step9.text;