Keep Enhance + Generate as one queue row with shared progress.

The PE stage now stays on the same jobId so the header and queue badge agree, and jobs behind it do not promote when enhance finishes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-26 14:42:22 -05:00
co-authored by Cursor
parent 30be4bd5b0
commit 99b61e67d7
9 changed files with 129 additions and 8 deletions
+3 -1
View File
@@ -15,7 +15,7 @@ test('deleted library files never return from job outputs; selection falls back
let assets=folderAssets({stills:[keep],clips:[]},jobs,'f');assert.deepEqual(assets.map(a=>a.id),['keep']);assert.equal(reconcileSelection(deleted,assets).id,'keep');assert.equal(reconcileSelection(deleted,[]),null)
})
test('bench includes only active execution phases and clear finished retains internal cleanup records',()=>{
assert.deepEqual(activeJobs(['waiting','waking','submitting','rendering','saving','complete','cancelled','failed'].map(state=>({state}))).map(j=>j.state),['waking','submitting','rendering','saving'])
assert.deepEqual(activeJobs(['waiting','waking','enhancing','submitting','rendering','saving','complete','cancelled','failed'].map(state=>({state}))).map(j=>j.state),['waking','enhancing','submitting','rendering','saving'])
const rows=[{id:'done',state:'complete',request:{folderId:'f'},cleanupPending:[{}]},{id:'active',state:'rendering',request:{folderId:'f'}}],saved=[]
const src=readFileSync(new URL('../server/api/studio-2/clear-finished.post.ts',import.meta.url),'utf8').replace(/^import .*;\r?\n/gm,'').replace('export default ','const handler=')
const js=ts.transpileModule(src,{compilerOptions:{target:ts.ScriptTarget.ES2022}}).outputText
@@ -144,6 +144,8 @@ test('custom negative chips survive normalizeStyles and bench exposes Load actio
assert.match(runner,/ensureQwen21EditPrompt/)
assert.match(runner,/stitchQwen21PeEditPrompt/)
assert.match(runner,/images\.image_1/)
assert.match(runner,/comfyPromptId/)
assert.match(runner,/generateComfyPromptId/)
assert.doesNotMatch(runner,/Keep the subject in <image1>/)
const t2i=readFileSync(new URL('../server/assets/studio2_qwen21_t2i.json',import.meta.url),'utf8')
assert.match(t2i,/EmptyLatentImage/);assert.match(t2i,/"latent_image":\s*\["16",\s*0\]/)
+16 -1
View File
@@ -1,11 +1,26 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import {queueJobs,jobView,elapsedSeconds} from '../shared/studio2/queue-view.mjs'
import {queueJobs,jobView,elapsedSeconds,jobStageLabel,jobProgressView} from '../shared/studio2/queue-view.mjs'
test('queue shows every waiting job in order alongside the running job',()=>{
const jobs=[{id:'late',state:'waiting',queuedAt:30},{id:'done',state:'complete',queuedAt:1},{id:'early',state:'waiting',queuedAt:10},{id:'active',state:'rendering',queuedAt:20},{id:'held',state:'held',queuedAt:40}]
assert.deepEqual(queueJobs(jobs).map(j=>j.id),['active','early','late','held'])
assert.equal(queueJobs(Array.from({length:15},(_,i)=>({id:i,state:'waiting',queuedAt:i}))).length,15)
})
test('enhancing stays on the active queue with the same job id as generate',()=>{
const pe={id:'one',state:'enhancing',queuedAt:10}
const behind={id:'two',state:'waiting',queuedAt:20}
assert.deepEqual(queueJobs([behind,pe]).map(j=>j.id),['one','two'])
assert.equal(jobStageLabel('enhancing'),'Enhancing prompt…')
assert.equal(jobStageLabel('rendering'),'Generating…')
assert.equal(jobStageLabel('submitting'),'Generating…')
assert.deepEqual(jobProgressView(pe),{indeterminate:true,percent:null})
assert.deepEqual(jobProgressView({state:'enhancing',progress:{step:2,maxStep:4,percent:50}}),{indeterminate:false,percent:50})
assert.deepEqual(jobProgressView({state:'rendering',progress:{step:3,maxStep:6,percent:50}}),{indeterminate:false,percent:50})
assert.equal(jobProgressView({state:'waiting'}),null)
// PE → generate is a stage change on the same row, not a vanish frame
const same=[{id:'one',state:'submitting',queuedAt:10},{id:'two',state:'waiting',queuedAt:20}]
assert.deepEqual(queueJobs(same).map(j=>j.id),['one','two'])
})
test('authoritative completion ends a stale rendering row and freezes its timer',()=>{
const record={state:'rendering',queuedAt:1000,startedAt:2000,updatedAt:5000}
for(const status of ['complete','error','cancelled']){