--- id: pg-fix-scheduler-error-classification name: Classify run_schedules' renew-failure arms on pg (the sqlite twin, review 004 item 9) status: pending depends_on: [] scope: narrow risk: low impact: component level: implementation tags: [review-004-fixes, postgres-engine, scheduler] --- ## Description The pg twin of `sqlite-fix-scheduler-error-classification` (completed), recorded in that task's Notes as an unresolved follow-up: `alkstore-postgres/src/scheduler.rs:682` carries the identical `.unwrap_or(false)` flattening the sqlite task fixed — a mid-run `lease.renew(LEADER_TTL_S)` failure (including the closed-store `Database` error) is mapped to `false` and misreported as `Error::LeadershipLost`, misdirecting the documented respawn recipe at a dead store. The classification decision of record is already landed and engine-neutral — the sqlite task's dated (2026-10-11) ADR-009 §1 / core-contract.md §Errors annotation pins the arm set for the family — so this task implements pg to match it, not re-decides: - `renew` → `Ok(false)` — the genuine loss arm (expired and re-acquired elsewhere; exclusion lapses silently at TTL expiry, ADR-008 §7) — returns `Err(LeadershipLost)`, unchanged, return-before-tick. The respawn recipe matches on this arm only. - `renew` → `Err(e)` — the machinery never reached the lock semantics; not a loss, never `LeadershipLost`. The `Err` propagates unchanged: the closed-store arm arrives pre-classified (pg's post-close `renew` fails closed with `Database`, pinned by pg lock_tests), and any other storage failure carries the driver's source chain. No new variant minted — ADR-008 §5's act-differently rule holds (`Closed` stays the wake-receiver variant; closed store is `Database` engine-wide, the pg posture already). **Tests need the harness server** (the reason the sqlite task recorded rather than landed this): model the two pins on the sqlite task's — a mid-run close exits with the closed `Database` classification, never `LeadershipLost`; a mid-run lease loss still returns `LeadershipLost` (the respawn arm unchanged). ## Acceptance Criteria - [ ] The pg renew arm implements the landed arm set: `Ok(false)` → `LeadershipLost` unchanged; `Err(e)` propagates unchanged (no flattening) — no re-deciding; any discrepancy with the sqlite task's decisions of record recorded, not deviated around - [ ] Both arms pinned by tests against the harness server (the closed-store arm and the genuine-loss arm, modeled on the sqlite task's two pins) - [ ] The ADR-009 §1 / core-contract annotations verified to read engine-neutral (they were landed as family-wide; cite them — only pg-specific doc text needs touching, e.g. engine-postgres.md's scheduler row if it describes the arm) - [ ] `cargo test -p alkstore-postgres` (harness server), clippy `-D warnings`, fmt clean; workspace gates green server-less ## References - tasks/sqlite-fix-scheduler-error-classification.md (the twin — the arm set's decisions of record, the two pin shapes, the recorded follow-up this task discharges) - docs/reviews/004-pre-release-general-review.md §"Findings recorded, not fixed" item 9 (the original finding — recorded the sqlite site only; the pg twin surfaced at implementation) - docs/architecture/decisions/009-*.md §1 (the dated classification annotation of record), core-contract.md §Errors - alkstore-postgres/src/scheduler.rs:675-690 (the flattening site) ## Notes > To be filled by implementation agent ## Summary > To be filled on completion