@ktpartners/dgs-platform 2.8.0 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +41 -13
  3. package/agents/dgs-plan-checker.md +29 -3
  4. package/agents/dgs-planner.md +10 -0
  5. package/commands/dgs/abandon-quick.md +28 -0
  6. package/commands/dgs/add-tests.md +2 -2
  7. package/commands/dgs/audit-milestone.md +2 -2
  8. package/commands/dgs/capture-principle.md +11 -11
  9. package/commands/dgs/cleanup.md +2 -2
  10. package/commands/dgs/complete-milestone.md +11 -11
  11. package/commands/dgs/complete-quick.md +28 -0
  12. package/commands/dgs/create-milestone-job.md +2 -2
  13. package/commands/dgs/debug.md +3 -3
  14. package/commands/dgs/develop-idea.md +1 -1
  15. package/commands/dgs/fast.md +3 -1
  16. package/commands/dgs/health.md +1 -1
  17. package/commands/dgs/map-codebase.md +6 -6
  18. package/commands/dgs/new-milestone.md +5 -5
  19. package/commands/dgs/new-project.md +6 -6
  20. package/commands/dgs/plan-milestone-gaps.md +1 -1
  21. package/commands/dgs/progress.md +3 -3
  22. package/commands/dgs/quick-abandon.md +8 -0
  23. package/commands/dgs/quick-complete.md +8 -0
  24. package/commands/dgs/quick.md +10 -3
  25. package/commands/dgs/research-idea.md +2 -2
  26. package/commands/dgs/research-phase.md +3 -3
  27. package/commands/dgs/switch-project.md +1 -1
  28. package/commands/dgs/write-spec.md +3 -3
  29. package/deliver-great-systems/bin/dgs-tools.cjs +284 -30
  30. package/deliver-great-systems/bin/lib/commands.cjs +316 -31
  31. package/deliver-great-systems/bin/lib/commands.test.cjs +336 -0
  32. package/deliver-great-systems/bin/lib/config.cjs +39 -6
  33. package/deliver-great-systems/bin/lib/context.cjs +120 -0
  34. package/deliver-great-systems/bin/lib/core.cjs +28 -11
  35. package/deliver-great-systems/bin/lib/execution.cjs +49 -17
  36. package/deliver-great-systems/bin/lib/flat-migration.test.cjs +396 -0
  37. package/deliver-great-systems/bin/lib/ideas.cjs +206 -91
  38. package/deliver-great-systems/bin/lib/ideas.test.cjs +244 -1
  39. package/deliver-great-systems/bin/lib/init.cjs +306 -39
  40. package/deliver-great-systems/bin/lib/init.test.cjs +416 -6
  41. package/deliver-great-systems/bin/lib/jobs.cjs +124 -21
  42. package/deliver-great-systems/bin/lib/jobs.test.cjs +193 -74
  43. package/deliver-great-systems/bin/lib/migration.cjs +409 -1
  44. package/deliver-great-systems/bin/lib/migration.test.cjs +158 -1
  45. package/deliver-great-systems/bin/lib/milestone.cjs +54 -29
  46. package/deliver-great-systems/bin/lib/phase.cjs +128 -2
  47. package/deliver-great-systems/bin/lib/phase.test.cjs +420 -0
  48. package/deliver-great-systems/bin/lib/projects.cjs +28 -8
  49. package/deliver-great-systems/bin/lib/projects.test.cjs +86 -0
  50. package/deliver-great-systems/bin/lib/quick.cjs +584 -0
  51. package/deliver-great-systems/bin/lib/quick.test.cjs +596 -0
  52. package/deliver-great-systems/bin/lib/repos.cjs +25 -1
  53. package/deliver-great-systems/bin/lib/roadmap.cjs +34 -13
  54. package/deliver-great-systems/bin/lib/specs.cjs +3 -81
  55. package/deliver-great-systems/bin/lib/state-transition-gate.test.cjs +160 -0
  56. package/deliver-great-systems/bin/lib/state.cjs +142 -54
  57. package/deliver-great-systems/bin/lib/sync.cjs +75 -0
  58. package/deliver-great-systems/bin/lib/verify.cjs +80 -1
  59. package/deliver-great-systems/bin/lib/worktrees.cjs +764 -0
  60. package/deliver-great-systems/bin/lib/worktrees.test.cjs +887 -0
  61. package/deliver-great-systems/templates/claude-md.md +16 -0
  62. package/deliver-great-systems/workflows/abandon-quick.md +89 -0
  63. package/deliver-great-systems/workflows/add-idea.md +3 -3
  64. package/deliver-great-systems/workflows/add-tests.md +14 -0
  65. package/deliver-great-systems/workflows/add-todo.md +1 -0
  66. package/deliver-great-systems/workflows/approve-spec.md +25 -4
  67. package/deliver-great-systems/workflows/audit-phase.md +15 -5
  68. package/deliver-great-systems/workflows/cancel-job.md +1 -1
  69. package/deliver-great-systems/workflows/check-todos.md +2 -3
  70. package/deliver-great-systems/workflows/complete-milestone.md +197 -22
  71. package/deliver-great-systems/workflows/complete-quick.md +68 -0
  72. package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
  73. package/deliver-great-systems/workflows/create-milestone-job.md +4 -4
  74. package/deliver-great-systems/workflows/develop-idea.md +11 -11
  75. package/deliver-great-systems/workflows/diagnose-issues.md +14 -0
  76. package/deliver-great-systems/workflows/discuss-idea.md +1 -1
  77. package/deliver-great-systems/workflows/execute-phase.md +121 -32
  78. package/deliver-great-systems/workflows/execute-plan.md +12 -21
  79. package/deliver-great-systems/workflows/help.md +33 -29
  80. package/deliver-great-systems/workflows/init-product.md +2 -18
  81. package/deliver-great-systems/workflows/new-milestone.md +40 -24
  82. package/deliver-great-systems/workflows/new-project.md +22 -680
  83. package/deliver-great-systems/workflows/progress-all.md +133 -0
  84. package/deliver-great-systems/workflows/quick-abandon.md +89 -0
  85. package/deliver-great-systems/workflows/quick-complete.md +68 -0
  86. package/deliver-great-systems/workflows/quick.md +152 -23
  87. package/deliver-great-systems/workflows/refine-spec.md +1 -1
  88. package/deliver-great-systems/workflows/research-idea.md +8 -8
  89. package/deliver-great-systems/workflows/resume-project.md +2 -2
  90. package/deliver-great-systems/workflows/run-job.md +8 -8
  91. package/deliver-great-systems/workflows/validate-phase.md +39 -1
  92. package/deliver-great-systems/workflows/verify-work.md +14 -0
  93. package/deliver-great-systems/workflows/write-spec.md +2 -2
  94. package/package.json +1 -1
@@ -1148,7 +1148,7 @@ describe('v2 guard: multiple projects (prompt)', () => {
1148
1148
  describe('branch_name with {project} resolution', () => {
1149
1149
  it('v1 execute-phase branch_name resolves {project} from current_project', () => {
1150
1150
  const fixture = createFixture({
1151
- 'config.json': JSON.stringify({ current_project: 'myapp', git: { branching_strategy: 'phase' } }),
1151
+ 'config.json': JSON.stringify({ current_project: 'myapp' }),
1152
1152
  'STATE.md': '# State',
1153
1153
  'ROADMAP.md': '# Roadmap\n\n## Phases\n\n- [ ] **Phase 3: Auth** - Auth\n',
1154
1154
  'REQUIREMENTS.md': '# Requirements',
@@ -1157,7 +1157,7 @@ describe('branch_name with {project} resolution', () => {
1157
1157
  });
1158
1158
  try {
1159
1159
  const result = runInit(fixture.cwd, 'execute-phase 3');
1160
- assert.equal(result.branch_name, 'dgs/myapp/phase-03-auth');
1160
+ assert.equal(result.branch_name, 'dgs/myapp/v1.0-milestone');
1161
1161
  } finally {
1162
1162
  fixture.cleanup();
1163
1163
  }
@@ -1165,7 +1165,7 @@ describe('branch_name with {project} resolution', () => {
1165
1165
 
1166
1166
  it('v2 execute-phase branch_name resolves {project} from current_project', () => {
1167
1167
  const fixture = createFixture({
1168
- 'config.json': JSON.stringify({ current_project: 'checkout', git: { branching_strategy: 'phase' } }),
1168
+ 'config.json': JSON.stringify({ current_project: 'checkout' }),
1169
1169
  'PROJECTS.md': '# Projects\n\n| Project | Status |\n|---------|--------|\n| checkout | Active |\n',
1170
1170
  'REPOS.md': '# Repos\n\n| Name | Path |\n|------|------|\n',
1171
1171
  'projects/checkout/STATE.md': '# State',
@@ -1176,13 +1176,13 @@ describe('branch_name with {project} resolution', () => {
1176
1176
  });
1177
1177
  try {
1178
1178
  const result = runInit(fixture.cwd, 'execute-phase 1');
1179
- assert.equal(result.branch_name, 'dgs/checkout/phase-01-init');
1179
+ assert.equal(result.branch_name, 'dgs/checkout/v1.0-milestone');
1180
1180
  } finally {
1181
1181
  fixture.cleanup();
1182
1182
  }
1183
1183
  });
1184
1184
 
1185
- it('branch_name with custom template without {project} is unchanged', () => {
1185
+ it('branch_name always uses milestone template regardless of config', () => {
1186
1186
  const fixture = createFixture({
1187
1187
  'config.json': JSON.stringify({
1188
1188
  current_project: 'myapp',
@@ -1196,7 +1196,7 @@ describe('branch_name with {project} resolution', () => {
1196
1196
  });
1197
1197
  try {
1198
1198
  const result = runInit(fixture.cwd, 'execute-phase 3');
1199
- assert.equal(result.branch_name, 'feature/phase-03-auth');
1199
+ assert.equal(result.branch_name, 'dgs/myapp/v1.0-milestone');
1200
1200
  } finally {
1201
1201
  fixture.cleanup();
1202
1202
  }
@@ -1521,3 +1521,413 @@ describe('archived milestone phase rejection', () => {
1521
1521
  assert.ok(result.phase_dir);
1522
1522
  });
1523
1523
  });
1524
+
1525
+ // ─── cmdInitQuick quick_dir resolution ──────────────────────────────────────
1526
+
1527
+ describe('cmdInitQuick quick_dir resolution', () => {
1528
+ describe('product mode (no active milestone)', () => {
1529
+ let fixture;
1530
+
1531
+ beforeEach(() => {
1532
+ fixture = v2FixtureWithProject();
1533
+ });
1534
+
1535
+ afterEach(() => {
1536
+ fixture.cleanup();
1537
+ });
1538
+
1539
+ it('init quick uses product-root quick_dir in product mode (no active milestone)', () => {
1540
+ const result = runInit(fixture.cwd, 'quick "test task"');
1541
+ assert.equal(result.quick_dir, 'quick');
1542
+ });
1543
+
1544
+ it('task_dir follows quick_dir in product mode', () => {
1545
+ const result = runInit(fixture.cwd, 'quick "test task"');
1546
+ assert.ok(result.task_dir, 'task_dir should be set');
1547
+ assert.ok(result.task_dir.startsWith('quick/'), 'task_dir should start with quick/');
1548
+ });
1549
+
1550
+ it('state_path resolves to product-root in product mode', () => {
1551
+ const result = runInit(fixture.cwd, 'quick "test task"');
1552
+ assert.equal(result.state_path, 'STATE.md');
1553
+ });
1554
+
1555
+ it('roadmap_exists remains project-scoped in product mode', () => {
1556
+ const result = runInit(fixture.cwd, 'quick "test task"');
1557
+ assert.equal(result.roadmap_exists, true);
1558
+ });
1559
+
1560
+ it('creates planning-root STATE.md on demand when missing', () => {
1561
+ // Remove the planning-root STATE.md if present (fixture has a project STATE.md but not a product one)
1562
+ const productStatePath = path.join(fixture.cwd, 'STATE.md');
1563
+ if (fs.existsSync(productStatePath)) {
1564
+ fs.unlinkSync(productStatePath);
1565
+ }
1566
+ assert.equal(fs.existsSync(productStatePath), false, 'precondition: product STATE.md absent');
1567
+
1568
+ runInit(fixture.cwd, 'quick "test task"');
1569
+
1570
+ assert.equal(fs.existsSync(productStatePath), true, 'product STATE.md should be auto-created');
1571
+ const contents = fs.readFileSync(productStatePath, 'utf-8');
1572
+ assert.match(contents, /### Blockers\/Concerns/, 'skeleton should contain Blockers/Concerns anchor');
1573
+ });
1574
+ });
1575
+
1576
+ describe('milestone-context mode (active milestone)', () => {
1577
+ let fixture;
1578
+
1579
+ beforeEach(() => {
1580
+ fixture = createFixture({
1581
+ 'config.json': JSON.stringify({ current_project: 'test-project' }),
1582
+ 'config.local.json': JSON.stringify({
1583
+ execution: { active_context: 'v19-git-worktrees' },
1584
+ projects: {
1585
+ 'test-project': {
1586
+ worktrees: {
1587
+ 'v19-git-worktrees': { type: 'milestone' }
1588
+ }
1589
+ }
1590
+ }
1591
+ }),
1592
+ 'PROJECTS.md': '# Projects\n\n| Project | Status |\n|---------|--------|\n| test-project | Active |\n',
1593
+ 'REPOS.md': '# Repos\n\n| Name | Path |\n|------|------|\n',
1594
+ 'projects/test-project/STATE.md': '# State',
1595
+ 'projects/test-project/ROADMAP.md': '# Roadmap\n\n## Phases\n\n- [ ] **Phase 1: Test Phase** - A test\n',
1596
+ 'projects/test-project/REQUIREMENTS.md': '# Requirements',
1597
+ 'projects/test-project/PROJECT.md': '# Project',
1598
+ 'projects/test-project/phases/01-test-phase/01-CONTEXT.md': '# Context',
1599
+ 'projects/test-project/phases/01-test-phase/01-01-PLAN.md': '---\nphase: 01-test-phase\nplan: 01\n---\n# Plan',
1600
+ });
1601
+ });
1602
+
1603
+ afterEach(() => {
1604
+ fixture.cleanup();
1605
+ });
1606
+
1607
+ it('init quick uses project quick_dir when in milestone context', () => {
1608
+ const result = runInit(fixture.cwd, 'quick "test task"');
1609
+ assert.equal(result.quick_dir, 'projects/test-project/quick');
1610
+ });
1611
+
1612
+ it('state_path remains project-scoped in milestone-context mode', () => {
1613
+ const result = runInit(fixture.cwd, 'quick "test task"');
1614
+ assert.equal(result.state_path, 'projects/test-project/STATE.md');
1615
+ });
1616
+ });
1617
+
1618
+ describe('--main flag override', () => {
1619
+ let fixture;
1620
+
1621
+ beforeEach(() => {
1622
+ fixture = createFixture({
1623
+ 'config.json': JSON.stringify({ current_project: 'test-project' }),
1624
+ 'config.local.json': JSON.stringify({
1625
+ execution: { active_context: 'v19-git-worktrees' },
1626
+ projects: {
1627
+ 'test-project': {
1628
+ worktrees: {
1629
+ 'v19-git-worktrees': { type: 'milestone' }
1630
+ }
1631
+ }
1632
+ }
1633
+ }),
1634
+ 'PROJECTS.md': '# Projects\n\n| Project | Status |\n|---------|--------|\n| test-project | Active |\n',
1635
+ 'REPOS.md': '# Repos\n\n| Name | Path |\n|------|------|\n',
1636
+ 'projects/test-project/STATE.md': '# State',
1637
+ 'projects/test-project/ROADMAP.md': '# Roadmap\n\n## Phases\n\n- [ ] **Phase 1: Test Phase** - A test\n',
1638
+ 'projects/test-project/REQUIREMENTS.md': '# Requirements',
1639
+ 'projects/test-project/PROJECT.md': '# Project',
1640
+ 'projects/test-project/phases/01-test-phase/01-CONTEXT.md': '# Context',
1641
+ 'projects/test-project/phases/01-test-phase/01-01-PLAN.md': '---\nphase: 01-test-phase\nplan: 01\n---\n# Plan',
1642
+ });
1643
+ });
1644
+
1645
+ afterEach(() => {
1646
+ fixture.cleanup();
1647
+ });
1648
+
1649
+ it('init quick --main forces product-level quick_dir even with active milestone', () => {
1650
+ const result = runInit(fixture.cwd, 'quick --main "test task"');
1651
+ assert.equal(result.quick_dir, 'quick');
1652
+ });
1653
+
1654
+ it('init quick --main forces product-level state_path even with active milestone', () => {
1655
+ const result = runInit(fixture.cwd, 'quick --main "test task"');
1656
+ assert.equal(result.state_path, 'STATE.md');
1657
+ });
1658
+
1659
+ it('init quick --main strips flag from description', () => {
1660
+ const result = runInit(fixture.cwd, 'quick --main "test task"');
1661
+ assert.equal(result.description, 'test task');
1662
+ });
1663
+ });
1664
+ });
1665
+
1666
+ // ─── init progress-all ───────────────────────────────────────────────────────
1667
+
1668
+ describe('init progress-all: v2 fixture with 1 active project', () => {
1669
+ let fixture;
1670
+ let result;
1671
+
1672
+ beforeEach(() => {
1673
+ fixture = createFixture({
1674
+ 'config.json': JSON.stringify({ current_project: 'alpha' }),
1675
+ 'PROJECTS.md': '# Projects\n\n| Project | Status |\n|---------|--------|\n| alpha | Active |\n',
1676
+ 'REPOS.md': '# Repos\n\n| Name | Path |\n|------|------|\n',
1677
+ 'projects/alpha/STATE.md': '# Project State\n\nPhase: 3\nStatus: In progress\nProgress: [####------] 40%\n',
1678
+ 'projects/alpha/ROADMAP.md': '# Roadmap\n\n- 🚧 **v2.0 Auth** — Phases 1-5 (in progress)\n',
1679
+ 'projects/alpha/PROJECT.md': '# Project',
1680
+ });
1681
+ result = runInit(fixture.cwd, 'progress-all --raw');
1682
+ });
1683
+
1684
+ afterEach(() => {
1685
+ fixture.cleanup();
1686
+ });
1687
+
1688
+ it('returns valid JSON with product and projects keys', () => {
1689
+ assert.ok(result.product, 'result.product must exist');
1690
+ assert.ok(Array.isArray(result.projects), 'result.projects must be an array');
1691
+ });
1692
+
1693
+ it('product has active_count, completed_count, and backlog', () => {
1694
+ assert.equal(typeof result.product.active_count, 'number');
1695
+ assert.equal(typeof result.product.completed_count, 'number');
1696
+ assert.ok(result.product.backlog, 'product.backlog must exist');
1697
+ });
1698
+
1699
+ it('product.backlog has all four int fields (todos, ideas, specs, debug_active)', () => {
1700
+ assert.equal(typeof result.product.backlog.todos, 'number');
1701
+ assert.equal(typeof result.product.backlog.ideas, 'number');
1702
+ assert.equal(typeof result.product.backlog.specs, 'number');
1703
+ assert.equal(typeof result.product.backlog.debug_active, 'number');
1704
+ });
1705
+
1706
+ it('product.backlog counts are all zero on empty fixture', () => {
1707
+ assert.equal(result.product.backlog.todos, 0);
1708
+ assert.equal(result.product.backlog.ideas, 0);
1709
+ assert.equal(result.product.backlog.specs, 0);
1710
+ assert.equal(result.product.backlog.debug_active, 0);
1711
+ });
1712
+
1713
+ it('product.quick_tasks_recent is [] when product-level STATE.md has no Quick Tasks table', () => {
1714
+ assert.deepEqual(result.product.quick_tasks_recent, []);
1715
+ });
1716
+
1717
+ it('product.shipped_milestones_recent is [] when product-level MILESTONES.md is missing', () => {
1718
+ assert.deepEqual(result.product.shipped_milestones_recent, []);
1719
+ });
1720
+
1721
+ it('projects array has 1 active project', () => {
1722
+ assert.equal(result.projects.length, 1);
1723
+ assert.equal(result.projects[0].name, 'alpha');
1724
+ });
1725
+
1726
+ it('project entry has expected fields', () => {
1727
+ const p = result.projects[0];
1728
+ assert.equal(typeof p.name, 'string');
1729
+ assert.equal(typeof p.status, 'string');
1730
+ assert.equal(typeof p.current_phase, 'string');
1731
+ assert.equal(typeof p.progress, 'number');
1732
+ assert.equal(typeof p.repos_touched, 'string');
1733
+ assert.ok('milestone_version' in p);
1734
+ assert.ok('milestone_name' in p);
1735
+ });
1736
+
1737
+ it('project milestone_version is parsed from ROADMAP.md', () => {
1738
+ assert.equal(result.projects[0].milestone_version, 'v2.0');
1739
+ assert.equal(result.projects[0].milestone_name, 'Auth');
1740
+ });
1741
+
1742
+ it('returns top-level auth, dgs_mode, planner_model, executor_model', () => {
1743
+ assert.ok('author' in result);
1744
+ assert.equal(typeof result.dgs_mode, 'string');
1745
+ assert.ok('planner_model' in result);
1746
+ assert.ok('executor_model' in result);
1747
+ });
1748
+ });
1749
+
1750
+ describe('init progress-all: excludes completed projects', () => {
1751
+ let fixture;
1752
+ let result;
1753
+
1754
+ beforeEach(() => {
1755
+ fixture = createFixture({
1756
+ 'config.json': JSON.stringify({}),
1757
+ 'PROJECTS.md': '# Projects\n',
1758
+ 'REPOS.md': '# Repos\n',
1759
+ 'projects/active-x/STATE.md': '# Project State\n\nPhase: 1\nStatus: In progress\nProgress: [#---------] 10%\n',
1760
+ 'projects/done-y/STATE.md': '# Project State\n\nPhase: 5\nStatus: completed\nProgress: [##########] 100%\nCompleted: 2026-01-20\n',
1761
+ });
1762
+ result = runInit(fixture.cwd, 'progress-all --raw');
1763
+ });
1764
+
1765
+ afterEach(() => {
1766
+ fixture.cleanup();
1767
+ });
1768
+
1769
+ it('projects array excludes completed projects', () => {
1770
+ assert.equal(result.projects.length, 1);
1771
+ assert.equal(result.projects[0].name, 'active-x');
1772
+ });
1773
+
1774
+ it('product.active_count and completed_count reflect the split', () => {
1775
+ assert.equal(result.product.active_count, 1);
1776
+ assert.equal(result.product.completed_count, 1);
1777
+ });
1778
+ });
1779
+
1780
+ describe('init progress-all: empty projects dir', () => {
1781
+ let fixture;
1782
+ let result;
1783
+
1784
+ beforeEach(() => {
1785
+ fixture = createFixture({
1786
+ 'config.json': JSON.stringify({}),
1787
+ 'PROJECTS.md': '# Projects\n',
1788
+ 'REPOS.md': '# Repos\n',
1789
+ });
1790
+ result = runInit(fixture.cwd, 'progress-all --raw');
1791
+ });
1792
+
1793
+ afterEach(() => {
1794
+ fixture.cleanup();
1795
+ });
1796
+
1797
+ it('projects is [] and active_count is 0', () => {
1798
+ assert.deepEqual(result.projects, []);
1799
+ assert.equal(result.product.active_count, 0);
1800
+ });
1801
+ });
1802
+
1803
+ describe('init progress-all: parses product-level STATE.md quick tasks', () => {
1804
+ let fixture;
1805
+ let result;
1806
+
1807
+ beforeEach(() => {
1808
+ const productState = `# Project State
1809
+
1810
+ ## Accumulated Context
1811
+
1812
+ ### Quick Tasks Completed
1813
+
1814
+ | # | Description | Date | Commit | Status | Directory |
1815
+ |---|-------------|------|--------|--------|-----------|
1816
+ | 260405-laf | Route product quick tasks | 2026-04-05 | 368074f | Verified | [260405-laf](./quick/260405-laf/) |
1817
+ | 260405-lvk | Strip trailing hyphen | 2026-04-05 | 536382a | | fast |
1818
+ | 260405-mbu | Make fast context-aware | 2026-04-05 | d79ffbc | Verified | [260405-mbu](./quick/260405-mbu/) |
1819
+ `;
1820
+ fixture = createFixture({
1821
+ 'config.json': JSON.stringify({}),
1822
+ 'PROJECTS.md': '# Projects\n',
1823
+ 'REPOS.md': '# Repos\n',
1824
+ 'STATE.md': productState,
1825
+ 'projects/alpha/STATE.md': '# Project State\n\nPhase: 1\nStatus: In progress\n',
1826
+ });
1827
+ result = runInit(fixture.cwd, 'progress-all --raw');
1828
+ });
1829
+
1830
+ afterEach(() => {
1831
+ fixture.cleanup();
1832
+ });
1833
+
1834
+ it('parses quick_tasks_recent with 3 entries', () => {
1835
+ assert.equal(result.product.quick_tasks_recent.length, 3);
1836
+ });
1837
+
1838
+ it('quick task entries have id, description, date, commit fields', () => {
1839
+ const q = result.product.quick_tasks_recent[0];
1840
+ assert.ok(q.id);
1841
+ assert.ok(q.description);
1842
+ assert.ok(q.date);
1843
+ assert.ok(q.commit);
1844
+ });
1845
+ });
1846
+
1847
+ describe('init progress-all: parses product-level MILESTONES.md shipped milestones', () => {
1848
+ let fixture;
1849
+ let result;
1850
+
1851
+ beforeEach(() => {
1852
+ const milestones = `# Milestones
1853
+
1854
+ ## v19.0 Git Worktrees (Shipped: 2026-03-27)
1855
+
1856
+ Some content.
1857
+
1858
+ ## v18.0 Root Layout (Shipped: 2026-03-25)
1859
+
1860
+ Some content.
1861
+
1862
+ ## v1.0 Multi-Project Platform (Shipped: 2026-02-24)
1863
+
1864
+ Some content.
1865
+ `;
1866
+ fixture = createFixture({
1867
+ 'config.json': JSON.stringify({}),
1868
+ 'PROJECTS.md': '# Projects\n',
1869
+ 'REPOS.md': '# Repos\n',
1870
+ 'MILESTONES.md': milestones,
1871
+ 'projects/alpha/STATE.md': '# Project State\n\nPhase: 1\nStatus: In progress\n',
1872
+ });
1873
+ result = runInit(fixture.cwd, 'progress-all --raw');
1874
+ });
1875
+
1876
+ afterEach(() => {
1877
+ fixture.cleanup();
1878
+ });
1879
+
1880
+ it('parses shipped_milestones_recent with 3 entries', () => {
1881
+ assert.equal(result.product.shipped_milestones_recent.length, 3);
1882
+ });
1883
+
1884
+ it('shipped milestone entries have version, name, shipped_date fields', () => {
1885
+ const m = result.product.shipped_milestones_recent[0];
1886
+ assert.equal(m.version, 'v19.0');
1887
+ assert.equal(m.name, 'Git Worktrees');
1888
+ assert.equal(m.shipped_date, '2026-03-27');
1889
+ });
1890
+ });
1891
+
1892
+ describe('init progress-all: counts backlog files', () => {
1893
+ let fixture;
1894
+ let result;
1895
+
1896
+ beforeEach(() => {
1897
+ fixture = createFixture({
1898
+ 'config.json': JSON.stringify({}),
1899
+ 'PROJECTS.md': '# Projects\n',
1900
+ 'REPOS.md': '# Repos\n',
1901
+ 'todos/pending/todo-1.md': '# todo 1',
1902
+ 'todos/pending/todo-2.md': '# todo 2',
1903
+ 'ideas/pending/idea-1.md': '# idea 1',
1904
+ 'specs/spec-1.md': '# spec 1',
1905
+ 'specs/spec-2.md': '# spec 2',
1906
+ 'specs/spec-3.md': '# spec 3',
1907
+ 'debug/active-session.md': '# debug',
1908
+ 'debug/session-resolved.md': '# resolved',
1909
+ 'projects/alpha/STATE.md': '# Project State\n\nPhase: 1\nStatus: In progress\n',
1910
+ });
1911
+ result = runInit(fixture.cwd, 'progress-all --raw');
1912
+ });
1913
+
1914
+ afterEach(() => {
1915
+ fixture.cleanup();
1916
+ });
1917
+
1918
+ it('counts todos in todos/pending/', () => {
1919
+ assert.equal(result.product.backlog.todos, 2);
1920
+ });
1921
+
1922
+ it('counts ideas in ideas/pending/', () => {
1923
+ assert.equal(result.product.backlog.ideas, 1);
1924
+ });
1925
+
1926
+ it('counts specs in specs/ (top-level only)', () => {
1927
+ assert.equal(result.product.backlog.specs, 3);
1928
+ });
1929
+
1930
+ it('counts debug files excluding those with "resolved" in name', () => {
1931
+ assert.equal(result.product.backlog.debug_active, 1);
1932
+ });
1933
+ });