How an institution captures, investigates, closes and hands off biosafety incidents and near misses as structured records built on the WIA-BIO-017 IncidentReport type.
Chapter 2 described the records that change slowly: a registered facility, its certification block, the people authorized to work there and the training that keeps them authorized. This chapter turns to the record that is created on the worst day of a laboratory's month, often in a hurry, by someone who is worried about what the report will mean for them. How that record is designed decides whether the institution learns anything from the event, and whether the next inspector can follow what happened from the first phone call to the final sign-off.
The WIA-BIO-017 specification gives the raw material for this chapter in three places: the severity table and response times in §7.1, the investigation and corrective-action outline in §7.3, and the documentation list in §7.4. The TypeScript SDK adds the IncidentReport interface with a four-state status field. This chapter treats all of these strictly as administrative structure. It does not describe how to respond physically to a spill or an exposure, what medical steps follow, or how to judge the hazard of any material. Those questions belong to the institution's biosafety officer, its occupational-health service and the official guidance issued by national authorities and the WHO.
Every incident database has two populations of events: the ones that reached it and the ones that did not. The second population is invisible, and in most institutions it is larger. A door that was propped open during a shift change, an authorization that expired a week before someone kept working under it, a transfer form that was signed after the material had already moved: none of these caused harm, and all of them are signals that the management system is drifting. If reporting them feels like confessing, they will not be reported.
The safety-science literature calls the alternative a just culture: an organization distinguishes honest error and system-induced mistakes, which are treated as learning opportunities, from reckless or deliberate violations, which are handled through normal accountability processes. A data standard cannot create a just culture, but a badly designed record can destroy one. If the incident form asks "Who is responsible?" in its first field, if the reporter's name is printed on every summary, or if an open incident automatically suspends the reporter's access, the form teaches people to keep quiet.
A non-punitive record design therefore follows a few concrete rules, all of which appear later in this chapter as field-level decisions:
Injury and exposure counts are lagging indicators: by the time they move, harm has already occurred. The number of near misses reported per period, normalized by the number of active researchers or registered facilities, is a leading indicator. A rising near-miss rate in a stable population is usually good news, because it means people trust the channel. A department that reports zero near misses for a year is rarely a department where nothing happened.
This has a direct consequence for the data model. If near misses are to be counted and trended, they must be first-class records with the same identifier scheme, timestamps and closure workflow as incidents, not free-text notes in a separate spreadsheet. The book does not suggest a target reporting rate; any threshold is an institutional choice and should be set by the biosafety committee after it has a year or so of its own baseline data.
The SDK's IncidentReport.type enumeration lists spill, exposure, equipment-failure, breach, procedural-violation and other. There is no near-miss value, and that is the right choice: "near miss" is not a kind of event but an outcome. A procedural violation can be a near miss (nothing happened) or an incident (something did). Encoding the outcome in the type field would force reporters to pick between two true descriptions.
The book's profile therefore adds one optional field, eventClass, with the values incident and near-miss. It is an additive extension in the sense of PHASE-1 Annex E: existing consumers that do not know the field ignore it, and no existing field is renamed. Throughout this chapter, fields that the SDK does not define are marked as profile extensions so that readers can tell the standard apart from the book's proposals.
IncidentReport Administrative Fields
The following table lists the fields of IncidentReport as they appear in api/typescript/src/types.ts, together with the handling this book recommends for a management record. Fields the SDK defines but the book excludes from a management record are listed too, so that nothing is silently dropped.
| Field | SDK type | Meaning | Book profile rule |
|---|---|---|---|
incidentId | string | Unique identifier | Stable, never reused; INC- prefix (Chapter 2.6) |
date | Date | string | When the event occurred | ISO 8601 in UTC; see the timestamp note below |
location | string | Where it occurred | Reference a registered facility ID plus room; no floor-plan detail |
severity | 1 | 2 | 3 | 4 | Severity level | Drives the notification SLA (§3.3.4) |
type | enum of six values | Kind of event | Keep as is; outcome goes in eventClass |
personnel | string[] | People involved | Pseudonymous personnel IDs only |
agent | BiologicalAgent | Material involved | Replaced by an internal registry reference (§3.2.3) |
concentration | number? | Quantitative material detail | Excluded from the management record |
description | string | Narrative | Factual, redaction-checked free text |
immediateActions | string[] | What was done at once | Administrative actions only (notified, secured, logged) |
medicalEvaluation | MedicalEvaluation? | Clinical content | Out of scope; link an occupational-health case ID instead |
rootCause | string? | Root cause | Summary string; structured detail in the RCA record (§3.4) |
correctiveActions | string[] | Corrective actions | Summaries; each backed by a CAPA record |
preventiveActions | string[]? | Preventive actions | Summaries; each backed by a CAPA record |
followUpRequired | boolean | Whether follow-up is needed | Must be false before closure, or CAPA must be open |
status | four-value enum | Workflow state | Governed by the state machine (§3.3) |
reportedBy | string | Reporter | Personnel ID; may be a shared anonymous-channel ID |
reviewedBy | string? | Reviewer | Required before closed |
The SDK types date as Date | string, which is convenient in code and ambiguous in exchange. A management record should serialize every timestamp as ISO 8601 with an explicit UTC offset (2026-03-11T05:40:00Z) and convert to local time only for display. A single date is also not enough for SLA tracking: the time an event occurred, the time it was discovered and the time it was reported can differ by hours or days, especially for near misses found during a later check. The book's profile adds discoveredAt and reportedAt as optional extensions and keeps date as the occurrence time the SDK expects.
For location, the SDK allows any string, and the specification's own example writes a free-text room description. In a management record, location should resolve to a registered facility: the facilityId from Chapter 2 plus a room identifier from the institution's space register. That makes it possible to aggregate events by facility and containment label without parsing prose, and it avoids a common leak: free-text location fields that end up describing building layouts in a report that is later shared with partners.
personnel, reportedBy and reviewedBy should hold institution-issued personnel IDs, never names, e-mail addresses or employee numbers that are printed on badges. The mapping from ID to person lives in the HR or identity system and is released only to roles with a need to know (the biosafety officer and the assigned investigator, typically). When records leave the institution, as discussed in Chapter 5, the IDs are replaced again with exchange-specific pseudonyms. Chapter 6 covers the privacy law side of this; here it is enough to note that a record which never contained names is much easier to share than one that must be scrubbed.
agent field under data minimization
The SDK makes agent: BiologicalAgent a required field, and the BiologicalAgent type carries characteristics of the material that are outside the scope of a management record. Following the data-minimization rule set out in Chapter 2.5, the book's profile replaces the embedded object with a reference: materialRef, an institution-internal registry ID (or null when no material was involved, as in most access and procedural events), together with the classification label already recorded in the registry. Anyone who needs to know what the registry entry contains follows the reference under their own access rights.
Because the SDK type still requires agent, a deployment using this profile needs an adapter at the boundary where it calls SDK functions. Chapter 4 shows how to wrap the SDK so that the management record stays minimal while type checks still pass. The important design point is the direction of dependency: the incident record points to the registry; it never copies registry content into itself.
The SDK also defines an optional medicalEvaluation object and a separate MedicalEvaluation type. In most jurisdictions, clinical information about an employee is health data subject to stricter handling than safety records, and it is owned by the occupational-health service, not by the biosafety office. The book therefore treats MedicalEvaluation as out of scope. The profile adds occupationalHealthCaseRef: an opaque case ID issued by the health service, which tells the biosafety officer that a case exists and whether it is still open, without revealing anything about it. If the health service's system cannot issue such a reference, the field stays empty and the link is kept manually; clinical text should not be pasted into the incident description as a workaround.
The SDK contains two different encodings of severity. IncidentReport.severity is numeric (1 | 2 | 3 | 4), while the exported type SeverityLevel is a string enumeration ('minor' | 'moderate' | 'serious' | 'critical'). The specification's §7.1.1 table uses the same four names for levels 1 to 4. A record system should store the number, because that is what the interface requires, and define the mapping once in a shared constant so that user interfaces and reports never disagree:
// Single source of truth for severity labels (book profile).
const SEVERITY_LABEL: Record<1 | 2 | 3 | 4, SeverityLevel> = {
1: 'minor',
2: 'moderate',
3: 'serious',
4: 'critical',
};
For near misses, the book recommends recording severity as the level of the event that did occur, and, optionally, a potentialSeverity extension capturing the committee's judgment of what could have happened. The two numbers answer different questions: the first sets notification obligations, the second sets investigation priority. Mixing them in one field inflates notification traffic and hides the most instructive near misses among routine ones.
IncidentReport.status has four values: open, under-investigation, resolved and closed. The SDK defines the values but not the rules for moving between them. Without rules, the status field becomes whatever the last editor typed, and an inspector cannot tell whether "closed" means "investigated and verified" or "someone got tired of seeing it on the dashboard". This section proposes those rules as the book's profile.
report submitted
|
v
+-----------+ assign investigator +---------------------+
| open | ----------------------> | under-investigation |
+-----------+ +---------------------+
| | ^
| triage: no investigation | RCA and | new facts
| needed (near miss, sev 1 only) | CAPA set | or failed
v v | review
+-----------+ effectiveness verified +-----------+ |
| closed | <------------------------ | resolved |-+
+-----------+ +-----------+
|
| reopen (biosafety officer, reason required)
+------------------> under-investigation
| From | To | Who may perform it (example roles) | Preconditions |
|---|---|---|---|
| (new) | open | Any registered personnel, or the anonymous channel | Minimum fields present: date, location, type, description, severity |
open | under-investigation | Biosafety officer | Investigator assigned; notification SLA recorded as met or missed |
open | closed | Biosafety officer | Only if institutional policy allows triage closure (for example, severity 1 near misses); reviewedBy and a triage note required |
under-investigation | resolved | Assigned investigator | rootCause set; at least one CAPA record with owner and due date |
resolved | under-investigation | Biosafety officer or IBC | Review found the RCA or CAPA insufficient; reason recorded |
resolved | closed | Biosafety officer (and IBC where policy requires) | All CAPA items done or formally transferred; effectiveness review recorded; reviewedBy set; followUpRequired is false |
closed | under-investigation | Biosafety officer | Reopen reason required; original closure remains in the audit trail |
Two transitions are deliberately absent. There is no direct path from open to resolved, because "resolved" asserts that a cause has been found and actions defined, which requires an investigation, however short. There is also no path backward to open: once someone has been assigned, the record never returns to an unowned state. Any transition not listed in the table is rejected by the service and logged as a finding.
The roles in the table are examples; institutions set their own. One rule is worth keeping regardless: the person who moves a record to resolved should not be the same person who moves it to closed. The SDK already separates reportedBy from reviewedBy; the profile adds that reviewedBy must differ from the investigator. Where only one qualified person exists, the second signature can come from the IBC chair or an external reviewer, recorded as such.
Reopening is normal: a CAPA that looked effective fails a month later, or a second event reveals that the first root cause was wrong. The rule is that reopening appends; it never overwrites. The closure decision, its reviewer and its timestamp stay in the audit trail (Chapter 5 describes the append-only AuditEvent), and the reopened investigation adds a new RCA version rather than editing the old one. An inspector should be able to see that the institution changed its mind and why.
A service can enforce the transition table with a small guard. The following sketch is illustrative; the role names and the transitionAllowed helper belong to the book's profile, not to the SDK:
type IncidentStatus = IncidentReport['status'];
const ALLOWED: Record<IncidentStatus, IncidentStatus[]> = {
'open': ['under-investigation', 'closed'],
'under-investigation': ['resolved'],
'resolved': ['under-investigation', 'closed'],
'closed': ['under-investigation'],
};
function transitionAllowed(
rec: IncidentReport,
to: IncidentStatus,
actorId: string,
investigatorId?: string,
): { ok: boolean; reason?: string } {
if (!ALLOWED[rec.status].includes(to)) {
return { ok: false, reason: `illegal transition ${rec.status} -> ${to}` };
}
if (to === 'closed') {
if (!rec.reviewedBy) return { ok: false, reason: 'reviewedBy required' };
if (rec.reviewedBy === investigatorId) {
return { ok: false, reason: 'reviewer must differ from investigator' };
}
if (rec.followUpRequired) return { ok: false, reason: 'follow-up still open' };
}
if (to === 'resolved' && !rec.rootCause) {
return { ok: false, reason: 'rootCause required' };
}
return { ok: true };
}
Specification §7.1.1 assigns each severity level a response time. In a record system these are best understood as notification service levels: the maximum interval between the moment an event becomes known and the moment the designated responder (normally the biosafety officer or the on-call contact) has acknowledged it. The record stores the deadline and whether it was met; it does not describe the response itself.
| Severity | Label | Response time (spec §7.1.1) | Record behaviour (book profile) |
|---|---|---|---|
| 1 | Minor | 24 hours | Queue notification; daily digest acceptable |
| 2 | Moderate | 4 hours | Direct notification to biosafety officer; escalate if unacknowledged |
| 3 | Serious | 1 hour | Page on-call contact; auto-escalate to deputy on timeout |
| 4 | Critical | Immediate | Emergency channel per institutional plan; record is filled in after the fact |
The specification's table also contains example events for each level. They are omitted here because they mix in containment and material specifics that are outside the book's scope; each institution should write its own severity criteria in its biosafety policy, approved by its committee, and reference that policy version from the record. Two practical points follow. First, the SLA clock should start at discoveredAt, not at date, because a near miss found three days later cannot meet a one-hour target retroactively. Second, for level 4 the record is never the notification channel: nobody should be typing into a web form while an emergency plan is running. The record is created afterwards and the actual notification time is entered from the call log.
Specification §7.3.1 recommends the 5-Whys method: what happened, then successive "why" questions moving from the immediate cause through contributing factors and systemic issues to a root cause. The SDK stores the result as a single optional string, rootCause. A single string is enough for a summary but loses the chain of reasoning, which is exactly what the reviewer needs to judge whether the analysis went deep enough.
The book's profile keeps rootCause as the one-line conclusion and adds a separate RCA record linked by incident ID. Each step is stored as its own entry, so a reviewer can see where the chain stopped and challenge it:
{
"rcaId": "RCA-2026-0042-v1",
"incidentId": "INC-2026-0042",
"method": "5-whys",
"version": 1,
"steps": [
{ "level": "what", "text": "..." },
{ "level": "immediate", "text": "..." },
{ "level": "contributing", "text": "..." },
{ "level": "systemic", "text": "..." },
{ "level": "root", "text": "..." }
],
"analyst": "PER-2C91",
"completedAt": "2026-03-13T02:10:00Z"
}
The five level values mirror the five questions in §7.3.1. The method field leaves room for other techniques (a fishbone diagram's categories, for example) without changing the schema. When an investigation is reopened, a new version is added with version: 2; the first version is never edited.
A good root-cause statement names a condition the institution can change: a missing checklist step, an unclear handover, an access rule that did not match the approved project. A statement that ends with a person ("researcher did not follow the SOP") usually means the chain stopped one "why" too early.
Specification §7.3.2 divides corrective actions into four tiers. The SDK flattens them into two string arrays, correctiveActions and preventiveActions. For tracking, each action needs an owner, a due date and a status, so the profile adds a CAPA record per action while keeping the SDK arrays as human-readable summaries:
| Tier (spec §7.3.2) | Purpose | SDK array | Required CAPA fields (profile) |
|---|---|---|---|
| Immediate | Stop recurrence | correctiveActions | owner, completedAt |
| Short-term | Fix immediate causes | correctiveActions | owner, dueDate, status |
| Long-term | Address systemic issues | correctiveActions | owner, dueDate, status, linked policy or SOP version |
| Preventive | Strengthen safety culture | preventiveActions | owner, dueDate, status, scope (department or institution) |
Due dates are set by the institution; the standard specifies none, and the book does not propose any. What the record system can do is make overdue items visible: a CAPA past its due date keeps the incident from closing and appears on the biosafety officer's dashboard. Where a long-term action belongs to another unit (facilities management, for instance), the CAPA can be transferred with a reference to that unit's ticket, which allows the incident to close without pretending the action is done.
The difference between resolved and closed is an effectiveness review: evidence that the actions actually changed something. Typical evidence is administrative, such as a revised checklist in the document system, updated training records for the affected group, or a follow-up access-log review showing the rule is now being followed. The profile stores the review as a short record: reviewer ID, date, the evidence references and a verdict (effective, partially-effective, not-effective). A verdict other than effective sends the incident back to under-investigation, which is why that backward transition exists.
Specification §7.4 lists what an incident report "must include". Mapping that list onto the SDK type is a useful exercise, because it shows where the specification and the reference implementation disagree and where a management profile has to decide.
| §7.4 item | SDK field | Status | Book profile handling |
|---|---|---|---|
| Date, time and location | date, location | Partial | Location is free text; add facility ID and room; add discoveredAt, reportedAt |
| Personnel involved | personnel | Covered | Pseudonymous IDs |
| Biological agents involved | agent | Covered, but over-detailed | Replace with materialRef to internal registry |
| Type of incident | type | Covered | Add eventClass for near miss |
| Severity classification | severity | Covered | Shared label mapping; optional potentialSeverity |
| Immediate actions taken | immediateActions | Covered | Administrative wording only |
| Medical evaluation summary | medicalEvaluation | Out of scope | occupationalHealthCaseRef only |
| Root cause analysis | rootCause | Partial | Linked RCA record with steps and versions |
| Corrective and preventive actions | correctiveActions, preventiveActions | Partial | Linked CAPA records with owner, due date, status |
| Follow-up plan | followUpRequired | Partial (boolean only) | Plan expressed as open CAPA items |
| Lessons learned | none | Missing | Add lessonsLearned (short, shareable text) |
Three gaps stand out. Lessons learned has no field at all, even though it is the part of the record most worth sharing across departments. The profile adds lessonsLearned as a short text written for a general audience, reviewed for restricted content before it is published internally, and suitable later for anonymous sharing (Chapter 6 discusses WIA-SOCIAL in that role). Location granularity is left to free text, which makes aggregation unreliable. The follow-up plan is reduced to a boolean, which cannot tell a reviewer what remains to be done.
In the other direction, the SDK contains a field that the checklist does not ask for: concentration. Its presence in a type called "incident report" encourages reporters to enter quantitative material data into a record that many people can read. The book's profile drops it. These findings are also collected in Chapter 8's list of issues proposed for version 1.1 of the standard.
Some incidents must be reported beyond the institution: to its institutional biosafety committee, and in certain cases to a national authority. In Korea, research involving living modified organisms falls under the LMO Act (유전자변형생물체법), and institutions operate an IBC and designate a biosafety officer. The specific events that must be reported, the recipient and the deadlines are set by the law, its enforcement rules and the competent authorities, and they change over time. This book does not restate them. Readers should confirm current requirements with the Korea Disease Control and Prevention Agency (KDCA), the Ministry of Science and ICT and the national biosafety information center, and with their own IBC. Institutions elsewhere follow their own national rules; the WHO Laboratory Biosafety Manual and, in the United States, the NIH Guidelines describe the role of institutional committees in general terms.
What the data standard can offer is a clean hand-off. The profile models each outward notification as a separate NotificationRecord linked to the incident, rather than as a status or a free-text comment:
| Field | Meaning |
|---|---|
notificationId | Stable ID, for example with an NTF- prefix |
incidentId | The incident being reported |
recipient | Recipient category (ibc, national-authority, funder, other) plus a free-text name |
basis | The rule that made notification necessary, as a reference to the institution's policy clause or the law's article, entered by the biosafety officer |
dueAt | Deadline as determined by the biosafety officer from the applicable rule |
sentAt, channel | When and how it was sent (portal, letter, e-mail) |
externalRef | Receipt or case number issued by the recipient, if any |
payloadRef | Reference to the exact document sent, stored in document management |
The key design choice is that basis and dueAt are entered by a person, not computed by the software. A rule engine that decides whether a national notification is needed would have to encode legal thresholds that the book cannot verify and that may change without the software being updated. The software's job is narrower and more reliable: make sure that once the biosafety officer decides a notification is required, it is not forgotten, its deadline is visible, and the exact content sent is preserved. An incident with an unsent notification whose dueAt has passed is a high-priority finding on the dashboard and blocks closure.
The specification's §9.3.1 example incident contains material and exposure details that fall outside this book's scope. The example below keeps the same structure but describes a procedural near miss with no material involved. All identifiers are fictitious. Fields marked in the surrounding text as profile extensions are grouped so that a strict SDK consumer can see them clearly.
The scenario: during an evening shift change, a researcher arriving at a registered laboratory finds the self-closing, access-controlled door held open by a cart. No work is in progress inside and no one has entered without authorization, according to the access log. The researcher closes the door and reports it through the incident form.
{
"incidentId": "INC-2026-0042",
"date": "2026-03-11T09:15:00Z",
"location": "FAC-EXAMPLE-0007 / ROOM-214",
"severity": 1,
"type": "procedural-violation",
"personnel": ["PER-7F3A"],
"description": "Access-controlled door to registered laboratory found held open by a cart at shift change. No work in progress. Access log shows no unauthorized entry. Door closed by reporter.",
"immediateActions": [
"Door closed and cart removed",
"Biosafety officer notified by phone",
"Access log for the preceding two hours exported and attached"
],
"rootCause": "Shift-handover checklist had no step for confirming door closure; cart parking area not defined near the entrance.",
"correctiveActions": [
"Add door-closure confirmation to handover checklist (CAPA-2026-0042-01)",
"Mark cart parking area away from entrance (CAPA-2026-0042-02)"
],
"preventiveActions": [
"Share lessons learned at department safety meeting (CAPA-2026-0042-03)"
],
"followUpRequired": false,
"status": "closed",
"reportedBy": "PER-7F3A",
"reviewedBy": "PER-0B44",
"x-profile": {
"eventClass": "near-miss",
"potentialSeverity": 2,
"discoveredAt": "2026-03-11T09:15:00Z",
"reportedAt": "2026-03-11T09:32:00Z",
"materialRef": null,
"occupationalHealthCaseRef": null,
"rcaRef": "RCA-2026-0042-v1",
"capaRefs": ["CAPA-2026-0042-01", "CAPA-2026-0042-02", "CAPA-2026-0042-03"],
"effectivenessReview": {
"reviewer": "PER-0B44",
"reviewedAt": "2026-04-15T01:00:00Z",
"evidence": ["DOC-SOP-HANDOVER-v4", "ACCESS-LOG-REVIEW-2026-04"],
"verdict": "effective"
},
"lessonsLearned": "Doors that must stay closed need a named step in every handover, not only a sign on the door.",
"notifications": []
}
}
Several design choices are visible in the example. The agent field is absent, and materialRef is null, because nothing was being handled; as discussed in §3.2.3, a strict SDK consumer will need an adapter to accept this. The reporter and the person involved happen to be the same pseudonymous ID, which is normal for self-reported near misses and shows why the two fields must be independent. The reviewer differs from the reporter. The description states facts that can be checked against the access log, and nothing more. The empty notifications array records a decision (no outward notification was required under institutional policy) rather than leaving the question unanswered; a biosafety officer who makes that decision should be able to point to the policy clause on request.
The extension fields are grouped under a single x-profile key in this example to make the boundary between the SDK and the book's proposals obvious. In production, an institution may prefer to place them at the top level; both approaches are additive and keep existing consumers working, provided that the choice is documented in the deployment's profile manifest (Chapter 7).
Before an incident module goes live, the following questions are worth answering in writing, ideally in the institution's biosafety policy or the system's configuration record:
agent content replaced with a registry reference, and is concentration disabled?eventClass field so they can be counted and trended.concentration).Technical safety content: consult official biosafety authorities. This chapter covers incident records and workflow only. How to respond to a spill, an exposure or any other event, and what medical follow-up is appropriate, must be taken from the institution's biosafety officer and occupational-health service and from official guidance such as that of the KDCA and national biosafety authorities, the WHO Laboratory Biosafety Manual and the BMBL.
type enumeration not need a near-miss value, and how does the profile record the outcome instead?open to resolved.