# Method

Everything below is produced by one script, `tools/report_caio_facts.py`, in
three stages: `fetch` (the queries), `review` (the inspection rules and the
per-row decisions) and `aggregate` (the published files). Rerunning all three
reproduces every number in this directory.

## The join

The company list is the 2026 Fortune 500 with domains, 500 rows, from
`installmap.com/data/universes/fortune500/companies.csv` (columns `rank`,
`company`, `domain`, `year`; all 500 rows carry `year` 2026). The 2023 list in
the same directory was not used.

The person source is the collection `blitz_people` in the Mongo database `data`,
137,403,249 rows, indexed on `company_domain`. The join is exact on that domain
string. Domains were queried in `$in` batches of 50 with the title regex applied
server side, so no query ran without a `company_domain` filter and no
full-collection title scan was performed. For each batch a second aggregation
counted the person rows per domain, which is the coverage and depth measure.

Snapshot month: September 2026.

## Coverage, the denominator

- 500 domains in the list.
- **483 have at least one person row.** 17 have none: Meta Platforms, Comcast,
  Procter & Gamble, Lithia Motors, PG&E, Edison International, Dick's Sporting
  Goods, Consolidated Edison, Venture Global, Expand Energy, Yum China Holdings,
  Equitable Holdings, Burlington Stores, Gold.com, Ameren, Monster Beverage,
  APi Group.
- 8,554,225 person rows across the 483.
- Rows per covered company: median 10,123, p10 812, p90 48,265, mean 17,710.
  The percentiles are taken as the value at the sorted index at or below the
  percentile, not by interpolating between two neighbouring companies. p90 is
  therefore a company's own row count and not a value between two of them.

**Every share in this dataset uses 483 as the denominator, never 500.**

Depth runs from 1 row to 123,808. PulteGroup holds the single row and Amazon
the 123,808. A company with few rows can lack a matched title for that reason
alone. Alphabet holds 488 rows and Berkshire Hathaway 16, so neither is evidence
of anything. The companies named
as lacking a Chief AI Officer are named together with their row counts so a
reader can see which ones are observed deeply enough for the absence to mean
something.

## The regexes

All are case insensitive and are matched against the `title` field.

Chief AI Officer:

    chief\s+(artificial\s+intelligence|ai)\s+officer

Head or vice-president of AI:

    (head|vp|vice\s+president|svp|evp|senior\s+vice\s+president|
     executive\s+vice\s+president)[\s,]+(of\s+)?(ai|artificial\s+intelligence)\b

The separator is `[\s,]+` rather than `\s+`. "Vice President, AI Strategy" and
"VP of AI Strategy" are the same role, and splitting them on a comma would be a
punctuation artefact. The narrower `\s+` variant is also recorded, in the column
`match_head_of_ai_nocomma` of the working files, so both counts can be read: it
matches 311 of the 456 inspected holders across 106 of the 125 companies.

Combined chief titles that carry AI alongside another remit, counted separately
and never folded into the Chief AI Officer number:

    chief\s+[a-z&,\s]*\b(ai|artificial\s+intelligence)\b[a-z&,\s]*\s+officer

A single server-side prefilter that is a superset of all three ran in Mongo;
the three regexes above were then applied in Python to the returned rows, so the
published flags and the published regexes are the same thing.

## The inspection

582 rows matched the prefilter. **Every one was read**, title and headline
together, and carries a keep decision, a tier and a reason in
`data/reports/caio_matches_reviewed.csv`. No count in this dataset comes from an
uninspected match.

Tiers:

| Tier | Meaning | Counted in |
|---|---|---|
| `own_title_enterprise` | The person's own title carries the AI remit and names no unit, region, product or segment | people counts, `caio_enterprise` |
| `own_title_divisional` | Same, but the title names a unit, region, product, function or client segment | people counts |
| `assistant_or_indirect` | An assistant, chief of staff, deputy or office-of row pointing at a leader | company-level `caio_any` only |
| `false_positive` | Not a holder of the title | nothing |

Rules, applied in order, first match wins. Rows needing an individual judgement
are held in a table in the script that overrides every rule; all 41 Chief AI
Officer and combined-title rows and 16 head-of-AI rows were decided that way.

1. **Assistant or indirect.** The title reads "executive assistant",
   "technical assistant", "staff assistant", "assistant to", "chief of staff
   to", "office of the" or "deputy chief". The role exists at the company; this
   person does not hold it.
2. **Duplicate.** Same company, name and title appearing more than once. 9 rows.
3. **Junior rank.** The leading rank is assistant, associate or second vice
   president, a grade banks and insurers use well below a head of AI. 20 rows.
4. **Bare unqualified title held by more than one person at one company.** Only
   one person can hold an unqualified company-wide AI title, so where a company
   showed several identical bare titles the whole cluster was dropped rather
   than one row picked arbitrarily. 22 rows across 8 companies. A company
   showing exactly one such title keeps it.
5. **Individual-contributor role at a vice-president grade.** The title names an
   analyst, scientist, architect, developer, engineer, product or project
   manager, associate, consultant, specialist or agilist, and no leadership
   noun. At several banks "vice president" is a grade rather than a job. 15 rows.
6. **Headline contradicts the title, or the profile is junk.** The headline
   names a plainly different occupation, a different employer, or the profile
   carries a placeholder name or a student's description. 17 rows.
7. Otherwise the row is a holder, tiered enterprise or divisional on whether the
   title names a unit, region, product, function or client segment.

Result: 499 rows kept, 83 dropped.

## People counts exclude assistant rows

Both the Chief AI Officer people counts and the head-of-AI people counts count
holders only, that is rows tiered `own_title_enterprise` or
`own_title_divisional`. An assistant, chief of staff, deputy or office-of row
raises a company's `caio_any` flag but is never counted as a person. Counting
every kept row instead gives 460 head-of-AI people at 126 companies on the
published regex, 315 at 107 with the comma form excluded and 229 at 98 on the
"head"-word variant.

## Small cells

Any published count below 5 prints as `<5`.

## Limitations

1. **Titles are self-reported.** The source is LinkedIn-derived. It is not a
   proxy statement, a filing or a company org chart. A self-reported title is
   weaker evidence than a company announcement, and rules 4 to 6 above exist
   because a measurable share of the raw matches were people describing
   themselves rather than people holding the job.
2. **No date on any row.** There is no as-of field per person, so a departed
   executive can still be present and a recent hire absent. Entergy shows two
   enterprise Chief AI Officer rows and public reporting says the first has
   moved on; the data cannot order them. No time series is possible.
3. **Coverage is 483, not 500,** and depth ranges from 1 row (PulteGroup) to
   123,808 (Amazon).
4. **Assistant and divisional matches were separated, not silently counted.**
   The three Chief AI Officer numbers, 11 and 10 and 7, differ only in which of
   those tiers is included, and all three are published.
5. **One of the eleven could not be confirmed.** Fastenal's match is a
   self-reported title with no public corroboration; the Fastenal leadership
   page returns 404 and a web search finds nothing.
6. **A company with no matched row may still have the executive.** It means
   no person in this snapshot carries the title. U.S. Bancorp demonstrates the gap in both
   directions: the bank does have a Chief AI Officer, confirmed publicly, but
   the only rows in the data are an assistant and a mismatched profile.
7. **The true count is more likely above ours than below it.** A person
   missing from the source lowers the count we can produce and cannot raise it,
   and so does a row that is out of date. The inspection only removed rows.
   Read 11 as a floor.

## Sanity checks against public sources

Five of the eleven Chief AI Officer companies were checked against a public
source. All fetches went through the Webshare rotating gateway.

| Company | Person in the data | Public source | Result |
|---|---|---|---|
| Dell Technologies | Jeff Boudreau | https://aimagazine.com/articles/dell-names-jeff-boudreau-as-first-ever-chief-ai-officer | Confirmed |
| eBay | Nitzan Mekel-Bobrov | https://sloanreview.mit.edu/audio/transforming-transactions-with-technology-ebays-nitzan-mekel-bobrov/ | Confirmed |
| GE HealthCare | Parminder Bhatia | https://www.fiercebiotech.com/medtech/conversation-ge-healthcares-chief-ai-officer-parry-bhatia | Confirmed |
| S&P Global | Bhavesh Dayalji | https://www.pymnts.com/artificial-intelligence-2/2025/sp-global-ai-chief-eyes-exciting-pace-of-agentic-ai-innovation/ | Confirmed |
| U.S. Bancorp | Vinke Xu | https://www.cxotalk.com/episode/u-s-banks-chief-ai-officer-on-strategy-governance-and-scaling-ai | Role confirmed, person is Prashant Mehrotra, so the matched row was dropped |

## Licence

CC BY 4.0.
