#!/usr/bin/env python3
"""Build a stricter strategic 10-loop Oracle company-deep-dive package."""

from __future__ import annotations

import csv
import html
import json
import urllib.error
import urllib.request
from datetime import datetime, timezone
from pathlib import Path


BASE = Path(__file__).resolve().parent
RUN = BASE.parent
PUBLIC = RUN / "public"
SERVED = Path("/home/dev/public/research-loop/strategic10-orcl-001")
PUBLIC.mkdir(parents=True, exist_ok=True)
SERVED.mkdir(parents=True, exist_ok=True)


def write_csv(name: str, rows: list[dict[str, object]]) -> None:
    with (BASE / name).open("w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=list(rows[0]))
        writer.writeheader()
        writer.writerows(rows)


def fetch_log(rows: list[dict[str, object]]) -> list[dict[str, object]]:
    out = []
    for row in rows:
        url = str(row["source_url"])
        try:
            req = urllib.request.Request(url, headers={"User-Agent": "research-loop research@example.com"})
            with urllib.request.urlopen(req, timeout=20) as resp:
                body = resp.read(2048)
                status = getattr(resp, "status", 200)
                final_url = resp.geturl()
                ok = 200 <= status < 400 and bool(body)
                detail = f"fetched {len(body)} bytes"
        except (urllib.error.URLError, TimeoutError, OSError) as exc:
            status = "error"
            final_url = url
            ok = False
            detail = type(exc).__name__
        out.append(
            {
                "source_id": row["source_id"],
                "source_url": url,
                "final_url": final_url,
                "status": status,
                "ok": int(ok),
                "detail": detail,
                "checked_at_utc": datetime.now(timezone.utc).isoformat(timespec="seconds"),
            }
        )
    return out


source_inventory = [
    {
        "source_id": "orcl_q1_fy26_ir_release",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "official_ir_release",
        "source_url": "https://investor.oracle.com/investor-news/news-details/2025/Oracle-Announces-Fiscal-Year-2026-First-Quarter-Financial-Results/default.aspx",
        "periods_supported": "FQ1 2026",
        "metrics_supported": "RPO, total revenue, cloud revenue, OCI revenue, capex",
        "caveat": "Quarterly release gives financial signals, not project-level data-center status.",
    },
    {
        "source_id": "orcl_q2_fy26_ir_release",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "official_ir_release",
        "source_url": "https://investor.oracle.com/investor-news/news-details/2025/Oracle-Announces-Fiscal-Year-2026-Second-Quarter-Financial-Results/default.aspx",
        "periods_supported": "FQ2 2026",
        "metrics_supported": "RPO, total revenue, cloud revenue, OCI revenue, capex",
        "caveat": "Some capex interpretation requires year-to-date arithmetic.",
    },
    {
        "source_id": "orcl_q3_fy26_ir_release",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "official_ir_release",
        "source_url": "https://investor.oracle.com/investor-news/news-details/2026/Oracle-Announces-Fiscal-Year-2026-Third-Quarter-Financial-Results/default.aspx",
        "periods_supported": "FQ3 2026, FY2026 guidance, FY2027 guidance",
        "metrics_supported": "RPO, total revenue, cloud revenue, OCI revenue, capex guidance",
        "caveat": "Latest release as of 2026-06-07; FY2026 Q4 not yet released.",
    },
    {
        "source_id": "orcl_q3_fy26_tables_pdf",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "official_financial_tables_pdf",
        "source_url": "https://investor.oracle.com/files/doc_financials/2026/q3/Q326_Form8K_Exhibit99-1_Earnings_Release_Tables_FINAL-V1.pdf",
        "periods_supported": "FQ3 2026 year-to-date",
        "metrics_supported": "cash flow and capex tables",
        "caveat": "PDF tables need parser extraction for automation.",
    },
    {
        "source_id": "orcl_q1_fy26_10q",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "sec_10q",
        "source_url": "https://www.sec.gov/Archives/edgar/data/0001341439/000119312525200095/orcl-20250831.htm",
        "periods_supported": "FQ1 2026",
        "metrics_supported": "lease commitments, RPO, data-center capacity language",
        "caveat": "HTML filing is large; evidence rows use short snippets.",
    },
    {
        "source_id": "orcl_q2_fy26_10q",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "sec_10q",
        "source_url": "https://www.sec.gov/Archives/edgar/data/0001341439/000119312525315925/orcl-20251130.htm",
        "periods_supported": "FQ2 2026",
        "metrics_supported": "lease commitments, data-center/cloud-capacity arrangements",
        "caveat": "Lease commitments are not the same as delivered data-center capacity.",
    },
    {
        "source_id": "orcl_q2_fy26_10q_pdf",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "official_10q_pdf",
        "source_url": "https://s23.q4cdn.com/440135859/files/doc_financials/2026/q2/Q2FY2026_10Q.pdf",
        "periods_supported": "FQ2 2026",
        "metrics_supported": "lease commitments and cash-flow detail",
        "caveat": "Duplicate official copy of SEC filing for PDF extraction testing.",
    },
    {
        "source_id": "orcl_financials_index",
        "company": "Oracle",
        "ticker": "ORCL",
        "source_type": "official_financials_index",
        "source_url": "https://investor.oracle.com/financials/default.aspx",
        "periods_supported": "FY2026 release index",
        "metrics_supported": "canonical quarterly result links",
        "caveat": "Index page is a locator, not itself an evidence source for values.",
    },
]


evidence_rows = [
    ["FQ1 2026", "remaining_performance_obligations", 455, "USD_billions", "orcl_q1_fy26_ir_release", "Q1 Remaining Performance Obligations $455 billion, up 359%."],
    ["FQ2 2026", "remaining_performance_obligations", 523, "USD_billions", "orcl_q2_fy26_ir_release", "Q2 Remaining Performance Obligations $523 billion, up 438% in USD."],
    ["FQ3 2026", "remaining_performance_obligations", 553, "USD_billions", "orcl_q3_fy26_ir_release", "Q3 Remaining Performance Obligations $553 billion, up 325% year-over-year."],
    ["FQ1 2026", "total_revenue", 14.9, "USD_billions", "orcl_q1_fy26_ir_release", "Q1 Total Revenue $14.9 billion, up 12% in USD."],
    ["FQ2 2026", "total_revenue", 16.1, "USD_billions", "orcl_q2_fy26_ir_release", "Q2 Total Revenue $16.1 billion, up 14% in USD."],
    ["FQ3 2026", "total_revenue", 17.2, "USD_billions", "orcl_q3_fy26_ir_release", "Q3 Total Revenue $17.2 billion, up 22% in USD."],
    ["FQ1 2026", "cloud_revenue", 7.2, "USD_billions", "orcl_q1_fy26_ir_release", "Q1 Cloud Revenue (IaaS plus SaaS) $7.2 billion."],
    ["FQ2 2026", "cloud_revenue", 8.0, "USD_billions", "orcl_q2_fy26_ir_release", "Q2 Cloud Revenue (IaaS plus SaaS) $8.0 billion."],
    ["FQ3 2026", "cloud_revenue", 8.9, "USD_billions", "orcl_q3_fy26_ir_release", "Q3 Cloud Revenue (IaaS plus SaaS) $8.9 billion."],
    ["FQ1 2026", "oci_iaas_revenue", 3.3, "USD_billions", "orcl_q1_fy26_ir_release", "Q1 Cloud Infrastructure (IaaS) Revenue $3.3 billion."],
    ["FQ2 2026", "oci_iaas_revenue", 4.1, "USD_billions", "orcl_q2_fy26_ir_release", "Q2 Cloud Infrastructure (IaaS) Revenue $4.1 billion."],
    ["FQ3 2026", "oci_iaas_revenue", 4.9, "USD_billions", "orcl_q3_fy26_ir_release", "Q3 Cloud Infrastructure Revenue (IaaS) $4.9 billion."],
    ["FQ1 2026 YTD", "capital_expenditures_ytd", 8.502, "USD_billions", "orcl_q1_fy26_ir_release", "Capital expenditures (8,502)."],
    ["FQ2 2026 YTD", "capital_expenditures_ytd", 20.535, "USD_billions", "orcl_q2_fy26_ir_release", "Capital expenditures (20,535)."],
    ["FQ3 2026 YTD", "capital_expenditures_ytd", 39.170, "USD_billions", "orcl_q3_fy26_ir_release", "Capital expenditures (39,170)."],
    ["FQ2 2026", "capital_expenditures_quarter_derived", 12.033, "USD_billions", "orcl_q2_fy26_ir_release", "Derived from YTD capex $20.535B less Q1 $8.502B."],
    ["FQ3 2026", "capital_expenditures_quarter_derived", 18.635, "USD_billions", "orcl_q3_fy26_ir_release", "Derived from YTD capex $39.170B less H1 $20.535B."],
    ["FY2026 guidance", "capex_guidance", 50, "USD_billions", "orcl_q3_fy26_ir_release", "For fiscal year 2026, we expect revenue of $67 billion and capital expenditures of $50 billion."],
    ["FY2026 guidance", "revenue_guidance", 67, "USD_billions", "orcl_q3_fy26_ir_release", "For fiscal year 2026, we expect revenue of $67 billion."],
    ["FY2027 guidance", "revenue_guidance", 90, "USD_billions", "orcl_q3_fy26_ir_release", "For fiscal year 2027, we are raising total revenue guidance to $90 billion."],
    ["FQ1 2026", "additional_lease_commitments", 99.8, "USD_billions", "orcl_q1_fy26_10q", "As of August 31, 2025, we had $99.8 billion of additional lease commitments, substantially all for data centers."],
    ["FQ2 2026", "additional_lease_commitments", 248, "USD_billions", "orcl_q2_fy26_10q", "As of November 30, 2025, we had $248 billion of additional lease commitments."],
    ["FQ2 2026", "lease_commitment_term_low", 15, "years", "orcl_q2_fy26_10q", "terms of fifteen to nineteen years."],
    ["FQ2 2026", "lease_commitment_term_high", 19, "years", "orcl_q2_fy26_10q", "terms of fifteen to nineteen years."],
    ["FQ2 2026", "lease_commitment_commence_start", 2026.75, "fiscal_year", "orcl_q2_fy26_10q", "expected to commence between the third quarter of fiscal 2026 and fiscal 2028."],
    ["FQ2 2026", "unconditional_purchase_obligations", 10, "USD_billions", "orcl_q2_fy26_10q", "unconditional purchase and certain other obligations ... primarily related to cloud capacity arrangements, were $10 billion."],
    ["FQ3 2026", "q4_total_revenue_growth_guidance_low", 15, "percent", "orcl_q3_fy26_ir_release", "Q4 total revenue is expected to grow between 15% to 17%."],
    ["FQ3 2026", "q4_total_revenue_growth_guidance_high", 17, "percent", "orcl_q3_fy26_ir_release", "Q4 total revenue is expected to grow between 15% to 17%."],
]

source_by_id = {row["source_id"]: row for row in source_inventory}
core_evidence_rows = [
    {
        "company": "Oracle",
        "ticker": "ORCL",
        "period": period,
        "metric": metric,
        "value": value,
        "unit": unit,
        "source_id": source_id,
        "source_url": source_by_id[source_id]["source_url"],
        "source_snippet": snippet,
        "derived_flag": int("Derived" in snippet),
        "interpretation": {
            "remaining_performance_obligations": "Forward cloud backlog signal tied to AI/customer commitments.",
            "total_revenue": "Revenue conversion base for Oracle overall.",
            "cloud_revenue": "Cloud segment conversion signal.",
            "oci_iaas_revenue": "Closest reported revenue series to AI infrastructure demand.",
            "capital_expenditures_ytd": "Actual cash spend proxy for infrastructure buildout.",
            "capital_expenditures_quarter_derived": "Derived quarterly capex from official YTD capex.",
            "capex_guidance": "Management forward spend plan for buildout.",
            "revenue_guidance": "Management forward conversion expectation.",
            "additional_lease_commitments": "Forward data-center/cloud-capacity supply commitment.",
            "lease_commitment_term_low": "Duration of long-term data-center/cloud-capacity commitments.",
            "lease_commitment_term_high": "Duration of long-term data-center/cloud-capacity commitments.",
            "lease_commitment_commence_start": "Expected start window for new committed capacity.",
            "unconditional_purchase_obligations": "Additional cloud-capacity-related purchase commitment.",
            "q4_total_revenue_growth_guidance_low": "Near-term revenue growth guidance.",
            "q4_total_revenue_growth_guidance_high": "Near-term revenue growth guidance.",
        }[metric],
        "caveat": "Oracle is a hyperscaler/cloud platform signal, not a named facility forecast-actual ledger.",
    }
    for period, metric, value, unit, source_id, snippet in evidence_rows
]

diagnostics = [
    {
        "period": "FQ1 2026",
        "rpo_usd_b": 455,
        "oci_revenue_usd_b": 3.3,
        "capex_ytd_usd_b": 8.502,
        "lease_commitments_usd_b": 99.8,
        "rpo_to_quarterly_oci_revenue_x": round(455 / 3.3, 1),
        "lease_to_ytd_capex_x": round(99.8 / 8.502, 1),
        "read": "Demand backlog already far exceeds current OCI revenue and capex run rate.",
    },
    {
        "period": "FQ2 2026",
        "rpo_usd_b": 523,
        "oci_revenue_usd_b": 4.1,
        "capex_ytd_usd_b": 20.535,
        "lease_commitments_usd_b": 248,
        "rpo_to_quarterly_oci_revenue_x": round(523 / 4.1, 1),
        "lease_to_ytd_capex_x": round(248 / 20.535, 1),
        "read": "Lease commitments jumped faster than YTD capex, making delivery timing the key test.",
    },
    {
        "period": "FQ3 2026",
        "rpo_usd_b": 553,
        "oci_revenue_usd_b": 4.9,
        "capex_ytd_usd_b": 39.17,
        "lease_commitments_usd_b": "",
        "rpo_to_quarterly_oci_revenue_x": round(553 / 4.9, 1),
        "lease_to_ytd_capex_x": "",
        "read": "RPO still expanded while capex guidance stayed at $50B for FY2026.",
    },
]

signal_scores = [
    ["rpo_growth", 9, "RPO rose from $455B to $553B from FQ1 to FQ3 FY2026.", "Very strong contracted demand signal.", "Track customer concentration and conversion timing."],
    ["oci_revenue_conversion", 8, "OCI revenue rose from $3.3B to $4.9B over two quarters.", "Revenue conversion is visible and accelerating.", "Separate AI GPU demand from other OCI workloads."],
    ["capex_intensity", 8, "YTD capex reached $39.170B by FQ3; FY2026 guide is $50B.", "Spending confirms buildout is not merely notional.", "Track FY2026 Q4 actual capex versus guide."],
    ["lease_commitments", 8, "Additional lease commitments rose to $248B, mostly data-center/cloud capacity.", "Forward supply commitments are explicit.", "Need facility/geography details and delivery schedule."],
    ["forecast_actual_quality", 5, "Oracle discloses financial commitments but little named-facility delivery.", "Useful for macro signal, weaker for project delivery accuracy.", "Mine 10-Qs and call transcripts for project-level data."],
    ["overall_signal_quality", 8, "RPO, OCI revenue, capex, and lease commitments triangulate AI infrastructure demand.", "High-value hyperscaler complement to APLD and CRWV.", "Add Q4 FY2026 once released and compare against NVDA."],
]
signal_score_rows = [
    {
        "signal_dimension": dim,
        "score_1_10": score,
        "evidence": evidence,
        "why_score": why,
        "what_to_verify_next": next_step,
    }
    for dim, score, evidence, why, next_step in signal_scores
]

comparison = [
    ["APLD", "physical campus delivery", "Named MW and ready-for-service milestones.", "Small delivered base; financing/execution risk."],
    ["CRWV", "scaled AI cloud demand conversion", "Quarterly backlog, revenue, capex, and active power.", "Less named-facility transparency."],
    ["ORCL", "hyperscaler AI demand and lease commitments", "RPO, OCI revenue, capex, and data-center/cloud leases.", "Little direct facility forecast-vs-actual detail."],
]
comparison_rows = [
    {
        "company": {"APLD": "Applied Digital", "CRWV": "CoreWeave", "ORCL": "Oracle"}[ticker],
        "ticker": ticker,
        "best_signal": signal,
        "strength": strength,
        "weakness": weakness,
        "role_in_research": "Use as one lens; do not average unlike bases.",
    }
    for ticker, signal, strength, weakness in comparison
]

decision_backlog = [
    ["Add Oracle Q4 FY2026 once released", "Q4 will test FY2026 $50B capex guidance and revenue conversion.", "Q4 release fetched, evidence ledger updated, validator still passes."],
    ["Extract Oracle earnings-call transcript", "Call may identify capacity constraints, customer prepayments, GPUs, and project timing.", "Transcript rows include source snippets and classify forecast versus actual."],
    ["Parse Q1/Q2 10-Q lease notes automatically", "Lease rows are a strong signal but should become parser-extracted, not hand-entered.", "Extractor reproduces $99.8B and $248B rows from filings."],
    ["Add NVDA lag comparison", "Original goal is semiconductor-cycle early warning, not just cloud demand.", "Differenced ORCL OCI/RPO/capex series compared with NVDA data-center revenue."],
    ["Add Equinix next", "Equinix gives colocation/xScale supply signal outside Oracle's balance-sheet model.", "EQIX evidence package passes company_deep_dive gate."],
]
decision_rows = [
    {"priority": i + 1, "next_work": a, "why": b, "acceptance_test": c}
    for i, (a, b, c) in enumerate(decision_backlog)
]

loop_summary = [
    [1, "Use Oracle because it tests hyperscaler demand closer to AI customers.", "Built official source spine for Q1-Q3 releases, SEC filings, tables, and financial index.", 9, "Sources are primary and current as of June 7.", "Extract financial demand and buildout rows."],
    [2, "Extract RPO, revenue, cloud, OCI, capex, and lease commitment evidence.", "Created 28 source-traced evidence rows with snippets and caveats.", 9, "Rows cover six signal families across five periods.", "Compute backlog and capex diagnostics."],
    [3, "Measure RPO against OCI revenue and lease commitments against capex.", "Built three diagnostic checkpoints for FQ1, FQ2, and FQ3.", 8, "Diagnostics are useful but facility-level delivery remains absent.", "Score signal quality honestly."],
    [4, "Score Oracle signal dimensions using evidence and explicit gaps.", "Created six signal scores for RPO, OCI revenue, capex, leases, and overall quality.", 9, "Scores separate strong macro evidence from weak project evidence.", "Compare Oracle to APLD and CRWV."],
    [5, "Position Oracle against existing APLD and CRWV research roles.", "Built comparison showing Oracle as hyperscaler demand and lease-commitment lens.", 9, "Comparison clarifies role without averaging unlike metrics.", "Write the research memo."],
    [6, "Write a direct memo answering the original research question.", "Wrote answer, evidence, call, watch items, remaining gaps, and falsifiers.", 9, "Memo is decisive and names unresolved weaknesses.", "Create dashboard and publish."],
    [7, "Build an auditable public dashboard.", "Rendered source inventory, evidence, diagnostics, scores, comparison, backlog, and loop table.", 8, "Dashboard is readable HTML; no image charts yet.", "Add validation gate."],
    [8, "Validate using stricter company-deep-dive production gate.", "Added validator invoking harness production_gate_failures and artifact-specific checks.", 9, "Validator catches shallow source, evidence, memo, and score failures.", "Publish through served URL."],
    [9, "Publish and verify the public URL.", "Copied public index to served directory and curl-checked the page.", 9, "The earlier CRWV 404 problem is now avoided.", "Summarize quality and next action."],
    [10, "Decide whether more loops should run now.", "Recommended EQIX or Oracle Q4 update before unattended twenty-loop expansion.", 8, "Harness is stronger, but research still needs more company breadth.", "Run EQIX company deep dive next."],
]
loop_rows = [
    {
        "iteration": i,
        "chose_20_words": chose,
        "did_20_words": did,
        "score_1_10": score,
        "score_reason_20_words": reason,
        "next_20_words": nxt,
    }
    for i, chose, did, score, reason, nxt in loop_summary
]

memo = """# Oracle Strategic Research Memo

## Original Question

Can company-level forecast-vs-actual evidence help us build an early-warning read on AI data-center buildout and downstream semiconductor demand?

## Answer

Yes, but Oracle answers a different part of the question than APLD or CRWV. Oracle is not yet a clean named-facility forecast-vs-actual ledger. It is a high-value hyperscaler demand and financing signal because it discloses RPO, OCI revenue, capex, and data-center/cloud-capacity lease commitments.

## Evidence

- RPO rose from $455B in FQ1 FY2026 to $553B in FQ3 FY2026.
- OCI IaaS revenue rose from $3.3B in FQ1 FY2026 to $4.9B in FQ3 FY2026.
- Year-to-date capex reached $39.170B by FQ3 FY2026, and FY2026 capex guidance stayed at $50B.
- Additional lease commitments rose from $99.8B at August 31, 2025 to $248B at November 30, 2025.
- Those lease commitments are substantially related to data centers and cloud capacity arrangements.

## Call

Oracle is a strong cycle signal but a weak project-level delivery ledger. It should be used to test whether hyperscaler AI demand is still expanding and whether that demand is translating into capex and long-duration data-center/cloud-capacity commitments.

## Watch Items

1. Does FQ4 FY2026 actual capex land near the $50B full-year guide?
2. Does OCI revenue continue converting RPO into recognized revenue?
3. Do new 10-Qs show lease commitments rising, flattening, or being revised?
4. Do calls or filings identify facility-level delivery timing?
5. Does NVDA data-center revenue lag Oracle RPO/capex changes?

## Remaining Gaps

The major gap is project specificity. Oracle's public disclosures are excellent for backlog, capex, and lease commitments, but this package does not yet map named data centers, grid capacity, GPU deployment, or ready-for-service dates to actual delivered revenue.

## What Would Change The View

- More bullish: Q4 capex hits guidance, OCI revenue keeps accelerating, and lease commitments remain firm.
- More bearish: RPO grows but OCI revenue slows, capex undershoots without explanation, or lease commitments are delayed/restructured.
"""


def make_dashboard() -> None:
    def table(title: str, rows: list[dict[str, object]]) -> str:
        headers = list(rows[0])
        bits = [f"<h2>{html.escape(title)}</h2>", "<table><thead><tr>"]
        bits += [f"<th>{html.escape(h)}</th>" for h in headers]
        bits.append("</tr></thead><tbody>")
        for row in rows:
            bits.append("<tr>")
            for h in headers:
                value = row[h]
                if h.endswith("url"):
                    bits.append(f'<td><a href="{html.escape(str(value))}">source</a></td>')
                else:
                    bits.append(f"<td>{html.escape(str(value))}</td>")
            bits.append("</tr>")
        bits.append("</tbody></table>")
        return "\n".join(bits)

    cards = [
        ("RPO", "$553B", "FQ3 FY2026"),
        ("OCI Revenue", "$4.9B", "FQ3 FY2026"),
        ("YTD Capex", "$39.170B", "FQ3 FY2026"),
        ("Lease Commitments", "$248B", "FQ2 FY2026"),
    ]
    card_html = "\n".join(f"<section><strong>{a}</strong><b>{b}</b><span>{c}</span></section>" for a, b, c in cards)
    page = f"""<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ORCL Strategic 10-Loop Research</title>
  <style>
    body {{ margin:0; font-family:Arial,sans-serif; background:#f7f8fa; color:#18202a; }}
    header {{ background:#4a1712; color:white; padding:28px 34px; }}
    main {{ padding:24px 34px 60px; }}
    .cards {{ display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:12px; margin:18px 0; }}
    section, article {{ background:white; border:1px solid #dfe4ea; border-radius:8px; padding:14px; }}
    .cards strong,.cards span {{ display:block; color:#53606f; font-size:13px; }}
    .cards b {{ display:block; font-size:28px; margin:8px 0; }}
    article {{ max-width:980px; line-height:1.45; }}
    table {{ width:100%; border-collapse:collapse; background:white; font-size:13px; margin-bottom:20px; }}
    th,td {{ border:1px solid #dde3ea; padding:8px; vertical-align:top; }}
    th {{ background:#eef2f6; text-align:left; }}
  </style>
</head>
<body>
  <header><h1>ORCL Strategic 10-Loop Research</h1><p>Oracle as hyperscaler AI demand, capex, RPO, and lease-commitment signal.</p></header>
  <main>
    <div class="cards">{card_html}</div>
    <article>{html.escape(memo).replace(chr(10), "<br>")}</article>
    {table("Loop Summary", loop_rows)}
    {table("Source Inventory", source_inventory)}
    {table("Source Fetch Log", fetch_log(source_inventory))}
    {table("Core Evidence Ledger", core_evidence_rows)}
    {table("Diagnostics", diagnostics)}
    {table("Signal Scores", signal_score_rows)}
    {table("APLD vs CRWV vs ORCL", comparison_rows)}
    {table("Decision Backlog", decision_rows)}
  </main>
</body>
</html>
"""
    (BASE / "strategic_orcl_dashboard.html").write_text(page, encoding="utf-8")
    (PUBLIC / "index.html").write_text(page, encoding="utf-8")
    (SERVED / "index.html").write_text(page, encoding="utf-8")


def main() -> None:
    write_csv("orcl_source_inventory.csv", source_inventory)
    write_csv("orcl_source_fetch_log.csv", fetch_log(source_inventory))
    write_csv("orcl_core_evidence_rows.csv", core_evidence_rows)
    write_csv("orcl_diagnostics.csv", diagnostics)
    write_csv("orcl_signal_scores.csv", signal_score_rows)
    write_csv("orcl_vs_apld_crwv_comparison.csv", comparison_rows)
    write_csv("orcl_decision_backlog.csv", decision_rows)
    write_csv("strategic10_loop_summary.csv", loop_rows)
    (BASE / "orcl_one_page_memo.md").write_text(memo, encoding="utf-8")
    make_dashboard()
    state = {
        "run_id": "strategic10-orcl-001",
        "status": "done",
        "built_at_utc": datetime.now(timezone.utc).isoformat(timespec="seconds"),
        "public_url": "http://204.168.210.83:8130/research-loop/strategic10-orcl-001/index.html",
    }
    (RUN / "state.json").write_text(json.dumps(state, indent=2) + "\n", encoding="utf-8")
    print("built strategic10 ORCL package")


if __name__ == "__main__":
    main()
