@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.
- package/CHANGELOG.md +96 -0
- package/README.md +41 -13
- package/agents/dgs-plan-checker.md +29 -3
- package/agents/dgs-planner.md +10 -0
- package/commands/dgs/abandon-quick.md +28 -0
- package/commands/dgs/add-tests.md +2 -2
- package/commands/dgs/audit-milestone.md +2 -2
- package/commands/dgs/capture-principle.md +11 -11
- package/commands/dgs/cleanup.md +2 -2
- package/commands/dgs/complete-milestone.md +11 -11
- package/commands/dgs/complete-quick.md +28 -0
- package/commands/dgs/create-milestone-job.md +2 -2
- package/commands/dgs/debug.md +3 -3
- package/commands/dgs/develop-idea.md +1 -1
- package/commands/dgs/fast.md +3 -1
- package/commands/dgs/health.md +1 -1
- package/commands/dgs/map-codebase.md +6 -6
- package/commands/dgs/new-milestone.md +5 -5
- package/commands/dgs/new-project.md +6 -6
- package/commands/dgs/plan-milestone-gaps.md +1 -1
- package/commands/dgs/progress.md +3 -3
- package/commands/dgs/quick-abandon.md +8 -0
- package/commands/dgs/quick-complete.md +8 -0
- package/commands/dgs/quick.md +10 -3
- package/commands/dgs/research-idea.md +2 -2
- package/commands/dgs/research-phase.md +3 -3
- package/commands/dgs/switch-project.md +1 -1
- package/commands/dgs/write-spec.md +3 -3
- package/deliver-great-systems/bin/dgs-tools.cjs +284 -30
- package/deliver-great-systems/bin/lib/commands.cjs +316 -31
- package/deliver-great-systems/bin/lib/commands.test.cjs +336 -0
- package/deliver-great-systems/bin/lib/config.cjs +39 -6
- package/deliver-great-systems/bin/lib/context.cjs +120 -0
- package/deliver-great-systems/bin/lib/core.cjs +28 -11
- package/deliver-great-systems/bin/lib/execution.cjs +49 -17
- package/deliver-great-systems/bin/lib/flat-migration.test.cjs +396 -0
- package/deliver-great-systems/bin/lib/ideas.cjs +206 -91
- package/deliver-great-systems/bin/lib/ideas.test.cjs +244 -1
- package/deliver-great-systems/bin/lib/init.cjs +306 -39
- package/deliver-great-systems/bin/lib/init.test.cjs +416 -6
- package/deliver-great-systems/bin/lib/jobs.cjs +124 -21
- package/deliver-great-systems/bin/lib/jobs.test.cjs +193 -74
- package/deliver-great-systems/bin/lib/migration.cjs +409 -1
- package/deliver-great-systems/bin/lib/migration.test.cjs +158 -1
- package/deliver-great-systems/bin/lib/milestone.cjs +54 -29
- package/deliver-great-systems/bin/lib/phase.cjs +128 -2
- package/deliver-great-systems/bin/lib/phase.test.cjs +420 -0
- package/deliver-great-systems/bin/lib/projects.cjs +28 -8
- package/deliver-great-systems/bin/lib/projects.test.cjs +86 -0
- package/deliver-great-systems/bin/lib/quick.cjs +584 -0
- package/deliver-great-systems/bin/lib/quick.test.cjs +596 -0
- package/deliver-great-systems/bin/lib/repos.cjs +25 -1
- package/deliver-great-systems/bin/lib/roadmap.cjs +34 -13
- package/deliver-great-systems/bin/lib/specs.cjs +3 -81
- package/deliver-great-systems/bin/lib/state-transition-gate.test.cjs +160 -0
- package/deliver-great-systems/bin/lib/state.cjs +142 -54
- package/deliver-great-systems/bin/lib/sync.cjs +75 -0
- package/deliver-great-systems/bin/lib/verify.cjs +80 -1
- package/deliver-great-systems/bin/lib/worktrees.cjs +764 -0
- package/deliver-great-systems/bin/lib/worktrees.test.cjs +887 -0
- package/deliver-great-systems/templates/claude-md.md +16 -0
- package/deliver-great-systems/workflows/abandon-quick.md +89 -0
- package/deliver-great-systems/workflows/add-idea.md +3 -3
- package/deliver-great-systems/workflows/add-tests.md +14 -0
- package/deliver-great-systems/workflows/add-todo.md +1 -0
- package/deliver-great-systems/workflows/approve-spec.md +25 -4
- package/deliver-great-systems/workflows/audit-phase.md +15 -5
- package/deliver-great-systems/workflows/cancel-job.md +1 -1
- package/deliver-great-systems/workflows/check-todos.md +2 -3
- package/deliver-great-systems/workflows/complete-milestone.md +197 -22
- package/deliver-great-systems/workflows/complete-quick.md +68 -0
- package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
- package/deliver-great-systems/workflows/create-milestone-job.md +4 -4
- package/deliver-great-systems/workflows/develop-idea.md +11 -11
- package/deliver-great-systems/workflows/diagnose-issues.md +14 -0
- package/deliver-great-systems/workflows/discuss-idea.md +1 -1
- package/deliver-great-systems/workflows/execute-phase.md +121 -32
- package/deliver-great-systems/workflows/execute-plan.md +12 -21
- package/deliver-great-systems/workflows/help.md +33 -29
- package/deliver-great-systems/workflows/init-product.md +2 -18
- package/deliver-great-systems/workflows/new-milestone.md +40 -24
- package/deliver-great-systems/workflows/new-project.md +22 -680
- package/deliver-great-systems/workflows/progress-all.md +133 -0
- package/deliver-great-systems/workflows/quick-abandon.md +89 -0
- package/deliver-great-systems/workflows/quick-complete.md +68 -0
- package/deliver-great-systems/workflows/quick.md +152 -23
- package/deliver-great-systems/workflows/refine-spec.md +1 -1
- package/deliver-great-systems/workflows/research-idea.md +8 -8
- package/deliver-great-systems/workflows/resume-project.md +2 -2
- package/deliver-great-systems/workflows/run-job.md +8 -8
- package/deliver-great-systems/workflows/validate-phase.md +39 -1
- package/deliver-great-systems/workflows/verify-work.md +14 -0
- package/deliver-great-systems/workflows/write-spec.md +2 -2
- package/package.json +1 -1
|
@@ -663,85 +663,94 @@ describe('jobs', () => {
|
|
|
663
663
|
|
|
664
664
|
describe('generateMilestoneSteps', () => {
|
|
665
665
|
|
|
666
|
-
it('produces
|
|
666
|
+
it('produces 5 steps for unplanned phase (no_directory)', () => {
|
|
667
667
|
const phases = [
|
|
668
668
|
{ number: '50', name: 'Job Creation', disk_status: 'no_directory', roadmap_complete: false },
|
|
669
669
|
];
|
|
670
670
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
671
671
|
|
|
672
|
-
assert.equal(steps.length,
|
|
672
|
+
assert.equal(steps.length, 5);
|
|
673
673
|
assert.equal(steps[0].command, 'map-codebase');
|
|
674
674
|
assert.equal(steps[0].args, '50 --auto');
|
|
675
675
|
assert.equal(steps[1].command, 'plan-phase');
|
|
676
676
|
assert.equal(steps[1].args, '50 --non-interactive');
|
|
677
677
|
assert.equal(steps[2].command, 'execute-phase');
|
|
678
678
|
assert.equal(steps[2].args, '50 --non-interactive');
|
|
679
|
-
assert.equal(steps[3].command, '
|
|
680
|
-
assert.equal(steps[3].args, '50');
|
|
679
|
+
assert.equal(steps[3].command, 'validate-phase');
|
|
680
|
+
assert.equal(steps[3].args, '50 --scaffold');
|
|
681
|
+
assert.equal(steps[4].command, 'audit-phase');
|
|
682
|
+
assert.equal(steps[4].args, '50');
|
|
681
683
|
});
|
|
682
684
|
|
|
683
|
-
it('produces
|
|
685
|
+
it('produces 5 steps for unplanned phase (empty)', () => {
|
|
684
686
|
const phases = [
|
|
685
687
|
{ number: '51', name: 'Job Execution', disk_status: 'empty', roadmap_complete: false },
|
|
686
688
|
];
|
|
687
689
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
688
690
|
|
|
689
|
-
assert.equal(steps.length,
|
|
691
|
+
assert.equal(steps.length, 5);
|
|
690
692
|
assert.equal(steps[0].command, 'map-codebase');
|
|
691
693
|
assert.equal(steps[1].command, 'plan-phase');
|
|
692
694
|
assert.equal(steps[2].command, 'execute-phase');
|
|
693
|
-
assert.equal(steps[3].command, '
|
|
695
|
+
assert.equal(steps[3].command, 'validate-phase');
|
|
696
|
+
assert.equal(steps[4].command, 'audit-phase');
|
|
694
697
|
});
|
|
695
698
|
|
|
696
|
-
it('produces
|
|
699
|
+
it('produces 5 steps for discussed phase (context but no plan)', () => {
|
|
697
700
|
const phases = [
|
|
698
701
|
{ number: '52', name: 'Silent Mode', disk_status: 'discussed', roadmap_complete: false },
|
|
699
702
|
];
|
|
700
703
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
701
704
|
|
|
702
|
-
assert.equal(steps.length,
|
|
705
|
+
assert.equal(steps.length, 5);
|
|
703
706
|
assert.equal(steps[0].command, 'map-codebase');
|
|
704
707
|
assert.equal(steps[1].command, 'plan-phase');
|
|
705
708
|
assert.equal(steps[2].command, 'execute-phase');
|
|
706
|
-
assert.equal(steps[3].command, '
|
|
709
|
+
assert.equal(steps[3].command, 'validate-phase');
|
|
710
|
+
assert.equal(steps[4].command, 'audit-phase');
|
|
707
711
|
});
|
|
708
712
|
|
|
709
|
-
it('produces
|
|
713
|
+
it('produces 5 steps for researched phase', () => {
|
|
710
714
|
const phases = [
|
|
711
715
|
{ number: '53', name: 'Audit Integration', disk_status: 'researched', roadmap_complete: false },
|
|
712
716
|
];
|
|
713
717
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
714
718
|
|
|
715
|
-
assert.equal(steps.length,
|
|
719
|
+
assert.equal(steps.length, 5);
|
|
716
720
|
assert.equal(steps[0].command, 'map-codebase');
|
|
717
721
|
assert.equal(steps[1].command, 'plan-phase');
|
|
718
722
|
assert.equal(steps[2].command, 'execute-phase');
|
|
719
|
-
assert.equal(steps[3].command, '
|
|
723
|
+
assert.equal(steps[3].command, 'validate-phase');
|
|
724
|
+
assert.equal(steps[4].command, 'audit-phase');
|
|
720
725
|
});
|
|
721
726
|
|
|
722
|
-
it('produces
|
|
727
|
+
it('produces 3 steps for planned phase', () => {
|
|
723
728
|
const phases = [
|
|
724
729
|
{ number: '50', name: 'Job Creation', disk_status: 'planned', roadmap_complete: false },
|
|
725
730
|
];
|
|
726
731
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
727
732
|
|
|
728
|
-
assert.equal(steps.length,
|
|
733
|
+
assert.equal(steps.length, 3);
|
|
729
734
|
assert.equal(steps[0].command, 'execute-phase');
|
|
730
735
|
assert.equal(steps[0].args, '50 --non-interactive');
|
|
731
|
-
assert.equal(steps[1].command, '
|
|
732
|
-
assert.equal(steps[1].args, '50');
|
|
736
|
+
assert.equal(steps[1].command, 'validate-phase');
|
|
737
|
+
assert.equal(steps[1].args, '50 --scaffold');
|
|
738
|
+
assert.equal(steps[2].command, 'audit-phase');
|
|
739
|
+
assert.equal(steps[2].args, '50');
|
|
733
740
|
});
|
|
734
741
|
|
|
735
|
-
it('produces
|
|
742
|
+
it('produces 3 steps for partially executed phase', () => {
|
|
736
743
|
const phases = [
|
|
737
744
|
{ number: '50', name: 'Job Creation', disk_status: 'partial', roadmap_complete: false },
|
|
738
745
|
];
|
|
739
746
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
740
747
|
|
|
741
|
-
assert.equal(steps.length,
|
|
748
|
+
assert.equal(steps.length, 3);
|
|
742
749
|
assert.equal(steps[0].command, 'execute-phase');
|
|
743
|
-
assert.equal(steps[1].command, '
|
|
744
|
-
assert.equal(steps[1].args, '50');
|
|
750
|
+
assert.equal(steps[1].command, 'validate-phase');
|
|
751
|
+
assert.equal(steps[1].args, '50 --scaffold');
|
|
752
|
+
assert.equal(steps[2].command, 'audit-phase');
|
|
753
|
+
assert.equal(steps[2].args, '50');
|
|
745
754
|
});
|
|
746
755
|
|
|
747
756
|
it('produces 0 steps for completed phase (disk_status complete)', () => {
|
|
@@ -771,34 +780,38 @@ describe('jobs', () => {
|
|
|
771
780
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
772
781
|
|
|
773
782
|
// Phase 49: 0 steps (complete)
|
|
774
|
-
// Phase 50:
|
|
775
|
-
// Phase 51:
|
|
776
|
-
assert.equal(steps.length,
|
|
783
|
+
// Phase 50: 3 steps (planned -> execute + validate + audit)
|
|
784
|
+
// Phase 51: 5 steps (unplanned -> map + plan + execute + validate + audit)
|
|
785
|
+
assert.equal(steps.length, 8);
|
|
777
786
|
assert.equal(steps[0].command, 'execute-phase');
|
|
778
787
|
assert.equal(steps[0].args, '50 --non-interactive');
|
|
779
|
-
assert.equal(steps[1].command, '
|
|
780
|
-
assert.equal(steps[1].args, '50');
|
|
781
|
-
assert.equal(steps[2].command, '
|
|
782
|
-
assert.equal(steps[2].args, '
|
|
783
|
-
assert.equal(steps[3].command, '
|
|
784
|
-
assert.equal(steps[3].args, '51 --
|
|
785
|
-
assert.equal(steps[4].command, '
|
|
788
|
+
assert.equal(steps[1].command, 'validate-phase');
|
|
789
|
+
assert.equal(steps[1].args, '50 --scaffold');
|
|
790
|
+
assert.equal(steps[2].command, 'audit-phase');
|
|
791
|
+
assert.equal(steps[2].args, '50');
|
|
792
|
+
assert.equal(steps[3].command, 'map-codebase');
|
|
793
|
+
assert.equal(steps[3].args, '51 --auto');
|
|
794
|
+
assert.equal(steps[4].command, 'plan-phase');
|
|
786
795
|
assert.equal(steps[4].args, '51 --non-interactive');
|
|
787
|
-
assert.equal(steps[5].command, '
|
|
788
|
-
assert.equal(steps[5].args, '51');
|
|
796
|
+
assert.equal(steps[5].command, 'execute-phase');
|
|
797
|
+
assert.equal(steps[5].args, '51 --non-interactive');
|
|
798
|
+
assert.equal(steps[6].command, 'validate-phase');
|
|
799
|
+
assert.equal(steps[6].args, '51 --scaffold');
|
|
800
|
+
assert.equal(steps[7].command, 'audit-phase');
|
|
801
|
+
assert.equal(steps[7].args, '51');
|
|
789
802
|
});
|
|
790
803
|
|
|
791
|
-
it('appends audit
|
|
804
|
+
it('appends audit step when check=true', () => {
|
|
792
805
|
const phases = [
|
|
793
806
|
{ number: '50', name: 'Job Creation', disk_status: 'planned', roadmap_complete: false },
|
|
794
807
|
];
|
|
795
808
|
const steps = generateMilestoneSteps(phases, { check: true, version: 'v6.0' });
|
|
796
809
|
|
|
797
|
-
//
|
|
810
|
+
// 3 phase steps + 1 audit step = 4
|
|
798
811
|
assert.equal(steps.length, 4);
|
|
799
|
-
assert.equal(steps[
|
|
800
|
-
assert.equal(steps[2].
|
|
801
|
-
assert.equal(steps[3].command, '
|
|
812
|
+
assert.equal(steps[1].command, 'validate-phase');
|
|
813
|
+
assert.equal(steps[2].command, 'audit-phase');
|
|
814
|
+
assert.equal(steps[3].command, 'audit-milestone');
|
|
802
815
|
assert.equal(steps[3].args, 'v6.0');
|
|
803
816
|
});
|
|
804
817
|
|
|
@@ -808,22 +821,21 @@ describe('jobs', () => {
|
|
|
808
821
|
];
|
|
809
822
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
810
823
|
|
|
811
|
-
assert.equal(steps.length,
|
|
812
|
-
// No audit/complete steps
|
|
824
|
+
assert.equal(steps.length, 3);
|
|
825
|
+
// No audit-milestone/complete-milestone steps
|
|
813
826
|
assert.ok(!steps.some(s => s.command === 'audit-milestone'));
|
|
814
827
|
assert.ok(!steps.some(s => s.command === 'complete-milestone'));
|
|
815
828
|
});
|
|
816
829
|
|
|
817
|
-
it('returns only audit
|
|
830
|
+
it('returns only audit when all phases complete and check=true', () => {
|
|
818
831
|
const phases = [
|
|
819
832
|
{ number: '49', name: 'Job File Format', disk_status: 'complete', roadmap_complete: true },
|
|
820
833
|
{ number: '50', name: 'Job Creation', disk_status: 'complete', roadmap_complete: true },
|
|
821
834
|
];
|
|
822
835
|
const steps = generateMilestoneSteps(phases, { check: true, version: 'v6.0' });
|
|
823
836
|
|
|
824
|
-
assert.equal(steps.length,
|
|
837
|
+
assert.equal(steps.length, 1);
|
|
825
838
|
assert.equal(steps[0].command, 'audit-milestone');
|
|
826
|
-
assert.equal(steps[1].command, 'complete-milestone');
|
|
827
839
|
});
|
|
828
840
|
|
|
829
841
|
it('returns empty array when all phases complete and check=false', () => {
|
|
@@ -844,15 +856,15 @@ describe('jobs', () => {
|
|
|
844
856
|
];
|
|
845
857
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
846
858
|
|
|
847
|
-
// Phase 50:
|
|
848
|
-
assert.equal(steps.length,
|
|
859
|
+
// Phase 50: 3 steps (execute + validate + audit), Phase 50.1: 5 steps (map + plan + execute + validate + audit), Phase 51: 3 steps (execute + validate + audit) = 11 total
|
|
860
|
+
assert.equal(steps.length, 11);
|
|
849
861
|
// First phase should be 50 (execute-phase)
|
|
850
862
|
assert.equal(steps[0].args, '50 --non-interactive');
|
|
851
|
-
// Then 50.1 (map-codebase at index
|
|
852
|
-
assert.equal(steps[
|
|
853
|
-
assert.equal(steps[
|
|
854
|
-
// Then 51 (execute-phase at index
|
|
855
|
-
assert.equal(steps[
|
|
863
|
+
// Then 50.1 (map-codebase at index 3)
|
|
864
|
+
assert.equal(steps[3].command, 'map-codebase');
|
|
865
|
+
assert.equal(steps[3].args, '50.1 --auto');
|
|
866
|
+
// Then 51 (execute-phase at index 8)
|
|
867
|
+
assert.equal(steps[8].args, '51 --non-interactive');
|
|
856
868
|
});
|
|
857
869
|
|
|
858
870
|
it('includes correct flags on plan-phase (--non-interactive), execute-phase (--non-interactive), map-codebase (--auto), and audit-phase (phase number only) steps', () => {
|
|
@@ -864,11 +876,14 @@ describe('jobs', () => {
|
|
|
864
876
|
const mapStep = steps.find(s => s.command === 'map-codebase');
|
|
865
877
|
const planStep = steps.find(s => s.command === 'plan-phase');
|
|
866
878
|
const executeStep = steps.find(s => s.command === 'execute-phase');
|
|
879
|
+
const validateStep = steps.find(s => s.command === 'validate-phase');
|
|
867
880
|
const auditStep = steps.find(s => s.command === 'audit-phase');
|
|
868
881
|
|
|
869
882
|
assert.ok(mapStep.args.includes('--auto'), 'map-codebase should have --auto');
|
|
870
883
|
assert.ok(planStep.args.includes('--non-interactive'), 'plan-phase should have --non-interactive');
|
|
871
884
|
assert.ok(executeStep.args.includes('--non-interactive'), 'execute-phase should have --non-interactive');
|
|
885
|
+
assert.ok(validateStep.args.includes('--scaffold'), 'validate-phase should have --scaffold');
|
|
886
|
+
assert.ok(!validateStep.args.includes('--non-interactive'), 'validate-phase should NOT have --non-interactive');
|
|
872
887
|
assert.strictEqual(auditStep.args, '50', 'audit-phase should have just the phase number');
|
|
873
888
|
});
|
|
874
889
|
|
|
@@ -880,12 +895,14 @@ describe('jobs', () => {
|
|
|
880
895
|
];
|
|
881
896
|
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
882
897
|
|
|
883
|
-
// Phase 49 complete (0 steps), Phase 50 planned (
|
|
884
|
-
assert.equal(steps.length,
|
|
898
|
+
// Phase 49 complete (0 steps), Phase 50 planned (3 steps), Phase 51 planned (3 steps) = 6
|
|
899
|
+
assert.equal(steps.length, 6);
|
|
885
900
|
assert.equal(steps[0].args, '50 --non-interactive');
|
|
886
|
-
assert.equal(steps[1].args, '50');
|
|
887
|
-
assert.equal(steps[2].args, '
|
|
888
|
-
assert.equal(steps[3].args, '51');
|
|
901
|
+
assert.equal(steps[1].args, '50 --scaffold');
|
|
902
|
+
assert.equal(steps[2].args, '50');
|
|
903
|
+
assert.equal(steps[3].args, '51 --non-interactive');
|
|
904
|
+
assert.equal(steps[4].args, '51 --scaffold');
|
|
905
|
+
assert.equal(steps[5].args, '51');
|
|
889
906
|
});
|
|
890
907
|
|
|
891
908
|
it('returns steps as objects with command and args properties', () => {
|
|
@@ -929,6 +946,21 @@ describe('jobs', () => {
|
|
|
929
946
|
}
|
|
930
947
|
});
|
|
931
948
|
|
|
949
|
+
it('validate-phase steps use --scaffold flag (not --non-interactive or --auto)', () => {
|
|
950
|
+
const phases = [
|
|
951
|
+
{ number: '50', name: 'Test', disk_status: 'no_directory', roadmap_complete: false },
|
|
952
|
+
{ number: '51', name: 'Test2', disk_status: 'planned', roadmap_complete: false },
|
|
953
|
+
];
|
|
954
|
+
const steps = generateMilestoneSteps(phases, { check: false, version: 'v6.0' });
|
|
955
|
+
const validateSteps = steps.filter(s => s.command === 'validate-phase');
|
|
956
|
+
assert.equal(validateSteps.length, 2, 'Should have 2 validate-phase steps');
|
|
957
|
+
for (const step of validateSteps) {
|
|
958
|
+
assert.ok(step.args.includes('--scaffold'), `validate-phase step for ${step.args} should have --scaffold`);
|
|
959
|
+
assert.ok(!step.args.includes('--auto'), 'validate-phase should NOT have --auto');
|
|
960
|
+
assert.ok(!step.args.includes('--non-interactive'), 'validate-phase should NOT have --non-interactive');
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
|
|
932
964
|
it('no map-codebase for NEEDS_EXECUTION phases', () => {
|
|
933
965
|
const phases = [
|
|
934
966
|
{ number: '50', name: 'Job Creation', disk_status: 'planned', roadmap_complete: false },
|
|
@@ -1085,7 +1117,7 @@ Plans:
|
|
|
1085
1117
|
assert.equal(result.created, true);
|
|
1086
1118
|
});
|
|
1087
1119
|
|
|
1088
|
-
it('writes job file to jobs/
|
|
1120
|
+
it('writes job file to flat jobs/ directory', () => {
|
|
1089
1121
|
fixture = createFixture({
|
|
1090
1122
|
'ROADMAP.md': FIXTURE_ROADMAP,
|
|
1091
1123
|
'phases/49-job-file-format/49-01-PLAN.md': '',
|
|
@@ -1097,14 +1129,14 @@ Plans:
|
|
|
1097
1129
|
});
|
|
1098
1130
|
|
|
1099
1131
|
const result = cmdJobsCreateMilestone(fixture.cwd, 'v6.0', true, false);
|
|
1100
|
-
assert.ok(result.file.includes('
|
|
1101
|
-
assert.ok(result.file.includes('
|
|
1132
|
+
assert.ok(result.file.includes('jobs/milestone-v6.0.md'));
|
|
1133
|
+
assert.ok(!result.file.includes('pending'), 'should write to flat jobs/ not jobs/pending/');
|
|
1102
1134
|
|
|
1103
1135
|
const jobFilePath = path.join(fixture.cwd, result.file);
|
|
1104
1136
|
assert.ok(fs.existsSync(jobFilePath), 'Job file should exist on disk');
|
|
1105
1137
|
});
|
|
1106
1138
|
|
|
1107
|
-
it('auto-creates jobs/
|
|
1139
|
+
it('auto-creates jobs/ directory', () => {
|
|
1108
1140
|
fixture = createFixture({
|
|
1109
1141
|
'ROADMAP.md': FIXTURE_ROADMAP,
|
|
1110
1142
|
'phases/49-job-file-format/49-01-PLAN.md': '',
|
|
@@ -1115,12 +1147,12 @@ Plans:
|
|
|
1115
1147
|
'phases/50-job-creation/50-02-PLAN.md': '',
|
|
1116
1148
|
});
|
|
1117
1149
|
|
|
1118
|
-
// Ensure
|
|
1119
|
-
const
|
|
1120
|
-
assert.ok(!fs.existsSync(
|
|
1150
|
+
// Ensure jobs dir does not exist yet
|
|
1151
|
+
const jobsDir = path.join(fixture.cwd, 'jobs');
|
|
1152
|
+
assert.ok(!fs.existsSync(jobsDir), 'jobs dir should not exist before call');
|
|
1121
1153
|
|
|
1122
1154
|
cmdJobsCreateMilestone(fixture.cwd, 'v6.0', true, false);
|
|
1123
|
-
assert.ok(fs.existsSync(
|
|
1155
|
+
assert.ok(fs.existsSync(jobsDir), 'jobs dir should be auto-created');
|
|
1124
1156
|
});
|
|
1125
1157
|
|
|
1126
1158
|
it('returns JSON with expected fields', () => {
|
|
@@ -2019,10 +2051,9 @@ Plans:
|
|
|
2019
2051
|
if (fixture) fixture.cleanup();
|
|
2020
2052
|
});
|
|
2021
2053
|
|
|
2022
|
-
it('cancels in-progress job: resets [>] steps to [ ], updates Status
|
|
2054
|
+
it('cancels in-progress job: resets [>] steps to [ ], updates Status via frontmatter', () => {
|
|
2023
2055
|
fixture = createFixture({
|
|
2024
2056
|
'jobs/in-progress/milestone-v6.0.md': WELL_FORMED_JOB,
|
|
2025
|
-
'jobs/pending/': null,
|
|
2026
2057
|
});
|
|
2027
2058
|
const result = cancelJob(fixture.cwd, 'v6.0');
|
|
2028
2059
|
|
|
@@ -2031,15 +2062,14 @@ Plans:
|
|
|
2031
2062
|
assert.ok(result.path);
|
|
2032
2063
|
assert.equal(result.steps_reset, 1); // one [>] step
|
|
2033
2064
|
|
|
2034
|
-
//
|
|
2035
|
-
assert.ok(fs.existsSync(path.join(fixture.cwd, 'jobs', '
|
|
2036
|
-
assert.ok(!fs.existsSync(path.join(fixture.cwd, 'jobs', 'in-progress', 'milestone-v6.0.md')));
|
|
2065
|
+
// File stays in place — status updated via frontmatter, no file move
|
|
2066
|
+
assert.ok(fs.existsSync(path.join(fixture.cwd, 'jobs', 'in-progress', 'milestone-v6.0.md')));
|
|
2037
2067
|
|
|
2038
|
-
// Verify content: [>] reset to [ ], [x] preserved
|
|
2039
|
-
const content = fs.readFileSync(path.join(fixture.cwd, 'jobs', '
|
|
2040
|
-
assert.ok(content.includes('**Status:** pending'), 'Status should be pending');
|
|
2068
|
+
// Verify content: [>] reset to [ ], [x] preserved, Status header updated
|
|
2069
|
+
const content = fs.readFileSync(path.join(fixture.cwd, 'jobs', 'in-progress', 'milestone-v6.0.md'), 'utf-8');
|
|
2070
|
+
assert.ok(content.includes('**Status:** pending'), 'Status header should be pending');
|
|
2071
|
+
assert.ok(content.includes('status: pending'), 'Frontmatter status should be pending');
|
|
2041
2072
|
assert.ok(!content.includes('[>]'), 'No in-progress markers should remain');
|
|
2042
|
-
// completed steps preserved
|
|
2043
2073
|
assert.ok(content.includes('[x]'), 'Completed steps should be preserved');
|
|
2044
2074
|
});
|
|
2045
2075
|
|
|
@@ -2071,11 +2101,11 @@ Plans:
|
|
|
2071
2101
|
it('keeps completed [x] steps marked done', () => {
|
|
2072
2102
|
fixture = createFixture({
|
|
2073
2103
|
'jobs/in-progress/milestone-v6.0.md': WELL_FORMED_JOB,
|
|
2074
|
-
'jobs/pending/': null,
|
|
2075
2104
|
});
|
|
2076
2105
|
cancelJob(fixture.cwd, 'v6.0');
|
|
2077
2106
|
|
|
2078
|
-
|
|
2107
|
+
// File stays in place — read from in-progress/
|
|
2108
|
+
const content = fs.readFileSync(path.join(fixture.cwd, 'jobs', 'in-progress', 'milestone-v6.0.md'), 'utf-8');
|
|
2079
2109
|
const stepLines = content.split('\n').filter(l => l.trim().startsWith('- ['));
|
|
2080
2110
|
const completedSteps = stepLines.filter(l => l.includes('[x]'));
|
|
2081
2111
|
assert.ok(completedSteps.length > 0, 'Completed steps should be preserved');
|
|
@@ -2618,3 +2648,92 @@ describe('rollbackJob', () => {
|
|
|
2618
2648
|
assert.equal(result.reason, 'no_start_shas');
|
|
2619
2649
|
});
|
|
2620
2650
|
});
|
|
2651
|
+
|
|
2652
|
+
// ─── JOB_STATUSES constant ──────────────────────────────────────────────────
|
|
2653
|
+
|
|
2654
|
+
describe('JOB_STATUSES constant', () => {
|
|
2655
|
+
it('exports correct values', () => {
|
|
2656
|
+
const { JOB_STATUSES } = require('./jobs.cjs');
|
|
2657
|
+
assert.deepStrictEqual(JOB_STATUSES, ['pending', 'in-progress', 'completed', 'failed', 'rolled_back']);
|
|
2658
|
+
});
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
// ─── setJobStatus ───────────────────────────────────────────────────────────
|
|
2662
|
+
|
|
2663
|
+
describe('setJobStatus', () => {
|
|
2664
|
+
it('throws on invalid status', () => {
|
|
2665
|
+
const { setJobStatus } = require('./jobs.cjs');
|
|
2666
|
+
assert.throws(() => {
|
|
2667
|
+
setJobStatus('/nonexistent', 'v99.0', 'invalid-status');
|
|
2668
|
+
}, (err) => {
|
|
2669
|
+
assert.ok(err.message.includes('Invalid status'));
|
|
2670
|
+
assert.ok(err.message.includes('pending'));
|
|
2671
|
+
assert.ok(err.message.includes('in-progress'));
|
|
2672
|
+
return true;
|
|
2673
|
+
});
|
|
2674
|
+
});
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2677
|
+
// ─── findJobFile flat-first scanning ──────────────────────────────────────
|
|
2678
|
+
|
|
2679
|
+
describe('findJobFile flat-first scanning', () => {
|
|
2680
|
+
let fixture;
|
|
2681
|
+
afterEach(() => { if (fixture) fixture.cleanup(); });
|
|
2682
|
+
|
|
2683
|
+
it('finds job in flat jobs/ directory', () => {
|
|
2684
|
+
const jobContent = '---\nstatus: pending\n---\n# Milestone Job: v20.0\n\n**Version:** v20.0\n**Created:** 2026-04-06T00:00:00Z\n**Status:** pending\n**Check:** true\n\n## Steps\n\n- [ ] `/dgs:plan-phase 132`\n';
|
|
2685
|
+
fixture = createFixture({
|
|
2686
|
+
'config.json': '{}',
|
|
2687
|
+
'jobs/milestone-v20.0.md': jobContent,
|
|
2688
|
+
});
|
|
2689
|
+
const result = findJobFile(fixture.cwd, 'v20.0');
|
|
2690
|
+
assert.equal(result.found, true);
|
|
2691
|
+
assert.equal(result.status, 'pending');
|
|
2692
|
+
assert.equal(result.directory, null, 'Flat files should have null directory');
|
|
2693
|
+
assert.ok(result.path.endsWith('jobs/milestone-v20.0.md'));
|
|
2694
|
+
});
|
|
2695
|
+
|
|
2696
|
+
it('reads status from bold-key header when no YAML frontmatter', () => {
|
|
2697
|
+
const jobContent = '# Milestone Job: v19.0\n\n**Version:** v19.0\n**Created:** 2026-01-01T00:00:00Z\n**Status:** completed\n**Check:** false\n\n## Steps\n\n- [x] `/dgs:plan-phase 100` \u2014 completed\n';
|
|
2698
|
+
fixture = createFixture({
|
|
2699
|
+
'config.json': '{}',
|
|
2700
|
+
'jobs/milestone-v19.0.md': jobContent,
|
|
2701
|
+
});
|
|
2702
|
+
const result = findJobFile(fixture.cwd, 'v19.0');
|
|
2703
|
+
assert.equal(result.found, true);
|
|
2704
|
+
assert.equal(result.status, 'completed');
|
|
2705
|
+
assert.equal(result.directory, null);
|
|
2706
|
+
});
|
|
2707
|
+
|
|
2708
|
+
it('falls back to legacy subdirectory with warning', () => {
|
|
2709
|
+
const jobContent = '# Milestone Job: v18.0\n\n**Version:** v18.0\n**Created:** 2026-01-01T00:00:00Z\n**Status:** in-progress\n**Check:** true\n\n## Steps\n\n- [>] `/dgs:plan-phase 90`\n';
|
|
2710
|
+
fixture = createFixture({
|
|
2711
|
+
'config.json': '{}',
|
|
2712
|
+
'jobs/in-progress/milestone-v18.0.md': jobContent,
|
|
2713
|
+
});
|
|
2714
|
+
const origWrite = process.stderr.write;
|
|
2715
|
+
let stderrOutput = '';
|
|
2716
|
+
process.stderr.write = (msg) => { stderrOutput += msg; };
|
|
2717
|
+
try {
|
|
2718
|
+
const result = findJobFile(fixture.cwd, 'v18.0');
|
|
2719
|
+
assert.equal(result.found, true);
|
|
2720
|
+
assert.ok(stderrOutput.includes('[DGS] Warning:'), 'Should emit legacy warning');
|
|
2721
|
+
assert.ok(stderrOutput.includes('legacy'), 'Warning should mention legacy');
|
|
2722
|
+
assert.equal(result.directory, 'in-progress');
|
|
2723
|
+
} finally {
|
|
2724
|
+
process.stderr.write = origWrite;
|
|
2725
|
+
}
|
|
2726
|
+
});
|
|
2727
|
+
|
|
2728
|
+
it('prefers flat over legacy', () => {
|
|
2729
|
+
const flatJob = '---\nstatus: pending\n---\n# Milestone Job: v17.0\n\n**Version:** v17.0\n**Created:** 2026-01-01T00:00:00Z\n**Status:** pending\n**Check:** true\n\n## Steps\n\n- [ ] `/dgs:plan-phase 80`\n';
|
|
2730
|
+
const legacyJob = '# Milestone Job: v17.0\n\n**Version:** v17.0\n**Created:** 2026-01-01T00:00:00Z\n**Status:** pending\n**Check:** true\n\n## Steps\n\n- [ ] `/dgs:plan-phase 80`\n';
|
|
2731
|
+
fixture = createFixture({
|
|
2732
|
+
'config.json': '{}',
|
|
2733
|
+
'jobs/milestone-v17.0.md': flatJob,
|
|
2734
|
+
'jobs/pending/milestone-v17.0.md': legacyJob,
|
|
2735
|
+
});
|
|
2736
|
+
const result = findJobFile(fixture.cwd, 'v17.0');
|
|
2737
|
+
assert.equal(result.directory, null, 'Flat should win over legacy');
|
|
2738
|
+
});
|
|
2739
|
+
});
|