dot-iu-cutter v0.4 PG-backed Dry-run — EXECUTION BLOCKED #2: accepted-code ↔ deployed-schema contract mismatch (2026-05-17)
dot-iu-cutter v0.4 — PG-backed Dry-run: EXECUTION BLOCKED #2 (pre-provision)
Date: 2026-05-17 · execution_status: BLOCKED_PRE_EXECUTION — accepted-code ↔ deployed-schema CONTRACT MISMATCH. Not an adapter defect; not an r3 defect; not fixable by code change or by the harness without violating boundaries.
After GPT PASSed verification-plan r3 and authorized execution, the authorized artefact-authoring step began with assembly-first read-only grounding (project rule: verify PG/Directus reality BEFORE building). That grounding found a hard structural blocker. No isolated env was provisioned, no pg_dump taken, no production connection, no secret/.env read, no code change, no commit. Per the hard rules (do not improvise · no self-advance · report honest · do not mark PASS) execution was halted before C-01.
1. Finding
The accepted code's append-only ledger writer (cutter_agent/ledger.py @ commit 56d3732) emits INSERT row shapes that do not match the deployed cutter_governance schema (the v0.2/v0.3 production DDL that a faithful pg_dump/restore reproduces, read live during grounding). LedgerWriter writes a simplified abstract ledger (e.g. from_state/to_state/actor/reason/at) while the deployed tables use a different, richer governance shape (e.g. entry_version_before/after, change_kind, changed_by, changed_at, rationale). 11 of 12 writer methods are incompatible; only append_entry → decision_backlog_entry matches.
A faithful PG-backed dry-run of the accepted code therefore cannot reach the r3 15-row baseline: the canonical happy path is MARK → SWEEP → REVIEW → CUT → VERIFY; mark() does append_entry (OK) then immediately append_history(...) — whose INSERT references non-existent columns (from_state,to_state,actor,reason,at) and omits the deployed NOT-NULL-no-default columns (entry_version_after,change_kind,changed_by,changed_at). psycopg3 raises 42703 (undefined_column) / 23502 (not_null_violation); classify_sqlstate → STOP → PhaseStop. S4 MARK fails on its second write statement; no further phase runs.
2. Evidence — per-writer column reconciliation (live DDL vs accepted ledger.py)
LedgerWriter method → table |
Code-inserted columns | Verdict vs deployed DDL |
|---|---|---|
append_entry → decision_backlog_entry |
entry_id,kind,status,payload,emitted_at,scenario_ref | MATCH — all columns exist; required kind supplied (only compatible writer) |
append_history → decision_backlog_history |
history_id,entry_id,from_state,to_state,actor,reason,at | FAIL — from_state/to_state/actor/reason/at do not exist (42703); deployed requires entry_version_after,change_kind,changed_by,changed_at NOT NULL (23502) |
append_dependency → decision_backlog_dependency |
dependency_id,from_entry_id,to_entry_id | FAIL — omits NOT NULL dependency_kind,created_at,created_by |
append_sweep_log → decision_backlog_sweep_log |
sweep_id,scanned,promoted,at | FAIL — scanned/promoted/at absent (42703); requires swept_at,swept_by,trigger_kind,entries_evaluated_count,entries_re_surfaced_count,escalations_routed_count NOT NULL |
write_manifest_envelope → manifest_envelope |
envelope_id,canonical_address,content_hash,source_entry_id,canon_lib_version | FAIL — those 4 absent (42703); requires operation_kind,status,source_doc_ref,created_by,created_at NOT NULL |
write_manifest_unit_block → manifest_unit_block |
envelope_id,unit_local_id,descriptor | FAIL — descriptor absent (42703); requires block_role,source_span,render_order,created_at NOT NULL |
write_review_decision → review_decision |
review_decision_id,manifest_id,manifest_unit_local_id,decision,reviewer,prior_review_decision_id,superseded_by_review_decision_id | FAIL — decision/reviewer absent (42703); requires 15 NOT-NULL-no-default cols incl. governance_event_kind,manifest_version,review_scope,status,verdict,findings,reviewer_class,reviewer_identity,risk_class_assessment,decision_at,decided_by,cross_signed_by_dot_verifier,version,created_at,updated_at |
write_signature → dot_pair_signature |
signature_id,lane,signer_identity,payload_digest,placeholder_signature,prior_signature_id,is_production | FAIL — those absent (42703); requires signature_kind,signer_dot_id,signer_tool_revision,payload_hash,payload_envelope,signature_payload NOT NULL |
write_cut_change_set → cut_change_set |
change_set_id,decision_backlog_entry_id,executor_signature_id,verifier_signature_id,manifest_id,content_hash | FAIL — content_hash absent (42703); requires rollback_key,manifest_version,review_decision_id,executor_tool_revision,verifier_tool_revision,emitted_by NOT NULL |
write_affected_row → cut_change_set_affected_row |
affected_row_id,change_set_id,target_ref,effect | FAIL — target_ref/effect absent (42703); requires target_table,target_row_id,operation_kind NOT NULL |
write_verify_result → verify_result |
verify_result_id,change_set_id,executor_signature_id,verifier_signature_id,outcome,rollback_change_set_id_triggered,escalation_ref,prior_verify_result_id | FAIL — outcome absent (42703); requires manifest_id,manifest_version,review_decision_id,executor_tool_revision,verifier_tool_revision NOT NULL |
(canonical_address_alias) |
never written (0 rows) | n/a — irrelevant to the failure (OD-2 deferral) |
Secondary corroboration: phases.py review()/cut() use the InMemory-only virtual filter find("review_decision", _source_entry=…); _source_entry is not a real column either (a separate symptom of the same code↔schema gap).
3. Why this is a hard blocker (and why I did not improvise around it)
- Cannot change code (forbidden, and the cycle's purpose is to dry-run the accepted
56d3732faithfully). - Cannot "fix" it in the harness. A harness that silently remaps/renames columns or injects the deployed NOT-NULL fields would no longer be exercising the accepted
LedgerWriter; it would be testing a bespoke shim. That is improvising past a discovered structural defect and would produce a misleading "PASS". Forbidden by do not improvise / no self-advance / report honest. - r3 is not at fault. r3's 15-row baseline correctly describes the accepted code's intended row counts; the defect is that the accepted code cannot physically write those rows into the deployed schema.
- The adapter is not at fault.
RealPostgresAdapterfaithfully parameterises and issues exactly the columnsLedgerWriterhands it; it would correctly surface 42703/23502 asPhaseStop.
This is an accepted-code ↔ deployed-cutter_governance-schema contract mismatch: the v0.4 LedgerWriter row shapes (an abstract ledger) were never reconciled with the v0.2/v0.3 deployed governance DDL (the schema dot-iu-cutter v0.2/v0.3 actually built and that a production restore reproduces). It must be adjudicated by GPT/User; it is out of scope for the agent to resolve unilaterally.
4. Candidate resolutions (for GPT/User — NOT self-applied)
- Schema-binding cycle for
LedgerWriter: author a design→review→code cycle that maps each ledger write to the realcutter_governancecolumns (supplying the deployed NOT-NULL fields:change_kind,governance_event_kind,signature_kind,rollback_key, tool-revisions, etc.). Largest, most correct; changes accepted code (new cycle, new commit, GPT-gated). - Restore-target reframing: if the PG-backed dry-run was intended to run against a v0.4 ledger schema (the abstract shape) rather than the deployed v0.2/v0.3 governance schema, define/seed that schema explicitly and re-spec the dry-run + r-series matrix against it.
- Adapter-mapping layer: a GPT-reviewed, accepted (committed) translation layer between
LedgerWriter's abstract rows and the deployed DDL — then dry-run that. - Re-scope the v0.4 dry-run to the InMemory adapter only and defer the PG-backed dry-run until (1)/(2)/(3) lands.
Each requires its own GPT-gated design→review chain. No self-advance.
5. Boundaries honoured (nothing executed)
No isolated env created · no pg_dump taken · no production DB connection (only read-only catalog/information_schema queries via the existing local docker exec trust path) · no production secret/.env read · no production row write · no production CUT/VERIFY · no code change · no git commit · no deploy/restart · no docker prune/wildcard · 3 protected prior dry-run envs untouched (snapshot only).
Git SSOT proof
- Branch
main;/opt/incomex/dotHEAD =56d3732cb74d07546c938242180a434ed1067a9a(accepted, unchanged);git status --short -- iu-cutterempty. - No code change ⇒ no commit needed. Last authoritative:
python3 -m unittest discover -s tests→ 92/92 at56d3732(the unit suite passes precisely because it uses the InMemory adapter / injected fakes and never asserts against the realcutter_governanceDDL — which is exactly how this gap stayed latent until a PG-backed dry-run was attempted).
Read-only grounding evidence
- PROD
system_identifier=7611578671664259111(unchanged); prodpostgrescontainer StartedAt2026-04-17T05:35:18.48439927Z(no restart). - 3 protected prior dry-run containers running, Id+StartedAt snapshotted, untouched.
- new env/network names collision-free (absent).
cutter_governance= 12 base tables + 12v_*_observeviews, all 0 rows; frozen privilege matrix validated (cutter_exec 18 table-priv + 2 col-UPDATE; cutter_verify 15 + 1; cutter_ro NOLOGIN + 13).- Full live DDL of all 12 base tables captured; reconciled against
ledger.py@56d3732(table in §2).
6. Next gate
GPT/User adjudication of this contract-mismatch blocker and selection of a resolution path (§4). Until then: no execution, no provisioning, no code/spec change, no self-advance. This finding supersedes, in priority, the (already-resolved) r2 matrix defect; r3 itself remains GPT-PASSed and correct as a statement of intended counts.