SPB Git

spb/firc0001_uqo

Public

FIRC-0001 v1.0 (2026-10-28): Russell 3000 Strategic Project Database — baseline import before peer review

Simon-Pierre Boucher committed (Nov 5, 2026) Browse files

87 changed files +2,468 −0

87 changed files
Unified Split
added .gitignore +14 −0
@@ -0,0 +1,14 @@
1+.DS_Store
2+*.aux
3+*.log
4+*.fls
5+*.fdb_latexmk
6+*.synctex.gz
7+*.out
8+*.toc
9+*.lof
10+*.lot
11+*.blg
12+*.bcf
13+*.run.xml
14+__pycache__/
added README.md +20 −0
@@ -0,0 +1,20 @@
1+# UQO Working Paper No. 0001 (FIRC-funded) — From Corporate Disclosures to a Russell 3000 Strategic Project Database
2+
3+*Identification, Measurement, Validation, and Research Applications* — Manel Kammoun, Djerrycharli Tandjambianda, Simon-Pierre Boucher (order to be confirmed; see `docs/contributions.md`). Version 1.0, October 28, 2026 — **working draft, research database under validation**. Research funded by the FIRC (Fonds institutionnel de recherche et de création, UQO).
4+
5+> **TL;DR** — SPID turns 527,548 SEC filings (10-K/10-Q/8-K, 2010–Oct 2026) of 2,564 issuers (S&P 500 ∪ iShares IWV Russell 3000 proxy) into 308,065 evidence-linked project mentions and 121,099 resolved candidate projects (21-category taxonomy, status, years, locations, technologies, partners, amounts). The paper documents the universe, the distributed acquisition/parsing architecture, the LLM extraction contract, the resolution rule and its failure modes, descriptive patterns, and a validation protocol — stating explicitly which gates are met and which are not.
6+
7+## Layout
8+```
9+paper/ main.tex, sections/, appendix/, tables/ (generated), figures/ (generated), references.bib → main.pdf
10+code/ export_stats.py (run on the master node against the two DuckDB files) · make_tables.py (CSV → LaTeX/figures)
11+data/ CSV exports + stats.json produced by export_stats.py (every number in the paper)
12+docs/ implementation_audit.md · universe_definition.md · project_ontology.md · data_dictionary.md · contributions.md · research_log.md
13+```
14+Database and code: `uqo-research-db.git` (revision 2324b58) on the UQO Git server; raw filings are not redistributed.
15+
16+## Rebuild the exhibits
17+```bash
18+# on the master node: python code/export_stats.py ~/UQO_RESEARCH_DB data/
19+python3 code/make_tables.py && cd paper && latexmk
20+```
added Russell3000-Strategic-Project-Database_WP0001_v1.0_2026-10-28.docx +0 −0

Binary file not shown.

added code/export_stats.py +84 −0
@@ -0,0 +1,84 @@
1+"""Export every number used in the working paper from the two DuckDB databases (run on the master node).
2+
3+Usage (on M3U96b): ~/UQO_RESEARCH_DB/.venv/bin/python export_stats.py ~/UQO_RESEARCH_DB <out_dir>
4+Writes CSV files (one per table) + stats.json. Nothing is hand-edited downstream.
5+"""
6+import csv, json, sys
7+from pathlib import Path
8+import duckdb
9+
10+root = Path(sys.argv[1]); out = Path(sys.argv[2]); out.mkdir(parents=True, exist_ok=True)
11+edgar = duckdb.connect(str(root / "data/duckdb/edgar_research.duckdb"), read_only=True)
12+spid = duckdb.connect(str(root / "project-db/data/db/spid.duckdb"), read_only=True)
13+# the src_* views stored in spid.duckdb point to the master node's parquet corpus (valid here)
14+S = {}
15+
16+def dump(name, con, sql):
17+ cur = con.execute(sql); cols = [d[0] for d in cur.description]; rows = cur.fetchall()
18+ with open(out / f"{name}.csv", "w", newline="") as f:
19+ w = csv.writer(f); w.writerow(cols); w.writerows(rows)
20+ print(f"{name}: {len(rows)} rows"); return rows
21+
22+# ---- universe reconciliation -------------------------------------------------
23+uni = json.load(open(root / "data/metadata/universe.json"))
24+ru = json.load(open(root / "data/metadata/russell3000_constituents.json"))
25+sp = json.load(open(root / "data/metadata/sp500_constituents.json"))
26+S["universe"] = {"built_at": uni["built_at"], "n": uni["count"], "n_sp500": uni["n_sp500"], "n_russell": uni["n_russell3000"],
27+ "russell_holdings_as_of": ru["holdings_as_of"], "russell_fetched_at": ru["fetched_at"],
28+ "russell_equity_lines_mapped": ru["count"], "russell_unmatched": len(ru["unmatched"]),
29+ "russell_unmatched_list": ru["unmatched"], "sp500_fetched_at": sp["fetched_at"], "sp500_rows": sp["count"],
30+ "russell_distinct_ciks": len({c["cik"] for c in ru["constituents"]}),
31+ "both": sum(1 for c in uni["constituents"] if c["in_sp500"] and c["in_russell3000"]),
32+ "sp_only": sum(1 for c in uni["constituents"] if c["in_sp500"] and not c["in_russell3000"]),
33+ "ru_only": sum(1 for c in uni["constituents"] if not c["in_sp500"] and c["in_russell3000"])}
34+S["issuers_with_filings"] = edgar.execute("select count(distinct cik) from filings").fetchone()[0]
35+S["issuers_without_filings"] = edgar.execute("select count(*) from companies c where not exists (select 1 from filings f where f.cik=c.cik)").fetchone()[0]
36+dump("issuers_without_filings", edgar, "select ticker, company_name, sector, in_sp500, in_russell3000 from companies c where not exists (select 1 from filings f where f.cik=c.cik) order by ticker")
37+S["issuers_with_projects"] = spid.execute("select count(distinct cik) from projects").fetchone()[0]
38+
39+# ---- corpus -------------------------------------------------------------------
40+dump("filings_by_form", edgar, "select form_type, count(*) n, min(filing_date) first, max(filing_date) last, round(sum(file_size_bytes)/1e9,1) gb from filings group by 1 order by 1")
41+dump("filings_by_year_form", edgar, "select extract(year from filing_date)::int yr, form_type, count(*) n, count(distinct cik) issuers from filings group by 1,2 order by 1,2")
42+dump("issuers_by_year", edgar, "select extract(year from filing_date)::int yr, count(distinct cik) issuers, count(*) filings from filings group by 1 order by 1")
43+dump("parse_status", edgar, "select form_type, parse_status, count(*) n from filings group by 1,2 order by 1,2")
44+dump("sections_by_form", edgar, "select f.form_type, count(*) sections, round(avg(s.word_count)) mean_words, round(median(s.word_count)) median_words from filing_sections s join filings f using(accession_number) group by 1 order by 1")
45+dump("sections_10k_by_item", edgar, "select s.section_name, count(*) n, round(median(s.word_count)) median_words from filing_sections s join filings f using(accession_number) where f.form_type='10-K' group by 1 order by 2 desc")
46+S["chunks"] = edgar.execute("select count(*), round(avg(word_count),1) from filing_chunks").fetchone()
47+S["embeddings"] = edgar.execute("select count(*) from embeddings_metadata").fetchone()[0]
48+S["sections_total"] = edgar.execute("select count(*) from filing_sections").fetchone()[0]
49+S["filings_total"] = edgar.execute("select count(*) from filings").fetchone()[0]
50+S["raw_gb"] = edgar.execute("select round(sum(file_size_bytes)/1e9,1) from filings").fetchone()[0]
51+dump("companies_by_sector", edgar, "select sector, count(*) n, count(*) filter (where in_sp500) sp500, count(*) filter (where in_russell3000) russell from companies group by 1 order by 2 desc")
52+dump("companies_by_shard", edgar, "select shard_node, count(*) n from companies group by 1 order by 1")
53+dump("factor_tables", edgar, "select 'chunk_scores' t, count(*) n from chunk_scores union all select 'filing_scores', count(*) from filing_scores union all select 'annual_panel', count(*) from annual_panel union all select 'quarterly_panel', count(*) from quarterly_panel")
54+
55+# ---- extraction ledger --------------------------------------------------------
56+dump("ledger_by_form", spid, "select v.form_type, count(*) sections, count(*) filter (where e.n_mentions>0) with_mentions, sum(e.n_mentions) mentions, count(distinct v.cik) issuers from extracted_sections e join v_sections v using(section_id) group by 1 order by 1")
57+S["ledger_total"] = spid.execute("select count(*) from extracted_sections").fetchone()[0]
58+dump("ledger_10k_items", spid, "select v.section_name, count(*) n, sum(e.n_mentions) mentions from extracted_sections e join v_sections v using(section_id) where v.form_type='10-K' group by 1 order by 2 desc")
59+dump("ledger_10q_items", spid, "select v.section_name, count(*) n, sum(e.n_mentions) mentions from extracted_sections e join v_sections v using(section_id) where v.form_type='10-Q' group by 1 order by 2 desc")
60+
61+# ---- mentions / projects ------------------------------------------------------
62+S["mentions"] = spid.execute("select count(*) from project_mentions").fetchone()[0]
63+S["projects"] = spid.execute("select count(*) from projects").fetchone()[0]
64+dump("mentions_by_form", spid, "select form_type, count(*) n, round(avg(confidence),3) mean_conf from project_mentions group by 1 order by 2 desc")
65+dump("mentions_by_year", spid, "select extract(year from filing_date)::int yr, count(*) mentions, count(distinct cik) issuers from project_mentions group by 1 order by 1")
66+dump("projects_by_type", spid, "select project_type, count(*) n, round(100.0*count(*)/(select count(*) from projects),1) pct, count(*) filter (where total_amount_usd is not null) with_amount, round(median(total_amount_usd)/1e6,1) median_amount_musd, round(avg(n_mentions),2) mean_mentions from projects group by 1 order by 2 desc")
67+dump("projects_by_sector", spid, "select coalesce(sector,'(unknown)') sector, count(*) n, count(distinct cik) issuers, round(count(*)*1.0/count(distinct cik),1) per_issuer from projects group by 1 order by 2 desc")
68+dump("projects_by_status", spid, "select status, count(*) n, round(100.0*count(*)/(select count(*) from projects),1) pct from projects group by 1 order by 2 desc")
69+dump("projects_by_first_seen_year", spid, "select extract(year from first_seen)::int yr, count(*) n from projects group by 1 order by 1")
70+dump("mentions_per_project", spid, "select case when n_mentions=1 then '1' when n_mentions=2 then '2' when n_mentions<=5 then '3-5' when n_mentions<=10 then '6-10' else '>10' end bucket, count(*) n from projects group by 1 order by min(n_mentions)")
71+dump("projects_per_issuer", spid, "select quantile_cont(n, [0.1,0.25,0.5,0.75,0.9,0.99]) q, min(n) mn, max(n) mx, avg(n) mean from (select cik, count(*) n from projects group by 1)")
72+dump("projects_by_index", spid, "select co.in_sp500, co.in_russell3000, count(*) projects, count(distinct p.cik) issuers from projects p join src_companies co on p.cik=co.cik group by 1,2")
73+dump("confidence_dist", spid, "select round(confidence,1) conf, count(*) n from project_mentions group by 1 order by 1")
74+dump("amount_dist", spid, "select quantile_cont(total_amount_usd, [0.1,0.25,0.5,0.75,0.9,0.99]) q, count(*) n, round(sum(total_amount_usd)/1e9,1) sum_gusd from projects where total_amount_usd is not null")
75+dump("duration", spid, "select case when last_year is null or first_year is null then 'unknown' when last_year=first_year then '0' when last_year-first_year<=2 then '1-2' when last_year-first_year<=5 then '3-5' else '>5' end span_years, count(*) n from projects group by 1 order by 1")
76+dump("kg", spid, "select 'nodes' k, node_type, count(*) n from kg_nodes group by 1,2 union all select 'edges', rel, count(*) from kg_edges group by 1,2 order by 1,3 desc")
77+dump("top_issuers", spid, "select ticker, company_name, sector, count(*) projects from projects group by 1,2,3 order by 4 desc limit 15")
78+dump("example_provenance", spid, "select p.project_id, p.ticker, p.project_name, p.project_type, p.status, p.total_amount_usd, p.n_mentions, p.first_seen, p.last_seen, t.accession_number, t.filing_date, t.form_type, t.status m_status, t.amount_usd, t.confidence, t.snippet from projects p join project_timeline t using(project_id) where p.n_mentions between 4 and 8 and p.total_amount_usd > 5e8 and p.project_type in ('plant_construction','data_center') order by p.n_filings desc, p.project_id, t.filing_date limit 24")
79+S["mentions_fields"] = spid.execute("select count(*) filter (where amount_usd is not null) amt, count(*) filter (where len(locations)>0) loc, count(*) filter (where len(technologies)>0) tech, count(*) filter (where len(partners)>0) partner, count(*) filter (where len(years)>0) yrs, count(*) from project_mentions").fetchone()
80+S["projects_fields"] = spid.execute("select count(*) filter (where total_amount_usd is not null), count(*) filter (where canonical_location is not null), count(*) filter (where len(technologies)>0), count(*) from projects").fetchone()
81+S["spid_runs"] = {"sp500_run": "2026-06 (gpt-4o-mini, 16 workers, 153,894 sections)", "russell_run": "2026-10-27/28 (gpt-4o-mini-2024-07-18, 48 workers, 968,134 sections)"}
82+S["extracted_at_range"] = [str(x) for x in spid.execute("select min(extracted_at), max(extracted_at) from extracted_sections").fetchone()]
83+json.dump(S, open(out / "stats.json", "w"), indent=2, default=str)
84+print(json.dumps(S, indent=1, default=str)[:3000])
added code/make_tables.py +195 −0
@@ -0,0 +1,195 @@
1+"""Build every LaTeX table and figure of the paper from data/*.csv (no hand-typed numbers)."""
2+import csv, json
3+from pathlib import Path
4+import matplotlib
5+matplotlib.use("Agg")
6+import matplotlib.pyplot as plt
7+
8+ROOT = Path(__file__).resolve().parent.parent
9+D, T, F = ROOT / "data", ROOT / "paper" / "tables", ROOT / "paper" / "figures"
10+T.mkdir(exist_ok=True); F.mkdir(exist_ok=True)
11+S = json.load(open(D / "stats.json"))
12+
13+def rows(name):
14+ with open(D / f"{name}.csv") as f:
15+ return list(csv.DictReader(f))
16+
17+def n(x, d=0):
18+ x = float(x)
19+ return f"{x:,.{d}f}".replace(",", "{,}")
20+
21+def w(name, body):
22+ (T / name).write_text(body); print("table", name)
23+
24+LAB = {"ma_integration": "M\\&A / integration", "capex_program": "Capital expenditure program", "partnership": "Strategic partnership",
25+ "geographic_expansion": "Geographic expansion", "plant_construction": "Plant / site construction", "cost_reduction": "Cost reduction / restructuring",
26+ "drug_pipeline": "Drug / clinical pipeline", "product_launch": "New product / launch", "rd_program": "R\\&D program",
27+ "infrastructure": "Infrastructure investment", "manufacturing_expansion": "Manufacturing / capacity expansion",
28+ "technology_deployment": "Technology deployment", "digital_transformation": "Digital transformation", "erp_implementation": "ERP / systems implementation",
29+ "sustainability": "Sustainability / ESG", "supply_chain": "Supply-chain initiative", "data_center": "Data center", "energy_transition": "Energy transition / renewables",
30+ "ai_initiative": "AI / ML initiative", "automation": "Automation / robotics", "cloud_migration": "Cloud migration"}
31+
32+# ---- Table: universe reconciliation ----
33+U = S["universe"]
34+w("tab_reconciliation.tex", f"""\\begin{{tabular}}{{lr}}
35+\\toprule
36+Step & Count \\\\
37+\\midrule
38+iShares IWV holdings file, equity lines with a ticker (as of {U['russell_holdings_as_of']}) & 2{{,}}587 \\\\
39+\\quad of which mapped to an SEC CIK (CVRs, escrow lines and FDIC-only banks excluded) & {n(U['russell_equity_lines_mapped'])} \\\\
40+\\quad distinct CIKs (share classes collapsed) & {n(U['russell_distinct_ciks'])} \\\\
41+S\\&P~500 constituents, cached snapshot of {U['sp500_fetched_at'][:10]} (500 CIKs) & {n(U['sp500_rows'])} \\\\
42+\\midrule
43+Merged issuer universe (distinct CIKs) & {n(U['n'])} \\\\
44+\\quad in both lists & {n(U['both'])} \\\\
45+\\quad S\\&P~500 only & {n(U['sp_only'])} \\\\
46+\\quad Russell proxy only & {n(U['ru_only'])} \\\\
47+\\midrule
48+Issuers with at least one 10-K, 10-Q or 8-K since 2010 & {n(S['issuers_with_filings'])} \\\\
49+Issuers without any such filing (foreign private issuers filing 20-F/40-F, FDIC-only banks) & {n(S['issuers_without_filings'])} \\\\
50+Issuers with at least one resolved project & {n(S['issuers_with_projects'])} \\\\
51+\\bottomrule
52+\\end{{tabular}}""")
53+
54+# ---- Table: filings by form ----
55+fb = rows("filings_by_form"); ps = rows("parse_status")
56+err = {}
57+for r in ps:
58+ err.setdefault(r["form_type"], {})[r["parse_status"]] = int(r["n"])
59+body = "\\begin{tabular}{lrrrrr}\n\\toprule\nForm & Filings & First filing & Last filing & Raw HTML (GB) & Not parsed \\\\\n\\midrule\n"
60+for r in fb:
61+ e = err.get(r["form_type"], {}); notp = e.get("error", 0) + e.get("missing", 0)
62+ body += f"{r['form_type']} & {n(r['n'])} & {r['first']} & {r['last']} & {float(r['gb']):.1f} & {notp} \\\\\n"
63+body += f"\\midrule\nTotal & {n(S['filings_total'])} & 2010-01-04 & 2026-10-27 & {S['raw_gb']:.1f} & {sum(e.get('error',0)+e.get('missing',0) for e in err.values())} \\\\\n\\bottomrule\n\\end{{tabular}}"
64+w("tab_filings.tex", body)
65+
66+# ---- Table: coverage by year ----
67+iy = rows("issuers_by_year"); fy = rows("filings_by_year_form"); py = {r["yr"]: r for r in rows("projects_by_first_seen_year")}; my = {r["yr"]: r for r in rows("mentions_by_year")}
68+byf = {}
69+for r in fy: byf.setdefault(r["yr"], {})[r["form_type"]] = r["n"]
70+body = "\\begin{tabular}{lrrrrrrr}\n\\toprule\nYear & Issuers & 10-K & 10-Q & 8-K & Mentions & Issuers w/ mentions & Projects first seen \\\\\n\\midrule\n"
71+for r in iy:
72+ y = r["yr"]; b = byf[y]
73+ body += f"{y} & {n(r['issuers'])} & {n(b['10-K'])} & {n(b['10-Q'])} & {n(b['8-K'])} & {n(my[y]['mentions'])} & {n(my[y]['issuers'])} & {n(py[y]['n'])} \\\\\n"
74+body += "\\bottomrule\n\\end{tabular}"
75+w("tab_coverage_year.tex", body)
76+
77+# ---- Table: sections and extraction ledger ----
78+sb = {r["form_type"]: r for r in rows("sections_by_form")}; lb = rows("ledger_by_form")
79+body = "\\begin{tabular}{lrrrrrr}\n\\toprule\nForm & Parsed sections & Median words & Sections sent to the extractor & Sections with $\\geq 1$ mention & Mentions & Issuers \\\\\n\\midrule\n"
80+tot = [0, 0, 0, 0]
81+for r in lb:
82+ s = sb[r["form_type"]]
83+ body += f"{r['form_type']} & {n(s['sections'])} & {n(s['median_words'])} & {n(r['sections'])} & {n(r['with_mentions'])} & {n(r['mentions'])} & {n(r['issuers'])} \\\\\n"
84+ tot[0] += int(s["sections"]); tot[1] += int(r["sections"]); tot[2] += int(r["with_mentions"]); tot[3] += int(r["mentions"])
85+body += f"\\midrule\nTotal & {n(tot[0])} & & {n(tot[1])} & {n(tot[2])} & {n(tot[3])} & {n(S['issuers_with_filings'])} \\\\\n\\bottomrule\n\\end{{tabular}}"
86+w("tab_sections.tex", body)
87+
88+# ---- Table: 10-K / 10-Q items in scope ----
89+body = "\\begin{tabular}{llrr}\n\\toprule\nForm & Section (item) & Sections extracted & Mentions \\\\\n\\midrule\n"
90+for r in rows("ledger_10k_items"): body += f"10-K & \\texttt{{{r['section_name'].replace('_','\\_')}}} & {n(r['n'])} & {n(r['mentions'])} \\\\\n"
91+body += "\\midrule\n"
92+for r in rows("ledger_10q_items"): body += f"10-Q & \\texttt{{{r['section_name'].replace('_','\\_')}}} & {n(r['n'])} & {n(r['mentions'])} \\\\\n"
93+body += "\\bottomrule\n\\end{tabular}"
94+w("tab_items.tex", body)
95+
96+# ---- Table: projects by type ----
97+pt = rows("projects_by_type")
98+body = "\\begin{tabular}{llrrrrr}\n\\toprule\nCode & Category & Projects & Share (\\%) & With amount & Median amount (M USD) & Mentions / project \\\\\n\\midrule\n"
99+for r in pt:
100+ body += f"\\texttt{{{r['project_type'].replace('_','\\_')}}} & {LAB[r['project_type']]} & {n(r['n'])} & {r['pct']} & {n(r['with_amount'])} & {n(r['median_amount_musd'],1)} & {float(r['mean_mentions']):.2f} \\\\\n"
101+body += f"\\midrule\n & Total & {n(S['projects'])} & 100.0 & {n(S['projects_fields'][0])} & 128.5 & {S['mentions']/S['projects']:.2f} \\\\\n\\bottomrule\n\\end{{tabular}}"
102+w("tab_types.tex", body)
103+
104+# ---- Table: by sector ----
105+cs = {r["sector"]: r for r in rows("companies_by_sector")}
106+body = "\\begin{tabular}{lrrrrr}\n\\toprule\nGICS sector (iShares) & Issuers in universe & of which S\\&P~500 & Issuers with projects & Projects & Projects per issuer \\\\\n\\midrule\n"
107+for r in rows("projects_by_sector"):
108+ c = cs.get(r["sector"], {"n": "", "sp500": ""})
109+ body += f"{r['sector']} & {n(c['n']) if c['n'] else ''} & {n(c['sp500']) if c['sp500'] else ''} & {n(r['issuers'])} & {n(r['n'])} & {float(r['per_issuer']):.1f} \\\\\n"
110+body += "\\bottomrule\n\\end{tabular}"
111+w("tab_sectors.tex", body)
112+
113+# ---- Table: status, mention buckets, duration, confidence ----
114+st = rows("projects_by_status"); mb = rows("mentions_per_project"); du = rows("duration"); cf = rows("confidence_dist")
115+body = "\\begin{tabular}{lr@{\\hspace{2em}}lr@{\\hspace{2em}}lr}\n\\toprule\nLatest status & Projects & Mentions per project & Projects & Span of cited years & Projects \\\\\n\\midrule\n"
116+du_order = ["0", "1-2", "3-5", ">5", "unknown"]; dus = {r["span_years"]: r["n"] for r in du}
117+rowsn = max(len(st), len(mb), len(du_order))
118+for i in range(rowsn):
119+ a = f"\\texttt{{{st[i]['status'].replace('_','\\_')}}} & {n(st[i]['n'])}" if i < len(st) else "&"
120+ b = f"{mb[i]['bucket']} & {n(mb[i]['n'])}" if i < len(mb) else "&"
121+ c = f"{du_order[i].replace('>','$>$')} years & {n(dus[du_order[i]])}" if i < len(du_order) else "&"
122+ body += f"{a} & {b} & {c} \\\\\n"
123+body += "\\bottomrule\n\\end{tabular}"
124+w("tab_status.tex", body)
125+body = "\\begin{tabular}{lr}\n\\toprule\nSelf-reported confidence (rounded) & Mentions \\\\\n\\midrule\n"
126+for r in cf: body += f"{r['conf']} & {n(r['n'])} \\\\\n"
127+body += "\\bottomrule\n\\end{tabular}"
128+w("tab_confidence.tex", body)
129+
130+# ---- Table: amounts ----
131+ad = rows("amount_dist")[0]; q = json.loads(ad["q"]); ppi = json.loads(rows("projects_per_issuer")[0]["q"])
132+body = f"""\\begin{{tabular}}{{lrrrrrr}}
133+\\toprule
134+ & P10 & P25 & Median & P75 & P90 & P99 \\\\
135+\\midrule
136+Largest amount cited per project, M USD ($N$ = {n(ad['n'])}) & {q[0]/1e6:,.1f} & {q[1]/1e6:,.0f} & {q[2]/1e6:,.1f} & {q[3]/1e6:,.0f} & {q[4]/1e6:,.0f} & {q[5]/1e6:,.0f} \\\\
137+Projects per issuer ($N$ = {n(S['issuers_with_projects'])}) & {ppi[0]:.0f} & {ppi[1]:.0f} & {ppi[2]:.0f} & {ppi[3]:.0f} & {ppi[4]:.0f} & {ppi[5]:.0f} \\\\
138+\\bottomrule
139+\\end{{tabular}}""".replace(",", "{,}")
140+w("tab_amounts.tex", body)
141+
142+# ---- Table: knowledge graph ----
143+kg = rows("kg")
144+body = "\\begin{tabular}{llr}\n\\toprule\n & Type & Count \\\\\n\\midrule\n"
145+for r in kg: body += f"{r['k'].capitalize()} & \\texttt{{{r['node_type'].replace('_','\\_')}}} & {n(r['n'])} \\\\\n"
146+body += "\\bottomrule\n\\end{tabular}"
147+w("tab_graph.tex", body)
148+
149+# ---- Table: top issuers ----
150+body = "\\begin{tabular}{llr}\n\\toprule\nTicker & Issuer & Resolved projects \\\\\n\\midrule\n"
151+for r in rows("top_issuers")[:12]: body += f"{r['ticker']} & {r['company_name'].title().replace('&','\\&')} & {n(r['projects'])} \\\\\n"
152+body += "\\bottomrule\n\\end{tabular}"
153+w("tab_top_issuers.tex", body)
154+
155+# ---- Table: provenance example (Cheniere Corpus Christi) ----
156+ex = [r for r in rows("example_provenance") if r["ticker"] == "LNG"]
157+body = "\\begin{tabular}{llllp{6.2cm}}\n\\toprule\nAccession & Filed & Form & Status / amount & Evidence snippet (model-generated description grounded in the section) \\\\\n\\midrule\n"
158+for r in ex[:8]:
159+ amt = f"{float(r['amount_usd'])/1e9:.1f} B USD" if r["amount_usd"] else "--"
160+ snip = r["snippet"].replace("&", "\\&").replace("%", "\\%").replace("$", "\\$")[:170]
161+ body += f"\\texttt{{{r['accession_number']}}} & {r['filing_date']} & {r['form_type']} & {r['m_status'].replace('_','\\_')} / {amt} & {snip} \\\\\n"
162+body += "\\bottomrule\n\\end{tabular}"
163+w("tab_provenance.tex", body)
164+S["example_project"] = {k: ex[0][k] for k in ("project_id", "ticker", "project_name", "project_type", "status", "total_amount_usd", "n_mentions", "first_seen", "last_seen")} if ex else {}
165+
166+# ---- Figures ----
167+BLUE, GOLD, GREY = "#16365c", "#c9a227", "#8a8a8a"
168+yrs = [int(r["yr"]) for r in iy]
169+fig, ax1 = plt.subplots(figsize=(7.2, 3.4))
170+ax1.bar(yrs, [int(r["filings"]) for r in iy], color=BLUE, alpha=.85, label="Filings (10-K, 10-Q, 8-K)")
171+ax1.set_ylabel("Filings per year"); ax1.set_xlabel("Filing year")
172+ax2 = ax1.twinx(); ax2.plot(yrs, [int(r["issuers"]) for r in iy], color=GOLD, marker="o", lw=2, label="Issuers with $\\geq$1 filing")
173+ax2.set_ylabel("Issuers"); ax2.set_ylim(0, 2700)
174+h1, l1 = ax1.get_legend_handles_labels(); h2, l2 = ax2.get_legend_handles_labels(); ax1.legend(h1 + h2, l1 + l2, loc="upper left", frameon=False, fontsize=8)
175+for a in (ax1, ax2): a.spines[["top"]].set_visible(False)
176+fig.tight_layout(); fig.savefig(F / "fig_coverage.pdf"); plt.close(fig)
177+
178+fig, ax = plt.subplots(figsize=(7.2, 5.2))
179+labs = [LAB[r["project_type"]].replace("\\&", "&") for r in pt][::-1]; vals = [int(r["n"]) for r in pt][::-1]
180+ax.barh(labs, vals, color=BLUE); ax.set_xlabel("Resolved projects"); ax.spines[["top", "right"]].set_visible(False)
181+for i, v in enumerate(vals): ax.text(v + 300, i, f"{v:,}", va="center", fontsize=7)
182+fig.tight_layout(); fig.savefig(F / "fig_types.pdf"); plt.close(fig)
183+
184+fig, ax = plt.subplots(figsize=(7.2, 3.2))
185+ax.plot(yrs, [int(my[str(y)]["mentions"]) for y in yrs], color=BLUE, marker="o", lw=2, label="Project mentions")
186+ax.plot(yrs, [int(py[str(y)]["n"]) for y in yrs], color=GOLD, marker="s", lw=2, label="Projects first observed")
187+ax.set_xlabel("Filing year"); ax.set_ylabel("Count"); ax.legend(frameon=False, fontsize=8); ax.spines[["top", "right"]].set_visible(False)
188+fig.tight_layout(); fig.savefig(F / "fig_mentions_year.pdf"); plt.close(fig)
189+
190+fig, ax = plt.subplots(figsize=(7.2, 3.0))
191+ax.bar([r["conf"] for r in cf], [int(r["n"]) for r in cf], width=0.08, color=BLUE)
192+ax.set_xlabel("Self-reported extraction confidence"); ax.set_ylabel("Mentions"); ax.spines[["top", "right"]].set_visible(False)
193+fig.tight_layout(); fig.savefig(F / "fig_confidence.pdf"); plt.close(fig)
194+print("figures ok")
195+json.dump(S, open(D / "stats.json", "w"), indent=2, default=str)
added code/tex2docx/post_docx.py +94 −0
@@ -0,0 +1,94 @@
1+import sys, re, datetime
2+from docx import Document
3+from docx.shared import Pt
4+from docx.enum.text import WD_ALIGN_PARAGRAPH
5+path = sys.argv[1]; doc = Document(path)
6+paras = doc.paragraphs
7+norm = lambda p: re.sub(r"\s+", " ", p.text.replace(" ", " ")).strip()
8+title = "From Corporate Disclosures to a Russell 3000 Strategic Project Database"
9+# --- title page: everything before the Abstract block is centred
10+i_abs = next(i for i, p in enumerate(paras) if norm(p) == "Abstract")
11+for p in paras[:i_abs - 1]:
12+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
13+ if norm(p) == title:
14+ for r in p.runs: r.font.size = Pt(18); r.font.bold = True
15+ elif norm(p).startswith("Identification, Measurement"):
16+ for r in p.runs: r.font.size = Pt(13)
17+# --- move References (pandoc appends it at the end) before the appendix, as in the PDF
18+h1 = [p for p in paras if p.style.name == "Heading 1"]
19+refs = next(p for p in h1 if norm(p) == "References")
20+appA = next(p for p in h1 if re.match(r"^A [A-Z]", norm(p)))
21+body = doc.element.body; kids = list(body)
22+i_refs, i_app = kids.index(refs._p), kids.index(appA._p)
23+moved = [k for k in kids[i_refs:] if k.tag.endswith("}sectPr") is False]
24+for k in moved: appA._p.addprevious(k)
25+# --- page breaks where the LaTeX source has \newpage
26+breaks = 0
27+paras[i_abs - 1].paragraph_format.page_break_before = True; breaks += 1
28+for p in h1:
29+ t = norm(p)
30+ if t.startswith("1 Introduction") or t == "References" or p is appA:
31+ p.paragraph_format.page_break_before = True; breaks += 1
32+# --- document properties
33+cp = doc.core_properties
34+cp.title = title
35+cp.subject = "Identification, Measurement, Validation, and Research Applications"
36+cp.author = "Manel Kammoun; Djerrycharli Tandjambianda; Simon-Pierre Boucher"
37+cp.keywords = "SEC filings, text as data, corporate investment, strategic projects, information extraction, large language models, entity resolution, Russell 3000"
38+cp.comments = "UQO Working Paper No. 0001 (FIRC-funded), Version 1.0, October 28, 2026 (working draft under validation)"
39+cp.created = cp.modified = datetime.datetime(2026, 10, 28, 12, 0)
40+# --- base fonts closer to the PDF (Times)
41+from docx.oxml.ns import qn
42+for st in doc.styles:
43+ try:
44+ if st.type == 1 and st.font is not None and (st.name == "Normal" or st.name.startswith("Heading") or st.name in ("Title", "Body Text", "First Paragraph", "Compact", "Caption", "Bibliography", "Block Text")):
45+ st.font.name = "Times New Roman"
46+ rpr = st.element.get_or_add_rPr(); rf = rpr.find(qn("w:rFonts"))
47+ if rf is None:
48+ rf = rpr.makeelement(qn("w:rFonts"), {}); rpr.append(rf)
49+ for a in ("w:ascii", "w:hAnsi", "w:eastAsia", "w:cs"): rf.set(qn(a), "Times New Roman")
50+ if st.name in ("Normal", "Body Text", "First Paragraph", "Compact"): st.font.size = Pt(12)
51+ except Exception:
52+ pass
53+# --- wide tables: smaller font and tight cell padding so that 8-10 column tables fit the page width
54+from docx.shared import Inches
55+from docx.oxml import OxmlElement
56+for sec in doc.sections:
57+ sec.left_margin = sec.right_margin = Inches(0.85)
58+for tbl in doc.tables:
59+ ncol = max(len(r.cells) for r in tbl.rows)
60+ if ncol >= 7:
61+ size = Pt(7) if ncol >= 9 else Pt(8)
62+ tbl.autofit = True
63+ for row in tbl.rows:
64+ for cell in row.cells:
65+ for para in cell.paragraphs:
66+ para.paragraph_format.space_after = Pt(0); para.paragraph_format.space_before = Pt(0)
67+ for run in para.runs: run.font.size = size
68+ tcPr = cell._tc.get_or_add_tcPr(); mar = OxmlElement("w:tcMar")
69+ for side in ("left", "right"):
70+ el = OxmlElement(f"w:{side}"); el.set(qn("w:w"), "30"); el.set(qn("w:type"), "dxa"); mar.append(el)
71+ tcPr.append(mar)
72+# --- proportional column widths for wide tables (pandoc emits equal 0.55-inch grid columns)
73+TOTAL = 9792 # twips: 8.5in - 2 x 0.85in margins
74+for tbl in doc.tables:
75+ ncol = max(len(r.cells) for r in tbl.rows)
76+ if ncol < 7: continue
77+ lens = [1] * ncol
78+ for row in tbl.rows:
79+ for j, cell in enumerate(row.cells[:ncol]):
80+ lens[j] = max(lens[j], min(28, max((len(w) for w in cell.text.split()), default=1)))
81+ weights = [max(l, 4) for l in lens]; tot = sum(weights); widths = [int(TOTAL * wgt / tot) for wgt in weights]
82+ grid = tbl._tbl.find(qn("w:tblGrid"))
83+ if grid is not None:
84+ for g, wd in zip(grid.findall(qn("w:gridCol")), widths): g.set(qn("w:w"), str(wd))
85+ tblPr = tbl._tbl.tblPr
86+ for e in tblPr.findall(qn("w:tblW")): e.set(qn("w:type"), "dxa"); e.set(qn("w:w"), str(TOTAL))
87+ for e in tblPr.findall(qn("w:tblLayout")): e.set(qn("w:type"), "fixed")
88+ for row in tbl.rows:
89+ for j, cell in enumerate(row.cells[:ncol]):
90+ tcPr = cell._tc.get_or_add_tcPr(); tcW = tcPr.find(qn("w:tcW"))
91+ if tcW is None: tcW = OxmlElement("w:tcW"); tcPr.insert(0, tcW)
92+ tcW.set(qn("w:type"), "dxa"); tcW.set(qn("w:w"), str(widths[j]))
93+doc.save(path)
94+print(f"post-processed: {i_abs-1} title-page paragraphs centred, {len(moved)} reference paragraphs moved before appendix, {breaks} page breaks, properties set")
added code/tex2docx/prep_tex.py +168 −0
@@ -0,0 +1,168 @@
1+"""Flatten a LaTeX working paper and resolve numbering from main.aux so that
2+pandoc produces a Word file with correct section/table/figure/equation numbers."""
3+import re, sys, pathlib
4+
5+PAPER = pathlib.Path(sys.argv[1]).resolve()
6+OUT = pathlib.Path(sys.argv[2])
7+warn = lambda *a: print("WARN:", *a, file=sys.stderr)
8+
9+def strip_comments(txt):
10+ out = []
11+ for line in txt.splitlines():
12+ if re.match(r'^\s*%', line):
13+ continue
14+ # drop trailing unescaped % comments
15+ line = re.sub(r'(?<!\\)%.*$', '', line)
16+ out.append(line)
17+ return "\n".join(out) + "\n"
18+
19+def inline(path):
20+ txt = strip_comments(path.read_text())
21+ def rep(m):
22+ name = m.group(1)
23+ p = PAPER / (name if name.endswith('.tex') else name + '.tex')
24+ if not p.exists():
25+ warn("missing input", p); return m.group(0)
26+ return inline(p)
27+ return re.sub(r'\\input\{([^}]+)\}', rep, txt)
28+
29+def brace_arg(s, i):
30+ """s[i] == '{' -> return (content, index after closing brace)"""
31+ assert s[i] == '{', s[i:i+20]
32+ depth = 0
33+ for j in range(i, len(s)):
34+ if s[j] == '{' and (j == 0 or s[j-1] != '\\'): depth += 1
35+ elif s[j] == '}' and s[j-1] != '\\':
36+ depth -= 1
37+ if depth == 0:
38+ return s[i+1:j], j+1
39+ raise ValueError("unbalanced braces")
40+
41+# ---------------------------------------------------------------- aux
42+aux = (PAPER / 'main.aux').read_text()
43+labels = {}
44+for m in re.finditer(r'\\newlabel\{([^}]+)\}\{', aux):
45+ outer, _ = brace_arg(aux, m.end()-1)
46+ num, _ = brace_arg(outer, 0)
47+ labels[m.group(1)] = num.strip()
48+toc = []
49+for m in re.finditer(r'\\contentsline \{(section|subsection|subsubsection)\}\{\\numberline \{([^}]*)\}(.*?)\}\{\d+\}\{', aux):
50+ toc.append((m.group(1), m.group(2), m.group(3)))
51+print(f"aux: {len(labels)} labels, {len(toc)} numbered headings")
52+
53+# ---------------------------------------------------------------- flatten
54+tex = inline(PAPER / 'main.tex')
55+
56+# ---- WP 28 shims ----------------------------------------------------------
57+# TikZ pipeline figure -> pre-rendered PNG
58+tex = re.sub(r'\\begin\{tikzpicture\}.*?\\end\{tikzpicture\}', r'\\includegraphics[width=\\textwidth]{figures/fig_pipeline.png}', tex, flags=re.S)
59+# PDF figures -> PNG (pandoc/docx cannot embed PDF)
60+tex = re.sub(r'\\includegraphics(\[[^\]]*\])?\{figures/([^}]+)\.pdf\}', r'\\includegraphics\1{figures/\2.png}', tex)
61+# \adjustbox{max width=...}{...} wrapper -> content only
62+def strip_adjustbox(t):
63+ out = []; pos = 0
64+ while True:
65+ m = re.search(r'\\adjustbox\{[^{}]*\}\{', t[pos:])
66+ if not m: break
67+ start = pos + m.start(); open_ = pos + m.end() - 1
68+ content, end = brace_arg(t, open_)
69+ out.append(t[pos:start]); out.append(content); pos = end
70+ out.append(t[pos:]); return "".join(out)
71+tex = strip_adjustbox(tex)
72+# threeparttable / tablenotes -> plain notes paragraph
73+tex = re.sub(r'\\(begin|end)\{threeparttable\}', '', tex)
74+tex = re.sub(r'\\begin\{tablenotes\}\\footnotesize\s*\\item\s*(.*?)\\end\{tablenotes\}', lambda m: '\\par\\textit{Notes:} ' + m.group(1).strip(), tex, flags=re.S)
75+# prompt block (quote + obeylines) -> verbatim
76+tex = re.sub(r'\\begin\{quote\}\\footnotesize\\ttfamily.*?\\end\{quote\}', lambda m: (PAPER / 'tables' / 'prompt_verbatim.tex').read_text(), tex, flags=re.S)
77+tex = re.sub(r'\\input\{tables/prompt\}', lambda m: (PAPER / 'tables' / 'prompt_verbatim.tex').read_text(), tex)
78+# tabularx -> tabular (pandoc handles tabular more reliably); X -> l
79+def fix_tabularx(m):
80+ spec = m.group(1).replace('X', 'l')
81+ return '\\begin{tabular}{' + spec + '}'
82+tex = re.sub(r'\\begin\{tabularx\}\{\\textwidth\}\{([^}]*)\}', fix_tabularx, tex)
83+tex = tex.replace('\\end{tabularx}', '\\end{tabular}')
84+tex = tex.replace('\\allowbreak', '')
85+tex = re.sub(r'\\setlength\{\\emergencystretch\}\{[^}]*\}', '', tex)
86+tex = re.sub(r'\\begin\{tabular\}\{lr@\{\\hspace\{2em\}\}lr@\{\\hspace\{2em\}\}lr\}', r'\\begin{tabular}{lrlrlr}', tex)
87+tex = re.sub(r'\\begin\{tabular\}\{llllp\{[^}]*\}\}', r'\\begin{tabular}{lllll}', tex)
88+
89+# titlepage: pandoc cannot evaluate \ifx -> keep the \else branch only
90+tex = re.sub(r'\\ifx\\WPsubtitle\\empty\\else(.*?)\\fi', r'\1', tex, flags=re.S)
91+
92+# appendix environment / counters that pandoc does not understand
93+tex = re.sub(r'\\(begin|end)\{appendices\}', '', tex)
94+tex = re.sub(r'\\renewcommand\{\\the(table|figure)\}\{[^{}]*(\{[^{}]*\})*[^{}]*\}', '', tex)
95+tex = re.sub(r'\\setcounter\{(table|figure)\}\{0\}', '', tex)
96+
97+# ---------------------------------------------------------------- headings
98+pos = 0; out = []; ti = 0
99+pat = re.compile(r'\\(section|subsection|subsubsection)\{')
100+while True:
101+ m = pat.search(tex, pos)
102+ if not m: break
103+ title, end = brace_arg(tex, m.end()-1)
104+ out.append(tex[pos:m.start()])
105+ lvl = m.group(1)
106+ if ti < len(toc) and toc[ti][0] == lvl:
107+ num = toc[ti][1]; ti += 1
108+ if re.sub(r'\W', '', toc[ti-1][2])[:12] != re.sub(r'\W', '', title)[:12]:
109+ warn("heading mismatch:", repr(toc[ti-1][2][:40]), "vs", repr(title[:40]))
110+ out.append(f"\\{lvl}{{{num}~{title}}}")
111+ else:
112+ warn("unnumbered heading kept:", lvl, title[:40])
113+ out.append(tex[m.start():end])
114+ pos = end
115+out.append(tex[pos:]); tex = "".join(out)
116+if ti != len(toc): warn(f"only {ti}/{len(toc)} toc entries consumed")
117+
118+# ---------------------------------------------------------------- floats: caption prefix
119+def fix_float(m):
120+ kind, body = m.group(1), m.group(2)
121+ labs = [l for l in re.findall(r'\\label\{([^}]+)\}', body) if l in labels]
122+ if not labs:
123+ warn("float without known label", body[:60]); return m.group(0)
124+ num = labels[labs[0]]
125+ word = 'Table' if kind == 'table' else 'Figure'
126+ cm = re.search(r'\\caption(\[[^\]]*\])?\{', body)
127+ if not cm: return m.group(0)
128+ cap, end = brace_arg(body, cm.end()-1)
129+ body = body[:cm.start()] + f"\\caption{{{word} {num}: {cap}}}" + body[end:]
130+ return f"\\begin{{{kind}}}{body}\\end{{{kind}}}"
131+tex = re.sub(r'\\begin\{(table|figure)\}(.*?)\\end\{\1\}', fix_float, tex, flags=re.S)
132+
133+# ---------------------------------------------------------------- equations: number tag
134+def fix_eq(m):
135+ body = m.group(1)
136+ lm = re.search(r'\\label\{([^}]+)\}', body)
137+ if lm and lm.group(1) in labels:
138+ body = body.replace(lm.group(0), '')
139+ body = body.rstrip() + f"\\qquad\\text{{({labels[lm.group(1)]})}}\n"
140+ return "\\begin{equation}" + body + "\\end{equation}"
141+tex = re.sub(r'\\begin\{equation\}(.*?)\\end\{equation\}', fix_eq, tex, flags=re.S)
142+
143+# ---------------------------------------------------------------- cross-references
144+missing = set()
145+def ref(m):
146+ k = m.group(2)
147+ if k not in labels:
148+ missing.add(k); return '??'
149+ return f"({labels[k]})" if m.group(1) == 'eqref' else labels[k]
150+tex = re.sub(r'\\(eqref|ref)\{([^}]+)\}', ref, tex)
151+if missing: warn("unresolved labels:", sorted(missing))
152+tex = re.sub(r'\\label\{[^}]+\}', '', tex)
153+
154+# booktabs partial rules have no Word equivalent and leak as text
155+tex = re.sub(r'\\cmidrule(\([^)]*\))?\{[^}]*\}', '', tex)
156+
157+# ---------------------------------------------------------------- pandoc shims
158+shims = r"""
159+\newcommand{\num}[1]{#1}
160+\renewcommand{\tablelegend}[1]{\par\textit{Notes:} #1}
161+\renewcommand{\tstat}[1]{(\textit{t}=#1)}
162+\newcommand{\textperiodcentered}{·}
163+"""
164+tex = tex.replace("\\begin{document}", shims + "\\begin{document}", 1)
165+tex = re.sub(r'(\\newcommand\{\\WPauthor\}\{[^}]*)\}', lambda m: m.group(1).replace('\\quad', '~~~~') + '}', tex)
166+tex = tex.replace("\\newcommand{\\textperiodcentered}{·}", "\\providecommand{\\textperiodcentered}{·}")
167+OUT.write_text(tex)
168+print("wrote", OUT, len(tex), "chars")
added data/amount_dist.csv +2 −0
@@ -0,0 +1,2 @@
1+q,n,sum_gusd
2+"[3200000.0, 23000000.0, 128500000.0, 500000000.0, 1503139999.9999979, 12500800000.000162]",56500,55910.9
added data/companies_by_sector.csv +12 −0
@@ -0,0 +1,12 @@
1+sector,n,sp500,russell
2+Financials,462,76,461
3+Health Care,433,59,433
4+Industrials,393,79,393
5+Information Technology,333,73,330
6+Consumer Discretionary,276,48,276
7+Real Estate,146,31,145
8+Materials,126,26,126
9+Energy,119,21,118
10+Communication Services,105,20,102
11+Consumer Staples,103,36,103
12+Utilities,68,31,68
added data/companies_by_shard.csv +11 −0
@@ -0,0 +1,11 @@
1+shard_node,n
2+M2U64,199
3+M3U96a,194
4+M3U96b,565
5+M4BP36,239
6+M4BP48,219
7+M4M64a,197
8+M4M64c,203
9+M5M36,198
10+m4mm,252
11+m4mn,298
added data/confidence_dist.csv +7 −0
@@ -0,0 +1,7 @@
1+conf,n
2+0.5,1611
3+0.6,3278
4+0.7,21647
5+0.8,72280
6+0.9,100831
7+1.0,108418
added data/duration.csv +6 −0
@@ -0,0 +1,6 @@
1+span_years,n
2+0,67170
3+1-2,17307
4+3-5,8697
5+>5,10326
6+unknown,17599
added data/example_provenance.csv +25 −0
@@ -0,0 +1,25 @@
1+project_id,ticker,project_name,project_type,status,total_amount_usd,n_mentions,first_seen,last_seen,accession_number,filing_date,form_type,m_status,amount_usd,confidence,snippet
2+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001193125-13-071643,2013-02-22,10-K,planned,800000000.0,0.8,Committed to new development projects totaling more than $800 million.
3+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001193125-14-080657,2014-03-03,10-K,in_progress,,0.9,We have seven development projects with First Choice Emergency Room that started in 2014.
4+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001193125-15-073689,2015-03-02,10-K,in_progress,,0.8,We have 11 development projects with First Choice Emergency Room currently under development.
5+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001193125-16-485612,2016-02-29,10-K,in_progress,,0.8,"The company has 10 development projects under development with Adeptus Health, Inc."
6+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001193125-18-067584,2018-03-01,10-K,in_progress,,0.8,"Three properties were under construction as of December 31, 2017."
7+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001564590-21-009901,2021-03-01,10-K,in_progress,,0.8,"The company has two development projects still under construction at December 31, 2020."
8+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0001564590-22-008100,2022-03-01,10-K,in_progress,,0.8,"Three properties were under construction as of December 31, 2021."
9+00461ab6a8ee44d809b436b3aaf59072,MPT,Development Projects with First Choice Emergency Room,plant_construction,in_progress,800000000.0,8,2013-02-22,2023-03-01,0000950170-23-005575,2023-03-01,10-K,in_progress,,0.8,"The portfolio includes several development projects still under construction as of December 31, 2022."
10+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-14-000070,2014-04-08,8-K,planned,,0.8,The construction of the first liquefaction train at the Corpus Christi Liquefaction facility is contingent upon receiving regulatory approvals and securing financing.
11+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-14-000114,2014-05-30,8-K,planned,,0.9,CCLNG will construct a second liquefaction train at its facility to sell LNG to IBERDROLA under a long-term agreement.
12+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-14-000117,2014-06-02,8-K,planned,,0.9,"Cheniere Energy, Inc. plans to construct a second liquefaction train at the Corpus Christi Liquefaction facility."
13+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-14-000139,2014-06-30,8-K,planned,,0.9,"Cheniere Energy, Inc. plans to construct a second liquefaction train at the Corpus Christi Liquefaction facility."
14+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-14-000153,2014-07-17,8-K,planned,,0.9,"Cheniere Energy, Inc. plans to construct a third liquefaction train at its Corpus Christi Liquefaction facility."
15+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-14-000306,2014-12-18,8-K,planned,,0.9,"Cheniere Energy, Inc. plans to construct a third liquefaction train at its Corpus Christi facility."
16+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0001193125-15-186045,2015-05-13,8-K,in_progress,11500000000.0,0.9,"The project involves the development, construction, operation, and maintenance of a natural gas liquefaction and export facility and related infrastructure in San Patricio County and Nueces County, Texas."
17+3e2705ba5b2dd27408b25f0145e34250,LNG,Corpus Christi Liquefaction Stage 3 Project,plant_construction,in_progress,11500000000.0,8,2014-04-08,2022-03-07,0000003570-22-000029,2022-03-07,8-K,in_progress,5484000000.0,1.0,"The project involves the engineering, procurement, construction, pre-commissioning, commissioning, start-up, and testing of a mid-scale natural gas liquefaction facility consisting of 7 liquefaction units in San Patricio County, Texas."
18+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-14-000029,2014-07-29,10-Q,in_progress,,0.8,The Company has two projects (one office and one mixed use) under active development.
19+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-15-000006,2015-02-12,10-K,in_progress,,0.8,The Company commenced development activities on two other development projects within its target markets.
20+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-16-000067,2016-07-28,10-Q,in_progress,,0.7,The company has three projects (two office and one mixed-use) under active development.
21+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-16-000076,2016-11-01,10-Q,in_progress,,0.8,"The Company has five projects (four office and one mixed-use) under active development as of September 30, 2016."
22+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-19-000006,2019-02-06,10-K,in_progress,245900000.0,0.8,"In 2018, the company commenced two new development projects as part of its ongoing strategy."
23+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-21-000008,2021-02-11,10-K,in_progress,449400000.0,0.9,"At year-end, we had five development projects in process with a total expected cost of $449.4 million."
24+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-21-000030,2021-04-29,10-Q,completed,,1.0,"The completed development and commencement of operations at Domain 10, Domain 12, and Avalon 10000 contributed to increased revenues and expenses."
25+5317e0ee56e32e3563b0d34a8ec04442,CUZ,"Development of Domain 10, Domain 12, and Avalon 10000",plant_construction,in_progress,759000000.0,8,2014-07-29,2022-02-03,0000025232-22-000005,2022-02-03,10-K,in_progress,759000000.0,0.9,"At year-end 2021, we had four development projects in process, and our share of the total expected costs of these projects totaled $759.0 million."
added data/factor_tables.csv +5 −0
@@ -0,0 +1,5 @@
1+t,n
2+chunk_scores,10720890
3+filing_scores,527531
4+annual_panel,32852
5+quarterly_panel,128043
added data/filings_by_form.csv +4 −0
@@ -0,0 +1,4 @@
1+form_type,n,first,last,gb
2+10-K,31775,2010-01-08,2026-10-27,88.6
3+10-Q,95167,2010-01-05,2026-10-27,155.9
4+8-K,400606,2010-01-04,2026-10-27,10.0
added data/filings_by_year_form.csv +52 −0
@@ -0,0 +1,52 @@
1+yr,form_type,n,issuers
2+2010,10-K,1357,1355
3+2010,10-Q,4152,1388
4+2010,8-K,17672,1383
5+2011,10-K,1409,1402
6+2011,10-Q,4260,1423
7+2011,8-K,18533,1416
8+2012,10-K,1436,1434
9+2012,10-Q,4363,1467
10+2012,8-K,19437,1464
11+2013,10-K,1483,1482
12+2013,10-Q,4530,1528
13+2013,8-K,19773,1534
14+2014,10-K,1549,1548
15+2014,10-Q,4780,1612
16+2014,8-K,20927,1615
17+2015,10-K,1635,1631
18+2015,10-Q,4983,1685
19+2015,8-K,22077,1687
20+2016,10-K,1692,1691
21+2016,10-Q,5146,1733
22+2016,8-K,22447,1741
23+2017,10-K,1745,1744
24+2017,10-Q,5359,1805
25+2017,8-K,22897,1807
26+2018,10-K,1827,1825
27+2018,10-Q,5582,1887
28+2018,8-K,23709,1892
29+2019,10-K,1903,1900
30+2019,10-Q,5829,1965
31+2019,8-K,24256,1971
32+2020,10-K,1985,1983
33+2020,10-Q,6095,2068
34+2020,8-K,27424,2088
35+2021,10-K,2116,2110
36+2021,10-Q,6606,2240
37+2021,8-K,27944,2246
38+2022,10-K,2258,2256
39+2022,10-Q,6830,2287
40+2022,8-K,26533,2290
41+2023,10-K,2295,2295
42+2023,10-Q,6938,2326
43+2023,8-K,26851,2329
44+2024,10-K,2341,2340
45+2024,10-Q,7092,2382
46+2024,8-K,27438,2386
47+2025,10-K,2406,2405
48+2025,10-Q,7300,2448
49+2025,8-K,28716,2453
50+2026,10-K,2338,2337
51+2026,10-Q,5322,2493
52+2026,8-K,23972,2498
added data/issuers_by_year.csv +18 −0
@@ -0,0 +1,18 @@
1+yr,issuers,filings
2+2010,1392,23181
3+2011,1424,24202
4+2012,1472,25236
5+2013,1539,25786
6+2014,1619,27256
7+2015,1689,28695
8+2016,1744,29285
9+2017,1810,30001
10+2018,1893,31118
11+2019,1973,31988
12+2020,2089,35504
13+2021,2247,36666
14+2022,2291,35621
15+2023,2329,36084
16+2024,2387,36871
17+2025,2453,38422
18+2026,2498,31632
added data/issuers_without_filings.csv +66 −0
@@ -0,0 +1,66 @@
1+ticker,company_name,sector,in_sp500,in_russell3000
2+ALM,ALMONTY INDUSTRIES,Materials,False,True
3+AMBP,ARDAGH METAL PACKAGING SA,Materials,False,True
4+AS,AMER SPORTS INC,Consumer Discretionary,False,True
5+ASC,ARDMORE SHIPPING CORP,Energy,False,True
6+ASND,ASCENDIS PHARMA,Health Care,False,True
7+AU,ANGLOGOLD ASHANTI PLC,Materials,False,True
8+AUGO,AURA MINERALS INC,Materials,False,True
9+BBUC,BROOKFIELD BUSINESS CORP CLASS A,Industrials,False,True
10+BEPC,BROOKFIELD RENEWABLE SUBORDINATE V,Utilities,False,True
11+BIPC,BROOKFIELD INFRASTRUCTURE CORP CLA,Utilities,False,True
12+BIRK,BIRKENSTOCK HOLDING PLC,Consumer Discretionary,False,True
13+BLSH,BULLISH,Financials,False,True
14+BLX,BLADEX INC CLASS E,Financials,False,True
15+BORR,BORR DRILLING,Energy,False,True
16+BRSL,BRIGHTSTAR LOTTERY PLC,Consumer Discretionary,False,True
17+BTDR,BITDEER TECHNOLOGIES GROUP CLASS A,Information Technology,False,True
18+BULL,WEBULL CORP CLASS A,Financials,False,True
19+CMCL,CALEDONIA MINING PLC,Materials,False,True
20+CMDB,COSTAMARE BULKERS HOLDINGS LTD,Industrials,False,True
21+CRML,CRITICAL METALS CORP,Materials,False,True
22+CSIQ,CANADIAN SOLAR,Information Technology,False,True
23+DHT,DHT HOLDINGS,Energy,False,True
24+DLO,DLOCAL CLASS A,Financials,False,True
25+DOX,AMDOCS,Information Technology,False,True
26+DRUG,BRIGHT MINDS BIOSCIENCES,Health Care,False,True
27+ELE,ELEMENTAL ROYALTY CORP,Materials,False,True
28+FLNG,FLEX LNG,Energy,False,True
29+GENI,GENIUS SPORTS LTD,Consumer Discretionary,False,True
30+GFL,GFL ENVIRONMENTAL SUBORDINATE VOTI,Industrials,False,True
31+GFS,GLOBALFOUNDRIES,Information Technology,False,True
32+GLNG,GOLAR LNG,Energy,False,True
33+GLOB,GLOBANT,Information Technology,False,True
34+HSHP,HIMALAYA SHIPPING LTD,Industrials,False,True
35+LZM,LIFEZONE METALS LTD,Materials,False,True
36+MDWD,MEDIWOUND LTD,Health Care,False,True
37+MRX,MAREX GROUP LTD,Financials,False,True
38+NAT,NORDIC AMERICAN TANKERS,Energy,False,True
39+NEXN,NEXXEN INTERNATIONAL LTD,Communication Services,False,True
40+NTB,BANK OF NT BUTTERFIELD & SON LTD,Financials,False,True
41+NU,NU HOLDINGS CLASS A,Financials,False,True
42+NVGS,NAVIGATOR HOLDINGS,Energy,False,True
43+ONON,ON HOLDING AG CLASS A,Consumer Discretionary,False,True
44+OZK,BANK OZK,Financials,False,True
45+PAGS,PAGSEGURO DIGITAL LTD CLASS A,Financials,False,True
46+PAX,PATRIA INVESTMENTS LTD CLASS A,Financials,False,True
47+PFBC,PREFERRED BANK,Financials,False,True
48+PLGO,PELAGOS INSURANCE CAPITAL LTD,Financials,False,True
49+PSFE,PAYSAFE LTD,Financials,False,True
50+QGEN,QIAGEN,Health Care,False,True
51+SB,SAFE BULKERS,Industrials,False,True
52+SFL,SFL,Energy,False,True
53+SGHC,SUPER GROUP,Consumer Discretionary,False,True
54+SPOT,SPOTIFY TECHNOLOGY,Communication Services,False,True
55+STNE,STONECO CLASS A,Financials,False,True
56+STNG,SCORPIO TANKERS,Energy,False,True
57+TATT,TAT TECHNOLOGIES LTD,Industrials,False,True
58+TIGO,MILLICOM INTERNATIONAL CELLULAR,Communication Services,False,True
59+TK,TEEKAY CORPORATION CORP LTD,Energy,False,True
60+TNK,TEEKAY TANKERS LTD CLASS A,Energy,False,True
61+TTAM,TITAN AMERICA SA,Materials,False,True
62+VIK,VIKING HOLDINGS LTD,Consumer Discretionary,False,True
63+VUECF,VERDERA ENERGY CORP,Energy,False,True
64+WSHP,WESHOP HOLDINGS CLASS A,Communication Services,False,True
65+XP,XP CLASS A INC,Financials,False,True
66+ZGN,ERMENEGILDO ZEGNA NV,Consumer Discretionary,False,True
added data/kg.csv +9 −0
@@ -0,0 +1,9 @@
1+k,node_type,n
2+edges,owns,121099
3+edges,located_in,54583
4+edges,uses,28774
5+nodes,Project,121099
6+nodes,Partner,36822
7+nodes,Location,14508
8+nodes,Technology,8551
9+nodes,Company,2492
added data/ledger_10k_items.csv +7 −0
@@ -0,0 +1,7 @@
1+section_name,n,mentions
2+business,29285,30492
3+risk_factors,29158,2797
4+md_and_a,28919,39099
5+properties,28236,7879
6+quantitative_disclosures,26876,531
7+full_body,1230,824
added data/ledger_10q_items.csv +9 −0
@@ -0,0 +1,9 @@
1+section_name,n,mentions
2+controls_and_procedures,92393,7082
3+financial_statements,91882,30159
4+md_and_a,91371,99305
5+quantitative_disclosures,83138,808
6+unregistered_sales,80210,9535
7+risk_factors,72560,4420
8+legal_proceedings,66998,845
9+full_document,241,107
added data/ledger_by_form.csv +4 −0
@@ -0,0 +1,4 @@
1+form_type,sections,with_mentions,mentions,issuers
2+10-K,143704,30877,81622,2464
3+10-Q,578793,75705,152261,2494
4+8-K,400601,61648,74594,2499
added data/mentions_by_form.csv +4 −0
@@ -0,0 +1,4 @@
1+form_type,n,mean_conf
2+10-Q,152004,0.885
3+10-K,81495,0.878
4+8-K,74566,0.922
added data/mentions_by_year.csv +18 −0
@@ -0,0 +1,18 @@
1+yr,mentions,issuers
2+2010,11908,1224
3+2011,13551,1287
4+2012,14302,1333
5+2013,14611,1408
6+2014,16274,1490
7+2015,17073,1535
8+2016,16429,1579
9+2017,17350,1636
10+2018,18064,1737
11+2019,18737,1806
12+2020,17970,1919
13+2021,21312,2077
14+2022,22720,2105
15+2023,22955,2129
16+2024,22503,2204
17+2025,23220,2280
18+2026,19086,2260
added data/mentions_per_project.csv +6 −0
@@ -0,0 +1,6 @@
1+bucket,n
2+1,72932
3+2,20544
4+3-5,17681
5+6-10,6173
6+>10,3769
added data/parse_status.csv +9 −0
@@ -0,0 +1,9 @@
1+form_type,parse_status,n
2+10-K,complete,31772
3+10-K,error,2
4+10-K,missing,1
5+10-Q,complete,95155
6+10-Q,error,11
7+10-Q,missing,1
8+8-K,complete,400605
9+8-K,error,1
added data/projects_by_first_seen_year.csv +18 −0
@@ -0,0 +1,18 @@
1+yr,n
2+2010,7835
3+2011,6679
4+2012,6444
5+2013,6286
6+2014,6732
7+2015,6675
8+2016,6240
9+2017,6490
10+2018,6815
11+2019,6837
12+2020,7212
13+2021,8216
14+2022,8158
15+2023,7977
16+2024,7819
17+2025,7990
18+2026,6694
added data/projects_by_index.csv +4 −0
@@ -0,0 +1,4 @@
1+in_sp500,in_russell3000,projects,issuers
2+False,True,93333,1992
3+True,True,27382,491
4+True,False,384,9
added data/projects_by_sector.csv +12 −0
@@ -0,0 +1,12 @@
1+sector,n,issuers,per_issuer
2+Health Care,23679,429,55.2
3+Industrials,17890,386,46.3
4+Consumer Discretionary,15072,267,56.4
5+Financials,14223,446,31.9
6+Information Technology,11756,325,36.2
7+Real Estate,9001,145,62.1
8+Materials,7407,117,63.3
9+Energy,6710,107,62.7
10+Consumer Staples,5921,103,57.5
11+Utilities,4920,66,74.5
12+Communication Services,4520,101,44.8
added data/projects_by_status.csv +5 −0
@@ -0,0 +1,5 @@
1+status,n,pct
2+in_progress,50327,41.6
3+completed,49687,41.0
4+planned,18340,15.1
5+mentioned,2745,2.3
added data/projects_by_type.csv +22 −0
@@ -0,0 +1,22 @@
1+project_type,n,pct,with_amount,median_amount_musd,mean_mentions
2+ma_integration,41742,34.5,28044,155.0,3.12
3+capex_program,10720,8.9,9452,300.0,1.79
4+partnership,9909,8.2,2943,75.0,1.77
5+geographic_expansion,7868,6.5,1431,36.7,2.25
6+plant_construction,7590,6.3,2511,56.0,2.42
7+cost_reduction,6481,5.4,4378,50.0,2.03
8+drug_pipeline,5360,4.4,778,46.1,4.99
9+product_launch,5356,4.4,447,26.7,2.33
10+rd_program,5080,4.2,1272,20.0,2.31
11+infrastructure,4481,3.7,1694,48.0,1.9
12+manufacturing_expansion,4140,3.4,1040,45.0,2.13
13+technology_deployment,4128,3.4,1219,135.0,1.63
14+digital_transformation,2642,2.2,240,34.2,1.99
15+erp_implementation,1735,1.4,184,15.0,2.44
16+sustainability,1305,1.1,410,34.4,2.0
17+supply_chain,684,0.6,139,38.0,1.71
18+data_center,449,0.4,129,166.0,1.98
19+energy_transition,415,0.3,109,109.0,2.09
20+ai_initiative,376,0.3,20,127.5,1.68
21+automation,374,0.3,25,13.7,1.68
22+cloud_migration,264,0.2,35,50.0,1.97
added data/projects_per_issuer.csv +2 −0
@@ -0,0 +1,2 @@
1+q,mn,mx,mean
2+"[12.0, 24.0, 42.0, 67.0, 94.0, 140.0]",1,240,48.59510433386838
added data/sections_10k_by_item.csv +16 −0
@@ -0,0 +1,16 @@
1+section_name,n,median_words
2+market_for_equity,31094,568.0
3+md_and_a,31000,10138.0
4+controls_and_procedures,30917,597.0
5+business,30738,5346.0
6+risk_factors,30685,9353.0
7+properties,30241,200.0
8+financial_statements,30096,16452.0
9+quantitative_disclosures,30025,502.0
10+legal_proceedings,29051,84.0
11+other_information,26506,2731.0
12+selected_financial_data,19509,524.0
13+changes_in_accounting,12628,2842.0
14+mine_safety,6240,533.0
15+unresolved_staff_comments,2391,53.0
16+full_document,86,72521.0
added data/sections_by_form.csv +4 −0
@@ -0,0 +1,4 @@
1+form_type,sections,mean_words,median_words
2+10-K,341207,5551.0,875.0
3+10-Q,600471,3843.0,500.0
4+8-K,400605,769.0,575.0
added data/stats.json +74 −0
@@ -0,0 +1,74 @@
1+{
2+ "universe": {
3+ "built_at": "2026-10-27T16:14:42.716940+00:00",
4+ "n": 2564,
5+ "n_sp500": 500,
6+ "n_russell": 2555,
7+ "russell_holdings_as_of": "Oct 23, 2026",
8+ "russell_fetched_at": "2026-10-27T16:14:42.459987+00:00",
9+ "russell_equity_lines_mapped": 2573,
10+ "russell_unmatched": 7,
11+ "russell_unmatched_list": [
12+ "TOWN (TOWNEBANK)",
13+ "FRBA (FIRST BANK)",
14+ "NBN (NORTHEAST BANK)",
15+ "HIFS (HINGHAM INSTITUTION FOR SAVINGS)",
16+ "ADRO (CHINOOK THERAPEUTICS INC)",
17+ "THRD (THIRD HARMONIC BIO INC)",
18+ "HOLX (HOLOGIC INC)"
19+ ],
20+ "sp500_fetched_at": "2026-05-25T16:15:58.318619+00:00",
21+ "sp500_rows": 503,
22+ "russell_distinct_ciks": 2555,
23+ "both": 491,
24+ "sp_only": 9,
25+ "ru_only": 2064
26+ },
27+ "issuers_with_filings": 2499,
28+ "issuers_without_filings": 65,
29+ "issuers_with_projects": 2492,
30+ "chunks": [
31+ 10720890,
32+ 479.7
33+ ],
34+ "embeddings": 10720890,
35+ "sections_total": 1342283,
36+ "filings_total": 527548,
37+ "raw_gb": 254.5,
38+ "ledger_total": 1123098,
39+ "mentions": 308065,
40+ "projects": 121099,
41+ "mentions_fields": [
42+ 120240,
43+ 118783,
44+ 36740,
45+ 81320,
46+ 252469,
47+ 308065
48+ ],
49+ "projects_fields": [
50+ 56500,
51+ 54583,
52+ 17694,
53+ 121099
54+ ],
55+ "spid_runs": {
56+ "sp500_run": "2026-06 (gpt-4o-mini, 16 workers, 153,894 sections)",
57+ "russell_run": "2026-10-27/28 (gpt-4o-mini-2024-07-18, 48 workers, 968,134 sections)"
58+ },
59+ "extracted_at_range": [
60+ "2026-10-27 13:12:32.140400",
61+ "2026-10-28 11:15:49.150818"
62+ ],
63+ "example_project": {
64+ "project_id": "3e2705ba5b2dd27408b25f0145e34250",
65+ "ticker": "LNG",
66+ "project_name": "Corpus Christi Liquefaction Stage 3 Project",
67+ "project_type": "plant_construction",
68+ "status": "in_progress",
69+ "total_amount_usd": "11500000000.0",
70+ "n_mentions": "8",
71+ "first_seen": "2014-04-08",
72+ "last_seen": "2022-03-07"
73+ }
74+}
\ No newline at end of file
added data/top_issuers.csv +16 −0
@@ -0,0 +1,16 @@
1+ticker,company_name,sector,projects
2+UEC,URANIUM ENERGY,Energy,240
3+UUUU,ENERGY FUELS,Energy,214
4+JBLU,JETBLUE AIRWAYS,Industrials,203
5+HAL,Halliburton,Energy,201
6+RIOT,RIOT PLATFORMS,Information Technology,195
7+EHC,ENCOMPASS HEALTH,Health Care,191
8+EBS,EMERGENT BIOSOLUTIONS,Health Care,187
9+MUX,MCEWEN INC,Materials,180
10+CDE,COEUR MINING,Materials,180
11+CLSK,CLEANSPARK,Information Technology,170
12+CWEN,CLEARWAY ENERGY INC CLASS C,Utilities,168
13+ALK,ALASKA AIR GROUP INC,Industrials,168
14+PENN,PENN ENTERTAINMENT,Consumer Discretionary,159
15+EOG,EOG Resources,Energy,159
16+APLE,APPLE HOSPITALITY REIT INC,Real Estate,155
added docs/contributions.md +15 −0
@@ -0,0 +1,15 @@
1+# Contributions (CRediT) — to be confirmed by all coauthors before circulation
2+
3+Author order used in the draft (UQO convention of the team's earlier WP 17): **Manel Kammoun, Djerrycharli (Charlie) Tandjambianda, Simon-Pierre Boucher**. The project brief proposed *Tandjambianda, Boucher, Kammoun*. **Open decision — the team must choose.** Affiliations, ORCID and corresponding author also to be confirmed.
4+
5+| Contribution (CRediT) | Kammoun | Tandjambianda | Boucher |
6+|---|---|---|---|
7+| Conceptualization | to confirm | to confirm | to confirm |
8+| Methodology (EDGAR corpus, SPID pipeline) | to confirm | to confirm | yes (code, Oct. 2026 extension) |
9+| Software / data curation (Russell 3000 build) | — | — | yes |
10+| Validation design | to confirm | to confirm | yes (protocol) |
11+| Writing – original draft (WP 28) | — | — | yes |
12+| Writing – review & editing | to confirm | to confirm | yes |
13+| Funding / supervision | to confirm | to confirm | to confirm |
14+
15+Nothing in this table attributes a role that has not been explicitly confirmed; cells marked "to confirm" are placeholders, not claims. The June 2026 methodological study (Tandjambianda, Boucher, Kammoun) and the S&P 500 release are prior joint work of the three authors.
added docs/data_dictionary.md +25 −0
@@ -0,0 +1,25 @@
1+# Data dictionary (current tables, revision 2324b58)
2+
3+## Corpus layer — `data/duckdb/edgar_research.duckdb` (68 GB) and `data/parquet/*`
4+| Table | Key | Fields |
5+|---|---|---|
6+| `companies` | `cik` | ticker, company_name, sector, sub_industry, sic_code, sic_description, state, state_of_incorporation, exchange, fiscal_year_end, date_added, in_sp500, in_russell3000, russell_weight_pct, shard_node |
7+| `filings` | `accession_number` | cik (FK), ticker, company_name, form_type, filing_date, report_date, fiscal_year, sec_url, local_path, primary_document, file_size_bytes, download_status, parse_status (complete/error/missing) |
8+| `filing_sections` | `section_id` (UUID) | accession_number (FK), section_name, item_number, section_order, text, word_count, char_count |
9+| `filing_chunks` | `chunk_id` (UUID) | section_id (FK), accession_number, chunk_text, chunk_order, word_count, char_count, start_char, end_char (offsets within the section) |
10+| `embeddings_metadata` / `parquet/embeddings/` | `embedding_id` | source_id = chunk_id, model all-MiniLM-L6-v2, 384-d float32 vectors in Parquet |
11+| `chunk_scores`, `filing_scores`, `quarterly_panel`, `annual_panel`, `*_final` | chunk / accession / firm-period | cosine scores to 8 thematic anchors and their aggregates |
12+
13+## Project layer — `project-db/data/db/spid.duckdb` (3.2 GB)
14+| Table | Key | Fields |
15+|---|---|---|
16+| `extracted_sections` | `section_id` | accession_number, backend, n_mentions, extracted_at (ledger of every section submitted) |
17+| `spid_sections` | `section_id` | legacy 10-K items ingested in June 2026 (27,164 rows) |
18+| `project_mentions` | `mention_id` | accession_number, cik, section_id, section_name, project_name, project_type, description, objective, amount_usd, amount_raw, locations[], technologies[], partners[], suppliers[], benefits[], risks[], years[], status, confidence, backend, ticker, company_name, form_type, filing_date, fiscal_year, sector |
19+| `projects` | `project_id` | cik, ticker, company_name, sector, project_type, project_name, canonical_location, technologies[], status, total_amount_usd (max), n_mentions, n_filings, first_seen, last_seen, first_year, last_year, avg_confidence, description |
20+| `project_timeline` | (project_id, accession_number) | filing_date, form_type, status, amount_usd, confidence, snippet |
21+| `kg_nodes` / `kg_edges` | node_id / (src, rel, dst) | node_type ∈ {Company, Project, Location, Technology, Partner}; rel ∈ {owns, located_in, uses} |
22+| `project_embeddings` | `project_id` | model, dim, vector FLOAT[384] |
23+| views `src_filings`, `src_sections`, `src_companies`, `v_sections` | — | read-only views over the corpus Parquet (paths of the master node) |
24+
25+Null conventions: unknown amounts/locations are NULL / empty lists; `fiscal_year` is NULL for mentions of the June 2026 run.
added docs/implementation_audit.md +33 −0
@@ -0,0 +1,33 @@
1+# Implementation audit — UQO WP 28 (checked-out revision 2324b58, 2026-10-28)
2+
3+Repository: `gitsrv:srv/git/uqo-research-db.git` (private UQO Git server; the public alias cited in the brief, `git.spboucher.ai/uqo-research-db.git`, points to the same server). The June 2026 methodological study examined `df7905d`; the paper's numbers come from the build at `2324b58`.
4+
5+## Inspected (paths as checked out)
6+- `src/acquisition/{universe,bulk_downloader,cik_mapper,sec_client}.py`, `scripts/{build_universe,download_filings,build_database,parse_filings,build_embeddings,build_factors,export_shard,merge_shards}.py`, `scripts/cluster/{orchestrate.py,rebalance.py,run_shard.sh,spid_run.sh}`
7+- `project-db/spid/{taxonomy,pipeline,resolve,graph,embed,store,config}.py`, `project-db/spid/extract/{base,rules,llm}.py`, `project-db/run_full_extract.py`
8+- Artifacts: `data/metadata/universe.json`, `shards.json`, `download_log*.jsonl`, the two DuckDB files, `data/logs/spid_extract.log` (outage window 2026-10-28 01:10 → 06:22)
9+- No `AGENTS.md`; `CLAUDE.md` files exist only at the user level. No unit tests exist for the SPID package.
10+
11+## Evidence-backed discrepancies vs. the brief
12+| # | Finding | Evidence | Priority |
13+|---|---|---|---|
14+| 1 | No human-labelled gold standard; precision/recall unknown | no `human_labels` table, no annotation files | High |
15+| 2 | Resolution key merges distinct phases / splits renamed projects; no link scores | `resolve.py` (MD5 of cik|type|loc/token); Cheniere example | High |
16+| 3 | `extracted_sections` cannot distinguish "no project" from "API failure" | 149,352 zero-mention rows written during the credit outage, removed by timestamp | High |
17+| 4 | Amounts untyped; project amount = max over mentions | `resolve.py`; `amount_raw` kept | High |
18+| 5 | No `project_events`; timeline = mention rows; cited years unordered | `project_timeline` schema | Medium |
19+| 6 | No prompt / taxonomy / schema hash per run | `extracted_sections.backend` = "llm" only | Medium |
20+| 7 | Evidence = model paraphrase; no character span of the supporting passage | `project_mentions.description` | Medium |
21+| 8 | Focus windows truncate long sections (budget ≈ 9–12.6k chars) — recall on 10-K MD&A unknown | `llm.py::_focus` | Medium |
22+| 9 | Universe: no file SHA-256, no match confidence, no membership dates, no security-vs-issuer table | `universe.json` | Medium |
23+| 10 | Form scope excludes 8-K exhibits (press releases) and 20-F/40-F (65 issuers) | `download_filings.py` | Medium |
24+| 11 | Sections lack offsets into raw HTML (chunks have offsets within sections) | `filing_sections` schema | Low |
25+| 12 | `pq.write_to_dataset(overwrite_or_ignore)` leaves stale partitions → duplicates in `filing_scores` until cleaned (done 2026-10-28) | `aggregation.py` | Low |
26+| 13 | Partner nodes of the knowledge graph have no edges | `graph.py` | Low |
27+
28+## Prioritised plan
29+1. Annotation study (protocol in paper §8): sample, two annotators, adjudication, locked holdout → metrics.
30+2. Schema migrations (additive): `mention_label`, `evidence_span`, `call_status` in the ledger, `extraction_runs` with hashes, `project_mention_links`, `project_events`, `project_amounts`, `human_labels`.
31+3. Probabilistic linker with reviewable clusters; re-derive `projects` from links.
32+4. Second extraction pass for `amount_type`; 8-K exhibit 99 ingestion; recall test on long MD&As with larger focus budgets.
33+5. Universe hardening (hashes, confidence, security table); evaluate a licensed historical constituent list.
added docs/project_ontology.md +28 −0
@@ -0,0 +1,28 @@
1+# Project ontology (SPID, revision 2324b58)
2+
3+## Units
4+- **project_mention** — one sourced statement about an initiative in one section of one filing (table `project_mentions`; id = SHA-1(accession | section_id | type | name)).
5+- **project** — a resolved initiative, one or more mentions (table `projects`; id = MD5(cik | project_type | first location or first ≥4-letter name token or "general")).
6+
7+## Labels (intended five-way classification)
8+| Label | Definition | Current implementation |
9+|---|---|---|
10+| `project` | distinct, identifiable, issuer-associated strategic initiative (planned/announced/ongoing/completed/postponed/cancelled) | stored |
11+| `generic_strategy` | non-specific aspirations ("we invest in innovation") | excluded by prompt rule + confidence ≥ 0.45; not stored |
12+| `historical_or_third_party_reference` | initiative not attributable to the sampled issuer | not stored (prompt instruction only) |
13+| `uncertain_candidate` | plausible initiative without identity evidence | not stored (confidence only) |
14+| `none` | no initiative | empty extraction |
15+
16+Proposed change: add `mention_label` (five values) and `evidence_span` (character offsets in the section) to `project_mentions`; filled by annotators first, then by a classifier evaluated on the gold standard.
17+
18+## Taxonomy
19+21 categories, unchanged since June 2026 (`project-db/spid/taxonomy.py`): manufacturing_expansion, plant_construction, data_center, cloud_migration, ai_initiative, digital_transformation, erp_implementation, rd_program, product_launch, drug_pipeline, ma_integration, geographic_expansion, capex_program, supply_chain, automation, sustainability, energy_transition, infrastructure, cost_reduction, partnership, technology_deployment. Any extension must be versioned with a crosswalk (old code → new code).
20+
21+## Status vocabulary
22+`planned | in_progress | completed | mentioned` (per mention; project status = latest mention by filing date). Proposed: dated `project_events` (announcement, filing/acceptance, expected start, observed start, expected completion, realized completion, postponement, cancellation).
23+
24+## Amounts
25+`amount_usd` per mention (coerced float, raw string kept); project `total_amount_usd` = **max** over mentions. Never sum across projects; never read as realized expenditure. Proposed `project_amounts` with `amount_type` ∈ {announced_budget, estimated_cost, capex_incurred, transaction_value, contract_value, financing, other}, currency, scale, basis, estimate/realized flag.
26+
27+## Known failure modes of the resolution key
28+False merges (same type + same location/token: multi-phase sites, serial acquirers in one city, drug programs sharing a token); false splits (renamed initiatives, location granularity, category change); no cross-issuer identity (JVs counted twice; inherited projects restart).
added docs/research_log.md +16 −0
@@ -0,0 +1,16 @@
1+# Research log — Russell 3000 strategic project database
2+
3+| Date | Decision / event | Owner | Status |
4+|---|---|---|---|
5+| 2026-05-25 | S&P 500 universe cached (Wikipedia table) | team | done |
6+| 2026-06 | SPID release 1: 500 issuers, 153,894 sections, 39,930 mentions, 19,227 projects (gpt-4o-mini) | team | done |
7+| 2026-10-27 | Extension to Russell 3000 requested; universe = iShares IWV holdings (23 Oct 2026) as proxy — **decision: snapshot estimand, no historical membership** | SPB | done |
8+| 2026-10-27 | Distributed build (10 cluster nodes + 1 external gateway); contiguous CIK shards rebalanced after 1 h (interleaving) | SPB | done |
9+| 2026-10-27/28 | Full LLM extraction of 968,134 new sections (48 workers), ≈ 775 USD total | SPB (authorised by SPB) | done |
10+| 2026-10-28 01:10–06:22 | OpenAI credit exhaustion → 149,352 ledger rows with 0 mentions removed and re-run; **decision: add `call_status` to the ledger** | SPB | rows fixed; schema change proposed |
11+| 2026-10-28 | Merge, factors, pdb-api redeploy, French methodological report updated, WP 28 draft (English) | SPB | done |
12+| open | Author order and CRediT confirmation | all | **pending** |
13+| open | Annotation study (sample design in WP 28 §8) | all | **pending** |
14+| open | Linkage audit; probabilistic linker | to assign | pending |
15+| open | Amount typing pass; 8-K exhibit ingestion; recall test on long MD&A | to assign | pending |
16+| open | Licensed Russell constituent history? | to assign | pending |
added docs/universe_definition.md +16 −0
@@ -0,0 +1,16 @@
1+# Universe definition (frozen for UQO WP 28, build of 2026-10-27/28)
2+
3+**Estimand.** Current constituents at a snapshot date, approximated by fund holdings. Not historical point-in-time Russell 3000 membership (no licensed constituent history was available).
4+
5+| Item | Value |
6+|---|---|
7+| Primary source | iShares Russell 3000 ETF (IWV) `latest-holdings.csv`, "Fund Holdings as of Oct 23, 2026", fetched 2026-10-27 (`data/metadata/russell3000_constituents.json`: `source`, `holdings_as_of`, `fetched_at`) |
8+| Secondary source | S&P 500 constituent table (Wikipedia), cached 2026-05-25 (`sp500_constituents.json`, 503 rows / 500 CIKs) |
9+| Identifier mapping | ticker → CIK via SEC `company_tickers.json` (fetched 2026-10-27); tickers normalised (`BRK B` → `BRK-B`) |
10+| Exclusions | asset class ≠ Equity; CVR / escrow / rights lines; tickers with no SEC CIK (FDIC-filing banks). The 21 unmatched lines are listed in the `unmatched` field of the artifact. |
11+| Result | 2,587 equity lines → 2,555 CIKs (share classes collapsed) → merged with S&P 500 → **2,564 issuers** (491 in both, 9 S&P-only, 2,064 Russell-only) |
12+| Filing scope | Forms 10-K, 10-Q, 8-K (primary document only, no amendments, no exhibits), filed 2010-01-01 → 2026-10-27 |
13+| Observed | 2,499 issuers with ≥1 filing; 65 without (foreign private issuers filing 20-F/40-F; FDIC banks) — `data/issuers_without_filings.csv` |
14+| Frozen artifact | `data/metadata/universe.json` (`built_at` 2026-10-27), copied to the paper folder under `data/` |
15+
16+**Open items (not implemented):** SHA-256 of the source files; per-row match confidence; membership dates; security-vs-issuer table (`universe_snapshots`); resolution of ticker changes and spin-offs over the sample period; a licensed historical constituent list.
added paper/.latexmkrc +5 −0
@@ -0,0 +1,5 @@
1+$pdf_mode = 1;
2+$pdflatex = 'pdflatex -interaction=nonstopmode -halt-on-error -synctex=1 %O %S';
3+$bibtex_use = 2;
4+$clean_ext = 'synctex.gz run.xml bbl bcf fdb_latexmk fls log aux out toc lof lot blg';
5+@default_files = ('main.tex');
added paper/Makefile +11 −0
@@ -0,0 +1,11 @@
1+.PHONY: all clean distclean
2+
3+all:
4+ latexmk
5+
6+clean:
7+ latexmk -c
8+
9+distclean:
10+ latexmk -C
11+ rm -f *.synctex.gz *.run.xml *.bbl *.bcf
added paper/appendix/appendix.tex +110 −0
@@ -0,0 +1,110 @@
1+% ============================================================================
2+\begin{appendices}
3+% ============================================================================
4+
5+\section{Taxonomy}\label{app:taxonomy}
6+
7+\begin{table}[H]
8+\centering\small
9+\caption{The 21-category SPID project taxonomy (\texttt{spid/taxonomy.py}, unchanged since the June 2026 release)}\label{tab:taxonomy}
10+\input{tables/tab_taxonomy}
11+\end{table}
12+
13+\section{Extraction contract}\label{app:prompt}
14+
15+The system prompt below is passed verbatim to the chat-completion endpoint, with the 21 taxonomy codes and labels substituted at the placeholder. The user message is the string ``\texttt{SECTION: <section\_name>}'' followed by the focused section text, truncated to 14{,}000 characters. Decoding parameters: temperature 0, JSON-object response format, 2{,}000 completion tokens. Model identifier recorded during the October 2026 run: \texttt{gpt-4o-mini-2024-07-18}.
16+
17+\input{tables/prompt}
18+
19+\noindent The candidate gate is the case-insensitive regular expression \texttt{\textbackslash b(project|initiative|program|invest|expan|construct|build|launch|develop|deploy|implement|acqui|partner|facilit|capacity|capital|migrat|transform|pipeline|plant|data ?cent|automat|sustainab|renewable|restructur)\textbackslash w*}. Sections without a match, or with fewer than 30 words, are not submitted.
20+
21+\section{Data dictionary and data-model audit}\label{app:dictionary}
22+
23+\begin{table}[H]
24+\centering\footnotesize
25+\caption{Tables required by the research design, current implementation and status}\label{tab:datamodel}
26+\begin{tabularx}{\textwidth}{lXl}
27+\toprule
28+Required table & Current implementation (database, table, key fields) & Status \\
29+\midrule
30+\texttt{universe\_snapshots} & \texttt{data/metadata/universe.json} + \texttt{companies} (CIK, ticker(s), name, sector, flags \texttt{in\_sp500}/\texttt{in\_russell3000}, fund weight, SIC, state, exchange, FYE, shard); snapshot date and source URL recorded & Partial: no file hash, no membership dates, no match confidence \\
31+\texttt{issuers} & \texttt{companies} (one row per CIK; former names from SEC header stored in metadata JSON only) & Partial: no name history, no parent/subsidiary links \\
32+\texttt{filings} & \texttt{filings} (accession PK, CIK FK, form, filing/report dates, SEC URL, local path, size, download and parse status) & Implemented; no acceptance timestamp, no checksum \\
33+\texttt{sections} & \texttt{filing\_sections} (UUID PK, accession FK, name, item, order, text, counts) & Implemented; no character offsets into raw HTML \\
34+\texttt{project\_mentions} & \texttt{project\_mentions} (SHA-1 id, accession, CIK, section id, name, type, description, objective, amount, raw amount, lists, years, status, confidence, backend, issuer context) & Implemented; no evidence span, no prompt hash, no five-way label \\
35+\texttt{projects} & \texttt{projects} (MD5 of CIK$|$type$|$location-or-name, representative fields, max amount, counts, first/last seen and cited years) & Implemented (deterministic key) \\
36+\texttt{project\_mention\_links} & implicit in the key (every mention maps to exactly one project) & Proposed: explicit table with score, resolver version, adjudication \\
37+\texttt{project\_events} & \texttt{project\_timeline} (one row per mention: date, form, status, amount, confidence, snippet) & Partial: mention rows, not typed events \\
38+\texttt{project\_amounts} & \texttt{amount\_usd}/\texttt{amount\_raw} on mentions; max on projects & Proposed: type, currency, scale, basis, realization flag \\
39+\texttt{extraction\_runs} & \texttt{extracted\_sections} ledger (section, accession, backend, n\_mentions, timestamp) + run logs & Partial: no prompt/schema/taxonomy hashes, no call status \\
40+\texttt{human\_labels} & --- & Proposed \\
41+\bottomrule
42+\end{tabularx}
43+\end{table}
44+
45+\noindent The full field-level dictionary of the current tables is maintained in \texttt{docs/data\_dictionary.md} of the working-paper folder and in the repository documentation.
46+
47+\section{Evidence register}\label{app:evidence}
48+
49+\begin{table}[H]
50+\centering\footnotesize
51+\caption{Evidence register: status of the main claims of this paper at revision \texttt{2324b58}}\label{tab:evidence}
52+\begin{tabularx}{\textwidth}{Xl}
53+\toprule
54+Claim & Status \\
55+\midrule
56+Universe of 2{,}564 issuers built from the IWV holdings file (2026-10-23) and the S\&P~500 cache (2026-05-25) & Implemented, verified (artifact counts) \\
57+527{,}548 primary documents of Forms 10-K/10-Q/8-K since 2010 downloaded; 16 unparsed & Implemented, verified (database counts) \\
58+Acquisition respected the SEC rate limit (aggregate token bucket; no 403 recorded) & Implemented; documented from run logs \\
59+1{,}342{,}283 sections, 10{,}720{,}890 chunks and embeddings; factor panels for 2{,}501 firms & Implemented, verified \\
60+1{,}123{,}098 sections submitted to the extractor; ledger free of outage artifacts & Implemented, verified after manual audit of the outage window \\
61+308{,}065 mentions, 121{,}099 projects, graph of 183{,}472 nodes & Implemented, verified \\
62+Each mention grounded in its source section & Documented (prompt rule), not mechanically verified \\
63+Extraction precision/recall, category accuracy, field accuracy, calibration & Not measured (protocol specified) \\
64+Linkage precision/recall of the resolution key & Not measured; failure modes documented with examples \\
65+Amounts typed and separated from realized expenditure & Not implemented (proposed) \\
66+Dated project events and point-in-time revision flags & Not implemented (mention-level timeline only) \\
67+Human-coded gold standard with two annotators and adjudication & Proposed \\
68+Author order, affiliations and CRediT contributions & Pending confirmation by the three coauthors \\
69+\bottomrule
70+\end{tabularx}
71+\end{table}
72+
73+\section{Additional tables}\label{app:tables}
74+
75+\begin{table}[H]
76+\centering\footnotesize
77+\caption{Coverage by filing year}\label{tab:coverage_year}
78+\adjustbox{max width=\textwidth}{\input{tables/tab_coverage_year}}
79+\end{table}
80+
81+\begin{table}[H]
82+\centering\small
83+\caption{Issuers with the most resolved projects}\label{tab:top_issuers}
84+\input{tables/tab_top_issuers}
85+\end{table}
86+
87+\begin{table}[H]
88+\centering\small
89+\caption{Self-reported confidence of mentions}\label{tab:confidence}
90+\input{tables/tab_confidence}
91+\end{table}
92+
93+\begin{table}[H]
94+\centering\small
95+\caption{Knowledge graph composition}\label{tab:graph}
96+\input{tables/tab_graph}
97+\end{table}
98+
99+\section{Replication}\label{app:replication}
100+
101+\begin{enumerate}
102+\item Clone the repository at revision \texttt{2324b58} and create the Python~3.12 environment (\texttt{requirements.txt}; DuckDB~2.0, pyarrow, lxml, sentence-transformers).
103+\item \texttt{python scripts/build\_universe.py} rebuilds \texttt{data/metadata/universe.json}; the frozen artifact used here is kept with the paper (\texttt{data/}) so that a later IWV file does not change the sample.
104+\item \texttt{python scripts/download\_filings.py --universe --rate 9} downloads the corpus (idempotent; about 255~GB; seven hours at two IP addresses). The cluster orchestration used for the October 2026 build is in \texttt{scripts/cluster/}.
105+\item \texttt{python scripts/build\_database.py}, \texttt{parse\_filings.py}, \texttt{build\_embeddings.py}, \texttt{build\_factors.py} build the corpus layer; \texttt{export\_shard.py}/\texttt{merge\_shards.py} are needed only for a distributed build.
106+\item In \texttt{project-db/}: \texttt{python -m spid pending} reports the sections to be extracted and the estimated cost; \texttt{python run\_full\_extract.py --workers 48} runs the extraction against the ledger; \texttt{python -m spid resolve graph embed analytics} builds the project layer.
107+\item In the working-paper folder: \texttt{python code/export\_stats.py <repo> data/} and \texttt{python code/make\_tables.py} regenerate every table and figure; \texttt{latexmk} in \texttt{paper/} compiles the manuscript.
108+\end{enumerate}
109+
110+\end{appendices}
added paper/figures/fig_confidence.pdf +0 −0

Binary file not shown.

added paper/figures/fig_confidence.png +0 −0

Binary file not shown.

added paper/figures/fig_coverage.pdf +0 −0

Binary file not shown.

added paper/figures/fig_coverage.png +0 −0

Binary file not shown.

added paper/figures/fig_mentions_year.pdf +0 −0

Binary file not shown.

added paper/figures/fig_mentions_year.png +0 −0

Binary file not shown.

added paper/figures/fig_pipeline.pdf +0 −0

Binary file not shown.

added paper/figures/fig_pipeline.png +0 −0

Binary file not shown.

added paper/figures/fig_types.pdf +0 −0

Binary file not shown.

added paper/figures/fig_types.png +0 −0

Binary file not shown.

added paper/main.bbl +109 −0
@@ -0,0 +1,109 @@
1+\ifx\undefined\bysame
2+\newcommand{\bysame}{\leavevmode\hbox to\leftmargin{\hrulefill\,\,}}
3+\fi
4+\begin{thebibliography}{xx}
5+
6+\harvarditem[Bloom et al.]{Bloom, Hassan, Kalyani, Lerner and
7+ Tahoun}{2021}{bloom2021}
8+{\bf Bloom, Nicholas, Tarek~A. Hassan, Aakash Kalyani, Josh Lerner, and Ahmed
9+ Tahoun}, ``The Diffusion of Disruptive Technologies,'' NBER Working Paper
10+ 28999, National Bureau of Economic Research 2021.
11+
12+\harvarditem[Christen]{Christen}{2012}{christen2012}
13+{\bf Christen, Peter}, {\it Data Matching: Concepts and Techniques for Record
14+ Linkage, Entity Resolution, and Duplicate Detection}, Berlin: Springer, 2012.
15+
16+\harvarditem[Cohen et al.]{Cohen, Malloy and Nguyen}{2020}{cohen2020}
17+{\bf Cohen, Lauren, Christopher Malloy, and Quoc Nguyen}, ``Lazy Prices,'' {\it
18+ Journal of Finance}, 2020, {\it 75} (3), 1371--1415.
19+
20+\harvarditem[Dyer et al.]{Dyer, Lang and Stice-Lawrence}{2017}{dyer2017}
21+{\bf Dyer, Travis, Mark Lang, and Lorien Stice-Lawrence}, ``The Evolution of
22+ 10-{K} Textual Disclosure: Evidence from {L}atent {D}irichlet {A}llocation,''
23+ {\it Journal of Accounting and Economics}, 2017, {\it 64} (2--3), 221--245.
24+
25+\harvarditem[Fellegi and Sunter]{Fellegi and Sunter}{1969}{fellegi1969}
26+{\bf Fellegi, Ivan~P. and Alan~B. Sunter}, ``A Theory for Record Linkage,''
27+ {\it Journal of the American Statistical Association}, 1969, {\it 64} (328),
28+ 1183--1210.
29+
30+\harvarditem[{FTSE Russell}]{{FTSE Russell}}{2026}{ftserussell2026}
31+{\bf {FTSE Russell}}, ``Russell {US} Equity Indexes: Construction and
32+ Methodology,'' \url{https://www.lseg.com/en/ftse-russell/indices/russell-us}
33+ 2026.
34+\newblock Accessed October 2026.
35+
36+\harvarditem[Gentzkow et al.]{Gentzkow, Kelly and Taddy}{2019}{gentzkow2019}
37+{\bf Gentzkow, Matthew, Bryan Kelly, and Matt Taddy}, ``Text as Data,'' {\it
38+ Journal of Economic Literature}, 2019, {\it 57} (3), 535--574.
39+
40+\harvarditem[Grimmer and Stewart]{Grimmer and Stewart}{2013}{grimmer2013}
41+{\bf Grimmer, Justin and Brandon~M. Stewart}, ``Text as Data: The Promise and
42+ Pitfalls of Automatic Content Analysis Methods for Political Texts,'' {\it
43+ Political Analysis}, 2013, {\it 21} (3), 267--297.
44+
45+\harvarditem[Hassan et al.]{Hassan, Hollander, van Lent and
46+ Tahoun}{2019}{hassan2019}
47+{\bf Hassan, Tarek~A., Stephan Hollander, Laurence van Lent, and Ahmed Tahoun},
48+ ``Firm-Level Political Risk: Measurement and Effects,'' {\it Quarterly
49+ Journal of Economics}, 2019, {\it 134} (4), 2135--2202.
50+
51+\harvarditem[Hoberg and Phillips]{Hoberg and Phillips}{2016}{hoberg2016}
52+{\bf Hoberg, Gerard and Gordon Phillips}, ``Text-Based Network Industries and
53+ Endogenous Product Differentiation,'' {\it Journal of Political Economy},
54+ 2016, {\it 124} (5), 1423--1465.
55+
56+\harvarditem[Hoberg and Maksimovic]{Hoberg and
57+ Maksimovic}{2015}{hobergmaksimovic2015}
58+{\bf \bysame{} and Vojislav Maksimovic}, ``Redefining Financial Constraints: A
59+ Text-Based Analysis,'' {\it Review of Financial Studies}, 2015, {\it 28} (5),
60+ 1312--1352.
61+
62+\harvarditem[Kelly et al.]{Kelly, Papanikolaou, Seru and
63+ Taddy}{2021}{kelly2021}
64+{\bf Kelly, Bryan, Dimitris Papanikolaou, Amit Seru, and Matt Taddy},
65+ ``Measuring Technological Innovation over the Long Run,'' {\it American
66+ Economic Review: Insights}, 2021, {\it 3} (3), 303--320.
67+
68+\harvarditem[Li et al.]{Li, Mai, Shen and Yan}{2021}{li2021}
69+{\bf Li, Kai, Feng Mai, Rui Shen, and Xinyan Yan}, ``Measuring Corporate
70+ Culture Using Machine Learning,'' {\it Review of Financial Studies}, 2021,
71+ {\it 34} (7), 3265--3315.
72+
73+\harvarditem[Loughran and McDonald]{Loughran and McDonald}{2011}{loughran2011}
74+{\bf Loughran, Tim and Bill McDonald}, ``When Is a Liability Not a Liability?
75+ {T}extual Analysis, Dictionaries, and 10-{K}s,'' {\it Journal of Finance},
76+ 2011, {\it 66} (1), 35--65.
77+
78+\harvarditem[Loughran and McDonald]{Loughran and McDonald}{2016}{loughran2016}
79+{\bf \bysame{} and \bysame{}}, ``Textual Analysis in Accounting and Finance: A
80+ Survey,'' {\it Journal of Accounting Research}, 2016, {\it 54} (4),
81+ 1187--1230.
82+
83+\harvarditem[Raasveldt and M{\"u}hleisen]{Raasveldt and
84+ M{\"u}hleisen}{2019}{raasveldt2019}
85+{\bf Raasveldt, Mark and Hannes M{\"u}hleisen}, ``{DuckDB}: An Embeddable
86+ Analytical Database,'' in ``Proceedings of the 2019 International Conference
87+ on Management of Data (SIGMOD)'' 2019, pp.~1981--1984.
88+
89+\harvarditem[Reimers and Gurevych]{Reimers and Gurevych}{2019}{reimers2019}
90+{\bf Reimers, Nils and Iryna Gurevych}, ``Sentence-{BERT}: Sentence Embeddings
91+ using {S}iamese {BERT}-Networks,'' in ``Proceedings of the 2019 Conference on
92+ Empirical Methods in Natural Language Processing (EMNLP-IJCNLP)'' 2019,
93+ pp.~3982--3992.
94+
95+\harvarditem[Tandjambianda et al.]{Tandjambianda, Boucher and
96+ Kammoun}{2026}{spid2026}
97+{\bf Tandjambianda, Djerrycharli, Simon-Pierre Boucher, and Manel Kammoun},
98+ ``{SPID} --- {SEC} Project Intelligence Database: A Methodological Report on
99+ the {S\&P}~500 Release,'' Internal methodological report, Université du
100+ Québec en Outaouais, Département des sciences administratives 2026.
101+\newblock Revision df7905d; October 28, 2026.
102+
103+\harvarditem[{U.S. Securities and Exchange Commission}]{{U.S. Securities and
104+ Exchange Commission}}{2024}{secfairaccess}
105+{\bf {U.S. Securities and Exchange Commission}}, ``Accessing {EDGAR} Data: Fair
106+ Access Policy,'' \url{https://www.sec.gov/os/accessing-edgar-data} 2024.
107+\newblock Accessed October 2026.
108+
109+\end{thebibliography}
added paper/main.pdf +0 −0

Binary file not shown.

added paper/main.tex +154 −0
@@ -0,0 +1,154 @@
1+% ============================================================================
2+% UQO Working Paper Template — Economics / Finance
3+% ============================================================================
4+\documentclass[12pt,letterpaper]{article}
5+
6+% --- Encoding & Language ---
7+\usepackage[utf8]{inputenc}
8+\usepackage[T1]{fontenc}
9+\usepackage[english]{babel}
10+
11+% --- Page Layout ---
12+\usepackage[
13+ letterpaper,
14+ top=1in,
15+ bottom=1in,
16+ left=1in,
17+ right=1in
18+]{geometry}
19+
20+% --- Typography ---
21+\usepackage{mathptmx} % Times-like font (text + math)
22+\usepackage{microtype} % Improved typographic refinements
23+\usepackage{setspace}
24+\onehalfspacing % 1.5 line spacing
25+
26+% --- Math ---
27+\usepackage{amsmath,amssymb,amsthm}
28+
29+% --- Tables ---
30+\usepackage{booktabs}
31+\usepackage{tabularx}
32+\usepackage{multirow}
33+\usepackage{threeparttable}
34+\usepackage{siunitx}
35+\sisetup{
36+ input-symbols = {()},
37+ table-format = -1.3,
38+ parse-numbers = false
39+}
40+
41+% --- Figures ---
42+\usepackage{graphicx}
43+\usepackage{subcaption}
44+\usepackage{float}
45+
46+% --- Colors & Links ---
47+\usepackage[dvipsnames]{xcolor}
48+\usepackage[
49+ colorlinks = true,
50+ linkcolor = NavyBlue,
51+ citecolor = NavyBlue,
52+ urlcolor = NavyBlue,
53+ breaklinks = true
54+]{hyperref}
55+
56+% --- Bibliography ---
57+\usepackage[authoryear,round,semicolon]{natbib}
58+\bibliographystyle{aer}
59+
60+% --- Captions ---
61+\usepackage[
62+ font = small,
63+ labelfont = bf,
64+ labelsep = period,
65+ justification = justified,
66+ singlelinecheck = false
67+]{caption}
68+
69+% --- Headers & Footers ---
70+\usepackage{fancyhdr}
71+\pagestyle{fancy}
72+\fancyhf{}
73+\renewcommand{\headrulewidth}{0pt}
74+\fancyfoot[C]{\thepage}
75+
76+% --- Section Formatting ---
77+\usepackage{titlesec}
78+\titleformat{\section}{\large\bfseries}{\thesection.}{0.5em}{}
79+\titleformat{\subsection}{\normalsize\bfseries}{\thesubsection.}{0.5em}{}
80+\titleformat{\subsubsection}{\normalsize\itshape}{\thesubsubsection.}{0.5em}{}
81+
82+% --- Appendix Support ---
83+\usepackage[toc,page]{appendix}
84+
85+% --- Misc ---
86+\setlength{\emergencystretch}{3em}
87+\usepackage{enumitem}
88+\setlist{nosep,leftmargin=*}
89+\usepackage{longtable}
90+\usepackage{tikz}
91+\usetikzlibrary{positioning,arrows.meta,shapes.misc}
92+\usepackage{pdflscape}
93+\usepackage{adjustbox}
94+
95+% ============================================================================
96+% METADATA — Edit these fields for each working paper
97+% ============================================================================
98+\newcommand{\WPnumber}{0001}
99+\newcommand{\WPtitle}{From Corporate Disclosures to a Russell~3000 Strategic Project Database}
100+\newcommand{\WPsubtitle}{Identification, Measurement, Validation, and Research Applications}
101+\newcommand{\WPdate}{October 28, 2026}
102+\newcommand{\WPversion}{1.0 (working draft under validation)}
103+\newcommand{\WPfunding}{Research funded by the FIRC (Fonds institutionnel de recherche et de création), Université du Québec en Outaouais.}
104+\newcommand{\WPabstract}{%
105+ We document the construction of SPID, a project-level research database that reconstructs the strategic initiatives disclosed by U.S.-listed companies in their SEC filings. Starting from an issuer universe that merges a current S\&P~500 snapshot with the holdings of the iShares Russell~3000 ETF (2{,}564 distinct SEC registrants), the pipeline collects every 10-K, 10-Q and 8-K filed since January 2010 (527{,}548 documents), segments them into 1.34 million sections, and submits 1.12 million eligible sections to a schema-constrained large-language-model extractor. The result is 308{,}065 evidence-linked project mentions, resolved into 121{,}099 candidate projects for 2{,}492 issuers, each tied to its SEC accession number, section and source passage, together with a controlled 21-category taxonomy, status, cited years, locations, technologies, partners and disclosed amounts. We describe the universe definition and its reconciliation, the distributed acquisition and parsing architecture, the extraction contract and its confidence semantics, the mention-to-project resolution rule and its known failure modes, and the point-in-time structure of the timelines. We then set out a validation framework---stratified human annotation, precision/recall on an independent gold standard, linkage audits and coverage gates---and state explicitly which of its components are implemented and which remain to be executed. Descriptive patterns are reported for the full corpus, with the caveat that a disclosed project is neither a verified investment nor a realized expenditure. The database should be read as a research instrument under validation rather than as an exhaustive census of Russell~3000 projects.%
106+}
107+\newcommand{\WPkeywords}{SEC filings, text as data, corporate investment, strategic projects, information extraction, large language models, entity resolution, Russell 3000, measurement validation}
108+\newcommand{\WPjel}{C55, C81, G31, G38, M41, O33}
109+
110+% --- Authors (order, affiliations and contributions to be confirmed by the team; see docs/contributions.md) ---
111+\newcommand{\WPauthor}{Manel Kammoun \quad Djerrycharli Tandjambianda \quad Simon-Pierre Boucher}
112+\newcommand{\WPaffiliation}{%
113+ Département des sciences administratives\\
114+ Université du Québec en Outaouais%
115+}
116+\newcommand{\WPemail}{%
117+ \href{mailto:manel.kammoun@uqo.ca}{manel.kammoun@uqo.ca} \,\textperiodcentered\,
118+ \href{mailto:djerrycharli.tandjambianda@uqo.ca}{djerrycharli.tandjambianda@uqo.ca} \,\textperiodcentered\,
119+ \href{mailto:simon-pierre.boucher@uqo.ca}{simon-pierre.boucher@uqo.ca}%
120+}
121+\newcommand{\WPaddress}{%
122+ Gatineau -- Pavillon Alexandre-Taché\\
123+ 283, boulevard Alexandre-Taché\\
124+ Gatineau, Québec, Canada J9A 1L8%
125+}
126+
127+% ============================================================================
128+% DOCUMENT
129+% ============================================================================
130+\begin{document}
131+
132+% --- Title Page ---
133+\input{sections/titlepage}
134+
135+% --- Main Body ---
136+\input{sections/introduction}
137+\input{sections/literature}
138+\input{sections/data}
139+\input{sections/methodology}
140+\input{sections/construct}
141+\input{sections/resolution}
142+\input{sections/results}
143+\input{sections/validation}
144+\input{sections/applications}
145+\input{sections/conclusion}
146+
147+% --- References ---
148+\newpage
149+\bibliography{references}
150+
151+% --- Appendix ---
152+\input{appendix/appendix}
153+
154+\end{document}
added paper/references.bib +137 −0
@@ -0,0 +1,137 @@
1+@article{loughran2011,
2+ author = {Loughran, Tim and McDonald, Bill},
3+ title = {When Is a Liability Not a Liability? {T}extual Analysis, Dictionaries, and 10-{K}s},
4+ journal = {Journal of Finance},
5+ year = {2011}, volume = {66}, number = {1}, pages = {35--65},
6+ doi = {10.1111/j.1540-6261.2010.01625.x}
7+}
8+@article{loughran2016,
9+ author = {Loughran, Tim and McDonald, Bill},
10+ title = {Textual Analysis in Accounting and Finance: A Survey},
11+ journal = {Journal of Accounting Research},
12+ year = {2016}, volume = {54}, number = {4}, pages = {1187--1230},
13+ doi = {10.1111/1475-679X.12123}
14+}
15+@article{hoberg2016,
16+ author = {Hoberg, Gerard and Phillips, Gordon},
17+ title = {Text-Based Network Industries and Endogenous Product Differentiation},
18+ journal = {Journal of Political Economy},
19+ year = {2016}, volume = {124}, number = {5}, pages = {1423--1465},
20+ doi = {10.1086/688176}
21+}
22+@article{hobergmaksimovic2015,
23+ author = {Hoberg, Gerard and Maksimovic, Vojislav},
24+ title = {Redefining Financial Constraints: A Text-Based Analysis},
25+ journal = {Review of Financial Studies},
26+ year = {2015}, volume = {28}, number = {5}, pages = {1312--1352},
27+ doi = {10.1093/rfs/hhu089}
28+}
29+@article{gentzkow2019,
30+ author = {Gentzkow, Matthew and Kelly, Bryan and Taddy, Matt},
31+ title = {Text as Data},
32+ journal = {Journal of Economic Literature},
33+ year = {2019}, volume = {57}, number = {3}, pages = {535--574},
34+ doi = {10.1257/jel.20181020}
35+}
36+@article{hassan2019,
37+ author = {Hassan, Tarek A. and Hollander, Stephan and van Lent, Laurence and Tahoun, Ahmed},
38+ title = {Firm-Level Political Risk: Measurement and Effects},
39+ journal = {Quarterly Journal of Economics},
40+ year = {2019}, volume = {134}, number = {4}, pages = {2135--2202},
41+ doi = {10.1093/qje/qjz021}
42+}
43+@article{cohen2020,
44+ author = {Cohen, Lauren and Malloy, Christopher and Nguyen, Quoc},
45+ title = {Lazy Prices},
46+ journal = {Journal of Finance},
47+ year = {2020}, volume = {75}, number = {3}, pages = {1371--1415},
48+ doi = {10.1111/jofi.12885}
49+}
50+@article{kelly2021,
51+ author = {Kelly, Bryan and Papanikolaou, Dimitris and Seru, Amit and Taddy, Matt},
52+ title = {Measuring Technological Innovation over the Long Run},
53+ journal = {American Economic Review: Insights},
54+ year = {2021}, volume = {3}, number = {3}, pages = {303--320},
55+ doi = {10.1257/aeri.20190499}
56+}
57+@techreport{bloom2021,
58+ author = {Bloom, Nicholas and Hassan, Tarek A. and Kalyani, Aakash and Lerner, Josh and Tahoun, Ahmed},
59+ title = {The Diffusion of Disruptive Technologies},
60+ institution = {National Bureau of Economic Research},
61+ type = {NBER Working Paper},
62+ number = {28999},
63+ year = {2021},
64+ doi = {10.3386/w28999}
65+}
66+@article{dyer2017,
67+ author = {Dyer, Travis and Lang, Mark and Stice-Lawrence, Lorien},
68+ title = {The Evolution of 10-{K} Textual Disclosure: Evidence from {L}atent {D}irichlet {A}llocation},
69+ journal = {Journal of Accounting and Economics},
70+ year = {2017}, volume = {64}, number = {2--3}, pages = {221--245},
71+ doi = {10.1016/j.jacceco.2017.07.002}
72+}
73+@article{li2021,
74+ author = {Li, Kai and Mai, Feng and Shen, Rui and Yan, Xinyan},
75+ title = {Measuring Corporate Culture Using Machine Learning},
76+ journal = {Review of Financial Studies},
77+ year = {2021}, volume = {34}, number = {7}, pages = {3265--3315},
78+ doi = {10.1093/rfs/hhaa079}
79+}
80+@article{grimmer2013,
81+ author = {Grimmer, Justin and Stewart, Brandon M.},
82+ title = {Text as Data: The Promise and Pitfalls of Automatic Content Analysis Methods for Political Texts},
83+ journal = {Political Analysis},
84+ year = {2013}, volume = {21}, number = {3}, pages = {267--297},
85+ doi = {10.1093/pan/mps028}
86+}
87+@article{fellegi1969,
88+ author = {Fellegi, Ivan P. and Sunter, Alan B.},
89+ title = {A Theory for Record Linkage},
90+ journal = {Journal of the American Statistical Association},
91+ year = {1969}, volume = {64}, number = {328}, pages = {1183--1210},
92+ doi = {10.1080/01621459.1969.10501049}
93+}
94+@book{christen2012,
95+ author = {Christen, Peter},
96+ title = {Data Matching: Concepts and Techniques for Record Linkage, Entity Resolution, and Duplicate Detection},
97+ publisher = {Springer},
98+ address = {Berlin},
99+ year = {2012},
100+ doi = {10.1007/978-3-642-31164-2}
101+}
102+@inproceedings{reimers2019,
103+ author = {Reimers, Nils and Gurevych, Iryna},
104+ title = {Sentence-{BERT}: Sentence Embeddings using {S}iamese {BERT}-Networks},
105+ booktitle = {Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing (EMNLP-IJCNLP)},
106+ year = {2019}, pages = {3982--3992},
107+ doi = {10.18653/v1/D19-1410}
108+}
109+@inproceedings{raasveldt2019,
110+ author = {Raasveldt, Mark and M{\"u}hleisen, Hannes},
111+ title = {{DuckDB}: An Embeddable Analytical Database},
112+ booktitle = {Proceedings of the 2019 International Conference on Management of Data (SIGMOD)},
113+ year = {2019}, pages = {1981--1984},
114+ doi = {10.1145/3299869.3320212}
115+}
116+@misc{secfairaccess,
117+ author = {{U.S. Securities and Exchange Commission}},
118+ title = {Accessing {EDGAR} Data: Fair Access Policy},
119+ howpublished = {\url{https://www.sec.gov/os/accessing-edgar-data}},
120+ year = {2024},
121+ note = {Accessed October 2026}
122+}
123+@misc{ftserussell2026,
124+ author = {{FTSE Russell}},
125+ title = {Russell {US} Equity Indexes: Construction and Methodology},
126+ howpublished = {\url{https://www.lseg.com/en/ftse-russell/indices/russell-us}},
127+ year = {2026},
128+ note = {Accessed October 2026}
129+}
130+@techreport{spid2026,
131+ author = {Tandjambianda, Djerrycharli and Boucher, Simon-Pierre and Kammoun, Manel},
132+ title = {{SPID} --- {SEC} Project Intelligence Database: A Methodological Report on the {S\&P}~500 Release},
133+ institution = {Université du Québec en Outaouais, Département des sciences administratives},
134+ type = {Internal methodological report},
135+ year = {2026},
136+ note = {Revision df7905d; October 28, 2026}
137+}
added paper/sections/applications.tex +25 −0
@@ -0,0 +1,25 @@
1+% ============================================================================
2+\section{Research Applications and Identification Limits}\label{sec:applications}
3+% ============================================================================
4+
5+\subsection{What the database can support}
6+
7+Four families of empirical designs are natural once the validation gates are passed; each is listed with the restriction it must respect.
8+
9+\paragraph{Project counts and composition as firm-year variables.} The issuer-year count of newly observed projects, by category, is a text-derived measure of disclosed strategic activity comparable in spirit to the thematic scores of the factor layer but with a discrete, interpretable unit. Because the panel is a snapshot universe, firm fixed effects and within-firm comparisons are safer than cross-sectional levels, and the later entry of smaller issuers (Figure~\ref{fig:coverage}) should be modeled explicitly.
10+
11+\paragraph{Technology adoption and diffusion.} The \texttt{technologies} field and the categories \texttt{ai\_initiative}, \texttt{data\_center}, \texttt{cloud\_migration}, \texttt{automation} and \texttt{erp\_implementation} allow the first-mention date of a technology to be located by issuer, sector and location, in the spirit of \citet{bloom2021} but from the issuer's own regulatory narrative. The small counts of these categories (a few hundred projects each) and the fact that technologies are free-text tags rather than a controlled vocabulary limit precision; a mapping of tags to a vocabulary is part of the proposed ontology work.
12+
13+\paragraph{Event studies.} Mentions in 8-K filings are dated to the day and are the closest the database comes to an announcement date (74{,}594 mentions). Because the primary document of an 8-K often refers to a press release filed as an exhibit that the pipeline does not read, the first \emph{public} announcement may precede the first mention in the corpus; event dates should be verified against the exhibit or the newswire before use.
14+
15+\paragraph{Project-level panels.} For projects with several mentions, the timeline provides a sequence of status and amount statements that can be used to study announcement-to-completion durations, budget revisions and abandonment, subject to the caveats on status (last statement, not terminal fate) and on merged phases.
16+
17+\subsection{Identification limits}
18+
19+\begin{itemize}
20+\item \textbf{Disclosure, not activity.} The database measures what issuers chose to say in a regulated document. Disclosure thresholds, materiality judgments, boilerplate habits and litigation risk vary by firm, sector and time; a difference in project counts is a difference in disclosure before it is a difference in investment.
21+\item \textbf{Snapshot universe.} Survivorship and backfilling are built in. Comparisons across years that mix cohorts of issuers confound activity with the composition of the observed panel.
22+\item \textbf{Measurement error of unknown structure.} Until the human-coded evaluation is run, extraction precision and recall, category accuracy and linkage error are unknown. They are unlikely to be classical: boilerplate-heavy sectors and long MD\&As are more exposed to false positives and to truncation, respectively.
23+\item \textbf{Amounts.} Disclosed amounts are heterogeneous in type and scope and are never realized expenditure; the project-level maximum is a reading convenience, not an estimate.
24+\item \textbf{Descriptive versus causal.} Associations between project disclosures and outcomes (returns, investment, employment) identify the joint determination of disclosure and outcomes, not the effect of the project. Causal designs require a source of variation in the \emph{project} that is orthogonal to the decision to \emph{describe} it, and the database does not supply one.
25+\end{itemize}
added paper/sections/conclusion.tex +15 −0
@@ -0,0 +1,15 @@
1+% ============================================================================
2+\section{Limitations, Reproducibility and Conclusion}\label{sec:conclusion}
3+% ============================================================================
4+
5+\subsection{Limitations}
6+
7+Beyond the identification limits above, the current build has concrete technical limitations that bound its use. The issuer universe is a fund-holdings proxy at one date, not licensed Russell membership, and 65 of its issuers are outside the form scope. The parser stores section-level provenance but not character offsets into the raw HTML. The focus windows truncate long sections around lexical cues, so recall on annual reports is unknown and probably lower than on 8-Ks. The resolver is a deterministic key that both merges distinct phases and splits renamed initiatives, and the resulting project count is therefore an artifact of the key as much as a count of initiatives. Amounts carry no type and no realization flag. The 10-Q financial-statement and 8-K sections are read whole, while 10-K Items 3--6 and 8--9B are not read at all. And the ledger cannot yet distinguish an empty result from a failed call.
8+
9+\subsection{Reproducibility, ethics and terms}
10+
11+The corpus layer and the project layer are reproducible from the Git repository at revision \texttt{2324b58}, the frozen universe artifact, the download logs and the extraction ledger; the paid extraction step is resumable and idempotent, and the complete run costs roughly 775~USD at October 2026 prices. All numbers in this paper are regenerated by \texttt{code/export\_stats.py} and \texttt{code/make\_tables.py}. The raw filings are public documents retrieved under the SEC's fair-access policy with an identified research client; they are not redistributed, and the repository contains no credentials and no private infrastructure details. Derived datasets (project-level, mention-level, issuer-year and event-time exports) will be released with sample restrictions and a data dictionary once gates (c), (d) and (g) are met. The database does not contain personal data; the only names it stores are those of corporations, places and technologies as they appear in public filings.
12+
13+\subsection{Conclusion}
14+
15+We have documented a reproducible pipeline that turns 527{,}548 SEC filings of 2{,}564 issuers into 308{,}065 evidence-linked statements about 121{,}099 disclosed strategic initiatives, each traceable to an accession number and a filing section, classified into a 21-category taxonomy and organized into timelines. We have been equally explicit about what has \emph{not} been done: the accuracy of extraction, classification and linkage has not been measured against independent human labels; amounts are not typed; events are not separated; the universe is a snapshot. The contribution of this paper is therefore the instrument and its audit rather than a validated measurement. The next step of the research program is the annotation study and the linkage audit described in Section~\ref{sec:validation}; only their results will determine whether the counts reported here can be used as variables, and with what measurement-error model. Until then, SPID is a research database under validation, and we ask that it be cited and used as such.
added paper/sections/construct.tex +35 −0
@@ -0,0 +1,35 @@
1+% ============================================================================
2+\section{The Project Construct and the Extraction Contract}\label{sec:construct}
3+% ============================================================================
4+
5+\subsection{What counts as a project}
6+
7+The unit of measurement is a \emph{distinct, identifiable, issuer-associated strategic initiative} involving a planned, announced, ongoing, completed, postponed or cancelled action: a plant expansion, a research program, a technology deployment, an AI initiative, a data-center build, a supply-chain transformation, an acquisition and its integration, a renewable-energy investment, and the other categories of the taxonomy. The database stores two tiers and, in its intended final form, five labels:
8+
9+\begin{itemize}
10+\item \texttt{project\_mention}: a particular sourced statement about an initiative, in one section of one filing, with the extracted fields and the model's one-sentence description;
11+\item \texttt{project}: a resolved initiative described by one or more mentions, carrying a stable identifier and a timeline;
12+\item \texttt{generic\_strategy}: non-specific aspirations (``we continue to invest in innovation'', ``we may pursue acquisitions'') that the contract instructs the model to exclude and that the resolver never sees unless the model fails to exclude them;
13+\item \texttt{historical\_or\_third\_party\_reference}: initiatives of a competitor, a customer or a former parent, mentioned in the issuer's filing but not attributable to the issuer;
14+\item \texttt{uncertain\_candidate}: a plausible initiative without enough identity evidence (no name, location, counterparty or amount) to be counted.
15+\end{itemize}
16+
17+The current build implements the first two tiers directly. The third is enforced only through the prompt and the confidence threshold; the fourth and fifth exist as instructions and as a confidence score, not as stored labels. Promoting them to explicit columns of the mention table, filled by annotators and eventually by a classifier, is the first schema change proposed in \texttt{docs/project\_ontology.md}.
18+
19+\subsection{Taxonomy}
20+
21+The SPID taxonomy has 21 categories (Table~\ref{tab:taxonomy} in Appendix~\ref{app:taxonomy}), defined in \texttt{spid/taxonomy.py} with a human-readable label and a set of lexical signals used by the rule-based extractor and by the candidate gate. The LLM extractor receives the 21 labels verbatim in its system prompt and must choose exactly one per project; a label outside the list is mapped back to the closest category by substring matching or, failing that, to \texttt{technology\_deployment}. The categories are not mutually exclusive in substance (an acquired plant is both \texttt{ma\_integration} and \texttt{plant\_construction}; a data center is also \texttt{infrastructure}), and the model's choice in such cases is a classification decision we have not yet audited. We document the taxonomy as it stands rather than redesign it: any extension would be versioned, with a crosswalk from old to new codes, so that counts published under the current version remain reproducible.
22+
23+\subsection{Candidate gating and focus windows}
24+
25+A section reaches the model only if it contains at least 30 words and matches a broad lexical gate (project, initiative, program, invest, expan-, construct, build, launch, develop, deploy, implement, acqui-, partner, facilit-, capacity, capital, migrat-, transform, pipeline, plant, data center, automat-, sustainab-, renewable, restructur-; see Appendix~\ref{app:prompt}). The gate is deliberately permissive---it exists to avoid paying for sections that cannot contain a project---and its false-negative rate is unknown; sampling rejected sections to estimate it is part of the validation plan. For sections longer than 9{,}000 characters, the extractor keeps windows of about 600 characters around each gate hit (merged when they overlap) up to a budget of roughly 9{,}000--12{,}600 characters, so that the cost per call is bounded regardless of section length. This ``focus'' step is the main source of \emph{silent truncation} in the pipeline: a project described far from any gate word, or beyond the budget in a very long MD\&A, is never shown to the model. The 10-K MD\&A sections submitted have a median length of 9{,}905 words (roughly 60{,}000 characters), so truncation is the rule rather than the exception for annual reports, and the recall of the pipeline on long sections is an open empirical question.
26+
27+\subsection{The extraction contract}
28+
29+The LLM call is a chat completion with a fixed system prompt, the section name and the focused text as user message, temperature~0, JSON-object output mode and a 2{,}000-token completion limit. The prompt (Appendix~\ref{app:prompt}) instructs the model to act as a financial analyst extracting corporate projects, lists the 21 allowed types, and returns a JSON object with one entry per project and the following fields: \texttt{project\_name}, \texttt{project\_type}, \texttt{description} (one sentence grounded in the text), \texttt{objective}, \texttt{status} (planned, in\_progress, completed, mentioned), \texttt{amount\_usd}, \texttt{locations}, \texttt{technologies}, \texttt{partners}, \texttt{benefits}, \texttt{risks}, \texttt{years} and \texttt{confidence}. Four rules in the prompt define the construct operationally: extract only real initiatives, not boilerplate or hypotheticals; return an empty list when no concrete project is described; do not invent facts---numbers and locations must appear in the text; prefer fewer high-quality projects. Output is post-processed deterministically: amounts are coerced to a float in USD (with the raw string retained in \texttt{amount\_raw}), list fields are truncated to eight items of at most 120 characters, years are parsed as four-digit integers, status is normalized, and mentions with confidence below 0.45 are dropped. The mention identifier is a SHA-1 of the accession number, section identifier, type and project name, so that re-running the extractor on the same section with the same output overwrites rather than duplicates.
30+
31+What the contract does \emph{not} guarantee should be equally explicit. The ``description'' is a model paraphrase, not a verbatim quote, and the character span of the supporting passage is not stored; grounding is therefore verifiable by a reader who opens the section, but not mechanically checkable. The model may extract a counterparty's initiative as the issuer's (the third-party label above). It may split one initiative into several entries when a section describes phases, or merge phases into one. It receives no information about the issuer other than the text, which protects against hallucinated background knowledge but also prevents it from recognizing a subsidiary name. And the same section is seen once; a project described across two sections of the same filing yields two mentions with distinct identifiers.
32+
33+\subsection{Cost, throughput and the extraction ledger}
34+
35+The October 2026 run submitted 968{,}134 sections in about twelve hours of effective extraction (48 concurrent calls, 27--36 sections per second), at a cost of roughly 775~USD for the full universe at current \texttt{gpt-4o-mini} prices (about 0.0008~USD per section). Every submitted section is written to an \texttt{extracted\_sections} ledger (section, accession, backend, number of mentions, timestamp), which makes the step resumable and prevents double billing. The ledger also protects against a failure mode encountered during the run: when the API key's credit balance was exhausted, the extractor's retry logic returned empty results that were recorded as ``extracted with zero mentions'' for 149{,}352 sections before the problem was detected. Those rows were removed from the ledger using the timestamp of the first quota error and the sections were re-submitted; the final ledger has no section processed during the outage. The episode is recorded in the research log because it illustrates a general point: a ledger that cannot distinguish ``no project'' from ``call failed'' is not sufficient, and an explicit \texttt{call\_status} column is now part of the proposed schema.
added paper/sections/data.tex +54 −0
@@ -0,0 +1,54 @@
1+% ============================================================================
2+\section{Sample: Issuer Universe and Filing Coverage}\label{sec:sample}
3+% ============================================================================
4+
5+\subsection{Estimand: current constituents at a snapshot date}
6+
7+Two different populations can be labeled ``the Russell~3000''. The first is the set of \emph{historical point-in-time constituents}: for each date, the companies that FTSE Russell included in the index after its annual reconstitution \citep{ftserussell2026}. The second is the set of \emph{current constituents at a snapshot date}. The two differ because of index entries and exits, mergers, delistings and IPOs; a panel built on the second population is subject to survivorship and backfilling biases that a point-in-time panel is not. SPID targets the second population. No licensed historical constituent list was available to the team at the time of the build, and FTSE Russell does not publish machine-readable membership lists for free. Any inference about the ``Russell~3000 over 2010--2026'' from this database is therefore an inference about firms that were in the proxy universe in October 2026 and about their filings back to 2010.
8+
9+\subsection{Universe construction and reconciliation}
10+
11+The issuer universe is frozen in a single JSON artifact, \texttt{data/metadata/universe.json}, built by \texttt{scripts/build\_universe.py} and documented in \texttt{docs/universe\_definition.md}. It merges two sources on the SEC Central Index Key (CIK):
12+
13+\begin{enumerate}
14+\item the S\&P~500 constituent list scraped from the public Wikipedia table on May~25, 2026 (503 rows, 500 distinct CIKs, with GICS sector and sub-industry), used for the first SPID release; and
15+\item the holdings file of the iShares Russell~3000 ETF (ticker IWV) published by iShares as \texttt{latest-holdings.csv}, with holdings as of October~23, 2026. IWV is an index-tracking fund; its holdings are a close but not exact replica of the index, and the file carries the fund's own sector labels.
16+\end{enumerate}
17+
18+Tickers are normalized to the SEC convention (share-class separators become hyphens: ``BRK B'' $\to$ \texttt{BRK-B}) and mapped to CIKs through the SEC's public \texttt{company\_tickers.json}. Lines that are not operating companies (contingent value rights, escrow shares, rights when-issued) and tickers without a CIK (banks that file with the FDIC rather than the SEC) are dropped and listed in the artifact's \texttt{unmatched} field. Table~\ref{tab:reconciliation} gives the full reconciliation from input securities to issuers with resolved projects.
19+
20+\begin{table}[H]
21+\centering\small
22+\caption{Universe-to-dataset reconciliation}\label{tab:reconciliation}
23+\begin{threeparttable}
24+\input{tables/tab_reconciliation}
25+\begin{tablenotes}\footnotesize
26+\item Source: \texttt{data/metadata/universe.json}, \texttt{russell3000\_constituents.json}, \texttt{sp500\_constituents.json} and the merged DuckDB database. The 65 issuers without filings are listed in \texttt{data/issuers\_without\_filings.csv}; most are foreign private issuers (e.g., Spotify, QIAGEN, Canadian Solar, Amdocs) that file annual reports on Form 20-F or 40-F, which are outside the current form scope, plus a few banks whose periodic reports are filed with the FDIC. Seven issuers have filings but no resolved project.
27+\end{tablenotes}
28+\end{threeparttable}
29+\end{table}
30+
31+Several design decisions deserve to be stated plainly. The proxy universe counts \emph{issuers}, not securities: two share classes of the same registrant collapse into one CIK (2{,}587 equity lines map to 2{,}555 CIKs). Companies in the S\&P~500 snapshot but no longer in the IWV file (nine issuers, typically recently acquired) are retained with an \texttt{in\_sp500} flag so that the June 2026 release remains a subset. The universe table records, for each issuer, the SEC name, SIC code and description, state of incorporation, exchange and fiscal-year end from the SEC submissions header, the fund weight, and the cluster node that processed its filings (\texttt{shard\_node}). The brief's recommendation of a \texttt{universe\_snapshots} table with source checksums, membership dates and match confidence is only partially met: snapshot date, source URL and match method are recorded; file hashes and per-row match confidence are not yet stored and are listed as open items in the implementation audit (\texttt{docs/implementation\_audit.md}).
32+
33+\subsection{Filing scope}
34+
35+For every issuer the pipeline enumerates the SEC submissions index (\texttt{data.sec.gov/}\allowbreak\texttt{submissions/}\allowbreak\texttt{CIK\{cik\}.json}, including the paginated archive blocks) and retains the primary document of every Form 10-K, 10-Q and 8-K filed on or after January~1, 2010. Amendments (\texttt{/A}), exhibits other than the primary document, proxy statements, registration statements and foreign-issuer forms are out of scope. The choice reflects where strategic initiatives are described in prose---Items 1, 1A, 2, 7 and 7A of the 10-K, the MD\&A and risk-factor items of the 10-Q, and the body of the 8-K---and it is a defensible but not innocuous restriction: press releases attached as Exhibit~99 to an 8-K, which often carry the first and most detailed announcement of a project, are not read when they are not the primary document. Testing whether this scope omits important announcements is part of the validation roadmap.
36+
37+\begin{table}[H]
38+\centering\small
39+\caption{Filings in the corpus, by form}\label{tab:filings}
40+\begin{threeparttable}
41+\input{tables/tab_filings}
42+\begin{tablenotes}\footnotesize
43+\item ``Not parsed'' counts filings whose primary document could not be cleaned (parser error) or was not found on disk after download. Raw HTML size is the sum of primary-document sizes recorded at download time.
44+\end{tablenotes}
45+\end{threeparttable}
46+\end{table}
47+
48+Table~\ref{tab:filings} summarizes the corpus: 527{,}548 primary documents, 254.5~GB of raw HTML, of which only 16 documents (0.003\%) failed parsing. Figure~\ref{fig:coverage} and Table~\ref{tab:coverage_year} in the appendix show the coverage by year. The number of issuers with at least one filing rises from 1{,}392 in 2010 to 2{,}498 in 2026: the snapshot universe contains many companies that went public after 2010, so the early years of the panel are populated by the older and larger survivors. This is the concrete form taken by the survivorship caveat above.
49+
50+\begin{figure}[H]
51+\centering
52+\includegraphics[width=\textwidth]{figures/fig_coverage.pdf}
53+\caption{Filings and issuers per year in the corpus. The 2026 bar covers January~1 to October~27, 2026. Issuers that went public after 2010 enter the panel at their first filing, so the number of observed issuers increases mechanically over time under the snapshot design.}\label{fig:coverage}
54+\end{figure}
added paper/sections/introduction.tex +15 −0
@@ -0,0 +1,15 @@
1+% ============================================================================
2+\section{Introduction}
3+% ============================================================================
4+
5+A large share of what firms do strategically---build plants, open data centers, integrate acquisitions, launch products, move systems to the cloud, commit to decarbonization targets---is described, often repeatedly and over several years, in the narrative sections of their regulatory filings. Yet the standard quantitative record of corporate investment is an aggregate: capital expenditure, R\&D expense, acquisitions, or counts of patents and plants. Researchers who want to observe the \emph{unit of decision}---a specific, identifiable initiative with a start, a scope, a budget and a fate---have had to assemble hand-collected samples, rely on commercial databases with opaque coverage, or infer initiatives indirectly from word frequencies. This paper documents an attempt to build that unit systematically, from primary sources, for a broad cross-section of U.S.-listed companies.
6+
7+The object we describe is SPID (\emph{SEC Project Intelligence Database}), developed at the Université du Québec en Outaouais on top of the UQO EDGAR research corpus. Its first release, in June 2026, covered the 500 constituents of the S\&P~500 \citep{spid2026}. The present paper documents its extension, carried out on October~27--28, 2026, to an issuer universe that approximates the Russell~3000: 2{,}564 distinct SEC registrants, 527{,}548 annual, quarterly and current reports filed between January 2010 and October 2026, 1.12 million filing sections submitted to a schema-constrained large-language-model (LLM) extractor, and 308{,}065 resulting project mentions resolved into 121{,}099 candidate projects. Every retained mention carries the SEC accession number, the section identifier and a model-generated description grounded in the source passage, so that any number in the database can be traced back to a public document.
8+
9+Our core research question is methodological: can we reliably identify, classify, date, link and quantify distinct strategic projects disclosed by Russell~3000 companies, while preserving source evidence and without claiming exhaustive coverage of real-world activity? The answer offered here is deliberately two-sided. On the positive side, the pipeline is reproducible end to end, every intermediate artifact is versioned, and the full corpus has been processed with a single extraction contract and an audit ledger that records which sections were seen. On the cautious side, several components that a fully validated measurement instrument requires---an independent human-coded gold standard, precision and recall estimates by category and field, an audit of cross-filing linkage---are specified in this paper but have not yet been executed at scale. We therefore describe SPID as a \emph{research database under validation}, and we keep a running register that separates implemented, documented-but-unverified, proposed and empirically verified claims (Appendix~\ref{app:evidence}).
10+
11+The paper makes four contributions. First, it specifies the measurement target precisely: a \emph{project} is a distinct, identifiable, issuer-associated strategic initiative described in one or more disclosures, as opposed to a \emph{project mention} (a particular sourced statement), a \emph{generic strategy} (``we invest in innovation''), a historical or third-party reference, or an uncertain candidate. Section~\ref{sec:construct} develops these distinctions and ties them to the existing 21-category SPID taxonomy, which we document rather than redesign. Second, it documents a scalable architecture: a frozen issuer universe reconciled against SEC identifiers; an acquisition layer that respects the SEC's fair-access limits by sharing one aggregate rate budget across ten cluster nodes and one external gateway; a parser that keeps raw-to-clean-text provenance; and an extraction ledger that makes the paid inference step resumable and idempotent (Section~\ref{sec:architecture}). Third, it exposes the extraction contract and the resolution rule in enough detail to be criticized: the prompt, the JSON schema, the confidence semantics, the deterministic project key, and the ways in which that key can both split and merge real initiatives (Section~\ref{sec:resolution}). Fourth, it lays out a validation protocol and a set of acceptance gates, and reports what the current build does and does not satisfy (Section~\ref{sec:validation}).
12+
13+Three caveats frame everything that follows. A present-day holdings file of an index-tracking fund is a proxy for index membership, not an authoritative historical constituent list; the universe is a snapshot, and survivorship is built into it (Section~\ref{sec:sample}). A disclosed project is evidence that an issuer \emph{described} an initiative, not evidence that the initiative happened, and a disclosed budget is a statement about intentions or estimates, not realized capital expenditure. Finally, the extractor only sees what the parser gives it: sections that were truncated, filings that failed to download, and exhibits outside the selected forms are invisible, and ``zero projects'' for an issuer-year must never be read as ``no projects''.
14+
15+The rest of the paper is organized as follows. Section~\ref{sec:literature} positions the work in the text-as-data, corporate-investment and information-extraction literatures. Section~\ref{sec:sample} defines the universe and reports its reconciliation. Section~\ref{sec:architecture} describes acquisition, parsing and the distributed build. Section~\ref{sec:construct} defines the project construct and the extraction contract. Section~\ref{sec:resolution} covers mention-to-project resolution, timelines and amounts. Section~\ref{sec:results} reports descriptive patterns. Section~\ref{sec:validation} presents the validation framework and the current audit. Section~\ref{sec:applications} discusses research applications and identification limits, and Section~\ref{sec:conclusion} concludes. Appendices provide the taxonomy, the data dictionary, the prompt, the evidence register and replication instructions.
added paper/sections/literature.tex +13 −0
@@ -0,0 +1,13 @@
1+% ============================================================================
2+\section{Related Literature}\label{sec:literature}
3+% ============================================================================
4+
5+\paragraph{Text as data in accounting and finance.} The narrative content of SEC filings has become a standard empirical input. \citet{loughran2011} showed that general-purpose sentiment dictionaries misclassify financial text and introduced finance-specific word lists; \citet{loughran2016} survey the subsequent decade of textual analysis in accounting and finance, and \citet{gentzkow2019} provide the general econometric framework for treating text as data, including the measurement-error and validation issues that any text-derived variable inherits. \citet{dyer2017} document how 10-K disclosure has lengthened and become more boilerplate over time, a trend that matters for us because repeated boilerplate is the principal source of false-positive ``projects''. \citet{cohen2020} show that changes in otherwise repetitive filing language carry information, which motivates our emphasis on \emph{repeated} mentions of the same initiative across filings rather than on single occurrences. \citet{grimmer2013} offer the general methodological warning that automated text measures must be validated against human judgment on the specific task at hand, which is the stance adopted in Section~\ref{sec:validation}.
6+
7+\paragraph{Measuring corporate activity from disclosures.} Closest to our construct are text-based measures of what firms do rather than how they feel. \citet{hoberg2016} build product-market classifications from 10-K business descriptions; \citet{hobergmaksimovic2015} identify financially constrained firms from the language of the liquidity discussion; \citet{hassan2019} measure firm-level political risk from the share of earnings-call text devoted to politics; \citet{li2021} measure corporate culture with word embeddings; and \citet{kelly2021} and \citet{bloom2021} track the emergence and diffusion of technologies through patents, calls and job postings. SPID differs from these approaches in its unit of observation. Rather than a firm-level score summarizing the intensity of a theme, it targets \emph{discrete initiatives}---objects with a name, a type, a status, cited years, a location, partners and an amount---that can be followed from filing to filing. This is the project-centric view that commercial intelligence products sell, but with open provenance and a documented extraction contract.
8+
9+\paragraph{Structured information extraction with language models.} The extraction step relies on an instruction-tuned LLM used in a constrained-output mode: a fixed system prompt, a strict JSON object schema, temperature zero, and an explicit instruction to extract only initiatives grounded in the supplied passage. This design follows the general lesson of the text-as-data literature that the measurement instrument must be fixed, versioned and auditable, and it is deliberately conservative: the model is asked to prefer fewer, concrete projects to many vague ones, to return an empty list when no concrete project is described, and to attach a self-reported confidence. Semantic search over projects uses sentence embeddings \citep{reimers2019}. We make no claim that self-reported confidence is calibrated; its calibration is one of the items of the validation protocol.
10+
11+\paragraph{Entity resolution and record linkage.} Linking repeated mentions of the same initiative across years is a record-linkage problem in the sense of \citet{fellegi1969}; \citet{christen2012} reviews the modern toolbox of blocking, comparison and classification. The current SPID resolver is a deterministic blocking rule (issuer, project type and a location or name token) rather than a probabilistic linker. We document its behavior and its failure modes in Section~\ref{sec:resolution}, and we treat the migration to an explainable, reversible linkage with human adjudication of ambiguous clusters as part of the validation roadmap.
12+
13+\paragraph{Infrastructure.} The database is implemented as a single analytical file in DuckDB \citep{raasveldt2019}, with Parquet exports; acquisition respects the SEC's published fair-access policy \citep{secfairaccess}, and the Russell~3000 is defined following FTSE Russell's published construction methodology \citep{ftserussell2026}, approximated here through a fund-holdings proxy (Section~\ref{sec:sample}).
added paper/sections/methodology.tex +70 −0
@@ -0,0 +1,70 @@
1+% ============================================================================
2+\section{Architecture: Acquisition, Parsing and the Distributed Build}\label{sec:architecture}
3+% ============================================================================
4+
5+\subsection{Overview}
6+
7+Figure~\ref{fig:pipeline} summarizes the pipeline. Two layers are distinguished throughout. The \emph{corpus layer} (code in \texttt{src/} and \texttt{scripts/}) acquires and parses filings, chunks them, embeds the chunks and computes thematic factor scores; it is universe-agnostic and produces a DuckDB database (\texttt{edgar\_research.duckdb}, 68~GB) plus Parquet exports. The \emph{project layer} (code in \texttt{project-db/spid/}) reads the Parquet exports through views, runs candidate gating and LLM extraction, resolves mentions into projects, builds a knowledge graph and project embeddings, and persists everything in a second, much smaller DuckDB file (\texttt{spid.duckdb}, 3.2~GB). The separation matters for reproducibility: the expensive and paid step (LLM extraction) depends only on the frozen Parquet exports and on a ledger of processed sections, so it can be re-run, resumed or audited independently of the corpus build.
8+
9+\begin{figure}[H]
10+\centering
11+\begin{tikzpicture}[node distance=4mm and 6mm, every node/.style={font=\footnotesize},
12+ box/.style={draw, rounded corners=2pt, align=center, minimum height=8mm, text width=28mm, fill=blue!4},
13+ art/.style={draw, align=center, minimum height=7mm, text width=28mm, fill=yellow!10, font=\scriptsize},
14+ arr/.style={-{Latex[length=2mm]}, thick}]
15+\node[box] (u) {Universe snapshot\\ \scriptsize S\&P~500 $\cup$ IWV $\to$ CIK};
16+\node[box, right=of u] (dl) {SEC acquisition\\ \scriptsize submissions index, primary docs, 10 req/s per IP};
17+\node[box, right=of dl] (p) {Parsing\\ \scriptsize HTML $\to$ text, Item segmentation, chunks};
18+\node[box, right=of p] (e) {Embeddings \& factors\\ \scriptsize MiniLM 384-d, 8 anchors};
19+\node[art, below=of u] (ua) {\texttt{universe.json}\\ \texttt{shards.json}};
20+\node[art, below=of dl] (da) {\texttt{download\_log.*.jsonl}\\ raw HTML on 10 nodes};
21+\node[art, below=of p] (pa) {\texttt{filings}, \texttt{filing\_sections}\\ \texttt{filing\_chunks} (Parquet)};
22+\node[art, below=of e] (ea) {\texttt{embeddings/} 16~GB\\ \texttt{chunk\_scores}, panels};
23+\node[box, below=14mm of da, fill=red!5] (x) {Candidate gate + LLM extraction\\ \scriptsize regex gate, focus windows, JSON schema};
24+\node[box, right=of x, fill=red!5] (r) {Resolution \& timeline\\ \scriptsize key = CIK $|$ type $|$ location/name};
25+\node[box, right=of r, fill=red!5] (g) {Graph, project embeddings,\\ analytics, API};
26+\node[art, below=of x] (xa) {\texttt{project\_mentions} 308{,}065\\ \texttt{extracted\_sections} 1.12~M};
27+\node[art, below=of r] (ra) {\texttt{projects} 121{,}099\\ \texttt{project\_timeline}};
28+\node[art, below=of g] (ga) {\texttt{kg\_nodes/edges}\\ \texttt{project\_embeddings}};
29+\draw[arr] (u)--(dl); \draw[arr] (dl)--(p); \draw[arr] (p)--(e);
30+\draw[arr] (u)--(ua); \draw[arr] (dl)--(da); \draw[arr] (p)--(pa); \draw[arr] (e)--(ea);
31+\draw[arr] (pa.south) |- ++(0,-5mm) -| (x.north);
32+\draw[arr] (x)--(r); \draw[arr] (r)--(g);
33+\draw[arr] (x)--(xa); \draw[arr] (r)--(ra); \draw[arr] (g)--(ga);
34+\node[above=1mm of dl, font=\scriptsize\itshape] {Corpus layer (\texttt{src/}, \texttt{scripts/})};
35+\node[above=1mm of r, yshift=-1mm, font=\scriptsize\itshape] {};
36+\node[left=1mm of x, font=\scriptsize\itshape, text width=16mm, align=right] {Project layer (\texttt{spid/})};
37+\end{tikzpicture}
38+\caption{Pipeline architecture. Rounded boxes are processing steps; rectangles are persisted artifacts. The project layer reads the Parquet exports of the corpus layer through read-only views and never modifies them.}\label{fig:pipeline}
39+\end{figure}
40+
41+\subsection{SEC acquisition}
42+
43+The SEC's fair-access policy allows ten requests per second per client and asks for a descriptive \texttt{User-Agent} with a contact address \citep{secfairaccess}. Both constraints shaped the acquisition layer (\texttt{src/acquisition/bulk\_downloader.py}). Requests are issued by a pool of threads behind a single token bucket; the bucket rate is a parameter, and when several processes share one public IP address the orchestrator divides nine requests per second among them. HTTP 403, 429 and 503 responses trigger a 65-second cool-down for the whole process rather than a per-request retry, so that a throttling signal is never answered with a burst. Downloads are idempotent: a filing is skipped when its file exists, and every completed download is appended to a JSON-lines log carrying the accession number, form, filing and report dates, primary document name, size and local path. A trusted-log option lets a node skip filings that an earlier corpus already contains without re-fetching them. The SEC submissions header of each issuer (name, SIC, state of incorporation, exchanges, fiscal-year end, former names) is saved alongside, and feeds the \texttt{companies} table.
44+
45+The October 2026 build used eleven machines: ten Apple-silicon nodes of the UQO MacLustr cluster behind one residential IP address, and one Linux server in an OVHcloud data center in Beauharnois, Québec, behind a second address. Issuers were assigned to nodes in proportion to CPU cores; half of each node's issuers were downloaded by the node itself (0.9 requests per second each) and half by the external server (one request per second per shard), then shipped to the node over SSH. A first allocation by contiguous CIK ranges proved badly unbalanced---old CIKs are large filers with up to 220 documents each---and was replaced after one hour by an interleaved allocation of the not-yet-started issuers. The whole acquisition (about 400{,}000 new documents) took seven hours at a sustained 17 requests per second, with no fair-access violation recorded.
46+
47+\subsection{Document processing}
48+
49+Each primary document is cleaned with \texttt{lxml}: \texttt{script}, \texttt{style}, \texttt{meta} and \texttt{link} elements are removed, tables are flattened to tab-separated text, lists are kept as line items, whitespace is normalized and files above 50~MB are skipped. The segmenter then locates ``Item~\emph{n}'' and ``Part~I/II'' headings by regular expression: 10-Ks are split into up to fourteen sections (Items 1 to 9B), 10-Qs into seven (Part~I Items 1--4, Part~II Items 1, 1A, 2), and 8-Ks are kept as a single \texttt{full\_body} section. A filing in which no heading is found is kept as one \texttt{full\_document} section rather than dropped, so that the foreign-key chain from chunks to sections to filings is never broken. Sections are chunked into 512-word windows with a 64-word overlap for the embedding and factor layers (10.72 million chunks). Provenance is preserved at the section level: each section row stores its item number, order, word and character counts, and each chunk stores its character offsets within the section. Character-level offsets of sections within the raw HTML are \emph{not} stored, a limitation recorded in the audit.
50+
51+Table~\ref{tab:sections} reports the outcome. The 10-K sections are long (median 875 words, mean 5{,}551) and the 10-Q sections somewhat shorter; 8-K bodies are short (median 575 words). Two coverage statistics deserve emphasis because they bound what the extractor could see: 0.003\% of filings failed parsing, and the segmenter's reliance on textual headings means that a 10-K whose items are rendered as images or in unusual markup collapses into a single \texttt{full\_document} section (1{,}230 such 10-K sections were sent to the extractor).
52+
53+\begin{table}[H]
54+\centering\small
55+\caption{Parsed sections and sections submitted to the extractor, by form}\label{tab:sections}
56+\begin{threeparttable}
57+\adjustbox{max width=\textwidth}{\input{tables/tab_sections}}
58+\begin{tablenotes}\footnotesize
59+\item ``Sections sent to the extractor'' counts rows of the \texttt{extracted\_sections} ledger: sections with at least 30 words, restricted for 10-Ks to Items 1, 1A, 2, 7 and 7A (see Table~\ref{tab:items}). Parsed 10-K sections also include Items 3--6, 8, 9--9B, which are not submitted.
60+\end{tablenotes}
61+\end{threeparttable}
62+\end{table}
63+
64+\subsection{Distributed build and merge}
65+
66+Parsing, chunking and embedding ran on every node against the node's own shard, using the same code revision and a local DuckDB file; each node then exported its tables to Parquet. The master node (M3U96b, which also holds the original S\&P~500 corpus) pulled the shards and merged them with an append-only rule: a row is inserted only if its primary key (accession number, section or chunk UUID) is absent, so that re-running the merge cannot create duplicates. The merged database holds 2{,}564 issuers, 527{,}548 filings, 1{,}342{,}283 sections, 10{,}720{,}890 chunks and as many 384-dimensional embeddings. Parsing throughput was about 100 filings per second per node (five to thirteen minutes per shard); embeddings took 15--40 minutes per node on Apple-silicon GPUs; the merge and the factor computation took seven minutes. The orchestration script (\texttt{scripts/cluster/orchestrate.py}) is stage-based and idempotent: every stage polls marker files on the nodes and can be restarted without side effects. Private infrastructure details (addresses, credentials, keys) are kept outside the repository and outside this paper.
67+
68+\subsection{Versioning}
69+
70+The repository is versioned in a private Git server; the revision used for the build described here is \texttt{2324b58} (October~28, 2026), which supersedes revision \texttt{df7905d} examined in the June 2026 methodological study \citep{spid2026}. Model and prompt versions are recorded in the extraction ledger as the backend name; the OpenAI model identifier returned by the API during the October run was \texttt{gpt-4o-mini-2024-07-18}. A hash of the prompt and of the taxonomy is \emph{not} yet stored per run---an item on the audit list---so prompt identity is currently guaranteed by the Git revision rather than by a per-run digest.
added paper/sections/resolution.tex +29 −0
@@ -0,0 +1,29 @@
1+% ============================================================================
2+\section{Resolution, Timelines and Amounts}\label{sec:resolution}
3+% ============================================================================
4+
5+\subsection{The resolution rule}
6+
7+Mentions are grouped into projects by a deterministic key computed in SQL (\texttt{spid/resolve.py}): the issuer CIK, the project type, and either the lower-cased first location of the mention or, when no location was extracted, the first token of at least four letters in the normalized project name (``general'' when none exists). The project identifier is the MD5 hash of that key. Within a group, the representative name and description are those of the highest-confidence mention, the canonical location is the group's location token, the technologies are the union of the mentions' technologies, the status is that of the \emph{latest} mention by filing date, the amount is the \emph{maximum} across mentions, and the timeline is the ordered list of mentions with their filing date, form, status, amount, confidence and snippet. First and last cited years are the minimum and maximum of the years mentioned in the text; first and last \emph{seen} are the filing dates of the earliest and latest mention.
8+
9+The rule is transparent, cheap and reversible (the mentions are untouched), and it behaves well for the common case of a recurring physical program at one site. Its failure modes are equally clear:
10+
11+\begin{itemize}
12+\item \textbf{False merges.} All mentions of one type at one location collapse into one project: every acquisition closed by a serial acquirer in the same city, every expansion of a multi-phase site (the Cheniere example of Table~\ref{tab:provenance}), every drug candidate of a pharmaceutical company with no location, which fall back to a name token that is often shared (``phase'', ``program'', ``study''). The mean of 4.99 mentions per \texttt{drug\_pipeline} project versus 1.63 for \texttt{technology\_deployment} partly reflects this.
13+\item \textbf{False splits.} The same initiative named differently across filings (``Project Atlas'' in 2019, ``our ERP transformation'' in 2021) or located at a different granularity (``Texas'' vs. ``Austin, Texas'') yields distinct keys; so does a change of category by the model.
14+\item \textbf{No cross-issuer identity.} A joint venture disclosed by both partners is two projects; a project inherited through an acquisition is a new project of the acquirer.
15+\end{itemize}
16+
17+The 60.2\% of projects with a single mention are the population in which false splits, true one-off disclosures and false positives are mixed, and the projects with more than ten mentions (3.1\%) are where false merges concentrate. The brief's requirement---explainable, reversible merge/split decisions with match scores, resolver version and human adjudication of ambiguous clusters---is not met by the current rule and is the second schema change proposed: a \texttt{project\_mention\_links} table with a match score and an adjudication status, fed first by the deterministic key (score~1 by construction) and then by a probabilistic linker that uses name similarity, embedding similarity, location and time, with ambiguous clusters routed to review.
18+
19+\subsection{Timelines and point-in-time structure}
20+
21+Each mention is dated by the filing date of its document, and each project by the filing dates of its first and last mentions and by the years cited in the text. This is a point-in-time structure in one specific sense: the state of a project \emph{as known at} a filing date can be reconstructed by filtering the timeline to mentions filed on or before that date, and nothing in a later filing is used to alter an earlier mention. It is \emph{not} yet the event structure the brief describes. The database does not separate first announcement, SEC acceptance timestamp (only the filing date is stored), referenced period, expected start, observed start, expected completion, realized completion and cancellation; cited years are stored as an unordered list without roles; and a project's status is the latest statement rather than a dated transition. The proposed \texttt{project\_events} table (Appendix~\ref{app:dictionary}) would record each such event with its date, its evidence mention and a flag distinguishing information available at the acceptance date from information revised later.
22+
23+\subsection{Amounts}\label{sec:amounts}
24+
25+The amount of a mention is whatever USD figure the model associated with the project in the passage, coerced to a float; the raw string is retained. The project-level \texttt{total\_amount\_usd} is the \emph{maximum} over mentions, chosen in the June 2026 release because the same budget is typically restated in successive filings and summing would multiply it by the number of mentions. The maximum avoids that error but introduces others: it cannot tell a transaction price from a capex budget, a contract value from a financing facility, an incurred cost from an estimate, or a figure for the whole program from a figure for one phase, and it is silently inflated when a merged project combines distinct initiatives. We therefore insist on two reading rules. First, \texttt{total\_amount\_usd} is ``the largest amount an issuer cited in connection with this project'', nothing more. Second, amounts must never be summed across projects or interpreted as realized investment. The proposed \texttt{project\_amounts} table keeps every amount mention with its original value, currency, scale, reporting date, an \texttt{amount\_type} (announced budget, estimated cost, capex incurred, transaction value, contract value, financing, other) and an estimate/realization flag; filling \texttt{amount\_type} requires either a second extraction pass or annotation, both of which are in the validation plan.
26+
27+\subsection{Data model: current and proposed}
28+
29+Table~\ref{tab:datamodel} in Appendix~\ref{app:dictionary} lists the tables of the current build alongside the tables required by the brief and marks each as implemented, partial or proposed. In summary, the current \texttt{companies}, \texttt{filings}, \texttt{filing\_sections}, \texttt{project\_mentions}, \texttt{projects}, \texttt{project\_timeline} and \texttt{extracted\_sections} tables cover universe, filings, sections, mentions, projects, a timeline and an extraction ledger; the \texttt{issuers} history, \texttt{project\_mention\_links}, \texttt{project\_events}, \texttt{project\_amounts}, \texttt{extraction\_runs} (with prompt, schema and taxonomy hashes) and \texttt{human\_labels} tables are proposed and would be added by migration, never by destructive replacement.
added paper/sections/results.tex +107 −0
@@ -0,0 +1,107 @@
1+% ============================================================================
2+\section{Descriptive Patterns}\label{sec:results}
3+% ============================================================================
4+
5+All numbers in this section are produced by \texttt{code/export\_stats.py} and \texttt{code/make\_tables.py} from the merged databases at revision \texttt{2324b58}; none is typed by hand. They describe \emph{disclosed} initiatives as detected by the current pipeline and resolved by the current rule. They are not estimates of real-world project counts, and they have not yet been corrected for the extraction and linkage errors whose measurement is described in Section~\ref{sec:validation}.
6+
7+\subsection{From sections to projects}
8+
9+The extraction ledger contains 1{,}123{,}098 sections. The model returned at least one project for 168{,}230 of them (15.0\%), yielding 308{,}065 mentions---0.27 per section on average and 1.83 per productive section. The density differs sharply by form and by item (Table~\ref{tab:items}): the MD\&A items of the 10-K and 10-Q produce 39{,}099 and 99{,}305 mentions respectively, Item~1 (Business) of the 10-K 30{,}492, and the 10-Q financial-statement notes 30{,}159, whereas risk-factor and market-risk items produce almost none despite their length. The 8-K body yields 74{,}594 mentions from 400{,}601 filings. Resolution reduces 308{,}065 mentions to 121{,}099 projects (2.54 mentions per project); 60.2\% of projects rest on a single mention, 17.0\% on two, and 8.2\% on more than five (Table~\ref{tab:status}).
10+
11+\begin{table}[H]
12+\centering\small
13+\caption{Sections submitted and mentions obtained, by form and item}\label{tab:items}
14+\begin{threeparttable}
15+\input{tables/tab_items}
16+\begin{tablenotes}\footnotesize
17+\item 8-K filings (400{,}601 sections, 74{,}594 mentions) are a single \texttt{full\_body} section each and are omitted from the table. \texttt{full\_body}/\texttt{full\_document} rows are filings in which no Item heading was recognized.
18+\end{tablenotes}
19+\end{threeparttable}
20+\end{table}
21+
22+\subsection{Composition by category, sector and status}
23+
24+Table~\ref{tab:types} and Figure~\ref{fig:types} show the distribution over the 21 categories. M\&A and post-merger integration dominate (34.5\% of projects), followed by capital-expenditure programs (8.9\%), strategic partnerships (8.2\%), geographic expansion (6.5\%) and plant or site construction (6.3\%). The technology-related categories that motivated the first SPID release---data centers, AI initiatives, cloud migration, automation---are small in count (between 264 and 449 projects each) but carry high disclosed amounts (median 128--166~M USD for data centers and AI initiatives). Two readings of this composition are possible and the data do not discriminate between them: either acquisitions are genuinely the most frequently disclosed discrete initiative in SEC filings, which is plausible given that every material acquisition triggers an 8-K and recurring 10-Q notes; or the extraction contract, which asks for ``concrete initiatives'', is more easily satisfied by acquisitions (named counterparty, date, price) than by internal programs. Disentangling the two is a question for the human-annotation study.
25+
26+\begin{table}[H]
27+\centering\small
28+\caption{Resolved projects by category}\label{tab:types}
29+\begin{threeparttable}
30+\adjustbox{max width=\textwidth}{\input{tables/tab_types}}
31+\begin{tablenotes}\footnotesize
32+\item ``With amount'' counts projects for which at least one mention carries a USD figure; ``median amount'' is the median across those projects of the largest amount cited (see Section~\ref{sec:amounts} on why amounts are never summed).
33+\end{tablenotes}
34+\end{threeparttable}
35+\end{table}
36+
37+\begin{figure}[H]
38+\centering
39+\includegraphics[width=0.95\textwidth]{figures/fig_types.pdf}
40+\caption{Resolved projects by taxonomy category (121{,}099 projects).}\label{fig:types}
41+\end{figure}
42+
43+By sector (Table~\ref{tab:sectors}), health care (23{,}679 projects, 429 issuers) and industrials (17{,}890) contribute the most projects; utilities, materials, energy and real estate have the most projects per issuer (63--75), and financials the fewest (32), consistent with the intuition that asset-heavy, regulated or resource-extracting firms narrate discrete physical programs while banks describe fewer discrete initiatives. The list of the most ``productive'' issuers (Table~\ref{tab:top_issuers} in the appendix)---uranium and gold miners, airlines, a bitcoin miner, an oilfield-services company---illustrates a second mechanism: issuers whose business \emph{consists} of projects (mines, routes, wells, data-center sites) and whose filings enumerate them one by one.
44+
45+\begin{table}[H]
46+\centering\small
47+\caption{Issuers and projects by sector}\label{tab:sectors}
48+\begin{threeparttable}
49+\adjustbox{max width=\textwidth}{\input{tables/tab_sectors}}
50+\begin{tablenotes}\footnotesize
51+\item Sector labels come from the iShares holdings file (Russell proxy) or, for S\&P~500-only issuers, from the Wikipedia GICS table. ``Issuers in universe'' includes issuers without filings.
52+\end{tablenotes}
53+\end{threeparttable}
54+\end{table}
55+
56+The latest status recorded for a project is \texttt{in\_progress} for 41.6\%, \texttt{completed} for 41.0\%, \texttt{planned} for 15.1\% and \texttt{mentioned} for 2.3\% (Table~\ref{tab:status}). Because status is taken from the most recent mention, it is a snapshot of the last thing the issuer said, not the terminal fate of the project: a project that was announced, mentioned once as in progress and then silently abandoned is recorded as in progress. The span between the earliest and latest year cited \emph{in the text} is zero for 55.5\% of projects with known years and exceeds five years for 8.5\%; the brief's distinction between announcement, filing, expected start, observed start, expected and realized completion, and cancellation is not yet implemented as separate dated events (Section~\ref{sec:resolution}).
57+
58+\begin{table}[H]
59+\centering\small
60+\caption{Status, number of mentions and cited time span of resolved projects}\label{tab:status}
61+\adjustbox{max width=\textwidth}{\input{tables/tab_status}}
62+\end{table}
63+
64+\subsection{Amounts, confidence and time}\label{sec:amounts_desc}
65+
66+A USD amount is attached to 120{,}240 mentions (39.0\%) and to 56{,}500 projects (46.7\%). Table~\ref{tab:amounts} reports the distribution of the largest amount cited per project: median 128.5~M USD, interquartile range 23--500~M, and a long right tail (P99 of 12.5~B USD). The sum of these maxima across projects is 55.9~trillion USD, a number we report only to make the point that it must \emph{not} be interpreted: it adds transaction values, capex budgets, contract values, financing amounts and estimated costs across projects and years, it double counts initiatives that the resolver split, and it treats a stated budget as if it were spent. Section~\ref{sec:amounts} explains how amounts are stored and what an \texttt{amount\_type} field would need to add.
67+
68+\begin{table}[H]
69+\centering\small
70+\caption{Distribution of disclosed amounts and of projects per issuer}\label{tab:amounts}
71+\adjustbox{max width=\textwidth}{\input{tables/tab_amounts}}
72+\end{table}
73+
74+The self-reported confidence of mentions is concentrated at the top of the scale (Figure~\ref{fig:confidence}; Table~\ref{tab:confidence} in the appendix): 35.2\% of mentions have confidence 1.0, 32.7\% have 0.9, and only 1.6\% fall at or below 0.6. Mentions below 0.45 are discarded at extraction time. The distribution says more about the model's reporting habits than about accuracy; whether a confidence of 0.7 corresponds to a lower precision than a confidence of 1.0 is precisely the calibration question that only labeled data can answer.
75+
76+\begin{figure}[H]
77+\centering
78+\includegraphics[width=0.85\textwidth]{figures/fig_confidence.pdf}
79+\caption{Self-reported extraction confidence of the 308{,}065 mentions (rounded to one decimal; mentions below 0.45 are not stored).}\label{fig:confidence}
80+\end{figure}
81+
82+Over time (Figure~\ref{fig:mentions_year}; Table~\ref{tab:coverage_year}), mentions rise from 11{,}908 in 2010 to 23{,}220 in 2025, almost in proportion to the number of issuers observed (1{,}224 to 2{,}280 with at least one mention), while the number of projects first observed in a year is flat at 6{,}200--8{,}200. The rise is therefore mostly a coverage effect of the snapshot universe, not evidence of an increase in strategic activity; the flat count of new projects per year combined with a growing issuer base implies a declining number of new projects per issuer, which may reflect the lower disclosure intensity of the smaller firms that enter the panel later, or the resolver's tendency to attach later mentions to existing keys. Both explanations are testable with the panel.
83+
84+\begin{figure}[H]
85+\centering
86+\includegraphics[width=\textwidth]{figures/fig_mentions_year.pdf}
87+\caption{Project mentions and projects first observed, by filing year (2026 covers January to October 27).}\label{fig:mentions_year}
88+\end{figure}
89+
90+\subsection{A provenance example}
91+
92+Table~\ref{tab:provenance} follows one resolved project through its mentions: the Corpus Christi liquefaction expansion of Cheniere Energy (ticker LNG), typed \texttt{plant\_construction}, with eight mentions in 8-K and 10-K filings between April 2014 and March 2022 and a largest cited amount of 11.5~B USD. Each row is traceable: the accession number identifies the document on EDGAR, the section identifier locates the passage in the parsed corpus, and the snippet is the model's one-sentence description, which the contract requires to be grounded in the passage. The example also exposes the resolver's coarseness: the project key for this issuer and type is the location token ``corpus christi'', so the Stage~3 expansion and the earlier trains share one project identifier although Cheniere describes them as distinct phases. A reviewer can see this immediately because the mentions and their snippets are preserved; a user who consumes only the \texttt{projects} table cannot.
93+
94+\begin{table}[H]
95+\centering\footnotesize
96+\caption{Provenance of one resolved project: Cheniere Energy, Corpus Christi liquefaction (project \texttt{3e2705ba\ldots})}\label{tab:provenance}
97+\begin{threeparttable}
98+\adjustbox{max width=\textwidth}{\input{tables/tab_provenance}}
99+\begin{tablenotes}\footnotesize
100+\item Snippets are the first 170 characters of the model-generated description stored in \texttt{project\_timeline.snippet}; the full source section is retrievable through \texttt{project\_mentions.section\_id}. Amounts are shown only for mentions that cite one.
101+\end{tablenotes}
102+\end{threeparttable}
103+\end{table}
104+
105+\subsection{Knowledge graph and research-ready exports}
106+
107+The resolved projects are projected into a graph of 183{,}472 nodes (2{,}492 companies, 121{,}099 projects, 14{,}508 locations, 8{,}551 technologies, 36{,}822 partners) and 204{,}456 edges (\texttt{owns}, \texttt{located\_in}, \texttt{uses}; Table~\ref{tab:graph} in the appendix); partner nodes are currently created without edges, a known gap. Each project also has a 384-dimensional sentence embedding of its name, type, description and technologies, which supports semantic search through the companion web application. Alongside the project layer, the corpus layer publishes the firm-year and firm-quarter panels of thematic factor scores (32{,}852 and 128{,}043 rows) used in earlier UQO work on AI disclosure, now covering the full universe.
added paper/sections/titlepage.tex +79 −0
@@ -0,0 +1,79 @@
1+% ============================================================================
2+% Title Page
3+% ============================================================================
4+\thispagestyle{empty}
5+
6+\begin{center}
7+
8+% --- Logo ---
9+\includegraphics[width=3.5cm]{uq_logo.jpg}
10+
11+\vspace{0.6cm}
12+
13+{\footnotesize\textsc{Université du Québec en Outaouais}}\\[0.15cm]
14+{\footnotesize\textsc{Département des sciences administratives}}
15+
16+\vspace{0.8cm}
17+
18+{\footnotesize\textsc{Working Paper No.~\WPnumber}}\\[0.15cm]
19+{\footnotesize\textit{\WPfunding}}
20+
21+\vspace{1.2cm}
22+
23+% --- Title ---
24+{\LARGE\bfseries \WPtitle\par}
25+
26+\ifx\WPsubtitle\empty\else
27+ \vspace{0.3cm}
28+ {\large\itshape \WPsubtitle\par}
29+\fi
30+
31+\vspace{1.2cm}
32+
33+% --- Author ---
34+{\large \WPauthor}\\[0.3cm]
35+{\normalsize \WPaffiliation}\\[0.15cm]
36+{\normalsize \WPemail}\\[0.15cm]
37+{\small \WPaddress}
38+
39+\vspace{0.8cm}
40+
41+% --- Date & Version ---
42+{\normalsize \WPdate}\\[0.1cm]
43+{\small Version~\WPversion}\\[0.4cm]
44+{\footnotesize\itshape Working draft. Author order, affiliations and CRediT contributions remain subject to confirmation by all coauthors (see \texttt{docs/contributions.md}). Not for citation without permission.}
45+
46+\end{center}
47+
48+\vfill
49+
50+\newpage
51+
52+% ============================================================================
53+% Abstract Page
54+% ============================================================================
55+\thispagestyle{empty}
56+
57+\vspace*{1cm}
58+
59+\noindent\rule{\textwidth}{0.4pt}
60+\vspace{0.3cm}
61+
62+\noindent\textbf{Abstract}
63+
64+\vspace{0.15cm}
65+
66+\noindent\WPabstract
67+
68+\vspace{0.4cm}
69+
70+\noindent\textbf{Keywords:} \WPkeywords
71+
72+\vspace{0.15cm}
73+
74+\noindent\textbf{JEL Classification:} \WPjel
75+
76+\vspace{0.3cm}
77+\noindent\rule{\textwidth}{0.4pt}
78+
79+\newpage
added paper/sections/validation.tex +50 −0
@@ -0,0 +1,50 @@
1+% ============================================================================
2+\section{Validation Framework and Current Audit}\label{sec:validation}
3+% ============================================================================
4+
5+The aim of identifying ``all projects'' of an issuer is a \emph{coverage target}, not an assumption. Only projects that appear in the captured sources, survive parsing and gating, are recognized by the model and are correctly linked are observable, and each of these steps loses or distorts some. This section specifies how those losses are to be measured and reports what has been measured so far. The headline is simple: the \emph{coverage} and \emph{integrity} components are implemented and quantified; the \emph{accuracy} components---which require independent human labels---are specified but not yet executed. Table~\ref{tab:gates} summarizes the status of the acceptance gates.
6+
7+\subsection{Coverage and integrity checks (implemented)}
8+
9+\begin{itemize}
10+\item \textbf{Universe reconciliation} (Table~\ref{tab:reconciliation}): every input line of the Russell proxy is accounted for as matched, dropped-as-non-company, or unmatched; the 65 issuers without filings are listed by name and reason.
11+\item \textbf{Acquisition completeness:} for every issuer the SEC submissions index is enumerated in full (recent block and archive blocks) and every retained filing is logged; download failures are counted per issuer and were zero in the final run. Filing counts by form and year are published (Table~\ref{tab:coverage_year}).
12+\item \textbf{Parsing coverage:} 16 of 527{,}548 primary documents could not be parsed (Table~\ref{tab:filings}); filings without recognizable Item headings are kept as a single section rather than dropped.
13+\item \textbf{Extraction ledger:} every section submitted to the model is recorded; the ledger was audited after the credit-exhaustion incident and all sections processed during the outage were re-submitted. The ledger does not yet record a per-call status, so an undetected API failure would still appear as ``no project''; the run logs (retried errors, JSON parse failures: five in the June run, one in the October run) are the only current evidence that such failures were rare.
14+\item \textbf{Referential integrity:} foreign keys from chunks to sections to filings to companies are enforced by the database; the merge of ten shards is append-only on globally unique keys and was checked for duplicate accession numbers and section identifiers (none).
15+\item \textbf{Reproducibility of exhibits:} every table and figure of this paper is regenerated from the databases by two scripts in \texttt{code/}; the numbers can be compared against \texttt{data/stats.json}.
16+\end{itemize}
17+
18+\subsection{Accuracy evaluation (specified, not executed)}
19+
20+\paragraph{Gold standard.} A stratified sample of sections will be drawn from the ledger with strata defined by form (10-K, 10-Q, 8-K), item, issuer size (fund weight tercile), sector, filing year (2010--2014, 2015--2019, 2020--2026) and disclosure length. The sample will include sections that produced mentions and, in equal number, randomly selected sections that produced none, so that both false positives and false negatives can be estimated. Two annotators will independently code each section: for each initiative present, its name, category (from the 21-code taxonomy), status, cited years, location, partners, amount and amount type, plus the five-way label of Section~\ref{sec:construct} (project, generic strategy, third-party, uncertain, none). Disagreements will be adjudicated by the third team member; inter-annotator agreement (Cohen's $\kappa$ for presence and category, exact-match rates for fields) will be reported. The sample will be split into a development half, on which prompt and threshold changes may be tuned, and a locked holdout half, used once per release.
21+
22+\paragraph{Metrics.} On the holdout: candidate-gate recall (share of annotated projects whose section passed the gate); project-detection precision, recall and F1 at the mention level; category macro- and micro-F1; field accuracy for amount, years and location; the abstention rate (sections where the model returned an empty list) conditional on annotated content; and confidence calibration (precision by confidence bin, with bootstrap intervals). The same metrics will be computed for the rule-based extractor and, if budget allows, for a semantic-retrieval baseline, so that the marginal value of the LLM step is documented.
23+
24+\paragraph{Linkage audit.} From the resolved projects, a sample stratified by number of mentions (1, 2--5, 6--10, more than 10) and by category will be reviewed to label each project cluster as correct, over-merged (contains distinct initiatives) or under-merged (a duplicate of another cluster of the same issuer). Link precision and recall and the error costs of false merges versus missed links will be reported, with particular attention to long-running programs, subsidiaries and near-duplicate names.
25+
26+\paragraph{Temporal test.} For a sample of projects with at least three mentions, the status and amount reconstructed using only mentions filed on or before each date will be compared with the full-information values, to verify that no later information leaks into earlier states and to document how often status reverses.
27+
28+\paragraph{Missingness.} Zero-project issuer-years will be cross-tabulated by sector, size and filing count to distinguish ``no disclosure'' from ``not processed''; the former is informative, the latter is a data gap, and the \texttt{coverage\_status} field (complete for the defined scope, partial, failed, not observed) proposed for the issuer-year export will carry that distinction.
29+
30+\subsection{Acceptance gates}
31+
32+\begin{table}[H]
33+\centering\small
34+\caption{Acceptance gates for a research-ready release and their status at revision \texttt{2324b58}}\label{tab:gates}
35+\begin{tabularx}{\textwidth}{lXl}
36+\toprule
37+Gate & Requirement & Status \\
38+\midrule
39+(a) & Target issuer universe and time window frozen and auditable & Met (snapshot design; file hashes pending) \\
40+(b) & Every published project links to at least one verifiable primary-source passage & Met at section level; character spans not stored \\
41+(c) & Duplicate, temporal and amount rules documented and tested & Documented; unit tests partial; no event or amount-type tables \\
42+(d) & Extraction and matching accuracy evaluated on independent human labels & Not met (protocol specified) \\
43+(e) & Issuer/form/year coverage and failures quantified & Met \\
44+(f) & Every empirical result regenerable from a pinned build & Met for this paper (\texttt{code/}, revision \texttt{2324b58}) \\
45+(g) & All coauthors have reviewed scientific claims and authorship & Pending \\
46+\bottomrule
47+\end{tabularx}
48+\end{table}
49+
50+Until gates (c), (d) and (g) are met, the database is a \emph{research database under validation}, and any use of its counts or amounts in empirical work should treat extraction and linkage error as measurement error of unknown magnitude.
added paper/tables/prompt.tex +30 −0
@@ -0,0 +1,30 @@
1+\begin{quote}\footnotesize\ttfamily\setlength{\parskip}{0pt}\obeylines
2+You are a financial analyst extracting CORPORATE PROJECTS from SEC filing text.
3+A ''project'' is any concrete initiative, investment, program, expansion, construction,
4+product/R\&D effort, acquisition/integration, technology deployment, partnership, or
5+strategic program the company is pursuing.
6+
7+Return STRICT JSON: \{''projects'': [ ... ]\}. Each project object has:
8+ project\_name : short descriptive name (use the official name if one is stated)
9+ project\_type : EXACTLY ONE of the allowed types below
10+ description : 1 sentence, grounded in the text (no speculation)
11+ objective : the goal/why (or '''')
12+ status : one of planned | in\_progress | completed | mentioned
13+ amount\_usd : numeric USD figure if stated (e.g. 1500000000), else null
14+ locations : array of places (city/state/country), else []
15+ technologies : array of technologies involved, else []
16+ partners : array of named partners/JV counterparties, else []
17+ benefits : array of expected benefits, else []
18+ risks : array of stated risks, else []
19+ years : array of 4-digit years mentioned, else []
20+ confidence : 0.0-1.0 (how clearly this is a real, concrete project)
21+
22+Allowed project\_type values:
23+ - <code>: <label> (the 21 taxonomy entries of Table A1)
24+
25+Rules:
26+- Extract ONLY real initiatives, not generic risk-factor boilerplate or hypotheticals.
27+- If the text describes no concrete project, return \{''projects'': []\}.
28+- Do not invent facts. Numbers/locations must appear in the text.
29+- Prefer fewer high-quality projects over many vague ones.
30+\end{quote}
added paper/tables/prompt_verbatim.tex +30 −0
@@ -0,0 +1,30 @@
1+\begin{verbatim}
2+You are a financial analyst extracting CORPORATE PROJECTS from SEC filing text.
3+A "project" is any concrete initiative, investment, program, expansion, construction,
4+product/R&D effort, acquisition/integration, technology deployment, partnership, or
5+strategic program the company is pursuing.
6+
7+Return STRICT JSON: {"projects": [ ... ]}. Each project object has:
8+ project_name : short descriptive name (use the official name if one is stated)
9+ project_type : EXACTLY ONE of the allowed types below
10+ description : 1 sentence, grounded in the text (no speculation)
11+ objective : the goal/why (or "")
12+ status : one of planned | in_progress | completed | mentioned
13+ amount_usd : numeric USD figure if stated (e.g. 1500000000), else null
14+ locations : array of places (city/state/country), else []
15+ technologies : array of technologies involved, else []
16+ partners : array of named partners/JV counterparties, else []
17+ benefits : array of expected benefits, else []
18+ risks : array of stated risks, else []
19+ years : array of 4-digit years mentioned, else []
20+ confidence : 0.0-1.0 (how clearly this is a real, concrete project)
21+
22+Allowed project_type values:
23+ - <code>: <label> (the 21 taxonomy entries of Table A1)
24+
25+Rules:
26+- Extract ONLY real initiatives, not generic risk-factor boilerplate or hypotheticals.
27+- If the text describes no concrete project, return {"projects": []}.
28+- Do not invent facts. Numbers/locations must appear in the text.
29+- Prefer fewer high-quality projects over many vague ones.
30+\end{verbatim}
added paper/tables/tab_amounts.tex +8 −0
@@ -0,0 +1,8 @@
1+\begin{tabular}{lrrrrrr}
2+\toprule
3+ & P10 & P25 & Median & P75 & P90 & P99 \\
4+\midrule
5+Largest amount cited per project{,} M USD ($N$ = 56{{,}}500) & 3.2 & 23 & 128.5 & 500 & 1{,}503 & 12{,}501 \\
6+Projects per issuer ($N$ = 2{{,}}492) & 12 & 24 & 42 & 67 & 94 & 140 \\
7+\bottomrule
8+\end{tabular}
\ No newline at end of file
added paper/tables/tab_confidence.tex +12 −0
@@ -0,0 +1,12 @@
1+\begin{tabular}{lr}
2+\toprule
3+Self-reported confidence (rounded) & Mentions \\
4+\midrule
5+0.5 & 1{,}611 \\
6+0.6 & 3{,}278 \\
7+0.7 & 21{,}647 \\
8+0.8 & 72{,}280 \\
9+0.9 & 100{,}831 \\
10+1.0 & 108{,}418 \\
11+\bottomrule
12+\end{tabular}
\ No newline at end of file
added paper/tables/tab_coverage_year.tex +23 −0
@@ -0,0 +1,23 @@
1+\begin{tabular}{lrrrrrrr}
2+\toprule
3+Year & Issuers & 10-K & 10-Q & 8-K & Mentions & Issuers w/ mentions & Projects first seen \\
4+\midrule
5+2010 & 1{,}392 & 1{,}357 & 4{,}152 & 17{,}672 & 11{,}908 & 1{,}224 & 7{,}835 \\
6+2011 & 1{,}424 & 1{,}409 & 4{,}260 & 18{,}533 & 13{,}551 & 1{,}287 & 6{,}679 \\
7+2012 & 1{,}472 & 1{,}436 & 4{,}363 & 19{,}437 & 14{,}302 & 1{,}333 & 6{,}444 \\
8+2013 & 1{,}539 & 1{,}483 & 4{,}530 & 19{,}773 & 14{,}611 & 1{,}408 & 6{,}286 \\
9+2014 & 1{,}619 & 1{,}549 & 4{,}780 & 20{,}927 & 16{,}274 & 1{,}490 & 6{,}732 \\
10+2015 & 1{,}689 & 1{,}635 & 4{,}983 & 22{,}077 & 17{,}073 & 1{,}535 & 6{,}675 \\
11+2016 & 1{,}744 & 1{,}692 & 5{,}146 & 22{,}447 & 16{,}429 & 1{,}579 & 6{,}240 \\
12+2017 & 1{,}810 & 1{,}745 & 5{,}359 & 22{,}897 & 17{,}350 & 1{,}636 & 6{,}490 \\
13+2018 & 1{,}893 & 1{,}827 & 5{,}582 & 23{,}709 & 18{,}064 & 1{,}737 & 6{,}815 \\
14+2019 & 1{,}973 & 1{,}903 & 5{,}829 & 24{,}256 & 18{,}737 & 1{,}806 & 6{,}837 \\
15+2020 & 2{,}089 & 1{,}985 & 6{,}095 & 27{,}424 & 17{,}970 & 1{,}919 & 7{,}212 \\
16+2021 & 2{,}247 & 2{,}116 & 6{,}606 & 27{,}944 & 21{,}312 & 2{,}077 & 8{,}216 \\
17+2022 & 2{,}291 & 2{,}258 & 6{,}830 & 26{,}533 & 22{,}720 & 2{,}105 & 8{,}158 \\
18+2023 & 2{,}329 & 2{,}295 & 6{,}938 & 26{,}851 & 22{,}955 & 2{,}129 & 7{,}977 \\
19+2024 & 2{,}387 & 2{,}341 & 7{,}092 & 27{,}438 & 22{,}503 & 2{,}204 & 7{,}819 \\
20+2025 & 2{,}453 & 2{,}406 & 7{,}300 & 28{,}716 & 23{,}220 & 2{,}280 & 7{,}990 \\
21+2026 & 2{,}498 & 2{,}338 & 5{,}322 & 23{,}972 & 19{,}086 & 2{,}260 & 6{,}694 \\
22+\bottomrule
23+\end{tabular}
\ No newline at end of file
added paper/tables/tab_filings.tex +11 −0
@@ -0,0 +1,11 @@
1+\begin{tabular}{lrrrrr}
2+\toprule
3+Form & Filings & First filing & Last filing & Raw HTML (GB) & Not parsed \\
4+\midrule
5+10-K & 31{,}775 & 2010-01-08 & 2026-10-27 & 88.6 & 3 \\
6+10-Q & 95{,}167 & 2010-01-05 & 2026-10-27 & 155.9 & 12 \\
7+8-K & 400{,}606 & 2010-01-04 & 2026-10-27 & 10.0 & 1 \\
8+\midrule
9+Total & 527{,}548 & 2010-01-04 & 2026-10-27 & 254.5 & 16 \\
10+\bottomrule
11+\end{tabular}
\ No newline at end of file
added paper/tables/tab_graph.tex +14 −0
@@ -0,0 +1,14 @@
1+\begin{tabular}{llr}
2+\toprule
3+ & Type & Count \\
4+\midrule
5+Edges & \texttt{owns} & 121{,}099 \\
6+Edges & \texttt{located\_in} & 54{,}583 \\
7+Edges & \texttt{uses} & 28{,}774 \\
8+Nodes & \texttt{Project} & 121{,}099 \\
9+Nodes & \texttt{Partner} & 36{,}822 \\
10+Nodes & \texttt{Location} & 14{,}508 \\
11+Nodes & \texttt{Technology} & 8{,}551 \\
12+Nodes & \texttt{Company} & 2{,}492 \\
13+\bottomrule
14+\end{tabular}
\ No newline at end of file
added paper/tables/tab_items.tex +21 −0
@@ -0,0 +1,21 @@
1+\begin{tabular}{llrr}
2+\toprule
3+Form & Section (item) & Sections extracted & Mentions \\
4+\midrule
5+10-K & \texttt{business} & 29{,}285 & 30{,}492 \\
6+10-K & \texttt{risk\_factors} & 29{,}158 & 2{,}797 \\
7+10-K & \texttt{md\_and\_a} & 28{,}919 & 39{,}099 \\
8+10-K & \texttt{properties} & 28{,}236 & 7{,}879 \\
9+10-K & \texttt{quantitative\_disclosures} & 26{,}876 & 531 \\
10+10-K & \texttt{full\_body} & 1{,}230 & 824 \\
11+\midrule
12+10-Q & \texttt{controls\_and\_procedures} & 92{,}393 & 7{,}082 \\
13+10-Q & \texttt{financial\_statements} & 91{,}882 & 30{,}159 \\
14+10-Q & \texttt{md\_and\_a} & 91{,}371 & 99{,}305 \\
15+10-Q & \texttt{quantitative\_disclosures} & 83{,}138 & 808 \\
16+10-Q & \texttt{unregistered\_sales} & 80{,}210 & 9{,}535 \\
17+10-Q & \texttt{risk\_factors} & 72{,}560 & 4{,}420 \\
18+10-Q & \texttt{legal\_proceedings} & 66{,}998 & 845 \\
19+10-Q & \texttt{full\_document} & 241 & 107 \\
20+\bottomrule
21+\end{tabular}
\ No newline at end of file
added paper/tables/tab_provenance.tex +14 −0
@@ -0,0 +1,14 @@
1+\begin{tabular}{llllp{6.2cm}}
2+\toprule
3+Accession & Filed & Form & Status / amount & Evidence snippet (model-generated description grounded in the section) \\
4+\midrule
5+\texttt{0000003570-14-000070} & 2014-04-08 & 8-K & planned / -- & The construction of the first liquefaction train at the Corpus Christi Liquefaction facility is contingent upon receiving regulatory approvals and securing financing. \\
6+\texttt{0000003570-14-000114} & 2014-05-30 & 8-K & planned / -- & CCLNG will construct a second liquefaction train at its facility to sell LNG to IBERDROLA under a long-term agreement. \\
7+\texttt{0000003570-14-000117} & 2014-06-02 & 8-K & planned / -- & Cheniere Energy, Inc. plans to construct a second liquefaction train at the Corpus Christi Liquefaction facility. \\
8+\texttt{0000003570-14-000139} & 2014-06-30 & 8-K & planned / -- & Cheniere Energy, Inc. plans to construct a second liquefaction train at the Corpus Christi Liquefaction facility. \\
9+\texttt{0000003570-14-000153} & 2014-07-17 & 8-K & planned / -- & Cheniere Energy, Inc. plans to construct a third liquefaction train at its Corpus Christi Liquefaction facility. \\
10+\texttt{0000003570-14-000306} & 2014-12-18 & 8-K & planned / -- & Cheniere Energy, Inc. plans to construct a third liquefaction train at its Corpus Christi facility. \\
11+\texttt{0001193125-15-186045} & 2015-05-13 & 8-K & in\_progress / 11.5 B USD & The project involves the development, construction, operation, and maintenance of a natural gas liquefaction and export facility and related infrastructure in San Patrici \\
12+\texttt{0000003570-22-000029} & 2022-03-07 & 8-K & in\_progress / 5.5 B USD & The project involves the engineering, procurement, construction, pre-commissioning, commissioning, start-up, and testing of a mid-scale natural gas liquefaction facility \\
13+\bottomrule
14+\end{tabular}
\ No newline at end of file
added paper/tables/tab_reconciliation.tex +19 −0
@@ -0,0 +1,19 @@
1+\begin{tabular}{lr}
2+\toprule
3+Step & Count \\
4+\midrule
5+iShares IWV holdings file, equity lines with a ticker (as of Oct 23, 2026) & 2{,}587 \\
6+\quad of which mapped to an SEC CIK (CVRs, escrow lines and FDIC-only banks excluded) & 2{,}573 \\
7+\quad distinct CIKs (share classes collapsed) & 2{,}555 \\
8+S\&P~500 constituents, cached snapshot of 2026-05-25 (500 CIKs) & 503 \\
9+\midrule
10+Merged issuer universe (distinct CIKs) & 2{,}564 \\
11+\quad in both lists & 491 \\
12+\quad S\&P~500 only & 9 \\
13+\quad Russell proxy only & 2{,}064 \\
14+\midrule
15+Issuers with at least one 10-K, 10-Q or 8-K since 2010 & 2{,}499 \\
16+Issuers without any such filing (foreign private issuers filing 20-F/40-F, FDIC-only banks) & 65 \\
17+Issuers with at least one resolved project & 2{,}492 \\
18+\bottomrule
19+\end{tabular}
\ No newline at end of file
added paper/tables/tab_sections.tex +11 −0
@@ -0,0 +1,11 @@
1+\begin{tabular}{lrrrrrr}
2+\toprule
3+Form & Parsed sections & Median words & Sections sent to the extractor & Sections with $\geq 1$ mention & Mentions & Issuers \\
4+\midrule
5+10-K & 341{,}207 & 875 & 143{,}704 & 30{,}877 & 81{,}622 & 2{,}464 \\
6+10-Q & 600{,}471 & 500 & 578{,}793 & 75{,}705 & 152{,}261 & 2{,}494 \\
7+8-K & 400{,}605 & 575 & 400{,}601 & 61{,}648 & 74{,}594 & 2{,}499 \\
8+\midrule
9+Total & 1{,}342{,}283 & & 1{,}123{,}098 & 168{,}230 & 308{,}477 & 2{,}499 \\
10+\bottomrule
11+\end{tabular}
\ No newline at end of file
added paper/tables/tab_sectors.tex +17 −0
@@ -0,0 +1,17 @@
1+\begin{tabular}{lrrrrr}
2+\toprule
3+GICS sector (iShares) & Issuers in universe & of which S\&P~500 & Issuers with projects & Projects & Projects per issuer \\
4+\midrule
5+Health Care & 433 & 59 & 429 & 23{,}679 & 55.2 \\
6+Industrials & 393 & 79 & 386 & 17{,}890 & 46.3 \\
7+Consumer Discretionary & 276 & 48 & 267 & 15{,}072 & 56.4 \\
8+Financials & 462 & 76 & 446 & 14{,}223 & 31.9 \\
9+Information Technology & 333 & 73 & 325 & 11{,}756 & 36.2 \\
10+Real Estate & 146 & 31 & 145 & 9{,}001 & 62.1 \\
11+Materials & 126 & 26 & 117 & 7{,}407 & 63.3 \\
12+Energy & 119 & 21 & 107 & 6{,}710 & 62.7 \\
13+Consumer Staples & 103 & 36 & 103 & 5{,}921 & 57.5 \\
14+Utilities & 68 & 31 & 66 & 4{,}920 & 74.5 \\
15+Communication Services & 105 & 20 & 101 & 4{,}520 & 44.8 \\
16+\bottomrule
17+\end{tabular}
\ No newline at end of file
added paper/tables/tab_status.tex +11 −0
@@ -0,0 +1,11 @@
1+\begin{tabular}{lr@{\hspace{2em}}lr@{\hspace{2em}}lr}
2+\toprule
3+Latest status & Projects & Mentions per project & Projects & Span of cited years & Projects \\
4+\midrule
5+\texttt{in\_progress} & 50{,}327 & 1 & 72{,}932 & 0 years & 67{,}170 \\
6+\texttt{completed} & 49{,}687 & 2 & 20{,}544 & 1-2 years & 17{,}307 \\
7+\texttt{planned} & 18{,}340 & 3-5 & 17{,}681 & 3-5 years & 8{,}697 \\
8+\texttt{mentioned} & 2{,}745 & 6-10 & 6{,}173 & $>$5 years & 10{,}326 \\
9+& & >10 & 3{,}769 & unknown years & 17{,}599 \\
10+\bottomrule
11+\end{tabular}
\ No newline at end of file
added paper/tables/tab_taxonomy.tex +27 −0
@@ -0,0 +1,27 @@
1+\begin{tabular}{ll}
2+\toprule
3+Code & Label \\
4+\midrule
5+\texttt{manufacturing\_expansion} & Manufacturing / Capacity Expansion \\
6+\texttt{plant\_construction} & Plant / Site Construction \\
7+\texttt{data\_center} & Data Center \\
8+\texttt{cloud\_migration} & Cloud Migration \\
9+\texttt{ai\_initiative} & AI / ML Initiative \\
10+\texttt{digital\_transformation} & Digital Transformation \\
11+\texttt{erp\_implementation} & ERP / Systems Implementation \\
12+\texttt{rd\_program} & R\&D Program \\
13+\texttt{product\_launch} & New Product / Launch \\
14+\texttt{drug\_pipeline} & Drug / Clinical Pipeline \\
15+\texttt{ma\_integration} & M\&A / Integration \\
16+\texttt{geographic\_expansion} & Geographic Expansion \\
17+\texttt{capex\_program} & Capital Expenditure Program \\
18+\texttt{supply\_chain} & Supply Chain Initiative \\
19+\texttt{automation} & Automation / Robotics \\
20+\texttt{sustainability} & Sustainability / ESG \\
21+\texttt{energy\_transition} & Energy Transition / Renewables \\
22+\texttt{infrastructure} & Infrastructure Investment \\
23+\texttt{cost\_reduction} & Cost Reduction / Restructuring \\
24+\texttt{partnership} & Strategic Partnership \\
25+\texttt{technology\_deployment} & Technology Deployment \\
26+\bottomrule
27+\end{tabular}
added paper/tables/tab_top_issuers.tex +18 −0
@@ -0,0 +1,18 @@
1+\begin{tabular}{llr}
2+\toprule
3+Ticker & Issuer & Resolved projects \\
4+\midrule
5+UEC & Uranium Energy & 240 \\
6+UUUU & Energy Fuels & 214 \\
7+JBLU & Jetblue Airways & 203 \\
8+HAL & Halliburton & 201 \\
9+RIOT & Riot Platforms & 195 \\
10+EHC & Encompass Health & 191 \\
11+EBS & Emergent Biosolutions & 187 \\
12+MUX & Mcewen Inc & 180 \\
13+CDE & Coeur Mining & 180 \\
14+CLSK & Cleanspark & 170 \\
15+CWEN & Clearway Energy Inc Class C & 168 \\
16+ALK & Alaska Air Group Inc & 168 \\
17+\bottomrule
18+\end{tabular}
\ No newline at end of file
added paper/tables/tab_types.tex +29 −0
@@ -0,0 +1,29 @@
1+\begin{tabular}{llrrrrr}
2+\toprule
3+Code & Category & Projects & Share (\%) & With amount & Median amount (M USD) & Mentions / project \\
4+\midrule
5+\texttt{ma\_integration} & M\&A / integration & 41{,}742 & 34.5 & 28{,}044 & 155.0 & 3.12 \\
6+\texttt{capex\_program} & Capital expenditure program & 10{,}720 & 8.9 & 9{,}452 & 300.0 & 1.79 \\
7+\texttt{partnership} & Strategic partnership & 9{,}909 & 8.2 & 2{,}943 & 75.0 & 1.77 \\
8+\texttt{geographic\_expansion} & Geographic expansion & 7{,}868 & 6.5 & 1{,}431 & 36.7 & 2.25 \\
9+\texttt{plant\_construction} & Plant / site construction & 7{,}590 & 6.3 & 2{,}511 & 56.0 & 2.42 \\
10+\texttt{cost\_reduction} & Cost reduction / restructuring & 6{,}481 & 5.4 & 4{,}378 & 50.0 & 2.03 \\
11+\texttt{drug\_pipeline} & Drug / clinical pipeline & 5{,}360 & 4.4 & 778 & 46.1 & 4.99 \\
12+\texttt{product\_launch} & New product / launch & 5{,}356 & 4.4 & 447 & 26.7 & 2.33 \\
13+\texttt{rd\_program} & R\&D program & 5{,}080 & 4.2 & 1{,}272 & 20.0 & 2.31 \\
14+\texttt{infrastructure} & Infrastructure investment & 4{,}481 & 3.7 & 1{,}694 & 48.0 & 1.90 \\
15+\texttt{manufacturing\_expansion} & Manufacturing / capacity expansion & 4{,}140 & 3.4 & 1{,}040 & 45.0 & 2.13 \\
16+\texttt{technology\_deployment} & Technology deployment & 4{,}128 & 3.4 & 1{,}219 & 135.0 & 1.63 \\
17+\texttt{digital\_transformation} & Digital transformation & 2{,}642 & 2.2 & 240 & 34.2 & 1.99 \\
18+\texttt{erp\_implementation} & ERP / systems implementation & 1{,}735 & 1.4 & 184 & 15.0 & 2.44 \\
19+\texttt{sustainability} & Sustainability / ESG & 1{,}305 & 1.1 & 410 & 34.4 & 2.00 \\
20+\texttt{supply\_chain} & Supply-chain initiative & 684 & 0.6 & 139 & 38.0 & 1.71 \\
21+\texttt{data\_center} & Data center & 449 & 0.4 & 129 & 166.0 & 1.98 \\
22+\texttt{energy\_transition} & Energy transition / renewables & 415 & 0.3 & 109 & 109.0 & 2.09 \\
23+\texttt{ai\_initiative} & AI / ML initiative & 376 & 0.3 & 20 & 127.5 & 1.68 \\
24+\texttt{automation} & Automation / robotics & 374 & 0.3 & 25 & 13.7 & 1.68 \\
25+\texttt{cloud\_migration} & Cloud migration & 264 & 0.2 & 35 & 50.0 & 1.97 \\
26+\midrule
27+ & Total & 121{,}099 & 100.0 & 56{,}500 & 128.5 & 2.54 \\
28+\bottomrule
29+\end{tabular}
\ No newline at end of file
added paper/uq_logo.jpg +0 −0

Binary file not shown.

Menu

Theme