mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-04 15:15:49 +00:00
maint: integrate reviewed contribution #3791
This commit is contained in:
3 files changed
+141
-10
No files matched your search
@@ -8,6 +8,8 @@
|
||||
*/
|
||||
|
||||
const DONE_STATUSES = new Set(['done', 'closed', 'resolved', 'merged', 'cancelled']);
|
||||
// Only explicitly admitted work may be newly claimed.
|
||||
const CLAIMABLE_STATUSES = new Set(['open', 'ready']);
|
||||
const PRIORITY_RANK = { critical: 0, high: 1, urgent: 1, medium: 2, normal: 2, low: 3 };
|
||||
|
||||
// Kanban lanes the board renders, and the canonical status each maps to on a move.
|
||||
@@ -21,11 +23,7 @@ const VALID_LANES = new Set(Object.keys(LANE_TO_STATUS));
|
||||
const VALID_ASSIGNEE_KINDS = new Set(['agent', 'human']);
|
||||
|
||||
function isOpenStatus(status) {
|
||||
return !DONE_STATUSES.has(
|
||||
String(status || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
);
|
||||
return CLAIMABLE_STATUSES.has(String(status || '').trim().toLowerCase());
|
||||
}
|
||||
|
||||
function priorityRank(priority) {
|
||||
@@ -44,8 +42,9 @@ function selectClaimTarget(store, { id } = {}) {
|
||||
}
|
||||
return item;
|
||||
}
|
||||
const { items } = store.listWorkItems({ limit: 100 });
|
||||
return items.filter(item => !item.owner && isOpenStatus(item.status)).sort((a, b) => priorityRank(a.priority) - priorityRank(b.priority))[0] || null;
|
||||
// The native SQL query filters all eligible items before choosing the
|
||||
// highest-priority claim; a newer 100-item slice can hide older READY work.
|
||||
return store.getNextClaimableWorkItem();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,13 +64,19 @@ function claimWorkItem(store, { id, owner, assigneeKind, sessionId, status } = {
|
||||
if (!target) {
|
||||
return { claimed: false, reason: 'no-unassigned-open-items' };
|
||||
}
|
||||
if (!isOpenStatus(target.status)) {
|
||||
throw new Error(`Work item ${target.id} is already done; cannot claim.`);
|
||||
}
|
||||
if (target.owner) {
|
||||
if (target.owner !== owner) {
|
||||
throw new Error(`Work item ${target.id} is already owned by ${target.owner}; cannot claim.`);
|
||||
}
|
||||
// Repeating an active claim by the same owner is idempotent.
|
||||
if (['running', 'claimed', 'in-progress'].includes(String(target.status).trim().toLowerCase())) {
|
||||
return { claimed: true, item: target };
|
||||
}
|
||||
}
|
||||
if (!isOpenStatus(target.status)) {
|
||||
throw new Error(`Work item ${target.id} is not READY; cannot claim.`);
|
||||
}
|
||||
if (target.owner) {
|
||||
return { claimed: true, item: target };
|
||||
}
|
||||
const metadata = { ...(target.metadata || {}) };
|
||||
|
||||
@@ -482,6 +482,22 @@ function createQueryApi(db, { prepareRead = sql => db.prepare(sql) } = {}) {
|
||||
FROM work_items
|
||||
ORDER BY updated_at DESC, id DESC
|
||||
`);
|
||||
// Read the entire native READY set in SQL instead of truncating to the
|
||||
// 100 most recently updated items before checking their eligibility.
|
||||
const nextClaimableWorkItemStatement = prepareRead(`
|
||||
SELECT *
|
||||
FROM work_items
|
||||
WHERE (owner IS NULL OR owner = '')
|
||||
AND LOWER(TRIM(COALESCE(status, ''))) IN ('open', 'ready')
|
||||
ORDER BY CASE LOWER(COALESCE(priority, ''))
|
||||
WHEN 'critical' THEN 0
|
||||
WHEN 'high' THEN 1
|
||||
WHEN 'urgent' THEN 1
|
||||
WHEN 'low' THEN 3
|
||||
ELSE 2
|
||||
END ASC, updated_at DESC, id DESC
|
||||
LIMIT 1
|
||||
`);
|
||||
const getWorkItemStatement = prepareRead(`
|
||||
SELECT *
|
||||
FROM work_items
|
||||
@@ -760,6 +776,11 @@ function createQueryApi(db, { prepareRead = sql => db.prepare(sql) } = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function getNextClaimableWorkItem() {
|
||||
const row = nextClaimableWorkItemStatement.get();
|
||||
return row ? mapWorkItemRow(row) : null;
|
||||
}
|
||||
|
||||
function getStatus(options = {}) {
|
||||
const activeLimit = normalizeLimit(options.activeLimit, 5);
|
||||
const recentSkillRunLimit = normalizeLimit(options.recentSkillRunLimit, 20);
|
||||
@@ -870,6 +891,7 @@ function createQueryApi(db, { prepareRead = sql => db.prepare(sql) } = {}) {
|
||||
},
|
||||
listRecentSessions,
|
||||
listWorkItems,
|
||||
getNextClaimableWorkItem,
|
||||
upsertInstallState(installState) {
|
||||
const normalized = normalizeInstallStateInput(installState);
|
||||
assertValidEntity('installState', normalized);
|
||||
|
||||
Reference in new issue
Block a user