KB-455E

41 — SB-11 Governance Event Domain + Handoff Path — Detailed Technical Design (register-before-emit, Điều 45, no registration/emit, design-only, read-only zero mutation, 2026-06-01)

21 min read Revision 1
one-roof-governanceimplementation-indexgcossb-11sb-4governance-event-domainregister-before-emitdieu45event-type-registryevent-outboxevent-pendingevent-readevent-subscriptionqueue-heartbeatsystem-issueshandoff-pathcursor-tailobserver-trigger-c7signal-not-dataevent-vs-jobmot-not-executorsilent-gap-heartbeatno-lost-handoffreplaynotification-boundaryno-emitno-islanddesign-onlybuild-no-go2026-06-01

41 — SB-11 Governance Event Domain + Handoff Path — Detailed Technical Design

Package: knowledge/dev/reports/architecture/one-roof-governance-technical-addendum-and-implementation-index-2026-06-01/ Track: GCOS substrate. Blocker SB-11 (subsumes SB-4 = no governance event domain). Status: Detailed technical design ONLY. NO event registration. NO emit. No DDL/DML, no event_type_registry insert, no event_outbox emit, no notification. KB document only. Reads / controls: doc 00 → concept canon → Round-4 law → prompt-muc-tieu-mo-for-claude-code.md (§4K Điều 45 reference). Builds on doc 24 (T7 register-before-emit + 20 types), doc 32 (handoff §1–§8, Birth-not-modified §4), doc 35 (one domain, GOV-SIV). Điều 45 Queue Law (enacted 2026-05-26) binds every clause. Date: 2026-06-01 · Mutation footprint: KB document only. Zero PG/Directus/Qdrant/Nuxt mutation.


41.0 §0-GOV — governed objects

governed_object class grain purpose
governance event domain Class-2 governed config (rows in event_type_registry) one domain, owned by GOV-SIV the single bus lane for all GCOS + coverage signals
governance_event_type Class-2 governed config one row per (governance, event_type) register-before-emit vocabulary
handoff_signal Class-2 process record (rides event_outbox/event_pending) one per change signal Birth/Registry → candidate-scan handoff (doc 32)

Owner: GOV-SIV (Điều 31) detects/emits the signal; it never executes remediation. Executor of any remediation = a governed DOT under an approved APR (Điều 35). A Mother (MOT/MOUT) is never the executor (Điều 45).


41.1 Problem statement

The T6 scanner (doc 25), T7 taxonomy (doc 24), backfill (doc 31), handoff (doc 32), input gate (doc 33) and candidate scan (doc 34) all need to emit signals — but no governance event domain exists (this is SB-4). Until it is registered, every component must raise system_issues findings only and emit nothing (Điều 45 register-before-emit). SB-11 designs (a) the governance event domain and its register-before-emit vocabulary, and (b) the handoff path that carries Birth/Registry changes to the candidate scan without modifying Birth — reusing the live event substrate, never minting a second bus.


41.2 Live PG validation (read-only, re-verified 2026-06-01)

Object Live finding Implication for SB-11
event_type_registry 40 rows. Domains: iu(16/16 active), mother(9/0 active), piece(6/6), staging(5/5), system(4/3). NO governance domain. Cols: event_domain NN, event_type NN, event_stream NN, delivery_lane NN default 'immediate', default_severity, description NN, active NN default true, created_at. SB-4 confirmed. SB-11 registers the governance domain rows here. Intended unique key (event_domain, event_type). mother domain shows the register-but-inactive pattern (registered, active=false) — exactly the staging state GCOS types sit in until activation.
event_outbox 181,712 rows (heavily used Điều 45 emit bus). Cols: id uuid, event_domain NN, event_type NN, event_stream NN, delivery_lane NN, event_severity, event_subject_table NN, event_subject_ref, canonical_address NN, actor_ref NN, source_system NN, correlation_id, payload_classification NN default 'safe_metadata', safe_payload jsonb NN default '{}', occurred_at, created_at. The one emit path. event_subject_table/event_subject_ref = the subject-addressing pattern; safe_payload = signal-not-data. SB-11 emits here (gated). No schema change.
event_pending 0 rows, structure present. Cols: event_domain NN, event_type_hint NN, entity_table NN, entity_ref, canonical_address NN, actor_ref NN, capture_payload jsonb, processed_at, error_count int NN, last_error. Pre-registration capture/staging + retry lane — unused, available. A signal whose type isn't yet registered+active is captured here (no-lost-handoff), promoted to event_outbox once registered.
event_read 181,351 rows. Cols: event_id uuid NN, actor_ref NN, read_at NN, read_status_source NN. Live consumer read-tracking — the notification-boundary "has the owner seen this signal?" substrate. Reuse for governance notifications.
event_subscription 3 rows. Cols: id uuid, recipient_ref NN, event_domain, event_type, event_stream, scope_subject_table, scope_filter jsonb NN, mute bool NN, created_at. Live consumer routing/subscription — owners subscribe to governance.* with a scope_filter; the notification-boundary substrate. Reuse, no new router.
queue_heartbeat 3 rows (dieu45_phase3_pilot proves Đ45 pilot live). executor_name/kind, last_tick_at, last_tick_status, lease_owner. The silent-gap heartbeat substrate (Điều 45 boundary #5). GCOS workers tick here; missed tick = finding.
system_issues 190,288 rows; free-text issue_type, coalesce_key, occurrence_count, reopen_count, parent_issue_id, status. The finding store the scanner writes before any emit; emit is downstream of a registered type.
registry_changelog 68,323 rows. The single audit channel (raise/close/emit/audit rows).
job_queue 13 rows (cut.*, lease/retry/idempotency). The JOB lane (Điều 45 event≠job). Governance signals do NOT go here; remediation jobs route via APR (Điều 32), not via this queue directly.

41.3 Reuse / Extend / New decision

Need Decision Rationale
Event domain + vocabulary REUSE event_type_registry — register governance domain rows (no new registry) One vocabulary store; iu/mother/… already coexist; governance is one more domain, GOV-SIV-owned.
Emit path REUSE event_outbox One Điều 45 bus, 181k rows live; signal-not-data + subject-addressing already enforced.
Pre-registration capture / retry / DLQ REUSE event_pending (unused, exact shape) No-lost-handoff staging without a new table.
Notification boundary REUSE event_subscription (routing) + event_read (delivery/seen) Live consumer-side substrate; no new notifier.
Silent-gap heartbeat REUSE queue_heartbeat Điều 45 boundary #5; live.
Findings / audit REUSE system_issues / registry_changelog One issue store, one audit channel.
Handoff transport REUSE cursor-tail (SB-13) of birth_registry+registry_changelog (Option A, default); observer-trigger (Option B) deferred to C-7; inline (Option C) REJECTED Birth not modified (doc 32 §4).
Dedicated handoff ledger NOT NOW (future option only, doc 32 §10) Minting one now = second roof. The cursor + event_pending + event_outbox triad suffices.

Net: SB-11 = 0 new tables. Pure reuse: register governance rows in the existing registry; emit via the existing outbox; capture via the existing pending lane; notify via the existing subscription/read substrate; heartbeat via the existing queue_heartbeat. This is the strongest no-island result of the four blockers.


41.4 The governance event domain — register-before-emit vocabulary (NONE registered now)

All rows below are proposed for event_type_registry with event_domain='governance', event_stream per family, active=false at first (the live mother-domain pattern), flipped to active=true only at the gated T7 build after the taxonomy + SB-10/12/13 land. Until then: findings to system_issues only, zero emit.

(A) Handoff signals (doc 32 §3) — event_stream='governance.handoff'

governance.handoff.object_born, .object_registered, .collection_changed, .count_changed, .object_retired, .source_changed, .axis_introduced, .policy_changed, .authority_changed, .ruleset_changed (the 10 kinds) + governance.handoff.heartbeat + faults governance.handoff.lag / .dlq / .silent_gap / .unregistered_type (doc 32 §8).

(B) Backfill (doc 31 §10) — event_stream='governance.backfill'

governance.backfill.inventory_gap, .batch_failed, .incomplete, .dlq_overflow, .sweep_completed (heartbeat).

(C) Input-quality (doc 33 §8) — event_stream='governance.input'

governance.input.incomplete, .invalid_schema, .duplicate, .conflict, .stale, .untrusted_source, .quarantined (the 7 states that route to a finding; birth_or_registry_missing reuses Đ19, needs_backfill reuses backfill).

(D) Candidate scan (doc 34 §10) — event_stream='governance.candidate'

governance.candidate.stale, .unknown, .scan_lag, .group_invalidation_storm, .scan_completed (heartbeat).

(E) Coverage detection + lifecycle (doc 24 §4/§5) — event_stream='governance.coverage' / 'governance.lifecycle'

The 20 detection events (governance.coverage.orphan, .owner.gap, .island.detected, … governance.phantom.gap) + governance.coverage.scan_completed / .audit_completed / .summary heartbeats + lifecycle events on approved apply (governance.owner.assigned, .exception.granted, .authority.delegated, .axis.owner_assigned, + .resolved/.revoked/.expired companions).

Counts are illustrative of structure, not a registration list. Nothing here is registered or emitted by this design.


41.5 Event subject-addressing + safe metadata (Điều 45 signal-not-data)

Every emit sets event_subject_table / event_subject_ref and a safe_payload of addresses/refs only:

event family event_subject_table event_subject_ref safe_payload (addresses + counts ONLY)
handoff.* birth_registry / registry_changelog entity_code (or candidate_key) {object_type, collection_name, group_key, change_kind, occurred_at, source}
backfill.* governance_candidate_state group_key {group_key, scope, batch_range, counts}
input.* governance_candidate_state candidate_key/group_key {input_quality_state, scope, source}
candidate.* governance_candidate_state group_key {candidate_verdict, ruleset_version, source_snapshot_ref, dirty_reason}
coverage.* / lifecycle.* the governed object's table object ref {gap_type, scope, coalesce_anchor, issue_id, proposed_action_code} (doc 24 §2)

Never the object's contents (Điều 45 boundary #1). payload_classification='safe_metadata' (the live default). canonical_address on the outbox row is the live NOT-NULL emit-address column (distinct from birth_registry.canonical_address which is NULL — the emit address is constructed from collection_name:entity_code at emit time).


41.6 The five Điều 45 boundaries — how SB-11 satisfies each

# Boundary (Điều 45) SB-11 compliance
1 Queue carries signal, not data safe_payload = address/refs/counts only; object contents never travel (§41.5).
2 Event ≠ job A governance.* signal asserts something is true; remediation is a JOB routed through the Điều 32 APR spine (a grant/assign action-type, SB-1/C-2) or, where a worker job is needed, job_queuenever executed off the event. The event carries no executable instruction.
3 Executor boundary GOV-SIV emits the signal; it does not apply. The executor = a governed DOT under an approved APR (Điều 35). The handoff intake worker only captures + dirty-marks (doc 32 §1.3); it does not classify or remediate.
4 MOT is not the executor No Mother consumes a governance signal to mutate governed state. MOUT may render coverage; it never executes remediation (doc 32 §1.4).
5 Silent-gap / heartbeat Each worker ticks queue_heartbeat + emits governance.<stream>.heartbeat/scan_completed/sweep_completed; a missed heartbeat is itself a finding (*_silent_gap). "Quiet" is distinguishable from "dead."

The doc cites Điều 45 (law §4K mandatory) and every clause here is checked against the five boundaries.


41.7 Handoff path — Birth NOT modified (doc 32 §4)

Option Description Verdict
A — Cursor-tail / CDC (DEFAULT) The gov_handoff_intake worker (SB-13) keyset-tails birth_registry (born_at,id) + registry_changelog (timestamp,id) and (for lifecycle) event_outbox. Maps each row to (change_kind, group_key, object_ref, canonical_address), coalesces within a window, enqueues a dirty-mark (the handoff) on candidate-state (SB-10), and emits governance.handoff.<kind> iff the type is registered+active, else captures to event_pending + raises handoff_unregistered_type once. RECOMMENDED. Touches Birth zero ways; lossless (append-only ordered); latency seconds–minutes.
B — Additive observer trigger AFTER INSERT trigger on birth_registry/registries writing a capture row to event_pending; fail-open (never blocks/alters a birth). DEFERRED to C-7. Adds a trigger to a Birth table → council ruling "does an observer-only, fail-open trigger count as modifying Birth?" Recommended answer: NO (observes, never gates) — but the ruling is council's, not the agent's. Build NO-GO until C-7.
C — Inline Birth-gate call Modify the Birth gate to call governance synchronously. REJECTED. Couples Birth to governance; a governance fault becomes a birth fault. Forbidden.

No-lost-handoff (doc 32 §6): lossless cursor-tail + register-before-emit capture (event_pending) + DLQ-instead-of-drop (dead_lettered counter + handoff_dlq). Worst case = delayed (in DLQ) and visible (a finding), never missing. The periodic full audit (doc 34 §5) is the catch-all if any dirty signal is lost.

Replay: reset the SB-13 cursor watermark and re-tail; dirty-marks are idempotent upserts (SB-10 §40.5); event_read records which consumers have seen each signal so replay does not double-notify a consumer that already acted.


41.8 Notification boundary (signal, not job)

  • Routing: an owner/role subscribes via event_subscription(recipient_ref, event_domain='governance', event_type/stream, scope_filter). The gov_issue_route DOT (doc 25 DOT #7) resolves the route owner (v_object_effective_owner, SB-2; else default_owner_hint; else GOV-COUNCIL) and emits a signal on the owner's delivery_lane — honoring cooldown / emit-ceiling / digest (doc 24 §6). Authority disputes (island, conflict, unratified exception, direct-PG) route to GOV-COUNCIL regardless.
  • Delivery / seen: event_read tracks consumption (the notification boundary "owner has seen the gap"). event_subscription.mute lets a recipient suppress a lane without losing the underlying system_issues finding (the issue stays open; only the notification is muted).
  • A notification is a signal, never a job. It does not carry or trigger an executable remediation; remediation is a separate APR (Điều 45 boundary #2). The notification's job is to make the gap visible to the accountable owner, nothing more.

41.9 Retry / DLQ / coalesce / failure modes

  • Retry/DLQ: event_pending.error_count/last_error + bounded backoff; dead-letter after N → dead_lettered (SB-13) + *_dlq finding. Never drop.
  • Coalesce: one open system_issues per coalesce_key (occurrence_count++); beyond the per-scan ceiling → governance.coverage.summary + digest (doc 24 §6). Handoff dirty-marks coalesce by group_key within the window.
  • Failure modes: event type not registered → HOLD (capture to event_pending, raise issue_event_gap/handoff_unregistered_type); domain registered but active=false → same hold (register ≠ activate); outbox emit fails → stays in event_pending, retried; subscription/route owner unresolved → escalate GOV-COUNCIL; consumer never reads (no event_read) past SLA → handoff_lag/notification escalation.

41.10 Relation to docs 24 / 32 / 35 and the no-island attestation

  • doc 24 (T7): SB-11 supplies the domain + register-before-emit substrate that doc 24's 20 detection events + lifecycle events + anti-spam assume but cannot create themselves. The GCOS event families (A–D) extend doc 24's taxonomy under the same one domain (doc 35 §3.2 patch #9).
  • doc 32 (handoff): SB-11 is the transport for doc 32's 10 handoff kinds; it ratifies Option A (cursor-tail) as default and carries Option B to C-7.
  • doc 35 (one domain, GOV-SIV): exactly one governance domain, GOV-SIV-owned; no per-component bus.
  • No-island: one domain (governance), one emit path (event_outbox), one capture lane (event_pending), one finding store (system_issues), one audit (registry_changelog), one notification substrate (event_subscription+event_read), one heartbeat (queue_heartbeat). Zero new tables. No parallel bus/store/notifier/heartbeat.
  • No-hardcode: event types are rows; routing is data (event_subscription + SB-2 owner resolution + default_owner_hint); severity computed (doc 24). No agency literal, no event-type array in code.

41.11 Acceptance tests (build-time; cannot run now)

  1. Register-before-emit: no event_outbox row with event_domain='governance' exists until the matching event_type_registry(governance, type) row is active=true; an unregistered emit attempt holds in event_pending + raises issue_event_gap.
  2. Signal-not-data: every governance event_outbox row has payload_classification='safe_metadata' and a safe_payload containing no object contents (addresses/refs/counts only).
  3. Birth untouched: Option A path runs with zero schema/trigger changes to birth_registry; (Option B only after C-7, and only as fail-open observer).
  4. No-lost-handoff: kill the intake worker mid-tail → on restart every change between the watermark and head is processed; a poison signal is dead-lettered + visible, never dropped; the periodic audit reconciles.
  5. Event ≠ job: no governance signal triggers a mutation directly; remediation occurs only via an approved APR / governed DOT.
  6. Silent-gap: stop a worker → *_silent_gap fires within threshold (heartbeat absence detected).
  7. Notification boundary: a finding routes to the subscribed owner via a signal; event_read records the owner's view; mute suppresses the notification but not the open system_issues finding.
  8. One domain: all GCOS + coverage event types live under event_domain='governance'; no second domain/bus is created.

41.12 Dependencies, gates, verdict

  • Designable now: YES (done). Build now: NO (register-before-emit; Điều 45). Registering the domain + types is the gated T7 build step.
  • Build gates: registering/activating governance event rows = gated (T7 build / T11); running the intake worker requires SB-13 (cursors) + SB-10 (dirty-mark target); C-7 owns the observer-trigger ruling (Option B) and input-trust policy (which input.* signals fire); SB-2 sharpens owner-routing (degrades to default_owner_hint/COUNCIL until live).
  • No COMMIT (os_proposal_approvals=0). No event registered, none emitted by this design.

SB-11 design verdict: COMPLETE — GO for build-prep, BUILD NO-GO (register-before-emit). Decision = 0 new tables; reuse event_type_registry (register one governance domain), event_outbox (emit), event_pending (capture/retry/DLQ), event_subscription+event_read (notification boundary), queue_heartbeat (silent-gap), system_issues/registry_changelog (findings/audit). Handoff path = Option A cursor-tail (default, Birth untouched); Option B observer-trigger deferred to C-7; Option C rejected. All five Điều 45 boundaries satisfied; strongest no-island result of the four blockers.

(Cross-refs: doc 24 §2/§4/§5/§6/§7, doc 32 §1/§3/§4/§6/§8, doc 35 §3.2 patch #9 + §5, doc 38 SB-12, doc 39 SB-13, doc 40 SB-10, doc 42 integration.)

Back to Knowledge Hub knowledge/dev/reports/architecture/one-roof-governance-technical-addendum-and-implementation-index-2026-06-01/41-sb11-governance-event-domain-handoff-path-detailed-design.md