# Gate failures → fixes (/docs/gate-failures)



When an audit gate goes red it names the file, the symbol, and the
missing fact in its output. This page maps each gate's failure string
to what it means and the fix, so an agent — or a human — reading CI
output can go from the red line to the change without re-deriving it.

Every gate exits `0` clean, `1` on a contract failure, `2` on a
config or IO error, and prints a bracketed, greppable prefix. Grep the
prefix; match the row. This is the companion to
[Audit gates](/docs/audit-gates) (the catalog by subsystem) and
[CI enforcement](/docs/ci-enforcement) (the catalog by contract
clause) — this page is indexed by the string you actually see.

<SourceMeta source="{ label: &#x22;scripts/audit/&#x22;, href: &#x22;https://github.com/pleachhq/core/tree/main/scripts/audit&#x22; }" />

## You added a graph node [#you-added-a-graph-node]

| Failure string (grep this)                                            | What it means → fix                                                                                                                                                                                                                                          |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `[graph-stages] Node "<name>" missing stageId in NODE_STAGE_MAP`      | The node has no lattice stage. Declare `stageId` in the node metadata (plugin path), or add a `NODE_STAGE_MAP` entry in `topology.ts` (upstream path). See [Extending Pleach](/docs/extending) and the [new-node checklist](/docs/nodes#new-node-checklist). |
| `[graph-stages] FORBIDDEN edge <from> (<stage>) → <to> (<stage>)`     | An edge crosses the lattice outside the nine allowed patterns. Don't route directly — write a state field the next stage's nodes subscribe to. See [the lattice gate](/docs/graph#the-lattice-gate).                                                         |
| `[graph-stages] Singleton violated: "synthesizer" registered N times` | Two nodes occupy the `synthesize` stage; it's a singleton. Move recovery or post-synthesis shaping to a [post-turn stream filter](/docs/plugins/stream-observers), not a graph node.                                                                         |

The gate also prints a summary line — `[graph-stages] FAIL — N
forbidden, N unstaged of N nodes / N edges` — so the count in the
failure tells you how many sites to fix.

## You added an event type or a writer [#you-added-an-event-type-or-a-writer]

| Failure string (grep this)                                                                       | What it means → fix                                                                                                                                                                                                             |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[audit:c8-event-type-allowlist-coverage] FAIL — N event type(s) missing from SCRUBBABLE_FIELDS` | A new event type has no redaction decision. Add an entry to `scrub/allowlist.ts` — `[]` is valid and documents "no fields to redact." See [Event-type allowlist coverage](/docs/scrubbers#event-type-allowlist-coverage).       |
| `[C8:unscrubbed-event-type]`                                                                     | The **runtime** fail-closed marker for the same gap — an event type reached the writer with no allowlist entry. Same fix: register the type in `scrub/allowlist.ts`. (This is emitted at write time, not by the gate's stdout.) |
| `[audit:c8-union-member-has-producer] FAIL — N union member(s) with no producer`                 | You declared an `EventLogInput` member that nothing writes. Add a producer that calls `writer.write({ type: "<type>", ... })`, or add a documented baseline entry. See [Event log](/docs/event-log).                            |

## You changed the audit row [#you-changed-the-audit-row]

| Failure string (grep this)                                                                           | What it means → fix                                                                                                                                                                                                                                                                                           |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[auditable-call-shape] FAIL — shape diverged from baseline`                                         | The `AuditableCall` TS wire shape changed. If intentional, run the gate with `--update-fingerprint` **and** bump `AuditRecordVersion` if any field's meaning moved. See [the AuditableCall row](/docs/auditable-call-row).                                                                                    |
| `[auditable-call-version] FAIL rule 3: fingerprint advanced ... but AuditRecordVersion did not move` | You changed the row shape without versioning it — the silent-breaking-change the gate exists to catch. Bump `export type AuditRecordVersion`. If you were trying to add a column, don't: use the `pluginPayloads` slot instead — see [the locked row](/docs/extending#the-locked-row-and-the-slot-that-isnt). |

## You added or changed a plugin hook [#you-added-or-changed-a-plugin-hook]

These fire on the substrate's own hook set and on sibling `@pleach/*`
SKUs — the upstream surface, not a host plugin that merely *uses*
hooks.

| Failure string (grep this)                                                                                                      | What it means → fix                                                                                                                                                                                                                                                                       |
| ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[audit:plugin-contract-completeness] FAIL — N missing-collector, N novel awaiting-consumer`                                    | A `contributeX` hook has no paired `PluginManager.collectX` accessor, or a collector with no consumer. Add both, or baseline an intentional bypass. The run also emits the probe `[UXParity:audit-plugin-contract-completeness:run:drift]`. See [Plugin contract](/docs/plugin-contract). |
| `[audit:plugin-hook-category-assigned] FAIL — N hook(s) declared on HarnessPlugin but NOT classified into any namespace bucket` | A new hook belongs to none of the nine namespaces. Add it to a bucket in `plugins/namespaces.ts`. See [contribution namespaces](/docs/plugins/namespaces).                                                                                                                                |

## Reading the loop [#reading-the-loop]

The failure string is the correction signal. An agent extending the
runtime runs the gate, reads the named fact (a stage, a producer, an
allowlist entry, a version bump), applies it, and re-runs — green
means the addition preserved the ledger contract. That loop is the
point of [agent-driven self-extension](/docs/extending#agent-driven-self-extension):
the gate output is legible enough to act on without a human in the
middle.

## Where to go next [#where-to-go-next]

<Cards>
  <Card title="Extending Pleach" href="/docs/extending" description="The extension point each gate guards, paired with the interface and registration call." />

  <Card title="Audit gates" href="/docs/audit-gates" description="The full gate catalog, organized by subsystem." />

  <Card title="CI enforcement" href="/docs/ci-enforcement" description="The same gates, indexed by the auditability and replayability clause each one holds up." />
</Cards>
