PG proof: fix task_attempts migration on real Postgres 16.15
ci / test (push) Failing after 33m23s

- attempt_ledger.ensure: relax gap-3 outcome NOT NULL on Postgres
  (ALTER COLUMN outcome DROP NOT NULL, probe-gated); without it
  every resume write on a gap-3-migrated PG table fails with
  NotNullViolation. SQLite keeps NOT NULL (no such ALTER).
- db.PgCursor: PRAGMA table_info fake rows had the notnull flag
  inverted (NO -> 0); now matches sqlite semantics (1 = NOT NULL).
  No caller read row[3] before, so this was latent.
- ledger_onboarding: stale 'PG path unverified' notes updated to
  point at the real-PG proof.
- tests: pragma_info notnull pin (fake driver) + _column_notnull
  probe pins (fresh + gap-3 SQLite).

Proof: 28/28 checks on real Postgres 16.15 (see verification/
codex-task-attempts-pg-proof-20261106.md).
This commit is contained in:
host committed 2026-11-05 21:11:56 +00:00
1 parent 698398dafb
commit 2fcde375a4
5 files changed
+104 -16

No files matched your search

@@ -30,6 +30,7 @@ from cloud.attempt_ledger import ( # noqa: E402
SQLITE_DDL,
TASK_ATTEMPTS_DDL,
UNIFIED_COLUMNS,
_column_notnull,
aggregate_attempts,
ensure_task_attempts_table,
read_attempt_rows,
@@ -133,6 +134,31 @@ class TestFreshAndMigrations(unittest.TestCase):
self.assertEqual(self.conn.execute(
"SELECT COUNT(*) FROM task_attempts").fetchone()[0], 1)
def test_column_notnull_probe(self):
# PG 实证(2026-11-06):ensure 的 outcome 放宽依赖
# PRAGMA row[3] 的 notnull 语义;SQLite 侧钉住探针本身。
ensure_task_attempts_table(self.conn)
flags = _column_notnull(self.conn, "task_attempts")
self.assertFalse(flags["outcome"])
self.assertTrue(flags["task_id"])
self.assertTrue(flags["attempt_no"])
self.assertTrue(flags["is_defect"])
def test_column_notnull_probe_sees_gap3_outcome(self):
# gap-3 形态的 outcome 是 NOT NULL;探针必须报 True,
# 否则 PG 上的放宽不会触发,恢复写路仍会炸。
self.conn.execute(GAP3_DDL)
flags = _column_notnull(self.conn, "task_attempts")
self.assertTrue(flags["outcome"])
# SQLite 上放宽语句不合法、被 ensure 吞掉:迁移本身
# 不抛错、列集仍齐,但 outcome 保持 NOT NULL(需重建
# 表才能改,见批总结诚实边界;本批只修 PG 路径)。
ensure_task_attempts_table(self.conn)
self.assertEqual(set(_columns(self.conn)),
set(UNIFIED_COLUMNS))
self.assertTrue(
_column_notnull(self.conn, "task_attempts")["outcome"])
def test_old_shape_migrates_with_rows_intact(self):
self.conn.execute(OLD_DDL)
self.conn.execute(