KB-3FDA
02 Guarded Handler Implementation
3 min read Revision 1
02 · Guarded Handler Implementation (Phase A)
Before
wf_candidate_action_vocabulary had 11 actions, all backend_handler='unimplemented'.
What was built (live, additive, reversible, birth-free)
wf_candidate_action_log— dedicated audit-first table. We did NOT reuseprocess_axis_action_logbecause that table FKsaction_code → process_axis_action_vocabulary(the apr/axis layer); candidate-layer codes would violate it.fn_wf_candidate_action_execute(action_code, subject_kind, subject_code, actor, actor_type, preview default true)— single fail-closed dispatcher mirroringfn_process_axis_execute_guarded_action.- Registered the handler on all 11 vocabulary rows (
UPDATE ... 11). v_wf_candidate_action_handler_status— handler + gate matrix for the UI.
Gate model (fail-closed by construction)
- Gate A —
requires_presidentAND actor_type ≠ human → BLOCKED. AI can never cast a president vote. - Gate B —
requires_president→ must find a real human presidentapprovevote inapr_approvalsjoined to the subject'sapproval_requests. None exist → BLOCKED (even for a human actor). - Gate C —
requires_owner→ must find an active row ingovernance_object_ownership (object_ref=subject). Ownership=0 → BLOCKED. - Gate D —
mutates_canon→ never executed here; canon is owner+president governed. - Otherwise → SAFE_TRIAGE: audit-logged; preview by default; no canon/owner/birth/event effect.
The handler can NEVER: approve, birth, write canon, activate events, create axis_assignment, or create ownership.
Live proof (run via psql, results captured)
| call | actor | result |
|---|---|---|
| MARK_NOT_PROCESS / WPC-OS-INFRA (preview) | ai_agent | PREVIEW ✅ |
| ASSIGN_OWNER / WPC-DOCKER-RUNTIME | ai_agent | BLOCKED — no active assigned owner |
| CREATE_BIRTH_REQUEST / job:cut | ai_agent | BLOCKED — president vote required; ai cannot satisfy |
| CREATE_BIRTH_REQUEST / job:cut | human (alice) | BLOCKED — no human president approve vote |
| RECONCILE_TO_DOT / WPC-DOT-BIN-RECONCILE | ai_agent | BLOCKED — no active assigned owner |
| SEND_TO_GOVERNANCE / job:cut | human | BLOCKED — no human president approve vote |
| NOPE (unknown) | ai_agent | BLOCKED — unknown action_code |
Gate matrix: 2 FAIL_CLOSED_OWNER, 2 FAIL_CLOSED_PRESIDENT, 7 SAFE_TRIAGE_PREVIEW. Action log after proof: 5 BLOCKED + 1 PREVIEW.
Handler list (action → gate_class)
- SAFE_TRIAGE: ACCEPT_OS_LEVEL, CONFIRM_PROCESS_CANDIDATE, MARK_NOT_PROCESS, MERGE_CANDIDATES, QUARANTINE_IGNORE, REQUEST_MORE_EVIDENCE, SPLIT_CANDIDATE
- FAIL_CLOSED_OWNER: ASSIGN_OWNER, RECONCILE_TO_DOT (also mutates_canon)
- FAIL_CLOSED_PRESIDENT: CREATE_BIRTH_REQUEST, SEND_TO_GOVERNANCE (both mutate_canon)
Blocker
None for handler implementation. The president/owner gates are designed to stay closed until real authority exists — that is the correct end state, not a blocker to fix.