@ai-sdlc/orchestrator 0.5.0 → 0.9.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 (227) hide show
  1. package/dist/action-enforcement.d.ts +26 -0
  2. package/dist/action-enforcement.js +70 -0
  3. package/dist/admission-composite.d.ts +67 -0
  4. package/dist/admission-composite.js +158 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +331 -0
  7. package/dist/admission-hc.d.ts +62 -0
  8. package/dist/admission-hc.js +83 -0
  9. package/dist/admission-score.d.ts +148 -0
  10. package/dist/admission-score.js +237 -0
  11. package/dist/analysis/analyzer.js +3 -2
  12. package/dist/analysis/convention-detector.d.ts +85 -2
  13. package/dist/analysis/convention-detector.js +375 -70
  14. package/dist/analysis/diff-analyzer.d.ts +33 -0
  15. package/dist/analysis/diff-analyzer.js +122 -0
  16. package/dist/analysis/hotspot-analyzer.js +3 -1
  17. package/dist/analysis/index.d.ts +2 -1
  18. package/dist/analysis/index.js +2 -1
  19. package/dist/artifacts/index.d.ts +65 -0
  20. package/dist/artifacts/index.js +142 -0
  21. package/dist/backlog-adapter.d.ts +121 -0
  22. package/dist/backlog-adapter.js +438 -0
  23. package/dist/calibration.d.ts +43 -0
  24. package/dist/calibration.js +76 -0
  25. package/dist/cli/commands/agents.d.ts +24 -0
  26. package/dist/cli/commands/agents.js +66 -1
  27. package/dist/cli/commands/git-remote.d.ts +49 -0
  28. package/dist/cli/commands/git-remote.js +91 -0
  29. package/dist/cli/commands/health.d.ts +4 -0
  30. package/dist/cli/commands/health.js +13 -1
  31. package/dist/cli/commands/init.d.ts +26 -0
  32. package/dist/cli/commands/init.js +164 -22
  33. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  34. package/dist/cli/commands/mcp-setup.js +78 -8
  35. package/dist/cli/formatters/table.js +15 -2
  36. package/dist/cli/index.d.ts +14 -1
  37. package/dist/cli/index.js +81 -20
  38. package/dist/cli/versions.d.ts +57 -0
  39. package/dist/cli/versions.js +128 -0
  40. package/dist/code-area-classifier.d.ts +21 -0
  41. package/dist/code-area-classifier.js +48 -0
  42. package/dist/config.d.ts +33 -1
  43. package/dist/config.js +78 -8
  44. package/dist/cycle-utils.d.ts +51 -0
  45. package/dist/cycle-utils.js +77 -0
  46. package/dist/database/adapters/external.d.ts +24 -0
  47. package/dist/database/adapters/external.js +80 -0
  48. package/dist/database/adapters/neon.d.ts +41 -0
  49. package/dist/database/adapters/neon.js +98 -0
  50. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  51. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  52. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  53. package/dist/database/adapters/sqlite-copy.js +145 -0
  54. package/dist/database/connection-injection.d.ts +35 -0
  55. package/dist/database/connection-injection.js +93 -0
  56. package/dist/database/index.d.ts +12 -0
  57. package/dist/database/index.js +23 -0
  58. package/dist/database/registry.d.ts +13 -0
  59. package/dist/database/registry.js +27 -0
  60. package/dist/database/topology.d.ts +52 -0
  61. package/dist/database/topology.js +44 -0
  62. package/dist/database/types.d.ts +89 -0
  63. package/dist/database/types.js +26 -0
  64. package/dist/defaults.d.ts +5 -0
  65. package/dist/defaults.js +5 -0
  66. package/dist/design-authority.d.ts +40 -0
  67. package/dist/design-authority.js +71 -0
  68. package/dist/design-lookahead.d.ts +64 -0
  69. package/dist/design-lookahead.js +86 -0
  70. package/dist/design-quality-trend.d.ts +87 -0
  71. package/dist/design-quality-trend.js +190 -0
  72. package/dist/design-system-context.d.ts +46 -0
  73. package/dist/design-system-context.js +80 -0
  74. package/dist/design-system-correction-loop.d.ts +64 -0
  75. package/dist/design-system-correction-loop.js +128 -0
  76. package/dist/design-system-metrics.d.ts +61 -0
  77. package/dist/design-system-metrics.js +104 -0
  78. package/dist/design-system-stewardship.d.ts +22 -0
  79. package/dist/design-system-stewardship.js +85 -0
  80. package/dist/design-system-validation.d.ts +37 -0
  81. package/dist/design-system-validation.js +88 -0
  82. package/dist/dispatch/index.d.ts +4 -0
  83. package/dist/dispatch/index.js +4 -0
  84. package/dist/dispatch/merge-gate.d.ts +46 -0
  85. package/dist/dispatch/merge-gate.js +90 -0
  86. package/dist/dispatch/requeue.d.ts +57 -0
  87. package/dist/dispatch/requeue.js +131 -0
  88. package/dist/dispatch/worker-pool.d.ts +62 -0
  89. package/dist/dispatch/worker-pool.js +60 -0
  90. package/dist/execute.d.ts +40 -0
  91. package/dist/execute.js +295 -30
  92. package/dist/fix-ci.js +40 -3
  93. package/dist/fix-review.d.ts +66 -0
  94. package/dist/fix-review.js +448 -0
  95. package/dist/harness/adapters/claude-code.d.ts +29 -0
  96. package/dist/harness/adapters/claude-code.js +191 -0
  97. package/dist/harness/adapters/codex.d.ts +25 -0
  98. package/dist/harness/adapters/codex.js +61 -0
  99. package/dist/harness/independence.d.ts +51 -0
  100. package/dist/harness/independence.js +67 -0
  101. package/dist/harness/index.d.ts +14 -0
  102. package/dist/harness/index.js +20 -0
  103. package/dist/harness/registry.d.ts +17 -0
  104. package/dist/harness/registry.js +31 -0
  105. package/dist/harness/types.d.ts +123 -0
  106. package/dist/harness/types.js +8 -0
  107. package/dist/harness/version-probe.d.ts +14 -0
  108. package/dist/harness/version-probe.js +123 -0
  109. package/dist/index.d.ts +39 -5
  110. package/dist/index.js +48 -3
  111. package/dist/models/classifier.d.ts +76 -0
  112. package/dist/models/classifier.js +221 -0
  113. package/dist/models/index.d.ts +3 -0
  114. package/dist/models/index.js +3 -0
  115. package/dist/models/registry.d.ts +97 -0
  116. package/dist/models/registry.js +173 -0
  117. package/dist/pillar-breakdown.d.ts +63 -0
  118. package/dist/pillar-breakdown.js +153 -0
  119. package/dist/pipeline-cycle-detector.d.ts +70 -0
  120. package/dist/pipeline-cycle-detector.js +111 -0
  121. package/dist/priority.d.ts +7 -76
  122. package/dist/priority.js +18 -5
  123. package/dist/review-meta.d.ts +65 -0
  124. package/dist/review-meta.js +149 -0
  125. package/dist/review.d.ts +35 -0
  126. package/dist/review.js +84 -0
  127. package/dist/runners/claude-code-sdk.d.ts +31 -0
  128. package/dist/runners/claude-code-sdk.js +238 -0
  129. package/dist/runners/claude-code.d.ts +3 -0
  130. package/dist/runners/claude-code.js +319 -73
  131. package/dist/runners/codex.js +4 -1
  132. package/dist/runners/copilot.js +4 -1
  133. package/dist/runners/cursor.js +4 -1
  134. package/dist/runners/git-utils.d.ts +81 -0
  135. package/dist/runners/git-utils.js +201 -0
  136. package/dist/runners/index.d.ts +5 -1
  137. package/dist/runners/index.js +4 -0
  138. package/dist/runners/review-agent.d.ts +67 -0
  139. package/dist/runners/review-agent.js +350 -0
  140. package/dist/runners/runner-registry.js +10 -0
  141. package/dist/runners/sdk-review-runner.d.ts +65 -0
  142. package/dist/runners/sdk-review-runner.js +185 -0
  143. package/dist/runners/security-triage.d.ts +20 -4
  144. package/dist/runners/security-triage.js +42 -14
  145. package/dist/runners/types.d.ts +25 -0
  146. package/dist/runtime/attestations.d.ts +274 -0
  147. package/dist/runtime/attestations.js +460 -0
  148. package/dist/runtime/git-env.d.ts +53 -0
  149. package/dist/runtime/git-env.js +60 -0
  150. package/dist/runtime/index.d.ts +7 -0
  151. package/dist/runtime/index.js +7 -0
  152. package/dist/runtime/parallelism-flag.d.ts +10 -0
  153. package/dist/runtime/parallelism-flag.js +18 -0
  154. package/dist/runtime/port-allocator.d.ts +21 -0
  155. package/dist/runtime/port-allocator.js +66 -0
  156. package/dist/runtime/worktree-pool.d.ts +86 -0
  157. package/dist/runtime/worktree-pool.js +204 -0
  158. package/dist/runtime/worktree.d.ts +25 -0
  159. package/dist/runtime/worktree.js +111 -0
  160. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  161. package/dist/sa-scoring/auto-calibrate.js +107 -0
  162. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  163. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  164. package/dist/sa-scoring/composite.d.ts +107 -0
  165. package/dist/sa-scoring/composite.js +139 -0
  166. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  167. package/dist/sa-scoring/depparse-client.js +187 -0
  168. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  169. package/dist/sa-scoring/did-compiler.js +286 -0
  170. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  171. package/dist/sa-scoring/drift-monitor.js +186 -0
  172. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  173. package/dist/sa-scoring/exemplar-bank.js +154 -0
  174. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  175. package/dist/sa-scoring/feedback-store.js +156 -0
  176. package/dist/sa-scoring/index.d.ts +71 -0
  177. package/dist/sa-scoring/index.js +158 -0
  178. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  179. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  180. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  181. package/dist/sa-scoring/layer2-structural.js +151 -0
  182. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  183. package/dist/sa-scoring/layer3-llm.js +282 -0
  184. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  185. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  186. package/dist/scheduling/burn-down.d.ts +27 -0
  187. package/dist/scheduling/burn-down.js +43 -0
  188. package/dist/scheduling/calibration.d.ts +42 -0
  189. package/dist/scheduling/calibration.js +150 -0
  190. package/dist/scheduling/index.d.ts +8 -0
  191. package/dist/scheduling/index.js +8 -0
  192. package/dist/scheduling/ledger.d.ts +59 -0
  193. package/dist/scheduling/ledger.js +216 -0
  194. package/dist/scheduling/off-peak.d.ts +27 -0
  195. package/dist/scheduling/off-peak.js +112 -0
  196. package/dist/scheduling/schedule-decision.d.ts +39 -0
  197. package/dist/scheduling/schedule-decision.js +89 -0
  198. package/dist/scheduling/tier-analysis.d.ts +47 -0
  199. package/dist/scheduling/tier-analysis.js +81 -0
  200. package/dist/scheduling/types.d.ts +140 -0
  201. package/dist/scheduling/types.js +11 -0
  202. package/dist/shared.d.ts +13 -0
  203. package/dist/shared.js +32 -0
  204. package/dist/state/index.d.ts +1 -1
  205. package/dist/state/schema.d.ts +6 -1
  206. package/dist/state/schema.js +276 -1
  207. package/dist/state/store.d.ts +71 -1
  208. package/dist/state/store.js +464 -7
  209. package/dist/state/types.d.ts +174 -0
  210. package/dist/types.d.ts +1 -1
  211. package/dist/validate-agent-output.js +4 -1
  212. package/dist/watch.js +6 -0
  213. package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
  214. package/dist/workflow-patterns/artifact-writer.js +34 -0
  215. package/dist/workflow-patterns/classifiers.d.ts +10 -0
  216. package/dist/workflow-patterns/classifiers.js +72 -0
  217. package/dist/workflow-patterns/detector.d.ts +27 -0
  218. package/dist/workflow-patterns/detector.js +186 -0
  219. package/dist/workflow-patterns/index.d.ts +8 -0
  220. package/dist/workflow-patterns/index.js +7 -0
  221. package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
  222. package/dist/workflow-patterns/proposal-generator.js +183 -0
  223. package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
  224. package/dist/workflow-patterns/telemetry-ingest.js +103 -0
  225. package/dist/workflow-patterns/types.d.ts +61 -0
  226. package/dist/workflow-patterns/types.js +11 -0
  227. package/package.json +2 -2
@@ -0,0 +1,90 @@
1
+ /**
2
+ * File-based merge gate per RFC-0010 §10. Single mutex (<pool>/.merge-gate.lock)
3
+ * serializes the merge-eligibility/rebase step across parallel agents. Stale-base
4
+ * detection + rebase-on-conflict per §10.2.
5
+ *
6
+ * Per project policy (feedback_never_merge_prs.md): the orchestrator MUST NOT execute
7
+ * `gh pr merge`. The merge gate ensures a PR is in known-mergeable state when the
8
+ * human reviewer hits merge — it does NOT perform the merge itself.
9
+ */
10
+ import { mkdir, open, rm, writeFile } from 'node:fs/promises';
11
+ import { dirname, join } from 'node:path';
12
+ const POLL_INTERVAL_MS = 100;
13
+ const DEFAULT_LOCK_TIMEOUT_MS = 5 * 60 * 1000;
14
+ export class MergeGateLockTimeoutError extends Error {
15
+ lockPath;
16
+ constructor(lockPath) {
17
+ super(`Timed out waiting for merge gate lock at ${lockPath}`);
18
+ this.lockPath = lockPath;
19
+ this.name = 'MergeGateLockTimeoutError';
20
+ }
21
+ }
22
+ /**
23
+ * Acquire the merge gate, run `work`, release. The lock is a sentinel file; if it
24
+ * already exists, the caller polls until it's released or the timeout elapses.
25
+ *
26
+ * Caveat: this is a cooperative file lock, not a kernel lock. Crashes can leave a
27
+ * stale lock; the caller is expected to forcibly remove and retry as a recovery step.
28
+ */
29
+ export async function withMergeGate(poolDir, work, options = {}, deps = {}) {
30
+ const lockPath = join(poolDir, '.merge-gate.lock');
31
+ const timeoutMs = options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;
32
+ const now = deps.now ?? (() => Date.now());
33
+ const startedAt = now();
34
+ await mkdir(dirname(lockPath), { recursive: true });
35
+ // Acquire — exclusive create, polling until success or timeout.
36
+ for (;;) {
37
+ try {
38
+ const fh = await open(lockPath, 'wx');
39
+ await fh.write(`pid=${process.pid}\nat=${new Date().toISOString()}\n`);
40
+ await fh.close();
41
+ break;
42
+ }
43
+ catch (err) {
44
+ if (err.code !== 'EEXIST')
45
+ throw err;
46
+ if (now() - startedAt > timeoutMs) {
47
+ throw new MergeGateLockTimeoutError(lockPath);
48
+ }
49
+ await sleep(POLL_INTERVAL_MS);
50
+ }
51
+ }
52
+ try {
53
+ return await work();
54
+ }
55
+ finally {
56
+ // Best-effort release; if removal fails (lock file already gone), continue.
57
+ await rm(lockPath, { force: true });
58
+ }
59
+ }
60
+ /**
61
+ * Force-remove a stale merge gate lock. Operators run this manually when an agent
62
+ * crashed mid-merge-gate and the lock file is orphaned.
63
+ */
64
+ export async function forceReleaseMergeGate(poolDir) {
65
+ await rm(join(poolDir, '.merge-gate.lock'), { force: true });
66
+ }
67
+ /**
68
+ * Test helper: write a sentinel lock file in an unexpected state (used by tests
69
+ * verifying the timeout path).
70
+ */
71
+ export async function _testWriteSentinel(poolDir) {
72
+ const path = join(poolDir, '.merge-gate.lock');
73
+ await mkdir(dirname(path), { recursive: true });
74
+ await writeFile(path, 'sentinel');
75
+ }
76
+ function sleep(ms) {
77
+ return new Promise((resolve) => setTimeout(resolve, ms));
78
+ }
79
+ /**
80
+ * Stale-base detection per RFC §10.2. Compares a branch's base SHA to the current
81
+ * remote target's HEAD; returns true when the branch is up-to-date.
82
+ *
83
+ * The actual git logic is delegated to the caller via `headOfRemote` so this stays
84
+ * unit-testable without shelling out.
85
+ */
86
+ export async function isBranchUpToDate(branchBaseSha, targetBranch, headOfRemote) {
87
+ const head = await headOfRemote(targetBranch);
88
+ return branchBaseSha === head;
89
+ }
90
+ //# sourceMappingURL=merge-gate.js.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * PPA re-scoring on requeue per RFC-0010 §9.4 (Q3 resolution).
3
+ *
4
+ * Hybrid algorithm: re-score IF
5
+ * - time since last triage > 24h, OR
6
+ * - failure type signals difficulty miscalibration, OR
7
+ * - operator-triggered requeue.
8
+ *
9
+ * Otherwise trust the original PPA composite. Failure-type taxonomy from §9.4.1
10
+ * encodes which events are transient (trust score) vs intrinsic (re-score).
11
+ */
12
+ export type FailureEvent = 'MergeConflict' | 'RebaseConflict' | 'CIFailure' | 'HarnessUnavailable' | 'BranchQuotaExceeded' | 'PortCollision' | 'BudgetExceeded' | 'OffPeakDeferralExceeded' | 'AgentTimeout' | 'MaxRetriesExhausted' | 'MigrationConflict' | 'MigrationFailed' | 'EstimateVariance' | 'WorktreeOwnershipMismatch';
13
+ export type RequeueTrigger = 'automatic' | 'operator';
14
+ export interface FailureClassification {
15
+ /** Trust the score on first occurrence; never re-score. */
16
+ alwaysTransient: boolean;
17
+ /** Re-score after this many failures of this type within 24h. */
18
+ reScoreAfterCount: number;
19
+ /** Re-score on first occurrence regardless. */
20
+ alwaysReScore: boolean;
21
+ }
22
+ export interface TriageHistoryEntry {
23
+ timestamp: string;
24
+ trigger: 'original' | 'time-threshold' | 'failure-type' | 'operator-requeue';
25
+ triggerDetail?: string;
26
+ composite: number;
27
+ costUsd?: number;
28
+ }
29
+ export interface RequeueDecisionInput {
30
+ /** Most recent triage entry for the issue. */
31
+ lastTriage: TriageHistoryEntry;
32
+ /** History of failures observed for this issue (unsorted). */
33
+ failureHistory: Array<{
34
+ at: string;
35
+ event: FailureEvent;
36
+ }>;
37
+ /** The failure that triggered this requeue (the fresh one). */
38
+ triggeringFailure: FailureEvent;
39
+ /** Whether the operator pressed the requeue button. */
40
+ trigger: RequeueTrigger;
41
+ now?: () => Date;
42
+ }
43
+ export interface RequeueDecision {
44
+ reScore: boolean;
45
+ reason: 'time-threshold' | 'failure-type' | 'operator-requeue' | 'trust-score';
46
+ detail: string;
47
+ }
48
+ export declare function decideRequeue(input: RequeueDecisionInput): RequeueDecision;
49
+ /**
50
+ * Append a triage event to the per-issue history file, returning whether this
51
+ * crosses the RetriageStorm threshold (>10 retriage events in 24h).
52
+ */
53
+ export declare function appendTriageHistory(artifactsDir: string, issueId: string, entry: TriageHistoryEntry): Promise<{
54
+ stormDetected: boolean;
55
+ eventsInWindow: number;
56
+ }>;
57
+ //# sourceMappingURL=requeue.d.ts.map
@@ -0,0 +1,131 @@
1
+ /**
2
+ * PPA re-scoring on requeue per RFC-0010 §9.4 (Q3 resolution).
3
+ *
4
+ * Hybrid algorithm: re-score IF
5
+ * - time since last triage > 24h, OR
6
+ * - failure type signals difficulty miscalibration, OR
7
+ * - operator-triggered requeue.
8
+ *
9
+ * Otherwise trust the original PPA composite. Failure-type taxonomy from §9.4.1
10
+ * encodes which events are transient (trust score) vs intrinsic (re-score).
11
+ */
12
+ import { appendFile, mkdir } from 'node:fs/promises';
13
+ import { dirname, join } from 'node:path';
14
+ const TAXONOMY = {
15
+ MergeConflict: { alwaysTransient: true, reScoreAfterCount: Infinity, alwaysReScore: false },
16
+ RebaseConflict: { alwaysTransient: true, reScoreAfterCount: Infinity, alwaysReScore: false },
17
+ CIFailure: { alwaysTransient: false, reScoreAfterCount: 3, alwaysReScore: false },
18
+ HarnessUnavailable: { alwaysTransient: true, reScoreAfterCount: Infinity, alwaysReScore: false },
19
+ BranchQuotaExceeded: { alwaysTransient: true, reScoreAfterCount: Infinity, alwaysReScore: false },
20
+ PortCollision: { alwaysTransient: true, reScoreAfterCount: Infinity, alwaysReScore: false },
21
+ BudgetExceeded: { alwaysTransient: false, reScoreAfterCount: 2, alwaysReScore: false },
22
+ OffPeakDeferralExceeded: {
23
+ alwaysTransient: true,
24
+ reScoreAfterCount: Infinity,
25
+ alwaysReScore: false,
26
+ },
27
+ AgentTimeout: { alwaysTransient: false, reScoreAfterCount: 2, alwaysReScore: false },
28
+ MaxRetriesExhausted: { alwaysTransient: false, reScoreAfterCount: 1, alwaysReScore: true },
29
+ MigrationConflict: { alwaysTransient: false, reScoreAfterCount: 1, alwaysReScore: true },
30
+ MigrationFailed: { alwaysTransient: false, reScoreAfterCount: 1, alwaysReScore: true },
31
+ EstimateVariance: { alwaysTransient: false, reScoreAfterCount: 1, alwaysReScore: true },
32
+ WorktreeOwnershipMismatch: {
33
+ alwaysTransient: true,
34
+ reScoreAfterCount: Infinity,
35
+ alwaysReScore: false,
36
+ },
37
+ };
38
+ const TIME_THRESHOLD_HOURS = 24;
39
+ const FAILURE_HISTORY_WINDOW_HOURS = 24;
40
+ const RETRIAGE_STORM_THRESHOLD = 10;
41
+ export function decideRequeue(input) {
42
+ const now = (input.now ?? (() => new Date()))();
43
+ if (input.trigger === 'operator') {
44
+ return { reScore: true, reason: 'operator-requeue', detail: 'operator manually requeued' };
45
+ }
46
+ const lastTriage = new Date(input.lastTriage.timestamp);
47
+ const hoursSinceTriage = (now.getTime() - lastTriage.getTime()) / (60 * 60 * 1000);
48
+ if (hoursSinceTriage > TIME_THRESHOLD_HOURS) {
49
+ return {
50
+ reScore: true,
51
+ reason: 'time-threshold',
52
+ detail: `${hoursSinceTriage.toFixed(1)}h since last triage (>24h)`,
53
+ };
54
+ }
55
+ const taxonomy = TAXONOMY[input.triggeringFailure];
56
+ if (taxonomy.alwaysReScore) {
57
+ return {
58
+ reScore: true,
59
+ reason: 'failure-type',
60
+ detail: `${input.triggeringFailure} signals difficulty miscalibration`,
61
+ };
62
+ }
63
+ if (!taxonomy.alwaysTransient) {
64
+ const recent = input.failureHistory.filter((f) => {
65
+ if (f.event !== input.triggeringFailure)
66
+ return false;
67
+ const at = new Date(f.at);
68
+ const hours = (now.getTime() - at.getTime()) / (60 * 60 * 1000);
69
+ return hours <= FAILURE_HISTORY_WINDOW_HOURS;
70
+ });
71
+ // The triggering failure is not yet in the history — count includes it.
72
+ if (recent.length + 1 >= taxonomy.reScoreAfterCount) {
73
+ return {
74
+ reScore: true,
75
+ reason: 'failure-type',
76
+ detail: `${input.triggeringFailure} occurred ${recent.length + 1}× in 24h (threshold ${taxonomy.reScoreAfterCount})`,
77
+ };
78
+ }
79
+ }
80
+ return {
81
+ reScore: false,
82
+ reason: 'trust-score',
83
+ detail: taxonomy.alwaysTransient
84
+ ? `${input.triggeringFailure} is transient`
85
+ : `${input.triggeringFailure} below re-score threshold`,
86
+ };
87
+ }
88
+ /**
89
+ * Append a triage event to the per-issue history file, returning whether this
90
+ * crosses the RetriageStorm threshold (>10 retriage events in 24h).
91
+ */
92
+ export async function appendTriageHistory(artifactsDir, issueId, entry) {
93
+ const path = join(artifactsDir, issueId, 'triage-history.jsonl');
94
+ await mkdir(dirname(path), { recursive: true });
95
+ await appendFile(path, JSON.stringify(entry) + '\n', 'utf8');
96
+ // Count retriage events in the last 24h (excludes 'original').
97
+ const eventsInWindow = await countRetriageInWindow(path, new Date());
98
+ return {
99
+ stormDetected: eventsInWindow > RETRIAGE_STORM_THRESHOLD,
100
+ eventsInWindow,
101
+ };
102
+ }
103
+ async function countRetriageInWindow(path, now) {
104
+ const { readFile } = await import('node:fs/promises');
105
+ let raw;
106
+ try {
107
+ raw = await readFile(path, 'utf8');
108
+ }
109
+ catch {
110
+ return 0;
111
+ }
112
+ let count = 0;
113
+ const cutoff = now.getTime() - FAILURE_HISTORY_WINDOW_HOURS * 60 * 60 * 1000;
114
+ for (const line of raw.split('\n')) {
115
+ if (!line.trim())
116
+ continue;
117
+ try {
118
+ const e = JSON.parse(line);
119
+ if (e.trigger === 'original')
120
+ continue;
121
+ const at = new Date(e.timestamp).getTime();
122
+ if (at >= cutoff)
123
+ count++;
124
+ }
125
+ catch {
126
+ // Skip malformed lines.
127
+ }
128
+ }
129
+ return count;
130
+ }
131
+ //# sourceMappingURL=requeue.js.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Bounded-concurrency worker pool per RFC-0010 §9. Accepts a stream of work items in
3
+ * PPA-priority order, dispatches up to `maxConcurrent` simultaneously, and surfaces
4
+ * structured admission/dispatch events.
5
+ */
6
+ export interface WorkItem<T> {
7
+ /** Unique id (typically issue id). */
8
+ id: string;
9
+ /** PPA composite score; higher = higher priority. */
10
+ ppaScore: number;
11
+ /** The actual work to perform when admitted. */
12
+ payload: T;
13
+ }
14
+ export interface WorkerPoolDeps<T, R> {
15
+ /** Number of in-flight items allowed at once. */
16
+ maxConcurrent: number;
17
+ /** Execute a single work item; called by the pool with concurrency-bounded parallelism. */
18
+ execute: (item: WorkItem<T>) => Promise<R>;
19
+ /** Optional admission gate; return false to defer the item back into the queue. */
20
+ admit?: (item: WorkItem<T>) => Promise<boolean>;
21
+ /** Optional structured event sink. */
22
+ onEvent?: (event: WorkerPoolEvent<T>) => void;
23
+ }
24
+ export type WorkerPoolEvent<T> = {
25
+ type: 'queued';
26
+ item: WorkItem<T>;
27
+ queueSize: number;
28
+ } | {
29
+ type: 'admission-deferred';
30
+ item: WorkItem<T>;
31
+ } | {
32
+ type: 'started';
33
+ item: WorkItem<T>;
34
+ inFlight: number;
35
+ } | {
36
+ type: 'completed';
37
+ item: WorkItem<T>;
38
+ durationMs: number;
39
+ } | {
40
+ type: 'failed';
41
+ item: WorkItem<T>;
42
+ error: Error;
43
+ };
44
+ export interface WorkerPoolResult<T, R> {
45
+ succeeded: Array<{
46
+ item: WorkItem<T>;
47
+ result: R;
48
+ }>;
49
+ failed: Array<{
50
+ item: WorkItem<T>;
51
+ error: Error;
52
+ }>;
53
+ deferred: Array<WorkItem<T>>;
54
+ }
55
+ /**
56
+ * Process all items with bounded concurrency. Items are dispatched in PPA score order
57
+ * (descending). Items that the admission gate rejects are returned in `deferred` for
58
+ * the caller to requeue or escalate. Failed executions are returned in `failed`; the
59
+ * pool does NOT auto-retry — that's the caller's responsibility (see RFC §9.4).
60
+ */
61
+ export declare function runWorkerPool<T, R>(items: WorkItem<T>[], deps: WorkerPoolDeps<T, R>): Promise<WorkerPoolResult<T, R>>;
62
+ //# sourceMappingURL=worker-pool.d.ts.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Bounded-concurrency worker pool per RFC-0010 §9. Accepts a stream of work items in
3
+ * PPA-priority order, dispatches up to `maxConcurrent` simultaneously, and surfaces
4
+ * structured admission/dispatch events.
5
+ */
6
+ /**
7
+ * Process all items with bounded concurrency. Items are dispatched in PPA score order
8
+ * (descending). Items that the admission gate rejects are returned in `deferred` for
9
+ * the caller to requeue or escalate. Failed executions are returned in `failed`; the
10
+ * pool does NOT auto-retry — that's the caller's responsibility (see RFC §9.4).
11
+ */
12
+ export async function runWorkerPool(items, deps) {
13
+ if (deps.maxConcurrent < 1) {
14
+ throw new Error(`maxConcurrent must be >= 1 (got ${deps.maxConcurrent})`);
15
+ }
16
+ const sorted = [...items].sort((a, b) => b.ppaScore - a.ppaScore);
17
+ const result = { succeeded: [], failed: [], deferred: [] };
18
+ const onEvent = deps.onEvent ?? (() => { });
19
+ for (const item of sorted) {
20
+ onEvent({ type: 'queued', item, queueSize: sorted.length });
21
+ }
22
+ let inFlight = 0;
23
+ const queue = [...sorted];
24
+ // Spawn N workers that pull from the shared queue until empty.
25
+ const worker = async () => {
26
+ for (;;) {
27
+ const item = queue.shift();
28
+ if (!item)
29
+ return;
30
+ inFlight++;
31
+ try {
32
+ if (deps.admit) {
33
+ const admitted = await deps.admit(item);
34
+ if (!admitted) {
35
+ onEvent({ type: 'admission-deferred', item });
36
+ result.deferred.push(item);
37
+ continue;
38
+ }
39
+ }
40
+ const start = Date.now();
41
+ onEvent({ type: 'started', item, inFlight });
42
+ const r = await deps.execute(item);
43
+ onEvent({ type: 'completed', item, durationMs: Date.now() - start });
44
+ result.succeeded.push({ item, result: r });
45
+ }
46
+ catch (err) {
47
+ const error = err instanceof Error ? err : new Error(String(err));
48
+ onEvent({ type: 'failed', item, error });
49
+ result.failed.push({ item, error });
50
+ }
51
+ finally {
52
+ inFlight--;
53
+ }
54
+ }
55
+ };
56
+ const workers = Array.from({ length: Math.min(deps.maxConcurrent, sorted.length) }, () => worker());
57
+ await Promise.all(workers);
58
+ return result;
59
+ }
60
+ //# sourceMappingURL=worker-pool.js.map
package/dist/execute.d.ts CHANGED
@@ -76,9 +76,49 @@ export interface ExecuteOptions {
76
76
  stateStore?: StateStore;
77
77
  /** Priority score from PPA scoring (set by watch loop or caller). */
78
78
  priorityScore?: PriorityScore;
79
+ /**
80
+ * Override the Pipeline resource selected for this execution. When set, takes
81
+ * precedence over the on-disk `config.pipeline` loaded from `.ai-sdlc/`. The
82
+ * watch loop uses this to pass the dispatcher-selected pipeline (e.g., the
83
+ * backlog pipeline for AISDLC-* issues) instead of letting executePipeline
84
+ * fall back to the canonical pipeline.yaml.
85
+ */
86
+ pipelineOverride?: import('@ai-sdlc/reference').Pipeline;
79
87
  }
80
88
  /**
81
89
  * Execute the full AI-SDLC pipeline for a given issue ID.
82
90
  */
83
91
  export declare function executePipeline(issueId: string, options?: ExecuteOptions): Promise<PipelineResult>;
92
+ /**
93
+ * Capture the current branch name (or commit SHA in detached-HEAD state) so the
94
+ * pipeline can restore the user's worktree after the run. Returns null on git
95
+ * failure — callers treat null as "don't try to restore".
96
+ *
97
+ * @internal — exported for unit tests.
98
+ */
99
+ export declare function captureCurrentBranch(workDir: string): Promise<string | null>;
100
+ /**
101
+ * Restore the user's original HEAD if the pipeline switched branches AND the
102
+ * working tree is clean. If the working tree has uncommitted changes (from a
103
+ * partial pipeline run), log a warning and leave HEAD where it is — better to
104
+ * preserve the pipeline's state than to clobber whatever the user has.
105
+ *
106
+ * @internal — exported for unit tests.
107
+ */
108
+ export declare function restoreOriginalBranch(workDir: string, originalHead: string | null, log: {
109
+ info: (msg: string) => void;
110
+ } | undefined): Promise<void>;
111
+ /**
112
+ * Push a branch to origin, rebasing onto the remote tip if it has advanced.
113
+ * Pre-AISDLC-68-rerun the pipeline did a plain `git push` and rejected with
114
+ * non-fast-forward when the remote branch had drifted (e.g. a previous run's
115
+ * commits or a manual edit). The fix: try push; on non-fast-forward, fetch +
116
+ * rebase HEAD onto origin/<branch>, retry once. Rebase conflicts surface as
117
+ * the original error so the operator can resolve manually.
118
+ *
119
+ * @internal — exported for unit tests.
120
+ */
121
+ export declare function pushBranchWithRebase(workDir: string, branchName: string, log: {
122
+ info: (msg: string) => void;
123
+ } | undefined): Promise<void>;
84
124
  //# sourceMappingURL=execute.d.ts.map