feat: resume interrupted Find Work batches (Closes #679)
CI / lint (pull_request) Successful in 1m25s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-12 21:02:25 +00:00
1 parent a0dd044995
commit 654ae8714e
5 files changed
+194 -35

No files matched your search

+89 -1
View File
@@ -109,6 +109,82 @@ flow.run([{{repository:'stackchain/dashboard',number:671}}])
}
def test_interrupted_batch_resumes_confirmed_assignment_without_claiming_twice():
script = f"""
const createBatchFindWork=require({json.dumps(str(BATCH_FIND_WORK))});
const values={{}};
const storage={{
getItem:key=>values[key] || null,
setItem:(key,value)=>{{values[key]=value;}},
removeItem:key=>{{delete values[key];}},
}};
const calls=[];
const item={{repository:'stackchain/dashboard',number:679,title:'Recover me'}};
const first=createBatchFindWork({{
capacity:()=>1, storage, owner:()=> 'timmy',
claim:value=>{{calls.push('claim');return Promise.resolve({{...value,assignees:['timmy']}});}},
queue:()=>{{calls.push('queue-failed');return 'sync-unavailable';}},
}});
first.run([item]).then(firstResult=>{{
const second=createBatchFindWork({{
capacity:()=>1, storage, owner:()=> 'timmy',
claim:()=>{{calls.push('duplicate-claim');return Promise.resolve(item);}},
queue:value=>{{calls.push('queue-resumed:'+value.assignees[0]);return 'queued';}},
}});
return second.resume().then(resumed=>process.stdout.write(JSON.stringify({{
firstResult, resumed, calls, keys:Object.keys(values),
}})));
}});
"""
assert run_node(script) == {
"firstResult": {
"status": "complete", "selected": 1, "available": 1, "queued": [],
"failed": [{
"key": "stackchain/dashboard#679",
"reason": "assigned but Today sync is unavailable",
"assigned": True,
}],
},
"resumed": {
"status": "complete", "selected": 1, "available": 1,
"queued": ["stackchain/dashboard#679"], "failed": [],
},
"calls": ["claim", "queue-failed", "queue-resumed:timmy"],
"keys": [],
}
def test_resumed_batch_skips_queued_items_and_continues_in_original_order():
script = f"""
const createBatchFindWork=require({json.dumps(str(BATCH_FIND_WORK))});
const key='stackchain.find-work-batch.v1.timmy';
const values={{[key]:JSON.stringify({{
owner:'timmy', available:2, items:[
{{item:{{repository:'stackchain/dashboard',number:678}},state:'queued'}},
{{item:{{repository:'stackchain/dashboard',number:679}},state:'pending'}},
],
}})}};
const calls=[];
const flow=createBatchFindWork({{
capacity:()=>2, owner:()=> 'timmy',
storage:{{getItem:key=>values[key]||null,setItem:(key,value)=>values[key]=value,removeItem:key=>delete values[key]}},
claim:item=>{{calls.push('claim:'+item.number);return Promise.resolve(item);}},
queue:item=>{{calls.push('queue:'+item.number);return 'queued';}},
}});
flow.resume().then(result=>process.stdout.write(JSON.stringify({{result,calls}})));
"""
assert run_node(script) == {
"result": {
"status": "complete", "selected": 2, "available": 2,
"queued": ["stackchain/dashboard#678", "stackchain/dashboard#679"],
"failed": [],
},
"calls": ["claim:679", "queue:679"],
}
def test_time_budget_blocks_claims_until_every_estimate_fits():
script = f"""
const createBatchFindWork=require({json.dumps(str(BATCH_FIND_WORK))});
@@ -178,7 +254,7 @@ def test_mobile_find_work_has_preclaim_time_review_controls():
assert 'id="find-work-estimate-list"' in html
assert 'id="confirm-find-work-estimates"' in html
assert 'inputmode="numeric"' in dashboard
assert "timeBudget: () =>" in dashboard
assert "timeBudget:" in dashboard
assert "persistEstimate:" in dashboard
assert ".find-work-estimate-review" in css
assert ".find-work-estimate-review input" in css and "min-height:44px" in css
@@ -292,6 +368,18 @@ def test_mobile_find_work_exposes_accessible_batch_controls_and_offline_asset():
assert '<script src="static/batch-find-work.js"></script>' in html
def test_mobile_find_work_exposes_touch_accessible_batch_recovery():
html = HTML.read_text()
dashboard = DASHBOARD.read_text()
css = CSS.read_text()
assert 'id="resume-find-work-batch"' in html
assert 'aria-describedby="find-work-status"' in html
assert "mountRecovery(" in BATCH_FIND_WORK.read_text()
assert "typeof localStorage === 'undefined'" in BATCH_FIND_WORK.read_text()
assert ".find-work-batch-recovery" in css and "min-height:44px" in css
def test_mobile_find_work_exposes_fill_today_action_with_capacity_handoff():
html = HTML.read_text()
dashboard = DASHBOARD.read_text()