@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.
- package/dist/action-enforcement.d.ts +26 -0
- package/dist/action-enforcement.js +70 -0
- package/dist/admission-score.d.ts +58 -0
- package/dist/admission-score.js +164 -0
- package/dist/cycle-utils.d.ts +51 -0
- package/dist/cycle-utils.js +77 -0
- package/dist/defaults.d.ts +5 -0
- package/dist/defaults.js +5 -0
- package/dist/execute.js +121 -26
- package/dist/fix-ci.js +32 -2
- package/dist/fix-review.d.ts +66 -0
- package/dist/fix-review.js +441 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +13 -1
- package/dist/pipeline-cycle-detector.d.ts +70 -0
- package/dist/pipeline-cycle-detector.js +111 -0
- package/dist/priority.d.ts +2 -76
- package/dist/review.d.ts +31 -0
- package/dist/review.js +74 -0
- package/dist/runners/claude-code.js +314 -32
- package/dist/runners/index.d.ts +2 -1
- package/dist/runners/index.js +1 -0
- package/dist/runners/review-agent.d.ts +47 -0
- package/dist/runners/review-agent.js +220 -0
- package/dist/runners/security-triage.js +4 -0
- package/dist/runners/types.d.ts +19 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/schema.d.ts +2 -1
- package/dist/state/schema.js +54 -1
- package/dist/state/store.d.ts +17 -1
- package/dist/state/store.js +122 -0
- package/dist/state/types.d.ts +35 -0
- package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
- package/dist/workflow-patterns/artifact-writer.js +34 -0
- package/dist/workflow-patterns/classifiers.d.ts +10 -0
- package/dist/workflow-patterns/classifiers.js +72 -0
- package/dist/workflow-patterns/detector.d.ts +27 -0
- package/dist/workflow-patterns/detector.js +186 -0
- package/dist/workflow-patterns/index.d.ts +8 -0
- package/dist/workflow-patterns/index.js +7 -0
- package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
- package/dist/workflow-patterns/proposal-generator.js +183 -0
- package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
- package/dist/workflow-patterns/telemetry-ingest.js +103 -0
- package/dist/workflow-patterns/types.d.ts +61 -0
- package/dist/workflow-patterns/types.js +11 -0
- 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
|
package/dist/priority.d.ts
CHANGED
|
@@ -10,82 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* RFC reference: PPA section (priority scoring).
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
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
|
*
|
package/dist/review.d.ts
ADDED
|
@@ -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
|