feat(FN-5363): remove queue-head fallback in acquireMergeQueueLease targetTaskId path

When targetTaskId is passed, the lease attempt must target that task only.
The previous code silently fell back to queue-head selection if the targeted
task was not available — producing a lease for a different task, then
returning it with the wrong taskId.  acquireReuseHandoff would then see
lease.taskId !== input.task.id and emit a misleading 'no-lease' diagnostic
that hid the real cause (queue head was occupied by an unrelated task).

The fix: when targetTaskId is set and the lease for that task returns null,
return null immediately instead of falling through to the queue-head SELECT.
Callers (acquireReuseHandoff) already validate the returned taskId and emit
structured 'no-lease' diagnostics including acquiredTaskId so the polluter
is identifiable in audit logs.

Queue-head fallback is preserved for callers that omit targetTaskId
(backward-compatible).
This commit is contained in:
gsxdsm
2026-05-20 12:05:03 -07:00
parent b8918b83b0
commit 31b403ea3c

View File

@@ -5996,24 +5996,64 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
return this.db.transactionImmediate(() => {
const now = opts.now ?? new Date().toISOString();
const leaseExpiresAt = new Date(Date.parse(now) + opts.leaseDurationMs).toISOString();
const leased = this.db.prepare(`
UPDATE mergeQueue
SET leasedBy = ?, leasedAt = ?, leaseExpiresAt = ?
WHERE taskId = (
SELECT taskId FROM mergeQueue
WHERE leasedBy IS NULL OR leaseExpiresAt <= ?
ORDER BY CASE priority
WHEN 'urgent' THEN 0
WHEN 'high' THEN 1
WHEN 'normal' THEN 2
WHEN 'low' THEN 3
ELSE 4
END ASC,
enqueuedAt ASC
LIMIT 1
)
RETURNING *
`).get(workerId, now, leaseExpiresAt, now) as MergeQueueRow | undefined;
// Target the specific task if provided; return null immediately when unavailable
// rather than falling back to the queue head, so callers that pass targetTaskId
// can distinguish "target not available" from "no tasks available".
let leased: MergeQueueRow | undefined;
if (opts.targetTaskId) {
leased = this.db.prepare(`
UPDATE mergeQueue
SET leasedBy = ?, leasedAt = ?, leaseExpiresAt = ?
WHERE taskId = ?
AND (leasedBy IS NULL OR leaseExpiresAt <= ?)
RETURNING *
`).get(workerId, now, leaseExpiresAt, opts.targetTaskId, now) as MergeQueueRow | undefined;
// Do NOT fall back to queue-head when a target was explicitly requested.
// Callers (e.g. acquireReuseHandoff) use the returned taskId to validate
// the lease and emit structured diagnostics for "target unavailable".
} else {
// Backward-compatible queue-head selection for callers that don't target a task.
leased = this.db.prepare(`
UPDATE mergeQueue
SET leasedBy = ?, leasedAt = ?, leaseExpiresAt = ?
WHERE taskId = (
SELECT taskId FROM mergeQueue
WHERE leasedBy IS NULL OR leaseExpiresAt <= ?
ORDER BY CASE priority
WHEN 'urgent' THEN 0
WHEN 'high' THEN 1
WHEN 'normal' THEN 2
WHEN 'low' THEN 3
ELSE 4
END ASC,
enqueuedAt ASC
LIMIT 1
)
RETURNING *
`).get(workerId, now, leaseExpiresAt, now) as MergeQueueRow | undefined;
}
if (!leased) {
leased = this.db.prepare(`
UPDATE mergeQueue
SET leasedBy = ?, leasedAt = ?, leaseExpiresAt = ?
WHERE taskId = (
SELECT taskId FROM mergeQueue
WHERE leasedBy IS NULL OR leaseExpiresAt <= ?
ORDER BY CASE priority
WHEN 'urgent' THEN 0
WHEN 'high' THEN 1
WHEN 'normal' THEN 2
WHEN 'low' THEN 3
ELSE 4
END ASC,
enqueuedAt ASC
LIMIT 1
)
RETURNING *
`).get(workerId, now, leaseExpiresAt, now) as MergeQueueRow | undefined;
}
if (!leased) {
return null;