STPA / HAZOP++ Field Guide
Two hazard-analysis lenses run together in commitwork: STPA judges control loops from the controller's side, HAZOP++ walks data boundaries from the crossing's side. The STPA vocabulary below is the one monitor/failure-taxonomy.json applies to every taxonomy class, and monitor/stpa-sweep.mjs re-evaluates commitwork's own panel and remediation control loop against a fixed UCA and HAZOP table. The HAZOP++ worked examples come from commitwork's analysis of its CWE → CAPEC → ATT&CK enrichment, with the figures measured over the tracked MITRE data.
| stat | value |
|---|---|
| STPA loops | 6 |
| UCA values | 8 |
| causal factors | 16 |
| guide words | 10 standard + 2 ext. + 2 proposed |
01 — STPA — System-Theoretic Process Analysis
STPA judges a system by its control loops: a controller issues actions to a process it does not directly see, and reads that process back through feedback. A hazard is a state the loop can reach — not a component that breaks.
The control loop being judged
Every STPA hazard traces to one of four faults in this loop: the controller's process model is wrong, the action it issues is unsafe, the actuator mangles or delays it, or the feedback lies. Control actions are drawn in red and feedback in blue throughout this page.
The four stages, in order
Standard four-stage STPA (Leveson & Thomas), unchanged from the textbook form — commitwork's own extension is in the vocabulary it judges with: named controller loops, UCA values and causal factors, below.
Commitwork's applied vocabulary
Every taxonomy class in monitor/failure-taxonomy.json carries an stpa judgment built from three closed sets — a UCA is a triple: which loop, which of 8 failure shapes, which of 16 root causes.
6 loops (who's the controller): operator · monitor · scanner · agent · oversight · attention
8 UCA values (how it failed): not-provided · unsafe · too-early · too-late · out-of-order · stopped-too-soon · applied-too-long · none
16 causes (why): controller-algorithm · controller-model · higher-input · actuator-delayed · actuator-missing · actuator-corrupt · process-failure · process-change · process-disturbance · feedback-missing · feedback-delayed · feedback-incorrect · sensor-inadequate · coordination-conflict · coordination-gap · coordination-duplicate
The main extension past the textbook: the attention loop has no equivalent in Leveson's original model. It treats a published verdict itself as a control action — the failure mode isn't a broken pipe, it's a report that spends or withholds the operator's attention wrongly. Built for a monitoring system, where the paper's control-structure model (built for physical/organisational systems) has nothing to say. A second, smaller one is the UCA value none, which records that a class describes a measurement or record rather than an action by that loop's controller.
Worked example — real UCAs from the CWE → CAPEC → ATT&CK enrichment path
From commitwork's analysis of the enrichment before it was built (8 UCAs, 6 hazards; five UCAs shown). H1 a finding carries a CAPEC or technique its evidence does not entail · H2 "no mapping" is indistinguishable from "mapped and clean" · H3 the vendored mapping is stale against upstream · H4 a roll-up walks a non-transitive relationship as if transitive · H5 enrichment multiplies one finding into N rows or counts.
| UCA | control action | unsafe because | hazard |
|---|---|---|---|
| UCA1 | attribution provided from an ancestor CWE | the ancestor is a generalisation — says nothing about this finding | H1 |
| UCA2 | attribution not provided, rendered blank | blank reads as "checked, nothing found" | H2 |
| UCA5 | attribution provided as one row per technique | one CWE reaches up to 59 CAPECs and 27 ATT&CK techniques (4.1 on average, for a CWE that reaches any), so per-technique rows silently inflate every count | H5 |
| UCA7 | attribution applied too long — snapshot never re-derived | mapping drifts from upstream with nothing measuring the drift | H3 |
| UCA8 | attribution provided by inverting ATT&CK→CAPEC | the published mapping is one-way; the inverse is unasserted | H1, H4 |
02 — HAZOP++ — extended guide-word deviation walk
Judges each place data crosses — a field, a call, a pipeline stage — asking one deviation question per guide word. Two ++ extensions widen the standard IEC 61882 set (the seven basic words plus the timing and order words); two more are proposed for boundaries where a model generated what crosses.
The walk, per boundary
A guide word with no plausible deviation should still be listed, marked n/a with its rationale — an omission reads as "not considered," which is its own false-clean.
The lexicon — ten standard, two extensions, two proposed
| word | deviation question | worked example | |
|---|---|---|---|
| core | NO / NONE | the thing never arrives | no CAPEC for this CWE — 633 of the 969 CWEs in the shipped view (65.3%) |
| core | MORE | more than expected — volume, size, frequency | one CWE → 59 CAPECs, the maximum over the catalogue |
| core | LESS | fewer, partial, truncated | CAPEC exists, no ATT&CK — 381 of the 558 live patterns (68%) |
| core | REVERSE | flows the other way, or arrives inverted | using ATT&CK→CAPEC — forbidden (UCA8) |
| core | PART OF | only a subset of the expected content is present | only part of each abstraction level maps — 449 of 558 live patterns carry a CWE (Meta 50/61, Standard 152/181, Detailed 247/316) |
| core | OTHER THAN | right shape, wrong semantics | deprecated ids — 57 of 615 patterns |
| core | EARLY | arrives before its precondition holds | enrich at scan time (frozen) vs. read time |
| core | LATE | arrives after the point it could still act | a vendored mapping read after upstream has moved — the snapshot is dated and nothing re-derives it on a schedule (UCA7) |
| core | AS WELL AS | two relations carry different semantics, conflated | Related_Weaknesses vs. Taxonomy_Mappings |
| core | BEFORE / AFTER | a pipeline step runs in the wrong order | enrich before cross-lane dedup — commitwork enriches only inside the vendored-code lane (bin/vendor-scan.mjs), so no second lane carries the same attribution |
| extension | ++ RECURSIVE | a deviation compounds if the same hop repeats | CWE ancestor walk — depth-1 only, and labelled |
| extension | ++ ADVERSARIAL | the deviation an attacker would deliberately induce | attribution never lowers a severity |
| proposed | ++ CONFABULATED | content crosses the boundary with the right shape but no underlying source — synthesised, not derived | a citation, file path, or API that was never real |
| proposed | ++ HIJACKED | content in the data channel is read as an instruction — the boundary has no separate control channel | text inside a fetched document redirects the agent's next action |
Why only two proposed, not adopted: HAZOP's guide words ask about a deviation at a boundary — something crossing in an unexpected form. ++ CONFABULATED and ++ HIJACKED fit that frame cleanly: both describe a boundary property normal software doesn't have — a generator that can synthesise plausible content with no source, and a channel that carries data and instruction over the same wire. Two other LLM failure modes were considered and rejected for this slot: non-determinism (same input, different output) and sycophancy (a correct answer abandoned under social pressure) aren't boundary deviations — they're a controller's behaviour varying, which is exactly what STPA's UCA values and causal factors already exist to capture. Forcing them into HAZOP's guide-word list would blur that split.
03 — Guide word → UCA cross-reference
HAZOP++ names a deviation at the boundary; STPA names the same defect from the control loop's side.
| guide word | → UCA value | why they align |
|---|---|---|
NO / NONE | not-provided | the action never issues, in either vocabulary |
MORE | unsafe | the duplicate/excess instance is itself what makes it hazardous |
LESS | stopped-too-soon | a process that halts mid-way, read as complete |
REVERSE | out-of-order | sequence-dependence violated in both readings |
PART OF | stopped-too-soon | a partial payload is a partial completion, judged the same way |
OTHER THAN | unsafe | right shape, wrong content — the action itself is the defect |
EARLY | too-early | literal match — a precondition race |
LATE | too-late | literal match — past the point of use |
AS WELL AS | coordination-duplicate (cause, not UCA) | two controllers/relations converge unexpectedly on one thing |
BEFORE / AFTER | too-early / too-late | the pipeline-order variant of the same two values |
++ RECURSIVE | no clean analogue | compounding-through-repetition has no single-action UCA shape — it's a property of a chain of actions, which per-action UCA judgment doesn't capture |
++ ADVERSARIAL | no clean analogue | attacker intent is orthogonal to both taxonomies — HAZOP asks "what if", STPA asks "which action"; neither has an axis for "on purpose" |
++ CONFABULATED (proposed) | unsafe | the closest fit — a fabricated action is still an action the controller issued, just one built on nothing |
++ HIJACKED (proposed) | no clean analogue | the existing UCA values assume the controller chose the action; here the choice was smuggled in through data — none of the 8 values name an origin this far outside the controller |
The same defect, found twice, named from two sides. UCA1 — "attribution provided from an ancestor CWE" — and HAZOP's ++ RECURSIVE — "a CWE's ancestors each reach different CAPECs, compounding" — are the identical finding in the CAPEC/ATT&CK design. STPA reached it by asking which control action was unsafe; HAZOP++ reached it by walking the boundary's guide words. commitwork runs both because a single hazard can be invisible to one lens and obvious to the other.
The STPA vocabulary and every measured figure on this page come from tracked files in this repository. The UCA and guide-word worked examples come from commitwork's own analysis of the CAPEC/ATT&CK enrichment, and every figure is measured here over the tracked data. The vocabulary counts come from monitor/failure-taxonomy.json's stpaVocabulary. The CWE and CAPEC figures are measured over monitor/data/cwe-graph.json and monitor/data/capec-graph.json as fetched from MITRE on 2026-08-23 (ATT&CK ids are the ones CAPEC itself records): 969 CWEs, 558 live CAPEC patterns plus 57 deprecated ones the graph records as dropped. The guide-word ↔ UCA cross-reference is this page's own synthesis, stated as a reading rather than a repo-declared mapping. The two proposed guide words, ++ CONFABULATED and ++ HIJACKED, are this page's own proposal for LLM-development boundaries — not sourced from any repo document, and not yet adopted anywhere the standard words or the two extensions are.