Data and API
The record and the classification as CSV and JSON files and a static API, with the same evidence and sources for each gap as the record pages.
Download
Each record file name includes its version, and released files are never changed. CSV files are encoded in UTF-8 with a byte-order mark so that spreadsheet programs read them correctly.
- Full recordEach entry with its evidence and sources, and the classification.
- EntriesOne row per entry, with a column for each gap.
- Evidence for each gapOne row for each gap marked in an entry, with the evidence, the kind of evidence, and the sources.
- ClassificationThe 13 gaps with definitions, the rules for applying them, and framework mappings.
- SchemaThe structure of the full record file.
Earlier versions
This is the first public release. Each later version will have its own file names, and the files of earlier versions will remain here unchanged so that anyone who cites a version can check the data they used.
Version 0.2
Specification mapping
The mapping of AARM, OWASP’s Agent Control Standard, and NVIDIA OpenShell to the 13 gaps, as shown on the specifications page. The file carries its date inside it rather than the record’s version number, because it is updated as the specifications change.
- Specification mappingEach specification and, for each gap, its level, the parts of the document that were read, and a note.
- Specification mappingOne row for each gap and specification.
Loading the data
The files are standard JSON and CSV. The evidence file (control-gap-record-v0.3-codes.csv) has one row for each gap marked in an entry, with its evidence, and is the simplest file to count from.
import pandas as pd
base = "https://externalcontrollayer.ai/data/"
entries = pd.read_csv(base + "control-gap-record-v0.3-entries.csv")
codes = pd.read_csv(base + "control-gap-record-v0.3-codes.csv")
# how often each gap was marked, by kind of system
counts = codes.merge(entries[["entry", "system_type"]], on="entry")
print(pd.crosstab(counts["code"], counts["system_type"]))
const res = await fetch("https://externalcontrollayer.ai/api/v0/entries.json");
const entries = await res.json();
// agent incidents with direct access (G2)
const direct = entries.filter(e => e.system_type === "agent" && e.codes.includes("G2"));
console.log(direct.map(e => `${e.entry}: ${e.title}`));
API addresses
The same data is published as static JSON at fixed addresses on externalcontrollayer.ai. These are version 0 addresses and may change before version 1.0 of the record, in which case the changes will be announced in the change log beforehand.
/api/v0/index.json list of the addresses below
/api/v0/record.json the full record, same as the JSON download
/api/v0/codebook.json the classification
/api/v0/entries.json one summary per entry
/api/v0/entries/{n}.json one entry in full, for n = 1 to 32
/data/control-gap-record.json the latest full record
/data/control-gap-record.schema.json JSON Schema for the full record
Fields
Each entry in the full record has the following fields. Fields that hold a category store a short identifier, listed here with the label the site uses. The classification defines each gap and category.
entry- Permanent number. Numbers are never reused, and new entries take the next number whatever their date.
key- Permanent text identifier, such as replit-2025.
url- The address of the entry’s page on this site.
title, short_name- The entry’s title, used as its page heading, and the shorter name used in lists and tables.
date, when- Date of the harmful action, or the start of the period the sources document, and the date or period as written on the entry’s page.
organization, system- Who ran the system, and what it was.
system_type- One of: agent (Agent with tools), chat (Conversational assistant), generative (Generative model in a workflow), perception (Perception and control), predictive (Predictive or matching model), automation (Conventional automation).
sector- One of: software (Software and IT), consumer (Consumer and retail), transport (Transportation), government (Government services), health (Healthcare), finance (Finance and real estate), justice (Policing and courts), media (Media and platforms).
precedent- True for conventional automation, counted separately from AI incidents.
action- The action that did the harm or nearly did.
trigger, trigger_note- What started the incident, one of: model (Model error), input (Manipulated input), change (Faulty or malicious change), fault (Conventional fault), design (Working as designed), with a note where the entry has one.
outcome- One of: harm (Harm), near-miss (Near miss), hazard (Hazard).
harm_types, harm- The kinds of harm, from: injury (Death or injury), money (Money), data (Data and records), rights (Rights and liberty), statement (Public statements), and a description.
summary, what_happened- The summary at the top of the entry’s page, and the account of what happened, as a list of paragraphs.
codes- The gaps found, as identifiers from G1 to G13. In the data files the gaps are called codes, the usual research term for the categories of a classification scheme, and the classification itself is the codebook (codebook.json).
evidence- For each gap: a summary of the evidence, the kind of evidence (basis), the sources, and a page or section reference where there is one. The kinds of evidence are finding (Finding), admission (Admission), allegation (Allegation), firsthand (First-hand account), reporting (Reporting).
not_coded- Gaps considered and left out, with the reason.
confidence, confidence_note- One of: high (High), medium (Medium), low (Low), set by the weakest evidence behind any of the entry's gaps, with a note.
analysis_url- The address of the incident analysis for the entry, where there is one.
aiid_incident- The incident’s number in the AI Incident Database, where the match has been confirmed.
sources- Each source, with title, publisher, date, URL, and type, one of: official (Official record), court (Court record), filing (Legal filing), company (Company statement), firsthand (First-hand account), press (News report), research (Independent research), commentary (Commentary). A note is added where the source needs one, for example when it could not be read directly.
The full record also lists the incidents that were researched and left out, under considered_not_coded, each with the reason. The entries CSV adds code_count, the number of gaps found, and source_urls, the addresses of the sources. The classification CSV gives, for each gap, its definition, when to mark it (code_when) and when not to (do_not_code_when), its question, and the statement used by the self-check (self_check). The specification mapping files give, for each gap, the number of entries in which it was found (entries_coded). In the API index, api/v0/entries.json, the url of each entry is the address of that entry’s JSON file rather than its page.
Identifiers and versions
- Entry numbers are permanent. A new entry takes the next number, whatever its date, and a withdrawn entry keeps its number with a note.
- Gap numbers are permanent. If a gap is retired, entries marked with it keep it, with a note.
- Each release has a version number. A release that adds entries or changes the gaps marked in them raises the minor number, and one that changes the structure of the data raises the major number. A correction that changes no marked gap raises the last number, as in 0.2.1.
- Corrections to an entry are listed in the change log with their date and the entry number.
License and citation
The data is published under the Creative Commons Attribution 4.0 license. You may copy, adapt, and redistribute it, including commercially, with attribution.
External Control Layer. Control Gap Record, version 0.3. Maintained by G-14, Inc. Released 2 November 2026. https://externalcontrollayer.ai/record
New entries and corrections are announced in the change log and in an Atom feed at /record.xml.