ai-sdlc 0.2.1-alpha.1 → 0.3.0-alpha.10
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/README.md +78 -6
- package/dist/agents/implementation.d.ts +30 -1
- package/dist/agents/implementation.d.ts.map +1 -1
- package/dist/agents/implementation.js +110 -23
- package/dist/agents/implementation.js.map +1 -1
- package/dist/agents/review.d.ts +49 -1
- package/dist/agents/review.d.ts.map +1 -1
- package/dist/agents/review.js +318 -46
- package/dist/agents/review.js.map +1 -1
- package/dist/agents/rework.d.ts.map +1 -1
- package/dist/agents/rework.js +3 -1
- package/dist/agents/rework.js.map +1 -1
- package/dist/agents/verification.d.ts.map +1 -1
- package/dist/agents/verification.js +26 -12
- package/dist/agents/verification.js.map +1 -1
- package/dist/cli/batch-processor.d.ts +64 -0
- package/dist/cli/batch-processor.d.ts.map +1 -0
- package/dist/cli/batch-processor.js +85 -0
- package/dist/cli/batch-processor.js.map +1 -0
- package/dist/cli/batch-validator.d.ts +80 -0
- package/dist/cli/batch-validator.d.ts.map +1 -0
- package/dist/cli/batch-validator.js +121 -0
- package/dist/cli/batch-validator.js.map +1 -0
- package/dist/cli/commands.d.ts +11 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +772 -46
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +5 -0
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/dependency-resolver.d.ts +49 -0
- package/dist/cli/dependency-resolver.d.ts.map +1 -0
- package/dist/cli/dependency-resolver.js +133 -0
- package/dist/cli/dependency-resolver.js.map +1 -0
- package/dist/cli/epic-processor.d.ts +16 -0
- package/dist/cli/epic-processor.d.ts.map +1 -0
- package/dist/cli/epic-processor.js +361 -0
- package/dist/cli/epic-processor.js.map +1 -0
- package/dist/cli/formatting.d.ts +15 -0
- package/dist/cli/formatting.d.ts.map +1 -1
- package/dist/cli/formatting.js +19 -0
- package/dist/cli/formatting.js.map +1 -1
- package/dist/cli/progress-dashboard.d.ts +58 -0
- package/dist/cli/progress-dashboard.d.ts.map +1 -0
- package/dist/cli/progress-dashboard.js +216 -0
- package/dist/cli/progress-dashboard.js.map +1 -0
- package/dist/cli/runner.d.ts.map +1 -1
- package/dist/cli/runner.js +17 -2
- package/dist/cli/runner.js.map +1 -1
- package/dist/cli/table-renderer.d.ts.map +1 -1
- package/dist/cli/table-renderer.js +5 -1
- package/dist/cli/table-renderer.js.map +1 -1
- package/dist/core/client.d.ts +19 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +191 -5
- package/dist/core/client.js.map +1 -1
- package/dist/core/config.d.ts +13 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +117 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/git-utils.d.ts +19 -0
- package/dist/core/git-utils.d.ts.map +1 -1
- package/dist/core/git-utils.js +58 -0
- package/dist/core/git-utils.js.map +1 -1
- package/dist/core/kanban.d.ts +125 -1
- package/dist/core/kanban.d.ts.map +1 -1
- package/dist/core/kanban.js +363 -4
- package/dist/core/kanban.js.map +1 -1
- package/dist/core/process-manager.d.ts +15 -0
- package/dist/core/process-manager.d.ts.map +1 -0
- package/dist/core/process-manager.js +132 -0
- package/dist/core/process-manager.js.map +1 -0
- package/dist/core/story.d.ts +24 -0
- package/dist/core/story.d.ts.map +1 -1
- package/dist/core/story.js +78 -0
- package/dist/core/story.js.map +1 -1
- package/dist/core/worktree.d.ts +111 -2
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +310 -2
- package/dist/core/worktree.js.map +1 -1
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +184 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -2
- package/templates/story.md +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/agents/review.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/agents/review.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAA8E,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAsHzL;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,EAAE,CA0BlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAOrE;AAiBD;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAyZrJ;;;;;;;;;;;GAWG;AACH,wBAAgB,wCAAwC,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG;IAC/E,gBAAgB,EAAE,OAAO,CAAC;IAC1B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;CACzB,CAsBA;AAkGD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA6BjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAuDpE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAkCpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAavE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CA6BxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CA+F1F;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,sBAAsB,CAAC,EAAE,4BAA4B,CAAC;CACvD;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,YAAY,CAAC,CA4fvB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAkB/F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAelE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAiB7F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAsC9E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAc,GAAG,MAAM,CAqC9E;AAgCD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,WAAW,CAAC,CAqKtB"}
|
package/dist/agents/review.js
CHANGED
|
@@ -2,6 +2,7 @@ import { execSync, spawn, spawnSync } from 'child_process';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { ProcessManager } from '../core/process-manager.js';
|
|
5
6
|
import { parseStory, updateStoryStatus, appendToSection, updateStoryField, isAtMaxRetries, appendReviewHistory, snapshotMaxRetries, getEffectiveMaxRetries, getEffectiveMaxImplementationRetries } from '../core/story.js';
|
|
6
7
|
import { runAgentQuery } from '../core/client.js';
|
|
7
8
|
import { getLogger } from '../core/logger.js';
|
|
@@ -10,6 +11,7 @@ import { extractStructuredResponseSync } from '../core/llm-utils.js';
|
|
|
10
11
|
import { ReviewDecision, ReviewSeverity } from '../types/index.js';
|
|
11
12
|
import { sanitizeInput, truncateText } from '../cli/formatting.js';
|
|
12
13
|
import { detectTestDuplicationPatterns } from './test-pattern-detector.js';
|
|
14
|
+
import { getBaseBranch, getMergeBase } from '../core/git-utils.js';
|
|
13
15
|
/**
|
|
14
16
|
* Security: Validate Git branch name to prevent command injection
|
|
15
17
|
* Only allows alphanumeric characters, hyphens, underscores, and forward slashes
|
|
@@ -173,6 +175,7 @@ async function runCommandAsync(command, workingDir, timeout, onProgress) {
|
|
|
173
175
|
cwd: workingDir,
|
|
174
176
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
175
177
|
});
|
|
178
|
+
ProcessManager.getInstance().registerChild(child);
|
|
176
179
|
const timeoutId = setTimeout(() => {
|
|
177
180
|
killed = true;
|
|
178
181
|
child.kill('SIGTERM');
|
|
@@ -589,9 +592,38 @@ function formatIssuesForDisplay(issues) {
|
|
|
589
592
|
}
|
|
590
593
|
return output;
|
|
591
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Get the base commit reference for git diff comparisons
|
|
597
|
+
*
|
|
598
|
+
* Attempts to find the merge-base between the current branch and the base branch (main/master).
|
|
599
|
+
* Falls back to HEAD~1 if merge-base cannot be determined.
|
|
600
|
+
*
|
|
601
|
+
* This allows detecting source code changes across the entire feature branch,
|
|
602
|
+
* not just from the most recent commit.
|
|
603
|
+
*
|
|
604
|
+
* @param workingDir - Working directory to run git commands in
|
|
605
|
+
* @returns Commit reference to use for git diff comparison
|
|
606
|
+
*/
|
|
607
|
+
function getBaseCommitForDiff(workingDir) {
|
|
608
|
+
try {
|
|
609
|
+
const baseBranch = getBaseBranch(workingDir);
|
|
610
|
+
const mergeBase = getMergeBase(workingDir, baseBranch);
|
|
611
|
+
if (mergeBase) {
|
|
612
|
+
return mergeBase;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
// If we can't determine base branch or merge-base, fall back to HEAD~1
|
|
617
|
+
}
|
|
618
|
+
// Fallback to HEAD~1 (original behavior)
|
|
619
|
+
return 'HEAD~1';
|
|
620
|
+
}
|
|
592
621
|
/**
|
|
593
622
|
* Get source code changes from git diff
|
|
594
623
|
*
|
|
624
|
+
* Compares current branch HEAD against the base branch (main/master) merge-base
|
|
625
|
+
* to detect all source code changes in the feature branch, not just the most recent commit.
|
|
626
|
+
*
|
|
595
627
|
* Returns list of source files that have been modified (excludes tests and story files).
|
|
596
628
|
* Uses spawnSync for security (prevents command injection).
|
|
597
629
|
*
|
|
@@ -600,8 +632,9 @@ function formatIssuesForDisplay(issues) {
|
|
|
600
632
|
*/
|
|
601
633
|
export function getSourceCodeChanges(workingDir) {
|
|
602
634
|
try {
|
|
635
|
+
const baseCommit = getBaseCommitForDiff(workingDir);
|
|
603
636
|
// Security: Use spawnSync with explicit args (not shell) to prevent injection
|
|
604
|
-
const result = spawnSync('git', ['diff', '--name-only',
|
|
637
|
+
const result = spawnSync('git', ['diff', '--name-only', baseCommit], {
|
|
605
638
|
cwd: workingDir,
|
|
606
639
|
encoding: 'utf-8',
|
|
607
640
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -624,9 +657,148 @@ export function getSourceCodeChanges(workingDir) {
|
|
|
624
657
|
return ['unknown'];
|
|
625
658
|
}
|
|
626
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Get configuration file changes from git diff
|
|
662
|
+
*
|
|
663
|
+
* Compares current branch HEAD against the base branch merge-base.
|
|
664
|
+
*
|
|
665
|
+
* Detects changes to configuration files including:
|
|
666
|
+
* - .claude/ directory (Agent SDK skills, CLAUDE.md)
|
|
667
|
+
* - .github/ directory (workflows, actions, issue templates)
|
|
668
|
+
* - Root config files (tsconfig.json, package.json, .gitignore, vitest.config.ts, etc.)
|
|
669
|
+
*
|
|
670
|
+
* Uses spawnSync for security (prevents command injection).
|
|
671
|
+
*
|
|
672
|
+
* @param workingDir - Working directory to run git diff in
|
|
673
|
+
* @returns Array of configuration file paths that have changed, or ['unknown'] if git fails
|
|
674
|
+
*/
|
|
675
|
+
export function getConfigurationChanges(workingDir) {
|
|
676
|
+
try {
|
|
677
|
+
const baseCommit = getBaseCommitForDiff(workingDir);
|
|
678
|
+
// Security: Use spawnSync with explicit args (not shell) to prevent injection
|
|
679
|
+
const result = spawnSync('git', ['diff', '--name-only', baseCommit], {
|
|
680
|
+
cwd: workingDir,
|
|
681
|
+
encoding: 'utf-8',
|
|
682
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
683
|
+
});
|
|
684
|
+
if (result.status !== 0) {
|
|
685
|
+
// Git command failed - fail open (assume changes exist)
|
|
686
|
+
return ['unknown'];
|
|
687
|
+
}
|
|
688
|
+
const output = result.stdout.toString();
|
|
689
|
+
return output
|
|
690
|
+
.split('\n')
|
|
691
|
+
.filter(f => f.trim())
|
|
692
|
+
.filter(f => {
|
|
693
|
+
// Configuration directories
|
|
694
|
+
if (f.startsWith('.claude/'))
|
|
695
|
+
return true;
|
|
696
|
+
if (f.startsWith('.github/'))
|
|
697
|
+
return true;
|
|
698
|
+
// Root configuration files (common patterns)
|
|
699
|
+
const rootConfigs = [
|
|
700
|
+
'tsconfig.json',
|
|
701
|
+
'package.json',
|
|
702
|
+
'package-lock.json',
|
|
703
|
+
'.gitignore',
|
|
704
|
+
'.gitattributes',
|
|
705
|
+
'vitest.config.ts',
|
|
706
|
+
'vitest.config.js',
|
|
707
|
+
'jest.config.js',
|
|
708
|
+
'jest.config.ts',
|
|
709
|
+
'.eslintrc',
|
|
710
|
+
'.eslintrc.js',
|
|
711
|
+
'.eslintrc.json',
|
|
712
|
+
'.prettierrc',
|
|
713
|
+
'.prettierrc.js',
|
|
714
|
+
'.prettierrc.json',
|
|
715
|
+
'Makefile',
|
|
716
|
+
'Dockerfile',
|
|
717
|
+
'docker-compose.yml',
|
|
718
|
+
'.env.example',
|
|
719
|
+
];
|
|
720
|
+
return rootConfigs.includes(f);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
// If git diff fails, assume there are changes (fail open, not closed)
|
|
725
|
+
return ['unknown'];
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Get documentation file changes from git diff
|
|
730
|
+
*
|
|
731
|
+
* Compares current branch HEAD against the base branch merge-base.
|
|
732
|
+
*
|
|
733
|
+
* Detects changes to documentation files including:
|
|
734
|
+
* - Markdown files (.md) anywhere in the project (excluding story files)
|
|
735
|
+
* - docs/ directory (any file type)
|
|
736
|
+
*
|
|
737
|
+
* Uses spawnSync for security (prevents command injection).
|
|
738
|
+
*
|
|
739
|
+
* @param workingDir - Working directory to run git diff in
|
|
740
|
+
* @returns Array of documentation file paths that have changed, or ['unknown'] if git fails
|
|
741
|
+
*/
|
|
742
|
+
export function getDocumentationChanges(workingDir) {
|
|
743
|
+
try {
|
|
744
|
+
const baseCommit = getBaseCommitForDiff(workingDir);
|
|
745
|
+
// Security: Use spawnSync with explicit args (not shell) to prevent injection
|
|
746
|
+
const result = spawnSync('git', ['diff', '--name-only', baseCommit], {
|
|
747
|
+
cwd: workingDir,
|
|
748
|
+
encoding: 'utf-8',
|
|
749
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
750
|
+
});
|
|
751
|
+
if (result.status !== 0) {
|
|
752
|
+
// Git command failed - fail open (assume changes exist)
|
|
753
|
+
return ['unknown'];
|
|
754
|
+
}
|
|
755
|
+
const output = result.stdout.toString();
|
|
756
|
+
return output
|
|
757
|
+
.split('\n')
|
|
758
|
+
.filter(f => f.trim())
|
|
759
|
+
.filter(f => {
|
|
760
|
+
// Markdown files (excluding story files in .ai-sdlc/stories/)
|
|
761
|
+
if (f.endsWith('.md') && !f.startsWith('.ai-sdlc/stories/'))
|
|
762
|
+
return true;
|
|
763
|
+
// Files in docs/ directory (any file type - images, diagrams, etc.)
|
|
764
|
+
if (f.startsWith('docs/'))
|
|
765
|
+
return true;
|
|
766
|
+
return false;
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
catch {
|
|
770
|
+
// If git diff fails, assume there are changes (fail open, not closed)
|
|
771
|
+
return ['unknown'];
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Determine the effective content type for validation
|
|
776
|
+
*
|
|
777
|
+
* Resolves the final content type based on story frontmatter fields:
|
|
778
|
+
* 1. If requires_source_changes === false, treat as 'configuration'
|
|
779
|
+
* 2. If requires_source_changes === true, treat as 'code'
|
|
780
|
+
* 3. Otherwise, use content_type field (default: 'code' for backward compatibility)
|
|
781
|
+
*
|
|
782
|
+
* @param story - Story with frontmatter to analyze
|
|
783
|
+
* @returns The effective content type to use for validation
|
|
784
|
+
*/
|
|
785
|
+
export function determineEffectiveContentType(story) {
|
|
786
|
+
const frontmatter = story.frontmatter;
|
|
787
|
+
// Manual override takes precedence
|
|
788
|
+
if (frontmatter.requires_source_changes === false) {
|
|
789
|
+
return 'configuration';
|
|
790
|
+
}
|
|
791
|
+
if (frontmatter.requires_source_changes === true) {
|
|
792
|
+
return 'code';
|
|
793
|
+
}
|
|
794
|
+
// Use explicit content_type or default to 'code'
|
|
795
|
+
return frontmatter.content_type || 'code';
|
|
796
|
+
}
|
|
627
797
|
/**
|
|
628
798
|
* Check if test files exist in git diff
|
|
629
799
|
*
|
|
800
|
+
* Compares current branch HEAD against the base branch merge-base.
|
|
801
|
+
*
|
|
630
802
|
* Returns true if any test files have been modified/added, false otherwise.
|
|
631
803
|
* Uses spawnSync for security (prevents command injection).
|
|
632
804
|
*
|
|
@@ -635,8 +807,9 @@ export function getSourceCodeChanges(workingDir) {
|
|
|
635
807
|
*/
|
|
636
808
|
export function hasTestFiles(workingDir) {
|
|
637
809
|
try {
|
|
810
|
+
const baseCommit = getBaseCommitForDiff(workingDir);
|
|
638
811
|
// Security: Use spawnSync with explicit args (not shell) to prevent injection
|
|
639
|
-
const result = spawnSync('git', ['diff', '--name-only',
|
|
812
|
+
const result = spawnSync('git', ['diff', '--name-only', baseCommit], {
|
|
640
813
|
cwd: workingDir,
|
|
641
814
|
encoding: 'utf-8',
|
|
642
815
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -813,94 +986,193 @@ export async function runReviewAgent(storyPath, sdlcRoot, options) {
|
|
|
813
986
|
feedback: errorMsg,
|
|
814
987
|
};
|
|
815
988
|
}
|
|
816
|
-
// PRE-CHECK GATE:
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
|
|
989
|
+
// PRE-CHECK GATE: Content type-aware validation before running expensive LLM reviews
|
|
990
|
+
const contentType = determineEffectiveContentType(story);
|
|
991
|
+
logger.info('review', 'Running content-type-specific validation', {
|
|
992
|
+
storyId: story.frontmatter.id,
|
|
993
|
+
contentType,
|
|
994
|
+
explicitContentType: story.frontmatter.content_type,
|
|
995
|
+
requiresSourceChanges: story.frontmatter.requires_source_changes,
|
|
996
|
+
});
|
|
997
|
+
// Validation flags
|
|
998
|
+
let validationFailed = false;
|
|
999
|
+
let validationReason = '';
|
|
1000
|
+
let validationCategory = 'implementation';
|
|
1001
|
+
// Check source code changes for 'code' and 'mixed' types
|
|
1002
|
+
if (contentType === 'code' || contentType === 'mixed') {
|
|
1003
|
+
const sourceChanges = getSourceCodeChanges(workingDir);
|
|
1004
|
+
if (sourceChanges.length === 0) {
|
|
1005
|
+
validationFailed = true;
|
|
1006
|
+
validationReason = contentType === 'mixed'
|
|
1007
|
+
? 'Mixed story requires both source AND configuration changes - no source code was modified.'
|
|
1008
|
+
: 'Implementation wrote documentation/planning only - no source code was modified.';
|
|
1009
|
+
logger.warn('review', 'Source code validation failed', {
|
|
1010
|
+
storyId: story.frontmatter.id,
|
|
1011
|
+
contentType,
|
|
1012
|
+
sourceChangesFound: sourceChanges.length,
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
logger.info('review', 'Source code changes detected', {
|
|
1017
|
+
storyId: story.frontmatter.id,
|
|
1018
|
+
fileCount: sourceChanges.length,
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
// Check configuration changes for 'configuration' and 'mixed' types
|
|
1023
|
+
if (!validationFailed && (contentType === 'configuration' || contentType === 'mixed')) {
|
|
1024
|
+
const configChanges = getConfigurationChanges(workingDir);
|
|
1025
|
+
if (configChanges.length === 0) {
|
|
1026
|
+
validationFailed = true;
|
|
1027
|
+
validationReason = contentType === 'mixed'
|
|
1028
|
+
? 'Mixed story requires both source AND configuration changes. No configuration file changes detected.'
|
|
1029
|
+
: 'Configuration story requires changes to config files (.claude/, .github/, or root config files). No configuration changes detected.';
|
|
1030
|
+
logger.warn('review', 'Configuration validation failed', {
|
|
1031
|
+
storyId: story.frontmatter.id,
|
|
1032
|
+
contentType,
|
|
1033
|
+
configChangesFound: configChanges.length,
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
logger.info('review', 'Configuration changes detected', {
|
|
1038
|
+
storyId: story.frontmatter.id,
|
|
1039
|
+
fileCount: configChanges.length,
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
// Check documentation changes for 'documentation' type
|
|
1044
|
+
if (!validationFailed && contentType === 'documentation') {
|
|
1045
|
+
const docChanges = getDocumentationChanges(workingDir);
|
|
1046
|
+
if (docChanges.length === 0) {
|
|
1047
|
+
validationFailed = true;
|
|
1048
|
+
validationReason = 'Documentation story requires changes to markdown files (.md) or docs/ directory. No documentation changes detected.';
|
|
1049
|
+
logger.warn('review', 'Documentation validation failed', {
|
|
1050
|
+
storyId: story.frontmatter.id,
|
|
1051
|
+
contentType,
|
|
1052
|
+
docChangesFound: docChanges.length,
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
logger.info('review', 'Documentation changes detected', {
|
|
1057
|
+
storyId: story.frontmatter.id,
|
|
1058
|
+
fileCount: docChanges.length,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
// Handle validation failure (if any)
|
|
1063
|
+
if (validationFailed) {
|
|
820
1064
|
const retryCount = story.frontmatter.implementation_retry_count || 0;
|
|
821
1065
|
const maxRetries = getEffectiveMaxImplementationRetries(story, config);
|
|
822
1066
|
if (retryCount < maxRetries) {
|
|
823
1067
|
// RECOVERABLE: Trigger implementation recovery
|
|
824
|
-
logger.warn('review', '
|
|
1068
|
+
logger.warn('review', 'Validation failed - triggering implementation recovery', {
|
|
825
1069
|
storyId: story.frontmatter.id,
|
|
826
1070
|
retryCount,
|
|
827
1071
|
maxRetries,
|
|
1072
|
+
contentType,
|
|
828
1073
|
});
|
|
829
1074
|
await updateStoryField(story, 'implementation_complete', false);
|
|
830
|
-
|
|
1075
|
+
// Set restart reason based on content type
|
|
1076
|
+
const restartReason = contentType === 'configuration'
|
|
1077
|
+
? 'Configuration story requires changes to config files (.claude/, .github/, or root config files). No configuration changes detected.'
|
|
1078
|
+
: contentType === 'mixed'
|
|
1079
|
+
? 'Mixed story requires both source AND configuration changes - no source code was modified.'
|
|
1080
|
+
: contentType === 'documentation'
|
|
1081
|
+
? 'Documentation story requires changes to markdown files (.md) or docs/ directory. No documentation changes detected.'
|
|
1082
|
+
: 'No source code changes detected. Implementation wrote documentation only.';
|
|
1083
|
+
await updateStoryField(story, 'last_restart_reason', restartReason);
|
|
1084
|
+
// Create user-friendly recovery description
|
|
1085
|
+
const recoveryDescription = contentType === 'configuration'
|
|
1086
|
+
? 'No configuration file modifications detected. Re-running implementation phase.'
|
|
1087
|
+
: contentType === 'mixed'
|
|
1088
|
+
? 'No source code modifications detected. Re-running implementation phase.'
|
|
1089
|
+
: contentType === 'documentation'
|
|
1090
|
+
? 'No documentation file modifications detected. Re-running implementation phase.'
|
|
1091
|
+
: 'No source code modifications detected. Re-running implementation phase.';
|
|
831
1092
|
return {
|
|
832
1093
|
success: true,
|
|
833
1094
|
story: parseStory(storyPath),
|
|
834
|
-
changesMade: ['Detected
|
|
1095
|
+
changesMade: ['Detected incomplete implementation', 'Triggered implementation recovery'],
|
|
835
1096
|
passed: false,
|
|
836
1097
|
decision: ReviewDecision.RECOVERY,
|
|
837
1098
|
reviewType: 'pre-check',
|
|
838
1099
|
issues: [{
|
|
839
1100
|
severity: 'critical',
|
|
840
|
-
category:
|
|
841
|
-
description:
|
|
1101
|
+
category: validationCategory,
|
|
1102
|
+
description: recoveryDescription,
|
|
842
1103
|
}],
|
|
843
|
-
feedback:
|
|
1104
|
+
feedback: `Implementation recovery triggered - ${validationReason}`,
|
|
844
1105
|
};
|
|
845
1106
|
}
|
|
846
1107
|
else {
|
|
847
1108
|
// NON-RECOVERABLE: Max retries reached
|
|
848
1109
|
const maxRetriesDisplay = Number.isFinite(maxRetries) ? maxRetries : '∞';
|
|
849
|
-
logger.error('review', '
|
|
1110
|
+
logger.error('review', 'Validation failed and max implementation retries reached', {
|
|
850
1111
|
storyId: story.frontmatter.id,
|
|
851
1112
|
retryCount,
|
|
852
1113
|
maxRetries,
|
|
1114
|
+
contentType,
|
|
853
1115
|
});
|
|
854
1116
|
return {
|
|
855
1117
|
success: true,
|
|
856
1118
|
story: parseStory(storyPath),
|
|
857
|
-
changesMade: ['Detected
|
|
1119
|
+
changesMade: ['Detected incomplete implementation', 'Max retries reached'],
|
|
858
1120
|
passed: false,
|
|
859
1121
|
decision: ReviewDecision.FAILED,
|
|
860
1122
|
severity: ReviewSeverity.CRITICAL,
|
|
861
1123
|
reviewType: 'pre-check',
|
|
862
1124
|
issues: [{
|
|
863
1125
|
severity: 'blocker',
|
|
864
|
-
category:
|
|
865
|
-
description:
|
|
866
|
-
suggestedFix: 'Review the story requirements and implementation plan.
|
|
1126
|
+
category: validationCategory,
|
|
1127
|
+
description: `${validationReason} This has occurred ${retryCount} time(s) (max: ${maxRetriesDisplay}). Manual intervention required.`,
|
|
1128
|
+
suggestedFix: 'Review the story requirements and implementation plan. Verify the content_type field matches the expected implementation. Consider simplifying the story or providing more explicit guidance.',
|
|
867
1129
|
}],
|
|
868
|
-
feedback: 'Implementation failed
|
|
1130
|
+
feedback: 'Implementation failed validation after multiple attempts.',
|
|
869
1131
|
};
|
|
870
1132
|
}
|
|
871
1133
|
}
|
|
872
|
-
//
|
|
873
|
-
logger.info('review', '
|
|
1134
|
+
// Validation passed - proceed with normal review flow
|
|
1135
|
+
logger.info('review', 'Content validation passed - proceeding with verification', {
|
|
874
1136
|
storyId: story.frontmatter.id,
|
|
875
|
-
|
|
1137
|
+
contentType,
|
|
876
1138
|
});
|
|
877
|
-
// PRE-CHECK GATE: Check if test files exist
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1139
|
+
// PRE-CHECK GATE: Check if test files exist (only for code/mixed types)
|
|
1140
|
+
// Documentation and configuration stories don't require test files
|
|
1141
|
+
const requiresTests = contentType === 'code' || contentType === 'mixed';
|
|
1142
|
+
if (requiresTests) {
|
|
1143
|
+
const testsExist = hasTestFiles(workingDir);
|
|
1144
|
+
if (!testsExist) {
|
|
1145
|
+
logger.warn('review', 'No test files detected in implementation changes', {
|
|
1146
|
+
storyId: story.frontmatter.id,
|
|
1147
|
+
});
|
|
1148
|
+
return {
|
|
1149
|
+
success: true,
|
|
1150
|
+
story: parseStory(storyPath),
|
|
1151
|
+
changesMade: ['No test files found for implementation'],
|
|
1152
|
+
passed: false,
|
|
1153
|
+
decision: ReviewDecision.REJECTED,
|
|
1154
|
+
severity: ReviewSeverity.CRITICAL,
|
|
1155
|
+
reviewType: 'pre-check',
|
|
1156
|
+
issues: [{
|
|
1157
|
+
severity: 'blocker',
|
|
1158
|
+
category: 'testing',
|
|
1159
|
+
description: 'No tests found for this implementation. All implementations must include tests.',
|
|
1160
|
+
suggestedFix: 'Add test files (*.test.ts, *.spec.ts, or files in __tests__/ directory) that verify the implementation.',
|
|
1161
|
+
}],
|
|
1162
|
+
feedback: formatIssuesForDisplay([{
|
|
1163
|
+
severity: 'blocker',
|
|
1164
|
+
category: 'testing',
|
|
1165
|
+
description: 'No tests found for this implementation. All implementations must include tests.',
|
|
1166
|
+
suggestedFix: 'Add test files (*.test.ts, *.spec.ts, or files in __tests__/ directory) that verify the implementation.',
|
|
1167
|
+
}]),
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
logger.info('review', 'Test file check skipped for non-code content type', {
|
|
881
1173
|
storyId: story.frontmatter.id,
|
|
1174
|
+
contentType,
|
|
882
1175
|
});
|
|
883
|
-
return {
|
|
884
|
-
success: true,
|
|
885
|
-
story: parseStory(storyPath),
|
|
886
|
-
changesMade: ['No test files found for implementation'],
|
|
887
|
-
passed: false,
|
|
888
|
-
decision: ReviewDecision.REJECTED,
|
|
889
|
-
severity: ReviewSeverity.CRITICAL,
|
|
890
|
-
reviewType: 'pre-check',
|
|
891
|
-
issues: [{
|
|
892
|
-
severity: 'blocker',
|
|
893
|
-
category: 'testing',
|
|
894
|
-
description: 'No tests found for this implementation. All implementations must include tests.',
|
|
895
|
-
suggestedFix: 'Add test files (*.test.ts, *.spec.ts, or files in __tests__/ directory) that verify the implementation.',
|
|
896
|
-
}],
|
|
897
|
-
feedback: formatIssuesForDisplay([{
|
|
898
|
-
severity: 'blocker',
|
|
899
|
-
category: 'testing',
|
|
900
|
-
description: 'No tests found for this implementation. All implementations must include tests.',
|
|
901
|
-
suggestedFix: 'Add test files (*.test.ts, *.spec.ts, or files in __tests__/ directory) that verify the implementation.',
|
|
902
|
-
}]),
|
|
903
|
-
};
|
|
904
1176
|
}
|
|
905
1177
|
// Run build and tests BEFORE reviews (async with progress)
|
|
906
1178
|
changesMade.push('Running build and test verification...');
|