# Approval emission detection surface **Owner:** gate-house **Source:** approval-engine **Archive:** audit-core **Observer:** kings-guard (silence-as-signal; offered) **Status:** normative for GH-WP-0002-T04 **Date:** 2026-08-29 **Statute:** `net-kingdom/canon/standards/security-layer-model_v0.7.md` §9.6 **Source declaration:** `approval-engine/cadence.yaml` A hash chain proves accepted records were not altered or truncated. It proves nothing about an event never emitted. Atomicity (the outbox contract) prevents accidental omission. This surface is what makes a *silent* loss visible. It does not prevent a compromised source from suppressing. That residual is stated in §9.6 and is not closed here. ## Form Approval evidence is **load-bearing** and **low-volume**. Rate monitoring is forbidden: a handful of revocations a month has no rate to drop below, and suppression is indistinguishable from a quiet month. The required form is **heartbeat plus reconciliation**. Reference instance: `approval-engine/cadence.yaml` and `approval-engine/docs/emission-cadence.md`. ### Heartbeat A signed positive claim: *nothing to report*, together with per-class transition counts since the previous heartbeat (or since process start on the first). The claim can itself go missing, which is the point. | Field | Value | | --- | --- | | Class | `heartbeat` | | Interval | 24 hours (SHOULD also be emittable on demand) | | Assertion | `nothing-to-report` | | Counts | `{issuance, use, supersession, revocation}` of committed outbox rows | | Missing heartbeat | **finding** | Heartbeat rows use the outbox table and the same at-least-once drain. They are not coupled to an object mutation. ### Reconciliation Compare `approval-engine`'s committed outbox counts per class to `audit-core`'s accepted event counts for `source=approval-engine` and the corresponding `action`. Divergence is a **finding**, not a log line. | Source class | `audit-core` `action` | | --- | --- | | `issuance` | `approval.issuance` | | `use` | `approval.use` | | `supersession` | `approval.supersession` | | `revocation` | `approval.revocation` | | `heartbeat` | `approval.heartbeat` | Undrained local rows are lag, not yet a divergence. A row with `drained_at` set that `audit-core` does not hold is the omission case §9.6 names. ### Lag bound | Bound | Default (reference) | Exceed | | --- | --- | --- | | Outbox depth | 100 undrained rows | **finding** | | Outbox age | 1 hour for the oldest undrained row | **finding** | These detect a stuck drain. They are not a completeness proof. ## What is a finding - No heartbeat arrives for more than one interval. - `audit-core` count for a class is less than the source's drained count for that class. - Outbox depth or age exceeds the lag bound. None of these are rate drops. None of these are informational logs. `kings-guard` consumes the finding as observation of the stream, not of its contents. Until it reports that it is watching in production, no argument may assume this surface is live (§12). The source MUST still publish the declaration; a detector that is not yet watching does not excuse an undeclared source. ## Who owns which half | Half | Owner | | --- | --- | | Publish cadence and expose committed counts / lag | approval-engine | | Hold accepted events and answer per-source per-class counts | audit-core | | Raise silence, divergence, and lag as findings | kings-guard | | Name the form and the residual | gate-house | ## Conformance check (queued for statute v0.8 §11) GH-WP-0002-T05. Mechanically checkable, in the same list as the existing §11 bullets: - every repository catalogued as an evidence source, or that emits **load-bearing** evidence, declares its emission guarantee in a machine-readable form at a path named in its layer declaration: - **load-bearing:** local transactional outbox (this contract's shape) **and** a heartbeat-or-reconciliation cadence declaration; - **attributive, deliberately non-atomic:** the named trade, and a statement that completeness is not claimed. A new engine catalogued under §9.4 / §4 as an evidence source that ships without this declaration reintroduces the approval omission gap silently. Until v0.8 lands, this paragraph is doctrine; `approval-engine`'s `cadence.yaml` plus `layer.yaml` is the reference form.