Repository navigation
feat(jobs): durable job store, leased runner, and WP-Cron dispatch - #176
Merged
Merged
Conversation
Background work now has a durable home that survives process crashes and
late cron, the foundation for M5 reconciliation, retries, and webhooks.
- Schema 7 adds {prefix}campaignbridge_jobs. A job records its type,
target, a small scalar payload that refuses credential-like keys, state
(queued, claimed, succeeded, failed, dead), attempts, run time, lease, and
a stable last-error code.
- Job_Repository claims and transitions jobs with conditional updates, so
two workers cannot hold one job and a worker whose lease expired can no
longer finish it. A dedupe key keeps one active job per type and target.
- Job_Runner runs a bounded batch through each type's Job_Handler. A job
taken over from a stopped worker, or whose handler is not retry-safe and
threw part way, runs again only if its handler is retry-safe; otherwise
the handler's abandoned() hook runs and the job ends dead, so an
irreversible effect is never repeated. Retries back off exponentially and
end dead once attempts are used up.
- Job_Dispatcher runs a batch on a one-minute WP-Cron tick, purges week-old
successes, and is unscheduled on deactivation.
- Status health reports waiting, stopped, and overdue jobs and warns when
work has stalled or the tick is not scheduled.
No job types are registered yet; background reconciliation (#171) is the
first. Verified on MySQL and on the Studio SQLite site.
Closes #168
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 10, 2026
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.
Closes #168. Part of #66 (M5).
What this adds
The foundation for M5's background work: jobs survive process crashes and late cron, and a job whose handler isn't safe to re-run is never re-run.
{prefix}campaignbridge_jobs. Each job records:queued,claimed,succeeded,failed,dead);Job_Repository(behind theJob_Sourceport).UPDATE, so two workers can't hold one job, and a worker whose lease expired can no longer finish it.Job_Runner,Job_Handler,Job_Queue(workflow).retry_safe.abandoned()hook runs and the job endsdead.deadwith the last error once attempts are used up.Cron/Job_Dispatcher. A one-minute WP-Cron tick runs a batch of 10 and purges succeeded jobs older than a week. It's unscheduled on deactivation.No job types are registered yet. Background reconciliation (#171) is the first.
Evidence (#168)
Migration from a populated schema 6 (a scheduled campaign is kept), including idempotent re-runs, plus an index check.
Job_Queue_Test:deadwith the last error;abandoned()instead), a retry-safe job runs again, an unsafe throw is abandoned rather than retried, and a crash on the last attempt ends the job.Removing the takeover guard makes the crash test fail.
Job_Dispatcher_Test: the plugin registers the tick, schedules it once, runs it through the production runner, and clears it on deactivation.Job_Dispatch_Performance_Test: an idle tick takes 5 or fewer queries. A tick with three batches queued stays within 5 + 3 per job in the batch, so its cost is bounded by the batch size, not by how much is queued.SQLite (Studio): a probe ran dedupe, the claim race, takeover (normal and exhausted), lockout, retry, finish, counts, and stalled detection. Studio was then re-stamped to schema 6, so master keeps working there until this merges.
Verification
pnpm qa:fastpasses.Notes
🤖 Generated with Claude Code