KB-1B1D

06 — Self-Approval Guard & Null-Proposer Risk Repair

3 min read Revision 1

06 — Self-approval guard & null-proposer risk repair (design)

No vote on APR-0415 was recorded. This file specifies how dot-apr-approve's G5 must behave; it does not act.

Live fact: APR-0415 has no recorded proposer

approval_requests.code            = APR-0415
source                            = 'dot-c1-w7-authorize-build-step-handler-proposal'
source_context (json)             = {macro, scope, dot_code, file_path, patch_mode, version_bump,
                                     live_sha256, patched_sha256, base64_sha256, bash_n, change,
                                     binds_handler_ref:false, mints_grant:false, registers_dot:false, kb_package}
source_context.proposer           = ABSENT (NULL)
source_context.created_by         = ABSENT (NULL)
reviewed_by                       = NULL

Why this matters — the existing guards go toothless on NULL proposer

Both quorum routines exclude the proposer via COALESCE(source_context->>'proposer', source_context->>'created_by'):

  • quorum_passed(p_code): counts votes WHERE (v_proposer IS NULL OR approver <> v_proposer) — when proposer is NULL the predicate is always true ⇒ no exclusion happens.
  • fn_apr_quorum_check() trigger: self-approve block runs only IF v_proposer IS NOT NULLskipped here.

So for APR-0415, the platform's self-approval protection is inert. A dot-apr-approve that merely trusted the DB guards would let the proposer self-approve undetected. G5 must be stricter than the DB.

G5 repair (fail-closed, in the tool)

1. Resolve proposer from EVERY available signal, not just source_context.proposer:
     source_context.proposer, source_context.created_by, source_context.actor/agent,
     approval_requests.source, approval_requests.reviewed_by, and any request creator/actor field.
2. If a proposer identity IS resolved:
     reject if --approver matches it (case-insensitive / normalized).  (true self-approval block)
3. If proposer is UNKNOWABLE (all signals NULL, as in APR-0415):
     DO NOT silently pass.  Require an explicit president/council OVERRIDE rationale AND set a
     `self_approval_risk=true` flag on the audit row.  The vote is recorded only with that explicit,
     logged acknowledgement; absent it ⇒ reject (fail-closed).
4. Never infer "safe" from absence of data.  Absence ⇒ risk, not permission.

This makes the null-proposer hole visible and gated, instead of an invisible bypass. It does not "fix" APR-0415 (which is out of scope and untouched); it ensures the future channel cannot be exploited through that hole.

Note on scope

G5 here governs dot-apr-approve only. Hardening the DB functions themselves (e.g. requiring a non-NULL proposer at propose-time, or recording proposer provenance) is a separate governance change, not part of this minimal bootstrap, and is flagged as a follow-up — not performed.

Back to Knowledge Hub knowledge/dev/laws-new/reports/sovereign-bootstrap-dot-apr-approve/06-self-approval-guard-and-null-proposer-risk.md