Deferred fix: future-valued publishedAt remove = reschedule (drained-map re-arm) - #338
Draft
JustMaier wants to merge 1 commit into
Draft
Deferred fix: future-valued publishedAt remove = reschedule (drained-map re-arm)#338JustMaier wants to merge 1 commit into
JustMaier wants to merge 1 commit into
Conversation
…an unschedule The #335 trigger guard emits {op: remove, field: publishedAt, value: <ts>} for scheduled posts — the remove hides the entity NOW, and the value says WHEN it publishes. The ops processor ignored the value: any remove of the deferred source field on a deferred slot took the unschedule branch (slot dropped from the deferred map, activated as a draft), and on a not-alive non-deferred slot the ops were memoized as confirmed-deleted and dropped forever. July heal-era removes drained bitdex-1's deferred map through exactly this path, leaving ~93K scheduled images with no activation path at their publish time (2026-08-07 incident). - get_future_schedule(): reads a FUTURE timestamp from a Set OR a value-bearing Remove on the deferred source field (LIFO last-wins, ms_to_secs honored; null/past resets — those remain unschedules). - Deferred branch: future-valued remove reschedules at the carried time; the doc stores the schedule as a SET so Tf activation replays a correct publishedAt with no re-emit dependency. - Not-alive discrimination chain: a future schedule in the ops re-ARMS the slot (deferred_alive at the carried time + doc write), checked BEFORE the confirmed-deleted memo — a drained scheduled slot is indistinguishable from a deleted one by doc-presence alone; the future timestamp is the discriminator. PG-deleted rows cannot reach this path (fan-out/re-emit queries join live Image rows). Unblocks the scheduled sweep (civitai #3717, gated off) which then becomes the standing re-arm heal for the drained maps. Deadline: before 2026-08-31 (earliest affected publish dates). Tests: deferred reschedule-via-remove, past-valued remove still unschedules, drained-slot re-arm (doc-present, below HWM, not deferred). Full --lib suite: 1317 passed; 2 known flakes (query_stream Win-timing, bound_store passes in isolation). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JustMaier
marked this pull request as draft
August 7, 2026 01:38
Contributor
Author
|
HOLD — do not merge. The adversarial sweep (2026-08-07) confirmed two criticals in this fix as written:
Also confirmed major: future-only arming leaves past-Tf drained slots memoized confirmed-deleted (unhealable through the ops pipeline). Redesign direction: resolve ambiguity at EMISSION (fan-out should not emit the old-side publishedAt remove — one unambiguous schedule op per row), plus patch both engine entrances, plus a past-Tf arming story. Full findings: docs/_in/adversarial-sweep-findings-2026-08-07.md (landing on main separately). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
The #335 trigger guard emits
{op: remove, field: publishedAt, value: <future ts>}for scheduled posts. The ops processor ignored the carried value: on a deferred slot ANY remove of the schedule field = unschedule (slot leaves the deferred map, activates as draft — Tf activation destroyed); on a not-alive non-deferred slot the ops were memoized confirmed-deleted and dropped forever. July heal-era removes drained bitdex-1's deferred map through this path: ~93K scheduled images with NO activation path at their publish dates (earliest Aug 31). Found by the #3717 adversarial review; state confirmed empirically on bitdex-1.The fix
get_future_schedule()reads a future timestamp from a Set OR value-bearing Remove on the deferred source field. Deferred slot → reschedule at that time (doc stores the schedule as a SET so activation replays a correct publishedAt). Not-alive non-deferred slot → re-ARM (before the confirmed-deleted memo — the future timestamp is what discriminates a drained scheduled slot from a deleted one). Null/past-valued removes keep unschedule semantics (genuine unpublish). PG-deleted rows can't reach the arming path (emission queries join live Image rows).Why this also heals
Once deployed, one pass of the (currently gated) scheduled sweep in civitai #3717 re-arms every drained slot on both pods — the sweep becomes the standing repair mechanism instead of the thing that causes the damage.
Tests
3 new: reschedule-via-future-remove, past-valued remove still unschedules, drained-slot re-arm. Full
--lib(server,pg-sync): 1317 passed, 2 pre-existing flakes (query_stream Win-timing; bound_store passes in isolation).🤖 Generated with Claude Code