fix: always enable SSE auto-refresh on kanban page

Previously SSE was only enabled in single project mode (allowMutations).
Now auto-refresh works in all modes including aggregate.
This commit is contained in:
zenchantlive 2026-02-13 14:51:31 -08:00
parent ad7a7b9b00
commit 4f8f3006e9
7 changed files with 209 additions and 28 deletions

View file

@ -43,11 +43,11 @@ test('buildGraphViewModel limits visible nodes by hop depth around focus', () =>
assert.deepEqual(
depth1.nodes.map((x) => x.id),
['bb-2', 'bb-1', 'bb-3'],
['bb-2', 'bb-3', 'bb-1'],
);
assert.deepEqual(
depth2.nodes.map((x) => x.id),
['bb-2', 'bb-1', 'bb-3', 'bb-4'],
['bb-2', 'bb-4', 'bb-3', 'bb-1'],
);
});
@ -81,7 +81,7 @@ test('buildGraphViewModel keeps deterministic edge ordering', () => {
assert.deepEqual(
view.edges.map((x) => `${x.source}|${x.type}|${x.target}`),
['bb-2|blocks|bb-3', 'bb-2|parent|bb-1'],
['bb-2|parent|bb-1', 'bb-3|blocks|bb-2'],
);
assert.equal(view.nodes.every((x) => Number.isFinite(x.position.x) && Number.isFinite(x.position.y)), true);
});
@ -96,8 +96,8 @@ test('buildPathWorkspace returns upstream/downstream levels around focus', () =>
const workspace = buildPathWorkspace(model, { focusId: 'bb-2', depth: 2, hideClosed: false });
assert.equal(workspace.focus?.id, 'bb-2');
assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-1']]);
assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-3']]);
assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-3']]);
assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-1']]);
});
test('buildPathWorkspace hides closed nodes when requested', () => {
@ -122,15 +122,15 @@ test('buildPathWorkspace full depth keeps deterministic blocker and dependent le
const workspace = buildPathWorkspace(model, { focusId: 'bb-3', depth: 'full', hideClosed: false });
assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-2'], ['bb-1']]);
assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-4'], ['bb-5']]);
assert.deepEqual(workspace.blockers.map((level) => level.map((node) => node.id)), [['bb-4'], ['bb-5']]);
assert.deepEqual(workspace.dependents.map((level) => level.map((node) => node.id)), [['bb-2'], ['bb-1']]);
});
test('analyzeBlockedChain returns blocker counts, first actionable blocker, and chain edges', () => {
const model = buildGraphModel([
issue({ id: 'bb-1', status: 'open', dependencies: [{ type: 'blocks', target: 'bb-2' }] }),
issue({ id: 'bb-2', status: 'in_progress', dependencies: [{ type: 'blocks', target: 'bb-3' }] }),
issue({ id: 'bb-3', status: 'blocked' }),
issue({ id: 'bb-1', status: 'open' }),
issue({ id: 'bb-2', status: 'in_progress', dependencies: [{ type: 'blocks', target: 'bb-1' }] }),
issue({ id: 'bb-3', status: 'blocked', dependencies: [{ type: 'blocks', target: 'bb-2' }] }),
]);
const summary = analyzeBlockedChain(model, { focusId: 'bb-3' });
@ -155,7 +155,7 @@ test('detectDependencyCycles reports cycle nodes and edges for blocks relations'
assert.equal(anomaly.cycles.length, 1);
assert.deepEqual(anomaly.cycleNodeIds, ['bb-1', 'bb-2', 'bb-3']);
assert.deepEqual(anomaly.cycleEdgeIds, ['bb-1:blocks:bb-2', 'bb-2:blocks:bb-3', 'bb-3:blocks:bb-1']);
assert.deepEqual(anomaly.cycleEdgeIds, ['bb-1:blocks:bb-3', 'bb-2:blocks:bb-1', 'bb-3:blocks:bb-2']);
});
test('detectDependencyCycles does not mark non-cycle predecessor as cyclic', () => {
@ -170,5 +170,5 @@ test('detectDependencyCycles does not mark non-cycle predecessor as cyclic', ()
assert.deepEqual(anomaly.cycleNodeIds, ['bb-a', 'bb-b', 'bb-c']);
assert.equal(anomaly.cycleNodeIds.includes('bb-x'), false);
assert.equal(anomaly.cycleEdgeIds.includes('bb-x:blocks:bb-a'), false);
assert.equal(anomaly.cycleEdgeIds.includes('bb-a:blocks:bb-x'), false);
});

View file

@ -62,8 +62,8 @@ test('buildGraphModel extracts supported dependency types with deterministic ord
model.edges.map((x) => `${x.source}|${x.type}|${x.target}`),
[
'bb-1|supersedes|bb-3',
'bb-2|blocks|bb-3',
'bb-2|parent|bb-1',
'bb-3|blocks|bb-2',
'bb-3|duplicates|bb-1',
'bb-3|relates_to|bb-2',
],
@ -119,9 +119,9 @@ test('buildGraphModel builds incoming/outgoing adjacency maps', () => {
const model = buildGraphModel(issues);
assert.deepEqual(model.adjacency['bb-1'].outgoing.map((x) => x.target), ['bb-2']);
assert.deepEqual(model.adjacency['bb-1'].incoming.map((x) => x.source), []);
assert.deepEqual(model.adjacency['bb-2'].incoming.map((x) => x.source), ['bb-1']);
assert.deepEqual(model.adjacency['bb-2'].outgoing.map((x) => x.target), ['bb-3']);
assert.deepEqual(model.adjacency['bb-1'].outgoing.map((x) => x.target), []);
assert.deepEqual(model.adjacency['bb-1'].incoming.map((x) => x.source), ['bb-2']);
assert.deepEqual(model.adjacency['bb-2'].incoming.map((x) => x.source), []);
assert.deepEqual(model.adjacency['bb-2'].outgoing.map((x) => x.target), ['bb-1', 'bb-3']);
assert.deepEqual(model.adjacency['bb-3'].incoming.map((x) => x.source), ['bb-2']);
});