KB-231F

Fix 3 — P5 Fail-Closed Exact Oracle

4 min read Revision 1
c1stagingcodex-r1-fixp5fail-closed2026-06-23

05 — FIX 3: P5 FAIL-CLOSED BAD-INPUT HARNESS (the rejecting blocker)

Root cause (Codex A9)

Rev-1 c1_run did pass=(ex='reject') in a WHEN OTHERS catch — so ANY exception (wrong SQLSTATE, missing prerequisite, typo, unrelated error) was marked PASS. Assertions were non-fatal SELECT 'RESIDUE_FAIL' … strings; psql exited 0; P5_DONE printed after logical failure.

Fix — exact oracle

c1_run(case_no, phase, label, expected_reject_code, expected_sqlstate, query):

  • Accepted bad input → FAIL: the EXECUTE q success branch hardcodes pass=false ("invalid input was accepted").
  • Rejected → PASS only on exact match: ok := (exp_code IS NOT NULL AND rc=exp_code) OR (exp_state IS NOT NULL AND st=exp_state) where rc=split_part(MESSAGE_TEXT,' |',1), st=RETURNED_SQLSTATE. An unexpected exception cannot match → pass=false.
  • c1_test_results.case_no is PRIMARY KEY (duplicate/missing case caught); result INSERTs are OUTSIDE the inner exception wrapper, so an insert failure propagates and aborts P5.

The 9 expected signals (each case → exact expected)

# label expected
1 missing operation_code reject_code C1_REJ_MISSING_CODE
2 duplicate operation_code SQLSTATE 23505 (unique_violation)
3 empty title C1_REJ_EMPTY_TITLE
4 invalid operation_group C1_REJ_INVALID_GROUP
5 missing required_inputs C1_REJ_MISSING_REQUIRED_INPUTS
6 bad expected_outputs (object) C1_REJ_BAD_EXPECTED_OUTPUTS
7 unknown allowed mode C1_REJ_UNKNOWN_MODE
8 write official dot_tools (isolation) SQLSTATE 42P01 (undefined_table)
9 mark production-ready C1_REJ_PRODUCTION_READY_FORBIDDEN

FATAL postcondition (after COMMIT; ON_ERROR_STOP=1 → psql exit 3)

IF total<>9    THEN RAISE 'P5_FATAL_CASE_COUNT'; END IF;
IF accepted<>0 THEN RAISE 'P5_FATAL_BAD_ACCEPTED'; END IF;   -- fail-open guard
IF failed<>0   THEN RAISE 'P5_FATAL_SIGNAL_MISMATCH'; END IF; -- unexpected exception guard
IF passed<>9   THEN RAISE 'P5_FATAL_PASS_COUNT'; END IF;
IF residue<>3  THEN RAISE 'P5_FATAL_RESIDUE'; END IF;

On any failure psql exits nonzero → the bin wrapper (set -euo pipefail) never prints P5_DONE → the plan aborts and the EXIT trap drops the sandbox.

Read-only verification of oracle assumptions (no sandbox, no write)

  • PostgreSQL short-circuits the trigger's OR chain: SELECT true OR (jsonb_array_length('null'::jsonb)<1)true; case5/case6 boolean exprs evaluate to true without raising → cases 5/6 reach C1_REJ_MISSING_REQUIRED_INPUTS / C1_REJ_BAD_EXPECTED_OUTPUTS (not a jsonb error).
  • split_part('C1_REJ_… | …',' |',1) → the reject code (verified for cases 1 and 9).

Honest caveat: the exact runtime reject_code/SQLSTATE of all 9 cases is statically derived (against the deployed trigger/constraint logic) and read-only-verified for the short-circuit + extraction mechanics; full runtime confirmation occurs during the Codex-R2-gated dry-run. If any expected signal were wrong, the FATAL gate fails closed (P5 aborts) — never a false PASS.

sha256 sql/p5-bad-input-harness.sql = 0658ba6260314ef293eb4c58d51f492a83e6ce86f427448ca116232fa4176146.

Back to Knowledge Hub knowledge/dev/laws-new/reports/c1-staging-codex-r1-fixes-ready-for-r2/05-fix-p5-fail-closed-harness.md