From e905ca1c32e21f91d28acaa54ceb31db221e1d3d Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Tue, 26 May 2026 12:20:40 -0400 Subject: [PATCH] feat(#293): stabilize workstream inventory ordering (#294) --- get-shit-done/bin/lib/workstream-inventory.cjs | 18 ++++++++++++++++-- tests/workstream.test.cjs | 16 +++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/get-shit-done/bin/lib/workstream-inventory.cjs b/get-shit-done/bin/lib/workstream-inventory.cjs index a8cc32049..a482ff079 100644 --- a/get-shit-done/bin/lib/workstream-inventory.cjs +++ b/get-shit-done/bin/lib/workstream-inventory.cjs @@ -55,6 +55,17 @@ function readStateProjection(statePath) { } } +function sortWorkstreamInventories(inventories, activeWorkstreamName) { + return [...inventories].sort((a, b) => { + const aActive = a.name === activeWorkstreamName ? 1 : 0; + const bActive = b.name === activeWorkstreamName ? 1 : 0; + if (aActive !== bActive) { + return bActive - aActive; + } + return a.name.localeCompare(b.name); + }); +} + function inspectWorkstream(cwd, name, options = {}) { const wsDir = path.join(workstreamsRoot(cwd), name); if (!fs.existsSync(wsDir)) return null; @@ -107,11 +118,13 @@ function listWorkstreamInventories(cwd) { if (inventory) workstreams.push(inventory); } + const ordered = sortWorkstreamInventories(workstreams, active); + return { mode: 'workstream', active, - workstreams, - count: workstreams.length, + workstreams: ordered, + count: ordered.length, }; } @@ -128,5 +141,6 @@ module.exports = { inspectWorkstream, isCompletedInventory, listWorkstreamInventories, + sortWorkstreamInventories, workstreamsRoot, }; diff --git a/tests/workstream.test.cjs b/tests/workstream.test.cjs index 3d4239615..6738b445f 100644 --- a/tests/workstream.test.cjs +++ b/tests/workstream.test.cjs @@ -451,18 +451,18 @@ describe('workstream list', () => { fs.mkdirSync(path.join(wsDir, 'phases'), { recursive: true }); fs.writeFileSync(path.join(wsDir, 'STATE.md'), `# State\n**Status:** Working on ${ws}\n**Current Phase:** 1\n`); } + fs.writeFileSync(path.join(tmpDir, '.planning', 'active-workstream'), 'beta\n'); }); after(() => cleanup(tmpDir)); - test('lists all workstreams', () => { + test('lists all workstreams with active first, then lexical name', () => { const result = runGsdTools(['workstream', 'list', '--raw'], tmpDir); assert.ok(result.success, `list failed: ${result.error}`); const data = JSON.parse(result.output); assert.strictEqual(data.mode, 'workstream'); assert.strictEqual(data.count, 2); - const names = data.workstreams.map(w => w.name).sort(); - assert.deepStrictEqual(names, ['alpha', 'beta']); + assert.deepStrictEqual(data.workstreams.map(w => w.name), ['beta', 'alpha']); }); describe('flat mode', () => { @@ -645,6 +645,11 @@ describe('workstream progress', () => { before(() => { tmpDir = createFixture(); + const alphaDir = path.join(tmpDir, '.planning', 'workstreams', 'alpha'); + fs.mkdirSync(path.join(alphaDir, 'phases'), { recursive: true }); + fs.writeFileSync(path.join(alphaDir, 'STATE.md'), '# State\n**Status:** In progress\n'); + fs.writeFileSync(path.join(alphaDir, 'ROADMAP.md'), '## Roadmap\n'); + const wsDir = path.join(tmpDir, '.planning', 'workstreams', 'feature'); fs.mkdirSync(path.join(wsDir, 'phases', '01-init'), { recursive: true }); fs.writeFileSync(path.join(wsDir, 'phases', '01-init', 'PLAN.md'), '# Plan\n'); @@ -656,12 +661,13 @@ describe('workstream progress', () => { after(() => cleanup(tmpDir)); - test('returns progress summary', () => { + test('returns progress summary in deterministic order', () => { const result = runGsdTools(['workstream', 'progress', '--raw'], tmpDir); assert.ok(result.success, `progress failed: ${result.error}`); const data = JSON.parse(result.output); assert.strictEqual(data.mode, 'workstream'); - assert.strictEqual(data.count, 1); + assert.strictEqual(data.count, 2); + assert.deepStrictEqual(data.workstreams.map(w => w.name), ['feature', 'alpha']); assert.strictEqual(data.workstreams[0].name, 'feature'); assert.strictEqual(data.workstreams[0].active, true); assert.strictEqual(data.workstreams[0].progress_percent, 50);