@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.
- package/dist/__test-helpers/git-env.d.ts +40 -0
- package/dist/__test-helpers/git-env.js +73 -0
- package/dist/admission-composite.d.ts +89 -0
- package/dist/admission-composite.js +241 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +341 -0
- package/dist/admission-hc.d.ts +81 -0
- package/dist/admission-hc.js +94 -0
- package/dist/admission-score.d.ts +109 -5
- package/dist/admission-score.js +90 -11
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +74 -0
- package/dist/cli/commands/git-remote.js +170 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init-features.d.ts +208 -0
- package/dist/cli/commands/init-features.js +473 -0
- package/dist/cli/commands/init-templates.d.ts +104 -0
- package/dist/cli/commands/init-templates.js +399 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.js +322 -23
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/design-authority.d.ts +53 -0
- package/dist/design-authority.js +84 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +188 -15
- package/dist/fix-ci.js +8 -1
- package/dist/fix-review.js +8 -1
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/models/classifier.d.ts +78 -0
- package/dist/models/classifier.js +277 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +85 -0
- package/dist/pillar-breakdown.js +162 -0
- package/dist/priority.d.ts +5 -0
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +4 -0
- package/dist/review.js +12 -2
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +30 -66
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +3 -0
- package/dist/runners/index.js +3 -0
- package/dist/runners/review-agent.d.ts +20 -0
- package/dist/runners/review-agent.js +171 -41
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +39 -15
- package/dist/runners/types.d.ts +6 -0
- package/dist/runtime/attestations.d.ts +766 -0
- package/dist/runtime/attestations.js +1195 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +28 -0
- package/dist/runtime/parallelism-flag.js +39 -0
- package/dist/runtime/port-allocator.d.ts +32 -0
- package/dist/runtime/port-allocator.js +96 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/schema.d.ts +5 -1
- package/dist/state/schema.js +223 -1
- package/dist/state/store.d.ts +55 -1
- package/dist/state/store.js +342 -7
- package/dist/state/types.d.ts +139 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- 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
|