@ktpartners/dgs-platform 2.9.0 → 3.3.0
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 +197 -0
- package/README.md +34 -2
- package/agents/dgs-executor.md +124 -3
- package/agents/dgs-idea-researcher.md +447 -0
- package/agents/dgs-plan-checker.md +61 -3
- package/agents/dgs-planner.md +51 -8
- package/bin/install.js +44 -0
- package/commands/dgs/abandon-quick.md +28 -0
- package/commands/dgs/add-tests.md +2 -2
- package/commands/dgs/audit-milestone.md +4 -3
- 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/diff-report.md +124 -0
- 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 +8 -21
- package/commands/dgs/package-scan.md +43 -0
- 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 +3 -2
- package/commands/dgs/research-phase.md +3 -3
- package/commands/dgs/switch-project.md +14 -1
- package/commands/dgs/write-spec.md +3 -3
- package/deliver-great-systems/bin/dgs-tools.cjs +401 -32
- package/deliver-great-systems/bin/lib/audit-tolerance.cjs +77 -0
- package/deliver-great-systems/bin/lib/audit-tolerance.test.cjs +101 -0
- package/deliver-great-systems/bin/lib/commands.cjs +626 -46
- package/deliver-great-systems/bin/lib/commands.test.cjs +451 -0
- package/deliver-great-systems/bin/lib/commit-verify.test.cjs +236 -0
- package/deliver-great-systems/bin/lib/config.cjs +80 -6
- package/deliver-great-systems/bin/lib/config.test.cjs +309 -0
- package/deliver-great-systems/bin/lib/context.cjs +120 -0
- package/deliver-great-systems/bin/lib/core.cjs +35 -14
- package/deliver-great-systems/bin/lib/core.test.cjs +79 -1
- package/deliver-great-systems/bin/lib/execution.cjs +49 -17
- package/deliver-great-systems/bin/lib/fast-routing.cjs +199 -0
- package/deliver-great-systems/bin/lib/fast-routing.test.cjs +108 -0
- package/deliver-great-systems/bin/lib/final-commit-precondition.test.cjs +87 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/bundler-audit-gemfile.json +21 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/gate-parity-expected.md +186 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/gate-parity-runresult.json +235 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/govulncheck-import.json +3 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/npm-audit-v10.json +37 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/osv-clean.json +3 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/osv-vulns.json +77 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/pip-audit-requirements.json +28 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/snyk-lodash.json +30 -0
- package/deliver-great-systems/bin/lib/fixtures/package-scan/snyk-workspaces.json +55 -0
- package/deliver-great-systems/bin/lib/flat-migration.test.cjs +396 -0
- package/deliver-great-systems/bin/lib/frontmatter.cjs +1 -1
- package/deliver-great-systems/bin/lib/governance.cjs +211 -0
- package/deliver-great-systems/bin/lib/governance.test.cjs +339 -0
- package/deliver-great-systems/bin/lib/health-untracked-phase.test.cjs +269 -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 +357 -61
- package/deliver-great-systems/bin/lib/init.test.cjs +625 -8
- package/deliver-great-systems/bin/lib/jobs.cjs +131 -25
- 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 +154 -31
- package/deliver-great-systems/bin/lib/milestone.test.cjs +203 -0
- package/deliver-great-systems/bin/lib/package-adapters.cjs +530 -0
- package/deliver-great-systems/bin/lib/package-adapters.test.cjs +618 -0
- package/deliver-great-systems/bin/lib/package-ecosystems.cjs +350 -0
- package/deliver-great-systems/bin/lib/package-ecosystems.test.cjs +348 -0
- package/deliver-great-systems/bin/lib/package-runner.cjs +199 -0
- package/deliver-great-systems/bin/lib/package-runner.test.cjs +198 -0
- package/deliver-great-systems/bin/lib/package-scan-provenance.cjs +56 -0
- package/deliver-great-systems/bin/lib/package-scan-provenance.test.cjs +103 -0
- package/deliver-great-systems/bin/lib/package-scan-report.cjs +1140 -0
- package/deliver-great-systems/bin/lib/package-scan-report.test.cjs +1963 -0
- package/deliver-great-systems/bin/lib/package-scan-skill.cjs +96 -0
- package/deliver-great-systems/bin/lib/package-scan-skill.test.cjs +136 -0
- package/deliver-great-systems/bin/lib/package-scan.cjs +919 -0
- package/deliver-great-systems/bin/lib/package-scan.test.cjs +2147 -0
- package/deliver-great-systems/bin/lib/phase.cjs +146 -3
- package/deliver-great-systems/bin/lib/phase.test.cjs +420 -0
- package/deliver-great-systems/bin/lib/plan-number-validity.test.cjs +48 -0
- package/deliver-great-systems/bin/lib/projects.cjs +65 -10
- package/deliver-great-systems/bin/lib/projects.test.cjs +198 -2
- package/deliver-great-systems/bin/lib/quick.cjs +739 -0
- package/deliver-great-systems/bin/lib/quick.test.cjs +730 -0
- package/deliver-great-systems/bin/lib/repos.cjs +37 -13
- package/deliver-great-systems/bin/lib/review.cjs +1821 -0
- 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 +147 -55
- package/deliver-great-systems/bin/lib/summary-frontmatter.cjs +54 -0
- package/deliver-great-systems/bin/lib/summary-frontmatter.test.cjs +78 -0
- package/deliver-great-systems/bin/lib/sweep-scope.test.cjs +263 -0
- package/deliver-great-systems/bin/lib/sync.cjs +75 -0
- package/deliver-great-systems/bin/lib/verify.cjs +198 -7
- package/deliver-great-systems/bin/lib/verify.test.cjs +82 -0
- package/deliver-great-systems/bin/lib/wave-0-template-rename.test.cjs +40 -0
- package/deliver-great-systems/bin/lib/worktrees.cjs +790 -0
- package/deliver-great-systems/bin/lib/worktrees.test.cjs +963 -0
- package/deliver-great-systems/references/agent-step-reliability.md +60 -0
- package/deliver-great-systems/references/conflict-resolution.md +4 -0
- package/deliver-great-systems/references/context-tiers.md +4 -0
- package/deliver-great-systems/references/package-scan-config.md +151 -0
- package/deliver-great-systems/references/questioning.md +0 -30
- package/deliver-great-systems/references/spec-review-loop.md +1 -2
- package/deliver-great-systems/references/workflow-conventions.md +29 -0
- package/deliver-great-systems/skills/dgs-tests/package-scan.md +44 -0
- package/deliver-great-systems/templates/REVIEW.md +35 -0
- package/deliver-great-systems/templates/VALIDATION.md +1 -1
- package/deliver-great-systems/templates/claude-md.md +27 -0
- package/deliver-great-systems/templates/package-scan-report.md +108 -0
- package/deliver-great-systems/templates/project.md +6 -170
- package/deliver-great-systems/templates/summary.md +3 -1
- 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-phase.md +5 -0
- 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-milestone.md +66 -10
- package/deliver-great-systems/workflows/audit-phase.md +15 -5
- package/deliver-great-systems/workflows/cancel-job.md +2 -2
- package/deliver-great-systems/workflows/check-todos.md +2 -3
- package/deliver-great-systems/workflows/codereview.md +103 -9
- package/deliver-great-systems/workflows/complete-milestone.md +218 -24
- package/deliver-great-systems/workflows/complete-quick.md +106 -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/discuss-phase.md +3 -2
- package/deliver-great-systems/workflows/execute-phase.md +209 -33
- package/deliver-great-systems/workflows/execute-plan.md +22 -22
- package/deliver-great-systems/workflows/help.md +53 -20
- package/deliver-great-systems/workflows/import-spec.md +65 -7
- package/deliver-great-systems/workflows/init-product.md +45 -167
- package/deliver-great-systems/workflows/new-milestone.md +140 -33
- package/deliver-great-systems/workflows/new-project.md +60 -331
- package/deliver-great-systems/workflows/package-scan.md +59 -0
- package/deliver-great-systems/workflows/plan-phase.md +79 -1
- 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 +106 -0
- package/deliver-great-systems/workflows/quick.md +328 -26
- package/deliver-great-systems/workflows/refine-spec.md +1 -1
- package/deliver-great-systems/workflows/research-idea.md +77 -139
- package/deliver-great-systems/workflows/resume-project.md +2 -2
- package/deliver-great-systems/workflows/run-job.md +29 -43
- package/deliver-great-systems/workflows/settings.md +13 -77
- 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 +11 -13
- package/hooks/dist/dgs-enforce-discipline.js +196 -0
- package/package.json +1 -1
- package/scripts/build-hooks.js +1 -0
|
@@ -550,16 +550,13 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
550
550
|
return;
|
|
551
551
|
}
|
|
552
552
|
|
|
553
|
-
// ─── Check 2: PROJECT.md exists and has
|
|
553
|
+
// ─── Check 2: PROJECT.md exists and has a heading ────────────────────────
|
|
554
554
|
if (!fs.existsSync(projectPath)) {
|
|
555
555
|
addIssue('error', 'E002', 'PROJECT.md not found', 'Run /dgs:new-project to create');
|
|
556
556
|
} else {
|
|
557
557
|
const content = fs.readFileSync(projectPath, 'utf-8');
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (!content.includes(section)) {
|
|
561
|
-
addIssue('warning', 'W001', `PROJECT.md missing section: ${section}`, 'Add section manually');
|
|
562
|
-
}
|
|
558
|
+
if (!/^#\s+\S/m.test(content)) {
|
|
559
|
+
addIssue('warning', 'W001', 'PROJECT.md missing top-level heading', 'Add a # Title line');
|
|
563
560
|
}
|
|
564
561
|
}
|
|
565
562
|
|
|
@@ -708,6 +705,201 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
708
705
|
}
|
|
709
706
|
}
|
|
710
707
|
|
|
708
|
+
// ─── Check 10: Uncommitted tracking files in completed phases ─────────────
|
|
709
|
+
// Surface the condition `phase finalize` was designed to prevent: a completed
|
|
710
|
+
// phase whose ROADMAP.md / STATE.md / REQUIREMENTS.md / VERIFICATION.md are
|
|
711
|
+
// modified in the working tree but not committed (e.g., Claude dropped the
|
|
712
|
+
// commit step between updating trackers and invoking `commit`).
|
|
713
|
+
try {
|
|
714
|
+
const { execGit } = require('./core.cjs');
|
|
715
|
+
const isGit = execGit(cwd, ['rev-parse', '--is-inside-work-tree']);
|
|
716
|
+
if (isGit.exitCode === 0 && isGit.stdout.trim() === 'true') {
|
|
717
|
+
// git resolves symlinks when reporting paths; normalize both sides against
|
|
718
|
+
// the real git root so relative paths compare correctly on macOS
|
|
719
|
+
// (/var/folders → /private/var/folders).
|
|
720
|
+
const gitRootRes = execGit(cwd, ['rev-parse', '--show-toplevel']);
|
|
721
|
+
const gitRoot = gitRootRes.exitCode === 0 ? gitRootRes.stdout.trim() : cwd;
|
|
722
|
+
const statusRes = execGit(cwd, ['status', '--porcelain']);
|
|
723
|
+
if (statusRes.exitCode === 0) {
|
|
724
|
+
// `git status --porcelain` emits "XY path" lines (X=index, Y=worktree,
|
|
725
|
+
// column 3 is a separator space, path starts at column 4). Note:
|
|
726
|
+
// execGit.stdout is already .trim()'d so lines may have lost a leading
|
|
727
|
+
// space when X=' ' (worktree-only changes). Extract the path from
|
|
728
|
+
// whatever follows the first whitespace group.
|
|
729
|
+
const dirtyPaths = statusRes.stdout
|
|
730
|
+
.split('\n')
|
|
731
|
+
.filter(Boolean)
|
|
732
|
+
.map(l => {
|
|
733
|
+
// Match "XY<space>path" OR "<space>Y<space>path" (leading space may be trimmed).
|
|
734
|
+
// A path after 2 status chars + 1 space begins at index 3 of the original,
|
|
735
|
+
// but lines like " M file" got left-trimmed to "M file" (only one status char).
|
|
736
|
+
// Normalize: skip the leading non-space + exactly one space.
|
|
737
|
+
const m = l.match(/^\S{1,2}\s(.+)$/);
|
|
738
|
+
return m ? m[1].trim() : l.trim();
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// Identify completed phases from ROADMAP.md checkboxes
|
|
742
|
+
const completedPhases = new Set();
|
|
743
|
+
if (fs.existsSync(roadmapPath)) {
|
|
744
|
+
const rm = fs.readFileSync(roadmapPath, 'utf-8');
|
|
745
|
+
const re = /-\s*\[x\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)/gi;
|
|
746
|
+
let m;
|
|
747
|
+
while ((m = re.exec(rm)) !== null) completedPhases.add(m[1]);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Tracking files at the planning root — compute relative to gitRoot
|
|
751
|
+
const trackingRoot = ['ROADMAP.md', 'STATE.md', 'REQUIREMENTS.md']
|
|
752
|
+
.map(f => path.relative(gitRoot, path.join(planningDir, f)));
|
|
753
|
+
|
|
754
|
+
// VERIFICATION.md files inside completed phase dirs
|
|
755
|
+
const trackingVerif = [];
|
|
756
|
+
if (completedPhases.size > 0) {
|
|
757
|
+
try {
|
|
758
|
+
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
759
|
+
for (const e of entries) {
|
|
760
|
+
if (!e.isDirectory()) continue;
|
|
761
|
+
const dm = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
762
|
+
if (!dm) continue;
|
|
763
|
+
const phaseNum = dm[1];
|
|
764
|
+
const unpadded = String(parseInt(phaseNum, 10));
|
|
765
|
+
if (!completedPhases.has(phaseNum) && !completedPhases.has(unpadded)) continue;
|
|
766
|
+
const phaseFiles = fs.readdirSync(path.join(phasesDir, e.name));
|
|
767
|
+
for (const f of phaseFiles) {
|
|
768
|
+
if (/-VERIFICATION\.md$/i.test(f)) {
|
|
769
|
+
trackingVerif.push(path.relative(gitRoot, path.join(phasesDir, e.name, f)));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
} catch { /* ignore */ }
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const allTracking = [...trackingRoot, ...trackingVerif];
|
|
777
|
+
const dirtyTracking = dirtyPaths.filter(p => allTracking.includes(p));
|
|
778
|
+
if (dirtyTracking.length > 0) {
|
|
779
|
+
addIssue('warning', 'W009',
|
|
780
|
+
`Uncommitted tracking files for completed phase(s): ${dirtyTracking.join(', ')}`,
|
|
781
|
+
'Run `dgs-tools phase finalize <phase>` for the relevant phase, or commit manually'
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} catch { /* non-git or git error — skip silently */ }
|
|
787
|
+
|
|
788
|
+
// ─── Check 11: Untracked scaffolding (.gitkeep) — REL-12 ────────────────
|
|
789
|
+
// Walks planning root for `.gitkeep` files in standard scaffold locations
|
|
790
|
+
// (specs/, docs/product/, quick/, projects/*/{phases,quick,debug,research}/)
|
|
791
|
+
// and warns if any exist on disk but aren't tracked in git. User-facing analog
|
|
792
|
+
// of REL-09 (init-product .gitkeep commit) — REL-09 prevented the leak,
|
|
793
|
+
// REL-12 catches any reintroduction immediately on the next health-check run.
|
|
794
|
+
try {
|
|
795
|
+
const { execGit } = require('./core.cjs');
|
|
796
|
+
const isGitWT = (() => {
|
|
797
|
+
try {
|
|
798
|
+
const res = execGit(cwd, ['rev-parse', '--is-inside-work-tree']);
|
|
799
|
+
return res && res.exitCode === 0 && (res.stdout || '').trim() === 'true';
|
|
800
|
+
} catch { return false; }
|
|
801
|
+
})();
|
|
802
|
+
|
|
803
|
+
if (isGitWT) {
|
|
804
|
+
const candidates = [];
|
|
805
|
+
|
|
806
|
+
// Direct scaffold locations under planning root
|
|
807
|
+
const directDirs = ['specs', 'docs/product', 'quick'];
|
|
808
|
+
for (const d of directDirs) {
|
|
809
|
+
const p = path.join(planningDir, d, '.gitkeep');
|
|
810
|
+
if (fs.existsSync(p)) candidates.push(path.join(d, '.gitkeep'));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Per-project scaffold locations: projects/*/{phases,quick,debug,research}/.gitkeep
|
|
814
|
+
const projectsDir = path.join(planningDir, 'projects');
|
|
815
|
+
if (fs.existsSync(projectsDir)) {
|
|
816
|
+
try {
|
|
817
|
+
const projects = fs.readdirSync(projectsDir, { withFileTypes: true });
|
|
818
|
+
for (const proj of projects) {
|
|
819
|
+
if (!proj.isDirectory()) continue;
|
|
820
|
+
for (const sub of ['phases', 'quick', 'debug', 'research']) {
|
|
821
|
+
const p = path.join(planningDir, 'projects', proj.name, sub, '.gitkeep');
|
|
822
|
+
if (fs.existsSync(p)) {
|
|
823
|
+
candidates.push(path.join('projects', proj.name, sub, '.gitkeep'));
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
} catch { /* ignore */ }
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// For each candidate, check whether it's tracked in git via
|
|
831
|
+
// `git ls-files <rel>` — empty stdout means untracked.
|
|
832
|
+
const untracked = [];
|
|
833
|
+
for (const rel of candidates) {
|
|
834
|
+
const lsResult = execGit(cwd, ['ls-files', '--', rel]);
|
|
835
|
+
const tracked = lsResult && lsResult.exitCode === 0 && (lsResult.stdout || '').trim().length > 0;
|
|
836
|
+
if (!tracked) untracked.push(rel);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (untracked.length > 0) {
|
|
840
|
+
addIssue(
|
|
841
|
+
'warning',
|
|
842
|
+
'untracked-scaffolding',
|
|
843
|
+
`Found ${untracked.length} untracked .gitkeep file(s) in standard scaffold locations: ${untracked.join(', ')}`,
|
|
844
|
+
`Run 'git add ${untracked.join(' ')} && git commit -m "docs: track scaffolding files"' to commit them; closes idea #29 (init-product .gitkeep leak) on the next health-check run.`,
|
|
845
|
+
false
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
} catch { /* non-git or git error — skip silently per existing convention */ }
|
|
850
|
+
|
|
851
|
+
// ─── Check 12: untracked-phase-artifacts (REL-11, Phase 156) ───────────────
|
|
852
|
+
// Walk every phase directory under projects/<project>/phases/ (or
|
|
853
|
+
// top-level phases/ in v1) and report any uncommitted PLAN.md /
|
|
854
|
+
// CONTEXT.md / RESEARCH.md / UAT.md / VERIFICATION.md files. This is
|
|
855
|
+
// the loud-fail safety net for REL-01 / REL-02 — if either of those
|
|
856
|
+
// commit-step contracts ever regresses, this health check surfaces
|
|
857
|
+
// the dangling artifact on the next /dgs:health run.
|
|
858
|
+
try {
|
|
859
|
+
const untracked = [];
|
|
860
|
+
if (fs.existsSync(phasesDir)) {
|
|
861
|
+
const ARTIFACT_RE = /^[0-9].*-(PLAN|CONTEXT|RESEARCH|UAT|VERIFICATION)\.md$/;
|
|
862
|
+
const phaseEntries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
863
|
+
for (const entry of phaseEntries) {
|
|
864
|
+
if (!entry.isDirectory()) continue;
|
|
865
|
+
const phaseDirAbs = path.join(phasesDir, entry.name);
|
|
866
|
+
let files;
|
|
867
|
+
try { files = fs.readdirSync(phaseDirAbs); } catch { continue; }
|
|
868
|
+
for (const f of files) {
|
|
869
|
+
if (!ARTIFACT_RE.test(f)) continue;
|
|
870
|
+
const absPath = path.join(phaseDirAbs, f);
|
|
871
|
+
const relPath = path.relative(planningDir, absPath);
|
|
872
|
+
// `git ls-files -- <relPath>` returns the path if tracked,
|
|
873
|
+
// empty string if untracked. Run with cwd = planningDir so
|
|
874
|
+
// the relative path resolves correctly.
|
|
875
|
+
const lsResult = execGit(planningDir, ['ls-files', '--', relPath]);
|
|
876
|
+
const tracked = lsResult.exitCode === 0 && (lsResult.stdout || '').trim().length > 0;
|
|
877
|
+
if (!tracked) untracked.push(relPath);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (untracked.length > 0) {
|
|
882
|
+
const remediation =
|
|
883
|
+
`Run 'node $TOOLS commit "docs: catch up untracked phase artifacts" --files ${untracked.join(' ')}' ` +
|
|
884
|
+
`or run /dgs:health --repair`;
|
|
885
|
+
addIssue(
|
|
886
|
+
'warning',
|
|
887
|
+
'W010',
|
|
888
|
+
`untracked-phase-artifacts: ${untracked.length} uncommitted phase artifact(s) — ${untracked.join(', ')}`,
|
|
889
|
+
remediation,
|
|
890
|
+
false
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
} catch (err) {
|
|
894
|
+
addIssue(
|
|
895
|
+
'warning',
|
|
896
|
+
'W011',
|
|
897
|
+
`untracked-phase-artifacts check failed: ${err.message}`,
|
|
898
|
+
'Investigate verify.cjs walker',
|
|
899
|
+
false
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
711
903
|
// ─── Perform repairs if requested ─────────────────────────────────────────
|
|
712
904
|
const repairActions = [];
|
|
713
905
|
if (options.repair && repairs.length > 0) {
|
|
@@ -720,7 +912,6 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
720
912
|
model_profile: 'balanced',
|
|
721
913
|
commit_docs: true,
|
|
722
914
|
search_gitignored: false,
|
|
723
|
-
branching_strategy: 'none',
|
|
724
915
|
research: true,
|
|
725
916
|
plan_checker: true,
|
|
726
917
|
verifier: true,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// deliver-great-systems/bin/lib/verify.test.cjs
|
|
2
|
+
// REL-12 untracked-scaffolding health check — initially RED. Turns GREEN after plan 04
|
|
3
|
+
// adds Check 11 to bin/lib/verify.cjs::cmdValidateHealth.
|
|
4
|
+
|
|
5
|
+
const test = require('node:test');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const os = require('node:os');
|
|
10
|
+
const { execSync } = require('node:child_process');
|
|
11
|
+
|
|
12
|
+
function setupREL12Fixture({ trackedKeeps, untrackedKeeps }) {
|
|
13
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'rel12-'));
|
|
14
|
+
execSync('git init -q', { cwd: root });
|
|
15
|
+
execSync('git config user.email test@test', { cwd: root });
|
|
16
|
+
execSync('git config user.name test', { cwd: root });
|
|
17
|
+
fs.writeFileSync(path.join(root, 'PROJECT.md'),
|
|
18
|
+
'# Test\n## What This Is\n## Core Value\n## Requirements\n');
|
|
19
|
+
fs.writeFileSync(path.join(root, 'ROADMAP.md'), '# Roadmap\n');
|
|
20
|
+
fs.writeFileSync(path.join(root, 'STATE.md'), '# State\n');
|
|
21
|
+
fs.mkdirSync(path.join(root, 'phases'), { recursive: true });
|
|
22
|
+
// Create + track requested .gitkeep paths
|
|
23
|
+
for (const p of trackedKeeps) {
|
|
24
|
+
fs.mkdirSync(path.join(root, path.dirname(p)), { recursive: true });
|
|
25
|
+
fs.writeFileSync(path.join(root, p), '');
|
|
26
|
+
execSync(`git add "${p}"`, { cwd: root });
|
|
27
|
+
}
|
|
28
|
+
// Always commit at least PROJECT.md so HEAD exists even when no keeps are tracked
|
|
29
|
+
execSync('git add PROJECT.md ROADMAP.md STATE.md', { cwd: root });
|
|
30
|
+
execSync('git commit -q -m "init"', { cwd: root });
|
|
31
|
+
// Create untracked .gitkeep paths (not added to git)
|
|
32
|
+
for (const p of untrackedKeeps) {
|
|
33
|
+
fs.mkdirSync(path.join(root, path.dirname(p)), { recursive: true });
|
|
34
|
+
fs.writeFileSync(path.join(root, p), '');
|
|
35
|
+
}
|
|
36
|
+
return root;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function captureHealthOutput(root) {
|
|
40
|
+
// cmdValidateHealth calls output() which process.exit(0)s — run in a child
|
|
41
|
+
// process and capture stdout JSON. Pattern matches health-untracked-phase.test.cjs.
|
|
42
|
+
const verifyPath = path.resolve(__dirname, 'verify.cjs');
|
|
43
|
+
const shim = [
|
|
44
|
+
`const v = require(${JSON.stringify(verifyPath)});`,
|
|
45
|
+
`v.cmdValidateHealth(${JSON.stringify(root)}, { raw: true }, true);`,
|
|
46
|
+
].join('\n');
|
|
47
|
+
const shimPath = path.join(os.tmpdir(), `rel12-shim-${process.pid}-${Date.now()}.cjs`);
|
|
48
|
+
fs.writeFileSync(shimPath, shim);
|
|
49
|
+
try {
|
|
50
|
+
const stdout = execSync(`node ${JSON.stringify(shimPath)}`, { encoding: 'utf-8' });
|
|
51
|
+
try { return JSON.parse(stdout); } catch { return { stdout, warnings: [] }; }
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const out = err.stdout && err.stdout.toString();
|
|
54
|
+
try { return JSON.parse(out); } catch { return { stdout: out, warnings: [] }; }
|
|
55
|
+
} finally {
|
|
56
|
+
try { fs.unlinkSync(shimPath); } catch { /* ignore */ }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
test('REL-12: cmdValidateHealth flags untracked .gitkeep in standard scaffold locations (untracked-scaffolding)', () => {
|
|
61
|
+
const root = setupREL12Fixture({
|
|
62
|
+
trackedKeeps: ['specs/.gitkeep'],
|
|
63
|
+
untrackedKeeps: ['docs/product/.gitkeep', 'quick/.gitkeep'],
|
|
64
|
+
});
|
|
65
|
+
const result = captureHealthOutput(root);
|
|
66
|
+
const scaffoldingWarnings = (result.warnings || []).filter(w => w.code === 'untracked-scaffolding');
|
|
67
|
+
assert.strictEqual(scaffoldingWarnings.length, 1, 'exactly one untracked-scaffolding warning expected');
|
|
68
|
+
assert.match(scaffoldingWarnings[0].message, /docs\/product\/\.gitkeep/);
|
|
69
|
+
assert.match(scaffoldingWarnings[0].message, /quick\/\.gitkeep/);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('REL-12: cmdValidateHealth does NOT flag tracked .gitkeep files', () => {
|
|
73
|
+
const root = setupREL12Fixture({
|
|
74
|
+
trackedKeeps: ['specs/.gitkeep', 'docs/product/.gitkeep', 'quick/.gitkeep'],
|
|
75
|
+
untrackedKeeps: [],
|
|
76
|
+
});
|
|
77
|
+
const result = captureHealthOutput(root);
|
|
78
|
+
const scaffoldingWarnings = (result.warnings || []).filter(w => w.code === 'untracked-scaffolding');
|
|
79
|
+
assert.strictEqual(scaffoldingWarnings.length, 0, 'no untracked-scaffolding warning when all keeps tracked');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// REL-12 sentinel — flag this block as a Wave-0 RED scaffold for plan 04.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// deliver-great-systems/bin/lib/wave-0-template-rename.test.cjs
|
|
2
|
+
// REL-03 convergence test scaffold — initially RED. Turns GREEN after plan 02
|
|
3
|
+
// lands the planner-prompt edits and the VALIDATION.md template rename.
|
|
4
|
+
|
|
5
|
+
const test = require('node:test');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
|
|
10
|
+
const repoRoot = path.resolve(__dirname, '../../..');
|
|
11
|
+
const planner = path.join(repoRoot, 'agents/dgs-planner.md');
|
|
12
|
+
const template = path.join(repoRoot, 'deliver-great-systems/templates/VALIDATION.md');
|
|
13
|
+
|
|
14
|
+
test('REL-03: VALIDATION template uses heading "Wave 0 Task Requirements"', () => {
|
|
15
|
+
const content = fs.readFileSync(template, 'utf-8');
|
|
16
|
+
assert.match(content, /## Wave 0 Task Requirements/, 'template must use new heading');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('REL-03: VALIDATION template no longer uses "Wave 0 Requirements" as a section heading', () => {
|
|
20
|
+
const content = fs.readFileSync(template, 'utf-8');
|
|
21
|
+
// The OLD heading must NOT exist as a section heading (paragraph mentions are OK)
|
|
22
|
+
assert.doesNotMatch(content, /^## Wave 0 Requirements\s*$/m, 'old heading must be gone');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('REL-03: planner prompt unambiguously states "Plans are numbered starting at 01"', () => {
|
|
26
|
+
const content = fs.readFileSync(planner, 'utf-8');
|
|
27
|
+
assert.match(content, /Plans are numbered starting at 01/, 'planner must state plan-numbering rule');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('REL-03: planner prompt explicitly forbids plan: 00 and wave: 0', () => {
|
|
31
|
+
const content = fs.readFileSync(planner, 'utf-8');
|
|
32
|
+
assert.match(content, /Never create a plan with `plan: 00` or `wave: 0`/, 'planner must forbid plan: 00 / wave: 0');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('REL-03: planner prompt mentions Wave 0 lives as Task 0 inside plan 01', () => {
|
|
36
|
+
const content = fs.readFileSync(planner, 'utf-8');
|
|
37
|
+
assert.match(content, /Wave 0 work lives as Task 0 inside plan 01/, 'planner must locate Wave 0 inside plan 01');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// REL-03 sentinel — flag this file as a Wave-0 RED scaffold for plan 02.
|