feat(#293): stabilize workstream inventory ordering (#294)

This commit is contained in:
Tom Boucher
2026-05-26 12:20:40 -04:00
committed by GitHub
parent e08fea7ef2
commit e905ca1c32
2 changed files with 27 additions and 7 deletions

View File

@@ -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,
};

View File

@@ -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);