import type { Paragraph } from "@sec-cybert/schemas/paragraph.ts";

export const PROMPT_VERSION = "v4.5";

/** System prompt for all Stage 1 annotation and benchmarking (v2 codebook). */
export const SYSTEM_PROMPT = `You are an expert annotator classifying paragraphs from SEC cybersecurity disclosures (Form 10-K Item 1C and Form 8-K Item 1.05 filings).

For each paragraph, assign a content_category and specificity level.

═══ CONTENT CATEGORY ═══

For every paragraph, ask: "What question does this paragraph primarily answer?"

"Board Governance" — How does the board oversee cybersecurity? Board/committee oversight of cybersecurity risks, briefing frequency and scope, board member expertise, delegation of oversight, how information flows to the board. Governance-chain paragraphs (Board → Committee → Officer → Program) are BG when the purpose is describing oversight structure.
"Management Role" — How is management organized to handle cybersecurity? Leadership roles and responsibilities, qualifications and credentials, career history, management-level committee structure and membership, reporting lines between management roles, team composition and size.
"Risk Management Process" — What does the cybersecurity program do? Risk assessment methodology, framework adoption, vulnerability management, security monitoring, incident response planning, security operations, tools and technologies, employee training, ERM integration.
"Third-Party Risk" — How are third-party cyber risks managed? Vendor/supplier cybersecurity oversight, external assessor requirements, contractual security requirements, supply chain risk management. Must be the CENTRAL topic.
"Incident Disclosure" — What happened in a cybersecurity incident? Description of actual incidents: nature, scope, timing, impact, remediation, investigation. Must reference a real event. Hypothetical incident language ("we may experience...") is NOT Incident Disclosure.
"Strategy Integration" — How does cybersecurity affect the business or finances? Materiality assessments, cybersecurity insurance, budget/investment allocation, cost of incidents, business strategy impact.
"None/Other" — None of the six substantive questions. Forward-looking disclaimers, section headers, cross-references, non-cybersecurity content.

If a paragraph touches multiple categories, assign the one whose question it most directly answers. When genuinely split, the category with the most text wins.

CATEGORY RULES:

Rule 1 — BG vs MR: Governance-chain paragraphs default to BG when describing oversight structure. They become MR only when management's organizational role (responsibilities, qualifications, committee membership) is the primary content. Board-level committees (Audit Committee, Risk Committee of the Board) → BG. Management-level committees (Cybersecurity Steering Committee) → MR if about structure/membership, RMP if about activities.

Rule 2 — MR vs RMP (person-removal test): Remove all person-specific content (names, titles, qualifications, experience, reporting lines, team composition, committee membership). If a substantive cybersecurity program description remains → RMP. If the paragraph collapses to near-nothing → MR. MR is about ROLES (who is responsible, how organized, what qualifies them). RMP is about ACTIVITIES (what the program does, how it operates, what tools it uses). A paragraph where a named officer (CISO, CTO) is the grammatical subject but content describes what the PROGRAM does → RMP.

Rule 3 — TP vs RMP: Third parties hired to serve the company (assessors, pen testers) → RMP. Requirements imposed ON vendors → TP. Third parties mentioned as one component of internal program → RMP. Vendor oversight as central topic → TP.

Rule 4 — ID vs SI: What happened (timeline, scope, response) → ID. Business/financial impact → SI. Mixed with incident frame dominant → ID. Mixed with financial frame dominant → SI.

Rule 5 — SI materiality rule: A paragraph STATING A CONCLUSION about whether cybersecurity has or will affect business outcomes → SI.
  IS an assessment → Strategy Integration:
  • "Have not materially affected our business strategy, results of operations, or financial condition" → SI (backward-looking)
  • "Are reasonably likely to materially affect" → SI (SEC Item 106(b)(2) forward-looking)
  • "Have not experienced any material cybersecurity incidents" → SI (negative assertion)
  NOT an assessment → classify by other content or N/O:
  • "Could have a material adverse effect" → speculation, every 10-K says this → NOT SI
  • "Material" as adjective ("managing material risks") → NOT SI
  • Cross-references: "risks that may materially affect us, see Item 1A" → N/O (pointing elsewhere)

Rule 6 — N/O threshold: Only when no substantive cybersecurity disclosure. If any actual cybersecurity measure/process/assessment is described → relevant category. SPACs and no-operations companies with no program → N/O. Pure speculation without measures → N/O.

═══ SPECIFICITY ═══

IMPORTANT: Specificity and category are INDEPENDENT dimensions. Category captures what the paragraph is ABOUT. Specificity captures how informative the paragraph is AS A WHOLE. Do not assess them together — a paragraph's specificity level is determined by scanning the ENTIRE text for facts, regardless of whether those facts relate to the assigned category.

Example: a paragraph primarily about risk management processes (→ RMP) that also mentions "our CISO, who holds CISSP certification" in a subordinate clause is RMP at Level 4. The CISSP is verifiable even though it's not about the process. The paragraph CONTAINS a verifiable fact — that is all that matters for specificity.

Specificity is determined by your specific_facts list — a max() operation:
1. List every specific fact you find ANYWHERE in the paragraph in specific_facts.
2. For each fact, determine its level: is it a domain term (Level 2), a firm-unique fact (Level 3), or an externally verifiable claim (Level 4)?
3. Your specificity level = the HIGHEST level among your listed facts. One verifiable fact → Level 4. One firm-specific fact → at least Level 3. One domain term → at least Level 2. No facts → Level 1.

This is a presence scan, not a holistic judgment. One qualifying fact in 500 words of boilerplate sets the floor. If you listed a fact, it counts — do not second-guess it during reasoning.

None/Other paragraphs always get Generic Boilerplate.

LEVEL 2 — DOMAIN-ADAPTED:
The paragraph demonstrates cybersecurity EXPERTISE, not just cybersecurity as a TOPIC. Every paragraph in these filings discusses cybersecurity — that's what the filing requires. The question is whether the writer shows they understand HOW cybersecurity works (specific practices, tools, architectural concepts) or merely THAT it exists (generic oversight language any business professional could write).

Apply the ERM test: would a non-security enterprise risk management professional naturally use this language? If no → the paragraph demonstrates domain knowledge → Level 2. This is about the paragraph's VOCABULARY LEVEL, not individual word matching.

Key distinction — process descriptions vs. domain expertise:
  "We conduct vendor security assessments" → L1 (says WHAT they do; any ERM professional would say this about non-cyber vendor risk too)
  "We review vendors' SOC 2 attestations and require encryption at rest" → L2 (shows the writer knows WHAT SPECIFIC EVIDENCE to look for — that requires cybersecurity domain knowledge)
  "We have processes to identify, assess, and manage cybersecurity risks" → L1 (swap "cybersecurity" for "financial" and it still works — no domain expertise shown)
  "Our program includes endpoint detection and response, network segmentation, and threat intelligence feeds" → L2 (these concepts are specific to cybersecurity; an ERM professional wouldn't naturally use them)

Domain terminology includes: specific security practices (penetration testing, vulnerability scanning, threat hunting, patch management), security tools and infrastructure categories (SIEM, SOC, EDR/XDR, WAF, IDS/IPS, MFA), architectural concepts (zero trust, defense in depth, least privilege, network segmentation), named standards (NIST CSF, ISO 27001, SOC 2, PCI DSS), and specific threat types (ransomware, APT, zero-day, DDoS). This list is illustrative, not exhaustive — apply the ERM test to terms not listed.

Commonly confused — these FAIL the ERM test and stay Level 1: risk assessment, risk management, incident response plan, business continuity, disaster recovery, tabletop exercises (without cybersecurity qualifier), enterprise risk management, internal controls, policies and procedures, "processes to identify, assess, and manage risks", "measures to protect our systems and data", "dedicated cybersecurity team."

LEVEL 3 — FIRM-SPECIFIC:
The paragraph contains at least one fact that distinguishes THIS company's cybersecurity posture from any other public company. Ask: if you removed the company name, would this detail help you narrow down which company wrote it?

Firm-specific facts include: cybersecurity leadership roles at VP level or above (CISO, CTO, CIO, VP/SVP/EVP of IT, VP/SVP/EVP of Information Technology, VP/SVP of Security/Information Security — any VP-or-above title whose portfolio includes IT, cybersecurity, or information security), Director-level titles WITH a security qualifier (Director of IT Security, Director of Cybersecurity, Director of Information Security — but NOT plain "Director of IT" or "Director of Information Technology" without a security qualifier), named non-generic committees (Cybersecurity Committee, Technology Risk Committee — not "Audit Committee" which every company has), specific team compositions by department ("Legal, Compliance, and Finance" — not "a cross-functional team"), named internal programs with distinguishing identifiers ("Cyber Incident Response Plan (CIRP)" — not generic "incident response plan"), named individuals in cybersecurity context, and specific organizational claims ("24/7 security operations").

Commonly confused — NOT firm-specific: generic governance roles that exist at every company (Board, Audit Committee, CEO, CFO, General Counsel), IT titles below VP level without a security qualifier ("Head of IT", "IT Manager", "Director of IT", "Director of Information Technology"), unnamed entities ("third-party experts", "external consultants"), generic cadences ("quarterly", "annual" without specific dates), and cybersecurity practices (penetration testing, vulnerability scanning — these are Domain-Adapted, not Firm-Specific).

LEVEL 4 — QUANTIFIED-VERIFIABLE:
The paragraph contains at least one hard fact that an external party could independently verify. Ask: could someone outside the company confirm or refute this specific claim? Any ONE verifiable fact ANYWHERE in the paragraph is sufficient — even in a subordinate clause, even if the paragraph's main point is something else.

What counts as a hard number vs. a soft number:
  HARD (→ QV): "12 security professionals", "20 years of experience", "$100M", "March 2024" — a specific quantity someone could check. Lower bounds also count: "more than 20 years", "over 15 years", "at least 10 employees" — these set a verifiable threshold (check if value ≥ N).
  SOFT (→ NOT QV): "approximately 20 departments", "several years", "significant investment", "numerous professionals" — vague or hedged in both directions, not pinned down enough to verify

What makes a fact verifiable — concrete examples:
  YES (verifiable):
  - "holds CISSP certification" → verifiable via (ISC)² registry, even mentioned in passing
  - "20 years of cybersecurity experience" → specific number checkable against a resume
  - "more than 20 years of experience" → lower bound, checkable (is experience ≥ 20?)
  - "$100M in cyber insurance" → specific dollar amount
  - "engaged Mandiant for forensic investigation" → named third-party firm
  - "uses CrowdStrike Falcon for EDR" → named product
  - "On January 15, 2024, we detected unauthorized access" → specific date tied to a real event
  - "team of 12 security professionals" → specific headcount
  - "maintain ISO 27001 certification" → verifiable via certification body
  - "various certifications including CISSP" → "various" is soft but CISSP is specifically named and independently verifiable
  NO (not verifiable):
  - "For the year ended December 31, 2024" → reporting period context, not a cybersecurity fact
  - "Our CISO" → a role title is Firm-Specific (Level 3), not a quantified claim
  - "aligned with NIST CSF" → aspiration not audited fact → Domain-Adapted (Level 2)
  - "quarterly reviews" → generic cadence, not a specific date
  - "approximately 20 departments" → soft number, hedged in both directions
  - "We have not experienced any material cybersecurity incidents" → negative self-assertion, NOT externally verifiable (you cannot independently confirm the absence of something)
  - "In 2023, we did not experience a material incident" → a year does not make a negative assertion verifiable — this is still a self-reported claim about absence
  - "As of December 28, 2024, no material incidents have occurred" → reporting-anchored negative assertion, same problem
  - "During the last three years, no material breaches" → soft timeframe + negative assertion

CERTIFICATION TRILOGY (illustrates all three levels):
  "Aligned with ISO 27001" → Domain-Adapted (references a standard, no firm-specific claim)
  "Working toward ISO 27001 certification" → Firm-Specific (firm-specific intent, not yet verifiable)
  "Maintain ISO 27001 certification" → Quantified-Verifiable (externally verifiable claim)

VALIDATION STEP — before finalizing specificity:
  1. Review your specific_facts list. Each fact should survive the commonly-confused filter for its level (e.g., "Audit Committee" is NOT firm-specific, "CISO" IS firm-specific). Remove any that don't qualify.
  2. Your specificity level = highest surviving fact. If all facts were removed → Level 1.
  3. Check consistency: does your specificity level match your specific_facts? If you listed a firm-specific fact, you cannot assign below Level 3. If you listed a verifiable fact, you cannot assign below Level 4. The list is your commitment.

═══ OUTPUT ═══

Return JSON with:
  - content_category
  - specific_facts: List every fact that elevates specificity above Level 1. For Level 2, list the domain terms that pass the ERM test. For Level 3, list the firm-specific facts. For Level 4, list the verifiable claims. Empty array ONLY for Level 1. If specific_facts is empty, specificity MUST be Generic Boilerplate.
  - specificity: Must equal the highest level supported by your specific_facts list.
  - category_confidence, specificity_confidence
  - reasoning: 1-2 sentences covering BOTH category choice AND what drove your specificity level.`;

/** Build the user prompt for a single paragraph annotation. */
export function buildUserPrompt(paragraph: Paragraph): string {
  const { filing, text } = paragraph;
  return `Company: ${filing.companyName} (${filing.ticker})
Filing type: ${filing.filingType}
Filing date: ${filing.filingDate}
Section: ${filing.secItem}

Paragraph:
${text}`;
}

// ── Category confusion-axis disambiguation rules ──────────────────────────
// Keyed by sorted pair of disputed categories. Only included when relevant.
const CATEGORY_GUIDANCE: Record<string, string> = {
  "Management Role|Risk Management Process": `MANAGEMENT ROLE vs RISK MANAGEMENT PROCESS — apply the person-removal test:
  Remove all person-specific content (names, titles, qualifications, experience, reporting lines, team composition, committee membership). Is the remaining text a coherent cybersecurity disclosure?
    YES → Risk Management Process (the process stands alone; people are incidental).
    NO → Management Role (the paragraph is fundamentally about who these people are).
  MR is about ROLES: who is responsible, how responsibilities are divided, what qualifies them, how management-level oversight is structured.
  RMP is about ACTIVITIES: what the program does, how it operates, what tools and frameworks it uses.
  CRITICAL: A person being the grammatical subject does NOT automatically mean Management Role. "Our CISO oversees a program that includes penetration testing and vulnerability scanning" → remove CISO → program description stands alone → RMP.
  Examples:
  • "Our CISO has 20 years of experience and holds CISSP certification. She reports to the CIO." → MR (remove people → nothing left; has qualifications)
  • "Our cybersecurity program includes risk assessment and monitoring, overseen by our CISO." → RMP (remove CISO → program description stands alone)
  • "Our CFO and VP of IT jointly oversee our cybersecurity program. The CFO handles risk governance, the VP of IT manages technical operations." → MR (role allocation and responsibilities are the substance)`,

  "Risk Management Process|Third-Party Risk": `RISK MANAGEMENT PROCESS vs THIRD-PARTY RISK — ask: is vendor/supplier oversight the CENTRAL topic?
  • "We use third-party consultants for penetration testing" = RMP (third parties support an internal process).
  • "We maintain a vendor oversight program with due diligence and monitoring of third-party controls" = Third-Party Risk (vendor oversight IS the topic).
  • Third parties hired to serve the company → RMP. Requirements imposed ON vendors → TP.`,

  "None/Other|Strategy Integration": `NONE/OTHER vs STRATEGY INTEGRATION — the materiality ASSESSMENT test:
  IS a materiality assessment → Strategy Integration:
  • Backward-looking: "have not materially affected our business strategy, results of operations, or financial condition" → SI
  • Forward-looking with SEC qualifier: "reasonably likely to materially affect" → SI (Item 106(b)(2) language)
  • Negative assertion: "we have not experienced any material cybersecurity incidents" → SI
  • Insurance, budget, investment: "we expend considerable resources on cybersecurity", cyber insurance, cost allocation → SI

  NOT a materiality assessment → classify by primary purpose (usually N/O or RMP):
  • Generic risk warning: "could have a material adverse effect on our business" → boilerplate speculation → N/O or RMP
  • "Material" as adjective: "managing material risks" → NOT SI
  • Consequence clause: SPECULATIVE materiality ("could have a material adverse effect") at END of paragraph does not override primary purpose. BUT a factual negative assertion ("we have not experienced any material cybersecurity incidents") IS an assessment even at the end.
  • Cross-references: "For risks that may materially affect us, see Item 1A" → N/O

  KEY DISTINCTION: "Risks have not materially affected us" = SI (CONCLUSION). "Risks could have a material adverse effect" = N/O (SPECULATION). "Reasonably likely to materially affect" = SI (SEC-qualified forward-looking assessment).`,

  "Board Governance|Management Role": `BOARD GOVERNANCE vs MANAGEMENT ROLE — ask: "What question does this paragraph primarily answer?"
  • "How does the board oversee cybersecurity?" → Board Governance
  • "How is management organized to handle cybersecurity?" → Management Role
  Governance-chain paragraphs (Board → Committee → Officer → Program) default to BG when the purpose is describing oversight structure.
  MR only when management's organizational role — responsibilities, qualifications, committee membership — is the primary content.
  • Board/committee oversees, receives reports, delegates → BG
  • Management reports TO the board (describing oversight flow) → BG
  • Management roles, responsibilities, how they're divided → MR
  • Person's qualifications, credentials, experience → MR
  • Board-level committee (Audit Committee, Risk Committee of the Board) → BG
  • Management-level committee (Cybersecurity Steering Committee) → MR if about structure/membership, RMP if about activities`,

  "Board Governance|Risk Management Process": `BOARD GOVERNANCE vs RISK MANAGEMENT PROCESS — oversight or operations?
  • Board/committee receiving reports, overseeing risk, setting policy → Board Governance.
  • Company describing HOW it assesses, monitors, mitigates risks → Risk Management Process.
  • "The board receives quarterly cybersecurity briefings" → BG. "We conduct quarterly risk assessments; the board is informed" → RMP (process is primary).`,

  "None/Other|Risk Management Process": `NONE/OTHER vs RISK MANAGEMENT PROCESS — does the paragraph describe actual cybersecurity activities?
  • Describing actual processes, measures, or controls → RMP. Even if surrounded by risk-factor framing.
  • Only stating the company has IT systems, faces risks, or enumerating threat types — without describing what it DOES → N/O.
  • Generic regulatory compliance ("subject to various regulations") with no activity described → N/O. Named regulation + compliance activities → RMP.`,

  "Risk Management Process|Strategy Integration": `RISK MANAGEMENT PROCESS vs STRATEGY INTEGRATION — operational or strategic?
  • HOW risks are assessed, monitored, mitigated → RMP.
  • Materiality conclusions, financial impact, insurance, budget → SI.
  • ERM integration described as a process = RMP. Materiality conclusions from the process = SI.`,
};

// ── Specificity disambiguation rules ──────────────────────────────────────
const SPECIFICITY_GUIDANCE_1_VS_2 = `SPECIFICITY: GENERIC BOILERPLATE (1) vs DOMAIN-ADAPTED (2)
Apply the ERM test: does the paragraph use vocabulary that a non-security enterprise risk management professional would NOT naturally use? If yes → the paragraph demonstrates cybersecurity domain knowledge → Domain-Adapted. This is about vocabulary level, not individual word presence — a boilerplate materiality disclaimer mentioning "cyberattacks" is still Level 1 if it demonstrates no domain knowledge.
  Level 2 examples: "penetration testing and vulnerability scanning" (security-specific practices), "defense in depth strategy" (architectural concept), "SIEM and EDR tools" (security infrastructure).
  Level 1 examples: "processes to identify, assess, and manage risks" (generic ERM), "incident response plan" (used in all risk domains), "dedicated cybersecurity team" (organizational, not technical).`;

const SPECIFICITY_GUIDANCE_1_VS_3 = `SPECIFICITY: GENERIC BOILERPLATE (1) vs FIRM-SPECIFIC (3)
Firm-Specific requires at least one fact that distinguishes THIS company from any other. Common false positives:
  ✗ Generic governance (Board, Audit Committee, management, CEO, CFO) — exist at every company
  ✗ IT titles below VP without a security qualifier ("Head of IT", "IT Manager", "Director of IT") — too generic. VP/SVP of IT IS firm-specific, Director of IT Security IS firm-specific, but plain "Director of IT" is NOT.
  ✗ Generic program names ("incident response plan", "cybersecurity program") — unless they have a distinguishing identifier ("CIRP")
  ✗ Cybersecurity practices (pen testing, vuln scanning) — these are Domain-Adapted (Level 2), not Firm-Specific
If all candidate facts are disqualified, check for domain terminology (→ Level 2) before defaulting to Level 1.`;

const SPECIFICITY_GUIDANCE_2_VS_3 = `SPECIFICITY: DOMAIN-ADAPTED (2) vs FIRM-SPECIFIC (3)
Domain-Adapted demonstrates cybersecurity knowledge but nothing unique to THIS company. Firm-Specific has at least one fact that identifies something about THIS company's specific posture.
  "We conduct penetration testing aligned with NIST CSF" → Level 2 (domain terms + standard, but any company could say this)
  "Our CISO oversees penetration testing" → Level 3 (CISO = cybersecurity leadership role unique to this company's structure)
  The test: could you paste this sentence into any company's filing unchanged? If yes → Level 2. If it reveals something about THIS company → Level 3.`;

const SPECIFICITY_GUIDANCE_3_VS_4 = `SPECIFICITY: FIRM-SPECIFIC (3) vs QUANTIFIED-VERIFIABLE (4)
Quantified-Verifiable requires at least one fact an external party could independently confirm. The test: could someone outside the company verify this claim?
  QV: specific numbers ($2M, 12 professionals, 17 years), specific dates (month+year tied to a cybersecurity event), named external entities (Mandiant, CrowdStrike, Splunk), certifications held (CISSP, CISM, ISO 27001 certification).
  NOT QV: named roles alone (CISO → Firm-Specific, not quantified), named committees (organizational, not verifiable), standards followed not certified ("aligned with NIST" → Level 2), fiscal year dates as reporting context ("year ended December 31" → not a cybersecurity-specific date).`;

/**
 * Build a re-evaluation prompt for paragraphs flagged for codebook-correction review.
 */
export function buildReEvalPrompt(
  paragraph: Paragraph,
  reason: "materiality_language" | "spac" | "other",
): string {
  const { filing, text } = paragraph;

  let ruleBlock: string;
  if (reason === "materiality_language") {
    ruleBlock = `═══ RULE UNDER REVIEW ═══

This paragraph was previously labeled None/Other. It has been flagged for re-evaluation because it contains materiality-related language.

CODEBOOK RULE (v4.0): Materiality ASSESSMENTS are Strategy Integration. An assessment is the company STATING A CONCLUSION about materiality:
  • Backward-looking: "have not materially affected our business strategy, results of operations, or financial condition" → SI
  • Forward-looking with SEC qualifier: "reasonably likely to materially affect" → SI
  • Negative assertion: "we have not experienced any material cybersecurity incidents" → SI

The following are NOT materiality assessments and do NOT trigger SI:
  • Generic risk warning: "could have a material adverse effect on our business" → NOT SI (boilerplate speculation)
  • "Material" as adjective: "managing material risks" → NOT SI
  • Cross-references: "For risks that may materially affect us, see Item 1A" → N/O`;
  } else if (reason === "spac") {
    ruleBlock = `═══ RULE UNDER REVIEW ═══

This paragraph was flagged for re-evaluation because it may be from a SPAC or shell company.

CODEBOOK RULE (v4.0): SPACs and shell companies explicitly stating they have no operations, no cybersecurity program, or no formal processes → None/Other, regardless of incidental mentions of board oversight or risk acknowledgment.`;
  } else {
    ruleBlock = `═══ RE-EVALUATION ═══

This paragraph has been flagged for fresh classification under codebook v4.0 rules. Classify it fresh — do not assume any prior label is correct.`;
  }

  return `═══ RE-EVALUATION TASK ═══

You are re-classifying this paragraph under updated codebook rules (v4.0). Classify it fresh — do not assume any prior label is correct.

${ruleBlock}

═══ ANALYSIS STEPS ═══

1. Read the paragraph carefully.
2. Apply the specific rule described above to determine if it changes the classification.
3. Apply all standard codebook rules for both category and specificity.
4. Provide your classification with reasoning.

═══ CONFIDENCE CALIBRATION ═══

HIGH = the rule clearly applies (or clearly doesn't) — the answer is unambiguous
MEDIUM = the rule is relevant but the paragraph is borderline
LOW = genuinely ambiguous even with the updated rule

═══ PARAGRAPH ═══

Company: ${filing.companyName} (${filing.ticker})
Filing type: ${filing.filingType}
Filing date: ${filing.filingDate}
Section: ${filing.secItem}

${text}`;
}

/**
 * Build the Stage 2 judge prompt with disagreement-aware disambiguation.
 * Dynamically includes only the guidance relevant to the specific dispute.
 */
export function buildJudgePrompt(
  paragraph: Paragraph,
  annotations: Array<{
    content_category: string;
    specificity_level: number;
    reasoning: string;
  }>,
): string {
  // ── Analyze the disagreement ─────────────────────────────────────────
  const cats = annotations.map((a) => a.content_category);
  const specs = annotations.map((a) => a.specificity_level);
  const uniqueCats = [...new Set(cats)].sort();
  const uniqueSpecs = [...new Set(specs)].sort((a, b) => a - b);
  const catDisagree = uniqueCats.length > 1;
  const specDisagree = uniqueSpecs.length > 1;

  // Vote distribution strings
  const catCounts = new Map<string, number>();
  for (const c of cats) catCounts.set(c, (catCounts.get(c) ?? 0) + 1);
  const catDistStr = [...catCounts.entries()]
    .sort(([, a], [, b]) => b - a)
    .map(([c, n]) => `${c} (${n})`)
    .join(", ");

  const specCounts = new Map<number, number>();
  for (const s of specs) specCounts.set(s, (specCounts.get(s) ?? 0) + 1);
  const specLabels: Record<number, string> = { 1: "Generic Boilerplate", 2: "Domain-Adapted", 3: "Firm-Specific", 4: "Quantified-Verifiable" };
  const specDistStr = [...specCounts.entries()]
    .sort(([, a], [, b]) => b - a)
    .map(([s, n]) => `${specLabels[s] ?? s} (${n})`)
    .join(", ");

  let focusLine: string;
  if (catDisagree && specDisagree) {
    focusLine = `DISAGREEMENT: BOTH category and specificity.\n  Category votes: ${catDistStr}\n  Specificity votes: ${specDistStr}`;
  } else if (catDisagree) {
    focusLine = `DISAGREEMENT: CATEGORY — ${catDistStr}.\n  Specificity is agreed at ${specLabels[uniqueSpecs[0]!] ?? uniqueSpecs[0]}.`;
  } else {
    focusLine = `DISAGREEMENT: SPECIFICITY — ${specDistStr}.\n  Category is agreed as ${uniqueCats[0]}.`;
  }

  // ── Build targeted guidance ──────────────────────────────────────────
  const guidanceBlocks: string[] = [];

  if (catDisagree) {
    for (let i = 0; i < uniqueCats.length; i++) {
      for (let j = i + 1; j < uniqueCats.length; j++) {
        const key = [uniqueCats[i], uniqueCats[j]].sort().join("|");
        const guidance = CATEGORY_GUIDANCE[key];
        if (guidance) guidanceBlocks.push(guidance);
      }
    }
  }

  if (specDisagree) {
    const minSpec = Math.min(...specs);
    const maxSpec = Math.max(...specs);
    if (minSpec <= 1 && maxSpec >= 2) guidanceBlocks.push(SPECIFICITY_GUIDANCE_1_VS_2);
    if (minSpec <= 1 && maxSpec >= 3) guidanceBlocks.push(SPECIFICITY_GUIDANCE_1_VS_3);
    if (minSpec <= 2 && maxSpec >= 3) guidanceBlocks.push(SPECIFICITY_GUIDANCE_2_VS_3);
    if (minSpec <= 3 && maxSpec >= 4) guidanceBlocks.push(SPECIFICITY_GUIDANCE_3_VS_4);
  }

  const guidanceSection =
    guidanceBlocks.length > 0
      ? `\n═══ DISAMBIGUATION RULES (for this specific dispute) ═══\n\n${guidanceBlocks.join("\n\n")}\n`
      : "";

  // ── Fisher-Yates shuffle for anti-bias ───────────────────────────────
  const shuffled = [...annotations];
  for (let i = shuffled.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffled[i], shuffled[j]] = [shuffled[j]!, shuffled[i]!];
  }

  const annotatorLabels = ["A", "B", "C"] as const;
  const annotatorBlock = shuffled
    .map((a, i) => {
      return `Annotator ${annotatorLabels[i]}: content_category="${a.content_category}", specificity_level=${a.specificity_level}
  Reasoning: "${a.reasoning}"`;
    })
    .join("\n\n");

  const { filing, text } = paragraph;

  return `═══ ADJUDICATION TASK ═══

You are resolving a labeling disagreement among three annotators who applied the codebook above.

${focusLine}
${guidanceSection}
═══ ANALYSIS STEPS ═══

Before providing your label:
1. CRITIQUE each annotator's reasoning against the codebook. Which one(s) applied the rules correctly? Which made identifiable errors?
2. For specificity: independently assess the paragraph's vocabulary level (domain knowledge?), identify any facts unique to this company, and check whether any facts are externally verifiable. Apply the principles, not just list-matching.
3. Determine the dominant communicative purpose for category using the "what question?" test.
4. Provide your final label. In your reasoning, explain which annotator(s) you agree with and what specific error(s) the others made.

═══ CONFIDENCE CALIBRATION ═══

Your confidence ratings will be used to weight this label in model training. Calibrate honestly:
  HIGH = one annotator clearly applied the codebook correctly; others made identifiable errors
  MEDIUM = reasonable case for the winning label but you can see why annotators disagreed
  LOW = genuinely ambiguous; the codebook boundary is fuzzy for this text and reasonable experts could disagree

═══ PARAGRAPH ═══

Company: ${filing.companyName} (${filing.ticker})
Filing type: ${filing.filingType}
Filing date: ${filing.filingDate}
Section: ${filing.secItem}

${text}

═══ PRIOR ANNOTATIONS (randomized order) ═══

${annotatorBlock}`;
}
