@ai-sdlc/orchestrator 0.6.0 → 0.10.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 (209) hide show
  1. package/dist/__test-helpers/git-env.d.ts +40 -0
  2. package/dist/__test-helpers/git-env.js +73 -0
  3. package/dist/admission-composite.d.ts +89 -0
  4. package/dist/admission-composite.js +241 -0
  5. package/dist/admission-enrichment.d.ts +142 -0
  6. package/dist/admission-enrichment.js +341 -0
  7. package/dist/admission-hc.d.ts +81 -0
  8. package/dist/admission-hc.js +94 -0
  9. package/dist/admission-score.d.ts +109 -5
  10. package/dist/admission-score.js +90 -11
  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 +74 -0
  28. package/dist/cli/commands/git-remote.js +170 -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-features.d.ts +208 -0
  32. package/dist/cli/commands/init-features.js +473 -0
  33. package/dist/cli/commands/init-templates.d.ts +104 -0
  34. package/dist/cli/commands/init-templates.js +399 -0
  35. package/dist/cli/commands/init.d.ts +48 -0
  36. package/dist/cli/commands/init.js +322 -23
  37. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  38. package/dist/cli/commands/mcp-setup.js +78 -8
  39. package/dist/cli/formatters/table.js +15 -2
  40. package/dist/cli/index.d.ts +14 -1
  41. package/dist/cli/index.js +81 -20
  42. package/dist/cli/versions.d.ts +57 -0
  43. package/dist/cli/versions.js +128 -0
  44. package/dist/code-area-classifier.d.ts +21 -0
  45. package/dist/code-area-classifier.js +48 -0
  46. package/dist/config.d.ts +33 -1
  47. package/dist/config.js +78 -8
  48. package/dist/database/adapters/external.d.ts +24 -0
  49. package/dist/database/adapters/external.js +80 -0
  50. package/dist/database/adapters/neon.d.ts +41 -0
  51. package/dist/database/adapters/neon.js +98 -0
  52. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  53. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  54. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  55. package/dist/database/adapters/sqlite-copy.js +145 -0
  56. package/dist/database/connection-injection.d.ts +35 -0
  57. package/dist/database/connection-injection.js +93 -0
  58. package/dist/database/index.d.ts +12 -0
  59. package/dist/database/index.js +23 -0
  60. package/dist/database/registry.d.ts +13 -0
  61. package/dist/database/registry.js +27 -0
  62. package/dist/database/topology.d.ts +52 -0
  63. package/dist/database/topology.js +44 -0
  64. package/dist/database/types.d.ts +89 -0
  65. package/dist/database/types.js +26 -0
  66. package/dist/design-authority.d.ts +53 -0
  67. package/dist/design-authority.js +84 -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 +188 -15
  92. package/dist/fix-ci.js +8 -1
  93. package/dist/fix-review.js +8 -1
  94. package/dist/harness/adapters/claude-code.d.ts +29 -0
  95. package/dist/harness/adapters/claude-code.js +191 -0
  96. package/dist/harness/adapters/codex.d.ts +25 -0
  97. package/dist/harness/adapters/codex.js +61 -0
  98. package/dist/harness/independence.d.ts +51 -0
  99. package/dist/harness/independence.js +67 -0
  100. package/dist/harness/index.d.ts +14 -0
  101. package/dist/harness/index.js +20 -0
  102. package/dist/harness/registry.d.ts +17 -0
  103. package/dist/harness/registry.js +31 -0
  104. package/dist/harness/types.d.ts +123 -0
  105. package/dist/harness/types.js +8 -0
  106. package/dist/harness/version-probe.d.ts +14 -0
  107. package/dist/harness/version-probe.js +123 -0
  108. package/dist/index.d.ts +30 -4
  109. package/dist/index.js +35 -2
  110. package/dist/models/classifier.d.ts +78 -0
  111. package/dist/models/classifier.js +277 -0
  112. package/dist/models/index.d.ts +3 -0
  113. package/dist/models/index.js +3 -0
  114. package/dist/models/registry.d.ts +97 -0
  115. package/dist/models/registry.js +173 -0
  116. package/dist/pillar-breakdown.d.ts +85 -0
  117. package/dist/pillar-breakdown.js +162 -0
  118. package/dist/priority.d.ts +5 -0
  119. package/dist/priority.js +18 -5
  120. package/dist/review-meta.d.ts +65 -0
  121. package/dist/review-meta.js +149 -0
  122. package/dist/review.d.ts +4 -0
  123. package/dist/review.js +12 -2
  124. package/dist/runners/claude-code-sdk.d.ts +31 -0
  125. package/dist/runners/claude-code-sdk.js +238 -0
  126. package/dist/runners/claude-code.d.ts +3 -0
  127. package/dist/runners/claude-code.js +30 -66
  128. package/dist/runners/codex.js +4 -1
  129. package/dist/runners/copilot.js +4 -1
  130. package/dist/runners/cursor.js +4 -1
  131. package/dist/runners/git-utils.d.ts +81 -0
  132. package/dist/runners/git-utils.js +201 -0
  133. package/dist/runners/index.d.ts +3 -0
  134. package/dist/runners/index.js +3 -0
  135. package/dist/runners/review-agent.d.ts +20 -0
  136. package/dist/runners/review-agent.js +171 -41
  137. package/dist/runners/runner-registry.js +10 -0
  138. package/dist/runners/sdk-review-runner.d.ts +65 -0
  139. package/dist/runners/sdk-review-runner.js +185 -0
  140. package/dist/runners/security-triage.d.ts +20 -4
  141. package/dist/runners/security-triage.js +39 -15
  142. package/dist/runners/types.d.ts +6 -0
  143. package/dist/runtime/attestations.d.ts +766 -0
  144. package/dist/runtime/attestations.js +1195 -0
  145. package/dist/runtime/git-env.d.ts +53 -0
  146. package/dist/runtime/git-env.js +60 -0
  147. package/dist/runtime/index.d.ts +7 -0
  148. package/dist/runtime/index.js +7 -0
  149. package/dist/runtime/parallelism-flag.d.ts +28 -0
  150. package/dist/runtime/parallelism-flag.js +39 -0
  151. package/dist/runtime/port-allocator.d.ts +32 -0
  152. package/dist/runtime/port-allocator.js +96 -0
  153. package/dist/runtime/worktree-pool.d.ts +86 -0
  154. package/dist/runtime/worktree-pool.js +204 -0
  155. package/dist/runtime/worktree.d.ts +25 -0
  156. package/dist/runtime/worktree.js +111 -0
  157. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  158. package/dist/sa-scoring/auto-calibrate.js +107 -0
  159. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  160. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  161. package/dist/sa-scoring/composite.d.ts +107 -0
  162. package/dist/sa-scoring/composite.js +139 -0
  163. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  164. package/dist/sa-scoring/depparse-client.js +187 -0
  165. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  166. package/dist/sa-scoring/did-compiler.js +286 -0
  167. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  168. package/dist/sa-scoring/drift-monitor.js +186 -0
  169. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  170. package/dist/sa-scoring/exemplar-bank.js +154 -0
  171. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  172. package/dist/sa-scoring/feedback-store.js +156 -0
  173. package/dist/sa-scoring/index.d.ts +71 -0
  174. package/dist/sa-scoring/index.js +158 -0
  175. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  176. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  177. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  178. package/dist/sa-scoring/layer2-structural.js +151 -0
  179. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  180. package/dist/sa-scoring/layer3-llm.js +282 -0
  181. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  182. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  183. package/dist/scheduling/burn-down.d.ts +27 -0
  184. package/dist/scheduling/burn-down.js +43 -0
  185. package/dist/scheduling/calibration.d.ts +42 -0
  186. package/dist/scheduling/calibration.js +150 -0
  187. package/dist/scheduling/index.d.ts +8 -0
  188. package/dist/scheduling/index.js +8 -0
  189. package/dist/scheduling/ledger.d.ts +59 -0
  190. package/dist/scheduling/ledger.js +216 -0
  191. package/dist/scheduling/off-peak.d.ts +27 -0
  192. package/dist/scheduling/off-peak.js +112 -0
  193. package/dist/scheduling/schedule-decision.d.ts +39 -0
  194. package/dist/scheduling/schedule-decision.js +89 -0
  195. package/dist/scheduling/tier-analysis.d.ts +47 -0
  196. package/dist/scheduling/tier-analysis.js +81 -0
  197. package/dist/scheduling/types.d.ts +140 -0
  198. package/dist/scheduling/types.js +11 -0
  199. package/dist/shared.d.ts +13 -0
  200. package/dist/shared.js +32 -0
  201. package/dist/state/schema.d.ts +5 -1
  202. package/dist/state/schema.js +223 -1
  203. package/dist/state/store.d.ts +55 -1
  204. package/dist/state/store.js +342 -7
  205. package/dist/state/types.d.ts +139 -0
  206. package/dist/types.d.ts +1 -1
  207. package/dist/validate-agent-output.js +4 -1
  208. package/dist/watch.js +6 -0
  209. package/package.json +3 -2
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Stewardship enforcement for DesignSystemBinding resources (RFC-0006 §5.3).
3
+ *
4
+ * Validates that changes to authority-scoped fields are submitted by
5
+ * authorized principals. Changes to sharedAuthority fields require
6
+ * approval from both disciplines when requireBothDisciplines is true.
7
+ */
8
+ import type { DesignSystemBinding } from '@ai-sdlc/reference';
9
+ export interface StewardshipDecision {
10
+ allowed: boolean;
11
+ reason: string;
12
+ requiredApprovals?: string[];
13
+ }
14
+ /**
15
+ * Enforce stewardship constraints on a proposed change to a DesignSystemBinding.
16
+ *
17
+ * @param changedFields - The field paths being modified
18
+ * @param principal - The identity of the person/agent making the change
19
+ * @param binding - The DesignSystemBinding being modified
20
+ */
21
+ export declare function enforceStewardship(changedFields: string[], principal: string, binding: DesignSystemBinding): StewardshipDecision;
22
+ //# sourceMappingURL=design-system-stewardship.d.ts.map
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Stewardship enforcement for DesignSystemBinding resources (RFC-0006 §5.3).
3
+ *
4
+ * Validates that changes to authority-scoped fields are submitted by
5
+ * authorized principals. Changes to sharedAuthority fields require
6
+ * approval from both disciplines when requireBothDisciplines is true.
7
+ */
8
+ /**
9
+ * Determine which authority scope a field belongs to.
10
+ */
11
+ function findAuthorityScope(stewardship, field) {
12
+ if (stewardship.sharedAuthority?.scope?.includes(field))
13
+ return 'shared';
14
+ if (stewardship.designAuthority.scope.includes(field))
15
+ return 'design';
16
+ if (stewardship.engineeringAuthority.scope.includes(field))
17
+ return 'engineering';
18
+ return 'none';
19
+ }
20
+ /**
21
+ * Check if a principal is authorized for a given authority scope.
22
+ */
23
+ function isAuthorized(stewardship, principal, scope) {
24
+ const authorityType = findAuthorityScope(stewardship, scope);
25
+ switch (authorityType) {
26
+ case 'design':
27
+ return stewardship.designAuthority.principals.includes(principal);
28
+ case 'engineering':
29
+ return stewardship.engineeringAuthority.principals.includes(principal);
30
+ case 'shared':
31
+ return (stewardship.designAuthority.principals.includes(principal) ||
32
+ stewardship.engineeringAuthority.principals.includes(principal) ||
33
+ (stewardship.sharedAuthority?.principals?.includes(principal) ?? false));
34
+ case 'none':
35
+ return true; // Unscoped fields are unrestricted
36
+ }
37
+ }
38
+ /**
39
+ * Enforce stewardship constraints on a proposed change to a DesignSystemBinding.
40
+ *
41
+ * @param changedFields - The field paths being modified
42
+ * @param principal - The identity of the person/agent making the change
43
+ * @param binding - The DesignSystemBinding being modified
44
+ */
45
+ export function enforceStewardship(changedFields, principal, binding) {
46
+ const stewardship = binding.spec.stewardship;
47
+ const requireBoth = stewardship.changeApproval?.requireBothDisciplines ?? true;
48
+ for (const field of changedFields) {
49
+ const scope = findAuthorityScope(stewardship, field);
50
+ // Shared authority fields may require both disciplines
51
+ if (scope === 'shared' && requireBoth) {
52
+ const isDesign = stewardship.designAuthority.principals.includes(principal);
53
+ const isEngineering = stewardship.engineeringAuthority.principals.includes(principal);
54
+ const isShared = stewardship.sharedAuthority?.principals?.includes(principal) ?? false;
55
+ if (!isDesign && !isEngineering && !isShared) {
56
+ return {
57
+ allowed: false,
58
+ reason: `Principal "${principal}" is not authorized for shared-authority field "${field}"`,
59
+ };
60
+ }
61
+ // Identify which discipline still needs to approve
62
+ const needed = [];
63
+ if (!isDesign && !isShared)
64
+ needed.push('design');
65
+ if (!isEngineering && !isShared)
66
+ needed.push('engineering');
67
+ if (needed.length > 0) {
68
+ return {
69
+ allowed: false,
70
+ reason: `Shared-authority field "${field}" requires approval from both disciplines. Pending: ${needed.join(', ')}`,
71
+ requiredApprovals: needed,
72
+ };
73
+ }
74
+ }
75
+ // Design/engineering scoped fields
76
+ if (!isAuthorized(stewardship, principal, field)) {
77
+ return {
78
+ allowed: false,
79
+ reason: `Principal "${principal}" is not authorized for ${scope}-authority field "${field}"`,
80
+ };
81
+ }
82
+ }
83
+ return { allowed: true, reason: 'All changed fields are within principal authority' };
84
+ }
85
+ //# sourceMappingURL=design-system-stewardship.js.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Validates DesignSystemBinding inheritance constraints (RFC-0006 §5.6).
3
+ *
4
+ * Multi-brand inheritance uses the `extends` field. Validation enforces:
5
+ * - Child compliance thresholds >= parent thresholds
6
+ * - Child cannot remove parent disallowHardcoded categories
7
+ * - Inheritance depth limited to two levels (parent → child)
8
+ */
9
+ import type { DesignSystemBinding } from '@ai-sdlc/reference';
10
+ export interface InheritanceError {
11
+ field: string;
12
+ message: string;
13
+ }
14
+ export interface InheritanceValidationResult {
15
+ valid: boolean;
16
+ errors: InheritanceError[];
17
+ }
18
+ /**
19
+ * Resolve the parent binding from the collection by name.
20
+ */
21
+ export declare function resolveParent(child: DesignSystemBinding, bindings: DesignSystemBinding[]): DesignSystemBinding | undefined;
22
+ /**
23
+ * Validate that a child DesignSystemBinding conforms to its parent's constraints.
24
+ *
25
+ * Rules:
26
+ * 1. Child compliance.coverage.minimum >= parent compliance.coverage.minimum
27
+ * 2. Child compliance.coverage.target >= parent compliance.coverage.target (if both defined)
28
+ * 3. Child must not remove any parent disallowHardcoded categories
29
+ * 4. Two-level depth limit: parent must not itself have an `extends` field
30
+ */
31
+ export declare function validateDesignSystemInheritance(child: DesignSystemBinding, parent: DesignSystemBinding): InheritanceValidationResult;
32
+ /**
33
+ * Validate all DesignSystemBinding inheritance relationships in a collection.
34
+ * Returns errors for any invalid inheritance chains.
35
+ */
36
+ export declare function validateAllInheritance(bindings: DesignSystemBinding[]): InheritanceValidationResult;
37
+ //# sourceMappingURL=design-system-validation.d.ts.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Validates DesignSystemBinding inheritance constraints (RFC-0006 §5.6).
3
+ *
4
+ * Multi-brand inheritance uses the `extends` field. Validation enforces:
5
+ * - Child compliance thresholds >= parent thresholds
6
+ * - Child cannot remove parent disallowHardcoded categories
7
+ * - Inheritance depth limited to two levels (parent → child)
8
+ */
9
+ /**
10
+ * Resolve the parent binding from the collection by name.
11
+ */
12
+ export function resolveParent(child, bindings) {
13
+ if (!child.spec.extends)
14
+ return undefined;
15
+ return bindings.find((b) => b.metadata.name === child.spec.extends);
16
+ }
17
+ /**
18
+ * Validate that a child DesignSystemBinding conforms to its parent's constraints.
19
+ *
20
+ * Rules:
21
+ * 1. Child compliance.coverage.minimum >= parent compliance.coverage.minimum
22
+ * 2. Child compliance.coverage.target >= parent compliance.coverage.target (if both defined)
23
+ * 3. Child must not remove any parent disallowHardcoded categories
24
+ * 4. Two-level depth limit: parent must not itself have an `extends` field
25
+ */
26
+ export function validateDesignSystemInheritance(child, parent) {
27
+ const errors = [];
28
+ // Depth limit: parent must not extend another binding
29
+ if (parent.spec.extends) {
30
+ errors.push({
31
+ field: 'extends',
32
+ message: `Inheritance depth exceeds two levels: "${child.metadata.name}" extends "${parent.metadata.name}" which extends "${parent.spec.extends}". Only parent → child is supported in v1alpha1.`,
33
+ });
34
+ }
35
+ // Coverage minimum: child >= parent
36
+ const parentMin = parent.spec.compliance.coverage.minimum;
37
+ const childMin = child.spec.compliance.coverage.minimum;
38
+ if (childMin < parentMin) {
39
+ errors.push({
40
+ field: 'compliance.coverage.minimum',
41
+ message: `Child minimum coverage (${childMin}) is less than parent minimum (${parentMin}). Child thresholds can only tighten, not loosen.`,
42
+ });
43
+ }
44
+ // Coverage target: child >= parent (if both defined)
45
+ const parentTarget = parent.spec.compliance.coverage.target;
46
+ const childTarget = child.spec.compliance.coverage.target;
47
+ if (parentTarget !== undefined && childTarget !== undefined && childTarget < parentTarget) {
48
+ errors.push({
49
+ field: 'compliance.coverage.target',
50
+ message: `Child target coverage (${childTarget}) is less than parent target (${parentTarget}). Child thresholds can only tighten, not loosen.`,
51
+ });
52
+ }
53
+ // disallowHardcoded: child must not remove parent categories
54
+ const parentCategories = new Set((parent.spec.compliance.disallowHardcoded ?? []).map((r) => r.category));
55
+ const childCategories = new Set((child.spec.compliance.disallowHardcoded ?? []).map((r) => r.category));
56
+ for (const category of parentCategories) {
57
+ if (!childCategories.has(category)) {
58
+ errors.push({
59
+ field: 'compliance.disallowHardcoded',
60
+ message: `Child removes parent disallowHardcoded category "${category}". Child bindings may add categories but must not remove parent categories.`,
61
+ });
62
+ }
63
+ }
64
+ return { valid: errors.length === 0, errors };
65
+ }
66
+ /**
67
+ * Validate all DesignSystemBinding inheritance relationships in a collection.
68
+ * Returns errors for any invalid inheritance chains.
69
+ */
70
+ export function validateAllInheritance(bindings) {
71
+ const allErrors = [];
72
+ for (const binding of bindings) {
73
+ if (!binding.spec.extends)
74
+ continue;
75
+ const parent = resolveParent(binding, bindings);
76
+ if (!parent) {
77
+ allErrors.push({
78
+ field: 'extends',
79
+ message: `Binding "${binding.metadata.name}" extends "${binding.spec.extends}" but no binding with that name exists.`,
80
+ });
81
+ continue;
82
+ }
83
+ const result = validateDesignSystemInheritance(binding, parent);
84
+ allErrors.push(...result.errors);
85
+ }
86
+ return { valid: allErrors.length === 0, errors: allErrors };
87
+ }
88
+ //# sourceMappingURL=design-system-validation.js.map
@@ -0,0 +1,4 @@
1
+ export { runWorkerPool, type WorkItem, type WorkerPoolDeps, type WorkerPoolEvent, type WorkerPoolResult, } from './worker-pool.js';
2
+ export { withMergeGate, forceReleaseMergeGate, isBranchUpToDate, MergeGateLockTimeoutError, type MergeGateDeps, } from './merge-gate.js';
3
+ export { decideRequeue, appendTriageHistory, type FailureEvent, type RequeueTrigger, type RequeueDecision, type RequeueDecisionInput, type TriageHistoryEntry, type FailureClassification, } from './requeue.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ export { runWorkerPool, } from './worker-pool.js';
2
+ export { withMergeGate, forceReleaseMergeGate, isBranchUpToDate, MergeGateLockTimeoutError, } from './merge-gate.js';
3
+ export { decideRequeue, appendTriageHistory, } from './requeue.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,46 @@
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
+ export declare class MergeGateLockTimeoutError extends Error {
11
+ readonly lockPath: string;
12
+ constructor(lockPath: string);
13
+ }
14
+ export interface MergeGateDeps {
15
+ /** Override for tests. */
16
+ now?: () => number;
17
+ }
18
+ /**
19
+ * Acquire the merge gate, run `work`, release. The lock is a sentinel file; if it
20
+ * already exists, the caller polls until it's released or the timeout elapses.
21
+ *
22
+ * Caveat: this is a cooperative file lock, not a kernel lock. Crashes can leave a
23
+ * stale lock; the caller is expected to forcibly remove and retry as a recovery step.
24
+ */
25
+ export declare function withMergeGate<T>(poolDir: string, work: () => Promise<T>, options?: {
26
+ timeoutMs?: number;
27
+ }, deps?: MergeGateDeps): Promise<T>;
28
+ /**
29
+ * Force-remove a stale merge gate lock. Operators run this manually when an agent
30
+ * crashed mid-merge-gate and the lock file is orphaned.
31
+ */
32
+ export declare function forceReleaseMergeGate(poolDir: string): Promise<void>;
33
+ /**
34
+ * Test helper: write a sentinel lock file in an unexpected state (used by tests
35
+ * verifying the timeout path).
36
+ */
37
+ export declare function _testWriteSentinel(poolDir: string): Promise<void>;
38
+ /**
39
+ * Stale-base detection per RFC §10.2. Compares a branch's base SHA to the current
40
+ * remote target's HEAD; returns true when the branch is up-to-date.
41
+ *
42
+ * The actual git logic is delegated to the caller via `headOfRemote` so this stays
43
+ * unit-testable without shelling out.
44
+ */
45
+ export declare function isBranchUpToDate(branchBaseSha: string, targetBranch: string, headOfRemote: (target: string) => Promise<string>): Promise<boolean>;
46
+ //# sourceMappingURL=merge-gate.d.ts.map
@@ -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