@ai-sdlc/orchestrator 0.5.0 → 0.6.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.
Files changed (47) hide show
  1. package/dist/action-enforcement.d.ts +26 -0
  2. package/dist/action-enforcement.js +70 -0
  3. package/dist/admission-score.d.ts +58 -0
  4. package/dist/admission-score.js +164 -0
  5. package/dist/cycle-utils.d.ts +51 -0
  6. package/dist/cycle-utils.js +77 -0
  7. package/dist/defaults.d.ts +5 -0
  8. package/dist/defaults.js +5 -0
  9. package/dist/execute.js +121 -26
  10. package/dist/fix-ci.js +32 -2
  11. package/dist/fix-review.d.ts +66 -0
  12. package/dist/fix-review.js +441 -0
  13. package/dist/index.d.ts +10 -2
  14. package/dist/index.js +13 -1
  15. package/dist/pipeline-cycle-detector.d.ts +70 -0
  16. package/dist/pipeline-cycle-detector.js +111 -0
  17. package/dist/priority.d.ts +2 -76
  18. package/dist/review.d.ts +31 -0
  19. package/dist/review.js +74 -0
  20. package/dist/runners/claude-code.js +314 -32
  21. package/dist/runners/index.d.ts +2 -1
  22. package/dist/runners/index.js +1 -0
  23. package/dist/runners/review-agent.d.ts +47 -0
  24. package/dist/runners/review-agent.js +220 -0
  25. package/dist/runners/security-triage.js +4 -0
  26. package/dist/runners/types.d.ts +19 -0
  27. package/dist/state/index.d.ts +1 -1
  28. package/dist/state/schema.d.ts +2 -1
  29. package/dist/state/schema.js +54 -1
  30. package/dist/state/store.d.ts +17 -1
  31. package/dist/state/store.js +122 -0
  32. package/dist/state/types.d.ts +35 -0
  33. package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
  34. package/dist/workflow-patterns/artifact-writer.js +34 -0
  35. package/dist/workflow-patterns/classifiers.d.ts +10 -0
  36. package/dist/workflow-patterns/classifiers.js +72 -0
  37. package/dist/workflow-patterns/detector.d.ts +27 -0
  38. package/dist/workflow-patterns/detector.js +186 -0
  39. package/dist/workflow-patterns/index.d.ts +8 -0
  40. package/dist/workflow-patterns/index.js +7 -0
  41. package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
  42. package/dist/workflow-patterns/proposal-generator.js +183 -0
  43. package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
  44. package/dist/workflow-patterns/telemetry-ingest.js +103 -0
  45. package/dist/workflow-patterns/types.d.ts +61 -0
  46. package/dist/workflow-patterns/types.js +11 -0
  47. package/package.json +2 -2
@@ -0,0 +1,111 @@
1
+ /**
2
+ * PipelineCycleDetector — tracks stage invocations per issue/PR across
3
+ * all workflow runs to detect infinite orchestration loops.
4
+ *
5
+ * Uses GitHub issue/PR comments as shared state (hidden HTML markers)
6
+ * to work across workflow boundaries.
7
+ */
8
+ /**
9
+ * Default max invocations per stage.
10
+ * These must be HIGHER than the per-stage retry limits (which are the
11
+ * primary guard). The cycle detector is a safety net for cross-stage
12
+ * loops, not a replacement for retry counting.
13
+ *
14
+ * Per-stage retry limits: fix-ci=2, fix-review=2
15
+ * Cycle limits: set to retry_limit + 2 to allow retries + headroom
16
+ */
17
+ export const DEFAULT_CYCLE_LIMITS = {
18
+ admission: 5,
19
+ triage: 5,
20
+ agent: 5,
21
+ review: 4,
22
+ 'fix-ci': 4,
23
+ 'fix-review': 4,
24
+ };
25
+ /**
26
+ * Generate HTML comment marker for a stage invocation.
27
+ * Format: <!-- ai-sdlc-cycle:{stage}:{timestamp} -->
28
+ */
29
+ export function createStageMarker(stage) {
30
+ const timestamp = Date.now();
31
+ return `<!-- ai-sdlc-cycle:${stage}:${timestamp} -->`;
32
+ }
33
+ /**
34
+ * Parse stage invocation markers from comment bodies.
35
+ * Returns a map of stage -> invocation count.
36
+ */
37
+ export function parseStageInvocations(comments) {
38
+ const counts = new Map();
39
+ // Non-backtracking pattern: stage names are alphanumeric with optional single hyphens
40
+ const markerPattern = /<!-- ai-sdlc-cycle:([a-z][a-z0-9-]{0,30}):(\d{1,15}) -->/g;
41
+ for (const body of comments) {
42
+ let match;
43
+ while ((match = markerPattern.exec(body)) !== null) {
44
+ const stage = match[1];
45
+ counts.set(stage, (counts.get(stage) ?? 0) + 1);
46
+ }
47
+ }
48
+ return counts;
49
+ }
50
+ export class PipelineCycleDetector {
51
+ config;
52
+ constructor(config) {
53
+ this.config = {
54
+ maxInvocations: { ...DEFAULT_CYCLE_LIMITS, ...config?.maxInvocations },
55
+ };
56
+ }
57
+ /**
58
+ * Check if a cycle exists for the given issue/PR by analyzing comment history.
59
+ * @param pendingStage — if set, adds +1 to this stage's count to account for the upcoming invocation
60
+ */
61
+ async detectCycle(tracker, issueOrPrId, pendingStage) {
62
+ const comments = await tracker.getComments(issueOrPrId);
63
+ const commentBodies = comments.map((c) => c.body);
64
+ return this.detectCycleFromComments(commentBodies, pendingStage);
65
+ }
66
+ /**
67
+ * Detect cycle from comment bodies (for testing without IssueTracker).
68
+ * @param pendingStage — if set, adds +1 to this stage's count for the pending invocation
69
+ */
70
+ detectCycleFromComments(comments, pendingStage) {
71
+ const invocations = parseStageInvocations(comments);
72
+ // Account for the pending invocation that hasn't been recorded yet
73
+ if (pendingStage) {
74
+ invocations.set(pendingStage, (invocations.get(pendingStage) ?? 0) + 1);
75
+ }
76
+ const loopingStages = [];
77
+ let totalInvocations = 0;
78
+ for (const [stage, count] of invocations.entries()) {
79
+ totalInvocations += count;
80
+ const max = this.config.maxInvocations[stage];
81
+ if (count >= max) {
82
+ loopingStages.push({ stage, count, max });
83
+ }
84
+ }
85
+ return {
86
+ cycleDetected: loopingStages.length > 0,
87
+ loopingStages,
88
+ totalInvocations,
89
+ };
90
+ }
91
+ /**
92
+ * Record a stage invocation by creating a marker.
93
+ * The caller should append this to their comment.
94
+ */
95
+ recordInvocation(stage) {
96
+ return createStageMarker(stage);
97
+ }
98
+ /**
99
+ * Get the max invocation limit for a stage.
100
+ */
101
+ getMaxInvocations(stage) {
102
+ return this.config.maxInvocations[stage];
103
+ }
104
+ /**
105
+ * Update max invocations for specific stages.
106
+ */
107
+ updateMaxInvocations(overrides) {
108
+ this.config.maxInvocations = { ...this.config.maxInvocations, ...overrides };
109
+ }
110
+ }
111
+ //# sourceMappingURL=pipeline-cycle-detector.js.map
@@ -10,82 +10,8 @@
10
10
  *
11
11
  * RFC reference: PPA section (priority scoring).
12
12
  */
13
- export interface PriorityScore {
14
- composite: number;
15
- dimensions: {
16
- soulAlignment: number;
17
- demandPressure: number;
18
- marketForce: number;
19
- executionReality: number;
20
- entropyTax: number;
21
- humanCurve: number;
22
- calibration: number;
23
- };
24
- confidence: number;
25
- timestamp: string;
26
- /** Present when the score was produced via override. */
27
- override?: {
28
- reason: string;
29
- expiry?: string;
30
- };
31
- }
32
- export interface PriorityInput {
33
- /** Work item identifier */
34
- itemId: string;
35
- /** Work item title and description for semantic analysis */
36
- title: string;
37
- description: string;
38
- /** Labels/tags on the work item */
39
- labels?: string[];
40
- /** Pre-computed soul alignment score, or undefined to skip */
41
- soulAlignment?: number;
42
- /** Number of customer requests for this feature */
43
- customerRequestCount?: number;
44
- /** Recency-weighted demand signal [0, 1] */
45
- demandSignal?: number;
46
- /** Bug severity if this is a bug (1-5, 5=critical) */
47
- bugSeverity?: number;
48
- /** Builder conviction / roadmap priority [0, 1] */
49
- builderConviction?: number;
50
- /** Technology inflection relevance [0, 1] */
51
- techInflection?: number;
52
- /** Competitive pressure relevance [0, 1] */
53
- competitivePressure?: number;
54
- /** Regulatory urgency [0, 1] */
55
- regulatoryUrgency?: number;
56
- /** Task complexity from parseComplexity() (1-10) */
57
- complexity?: number;
58
- /** Budget utilization percent from CostTracker */
59
- budgetUtilization?: number;
60
- /** Are dependencies clear? [0, 1] */
61
- dependencyClearance?: number;
62
- /** Competitive drift score [0, 1] */
63
- competitiveDrift?: number;
64
- /** Market divergence [0, 1] */
65
- marketDivergence?: number;
66
- /** Explicit priority from backlog tool [0, 1] */
67
- explicitPriority?: number;
68
- /** Team consensus signal (votes, watchers) [0, 1] */
69
- teamConsensus?: number;
70
- /** Meeting decision weight [0, 1] */
71
- meetingDecision?: number;
72
- /** Override flag — if true, bypasses algorithm */
73
- override?: boolean;
74
- /** Override reason (required when override=true) */
75
- overrideReason?: string;
76
- /** Override expiry ISO timestamp */
77
- overrideExpiry?: string;
78
- }
79
- export interface PriorityConfig {
80
- /** Weights for human curve sub-components */
81
- humanCurveWeights?: {
82
- explicit?: number;
83
- consensus?: number;
84
- decision?: number;
85
- };
86
- /** Calibration coefficient (default 1.0) */
87
- calibrationCoefficient?: number;
88
- }
13
+ import type { PriorityScore, PriorityInput, PriorityConfig } from '@ai-sdlc/reference';
14
+ export type { PriorityScore, PriorityInput, PriorityConfig };
89
15
  /**
90
16
  * Compute the PPA composite priority score for a single work item.
91
17
  *
@@ -0,0 +1,31 @@
1
+ /**
2
+ * PR Review orchestrator — thin wrapper around ReviewAgentRunner
3
+ * that handles context assembly and verdict extraction.
4
+ */
5
+ import { ReviewAgentRunner, type ReviewType, type ReviewVerdict, type ReviewAgentConfig } from './runners/review-agent.js';
6
+ import type { Logger } from './logger.js';
7
+ export interface ReviewContext {
8
+ issueTitle: string;
9
+ issueBody: string;
10
+ acceptanceCriteria?: string;
11
+ }
12
+ export interface ReviewOptions {
13
+ /** Anthropic API config overrides. */
14
+ apiConfig?: Omit<ReviewAgentConfig, 'reviewType'>;
15
+ /** Logger for diagnostic output. */
16
+ logger?: Logger;
17
+ /** Inject runner for testing. */
18
+ runner?: ReviewAgentRunner;
19
+ }
20
+ /**
21
+ * Execute a single review agent against a PR diff.
22
+ *
23
+ * @param prNumber - PR number (for logging/identification)
24
+ * @param diff - The full PR diff text
25
+ * @param reviewType - Which review perspective (testing, critic, security)
26
+ * @param context - Issue context for the review
27
+ * @param options - Optional configuration overrides
28
+ * @returns Review verdict with findings
29
+ */
30
+ export declare function executeReview(prNumber: number, diff: string, reviewType: ReviewType, context: ReviewContext, options?: ReviewOptions): Promise<ReviewVerdict>;
31
+ //# sourceMappingURL=review.d.ts.map
package/dist/review.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * PR Review orchestrator — thin wrapper around ReviewAgentRunner
3
+ * that handles context assembly and verdict extraction.
4
+ */
5
+ import { ReviewAgentRunner, } from './runners/review-agent.js';
6
+ // ── Public API ───────────────────────────────────────────────────────
7
+ /**
8
+ * Execute a single review agent against a PR diff.
9
+ *
10
+ * @param prNumber - PR number (for logging/identification)
11
+ * @param diff - The full PR diff text
12
+ * @param reviewType - Which review perspective (testing, critic, security)
13
+ * @param context - Issue context for the review
14
+ * @param options - Optional configuration overrides
15
+ * @returns Review verdict with findings
16
+ */
17
+ export async function executeReview(prNumber, diff, reviewType, context, options) {
18
+ const logger = options?.logger;
19
+ logger?.info?.(`Starting ${reviewType} review for PR #${prNumber}`);
20
+ const runner = options?.runner ??
21
+ new ReviewAgentRunner({
22
+ ...options?.apiConfig,
23
+ reviewType,
24
+ });
25
+ const result = await runner.run({
26
+ issueId: `PR-${prNumber}`,
27
+ issueNumber: prNumber,
28
+ issueTitle: context.issueTitle,
29
+ issueBody: diff,
30
+ workDir: '',
31
+ branch: '',
32
+ constraints: {
33
+ maxFilesPerChange: 0,
34
+ requireTests: false,
35
+ blockedPaths: [],
36
+ },
37
+ // Reuse ciErrors field for acceptance criteria
38
+ ciErrors: context.acceptanceCriteria,
39
+ });
40
+ if (!result.success) {
41
+ logger?.error?.(`${reviewType} review failed: ${result.error}`);
42
+ return {
43
+ type: reviewType,
44
+ approved: false,
45
+ findings: [
46
+ {
47
+ severity: 'critical',
48
+ message: `Review agent failed: ${result.error ?? 'unknown error'}`,
49
+ },
50
+ ],
51
+ summary: `${reviewType} review could not be completed`,
52
+ };
53
+ }
54
+ try {
55
+ const verdict = JSON.parse(result.summary);
56
+ logger?.info?.(`${reviewType} review complete: ${verdict.approved ? 'APPROVED' : 'CHANGES REQUESTED'} (${verdict.findings.length} findings)`);
57
+ return { ...verdict, type: reviewType };
58
+ }
59
+ catch {
60
+ logger?.error?.(`Failed to parse ${reviewType} verdict from runner output`);
61
+ return {
62
+ type: reviewType,
63
+ approved: false,
64
+ findings: [
65
+ {
66
+ severity: 'critical',
67
+ message: 'Failed to parse review verdict from runner output',
68
+ },
69
+ ],
70
+ summary: `${reviewType} review verdict was not valid JSON`,
71
+ };
72
+ }
73
+ }
74
+ //# sourceMappingURL=review.js.map