@xemahq/dsl 0.1.1
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/LICENSE +201 -0
- package/dist/deliverable-spec/index.d.ts +3 -0
- package/dist/deliverable-spec/index.d.ts.map +1 -0
- package/dist/deliverable-spec/index.js +19 -0
- package/dist/deliverable-spec/index.js.map +1 -0
- package/dist/deliverable-spec/lib/schema.d.ts +151 -0
- package/dist/deliverable-spec/lib/schema.d.ts.map +1 -0
- package/dist/deliverable-spec/lib/schema.js +139 -0
- package/dist/deliverable-spec/lib/schema.js.map +1 -0
- package/dist/deliverable-spec/lib/types.d.ts +8 -0
- package/dist/deliverable-spec/lib/types.d.ts.map +1 -0
- package/dist/deliverable-spec/lib/types.js +3 -0
- package/dist/deliverable-spec/lib/types.js.map +1 -0
- package/dist/payload-codec/index.d.ts +8 -0
- package/dist/payload-codec/index.d.ts.map +1 -0
- package/dist/payload-codec/index.js +27 -0
- package/dist/payload-codec/index.js.map +1 -0
- package/dist/payload-codec/lib/blob-store.d.ts +37 -0
- package/dist/payload-codec/lib/blob-store.d.ts.map +1 -0
- package/dist/payload-codec/lib/blob-store.js +0 -0
- package/dist/payload-codec/lib/blob-store.js.map +1 -0
- package/dist/payload-codec/lib/codec-context.d.ts +6 -0
- package/dist/payload-codec/lib/codec-context.d.ts.map +1 -0
- package/dist/payload-codec/lib/codec-context.js +16 -0
- package/dist/payload-codec/lib/codec-context.js.map +1 -0
- package/dist/payload-codec/lib/codec.d.ts +51 -0
- package/dist/payload-codec/lib/codec.d.ts.map +1 -0
- package/dist/payload-codec/lib/codec.js +330 -0
- package/dist/payload-codec/lib/codec.js.map +1 -0
- package/dist/payload-codec/lib/enums.d.ts +18 -0
- package/dist/payload-codec/lib/enums.d.ts.map +1 -0
- package/dist/payload-codec/lib/enums.js +23 -0
- package/dist/payload-codec/lib/enums.js.map +1 -0
- package/dist/payload-codec/lib/errors.d.ts +18 -0
- package/dist/payload-codec/lib/errors.d.ts.map +1 -0
- package/dist/payload-codec/lib/errors.js +39 -0
- package/dist/payload-codec/lib/errors.js.map +1 -0
- package/dist/payload-codec/lib/http-blob-store.d.ts +21 -0
- package/dist/payload-codec/lib/http-blob-store.d.ts.map +1 -0
- package/dist/payload-codec/lib/http-blob-store.js +139 -0
- package/dist/payload-codec/lib/http-blob-store.js.map +1 -0
- package/dist/payload-codec/lib/lru-cache.d.ts +12 -0
- package/dist/payload-codec/lib/lru-cache.d.ts.map +1 -0
- package/dist/payload-codec/lib/lru-cache.js +59 -0
- package/dist/payload-codec/lib/lru-cache.js.map +1 -0
- package/dist/schema/action.schema.json +181 -0
- package/dist/schema/reusable-workflow.schema.json +46 -0
- package/dist/schema/workflow.schema.json +373 -0
- package/dist/workflow/index.d.ts +14 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +49 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/lib/action-input-validator.d.ts +10 -0
- package/dist/workflow/lib/action-input-validator.d.ts.map +1 -0
- package/dist/workflow/lib/action-input-validator.js +69 -0
- package/dist/workflow/lib/action-input-validator.js.map +1 -0
- package/dist/workflow/lib/compiler/action-shape.d.ts +5 -0
- package/dist/workflow/lib/compiler/action-shape.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/action-shape.js +43 -0
- package/dist/workflow/lib/compiler/action-shape.js.map +1 -0
- package/dist/workflow/lib/compiler/canonical-json.d.ts +3 -0
- package/dist/workflow/lib/compiler/canonical-json.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/canonical-json.js +45 -0
- package/dist/workflow/lib/compiler/canonical-json.js.map +1 -0
- package/dist/workflow/lib/compiler/compile.d.ts +4 -0
- package/dist/workflow/lib/compiler/compile.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/compile.js +794 -0
- package/dist/workflow/lib/compiler/compile.js.map +1 -0
- package/dist/workflow/lib/compiler/concurrency.d.ts +5 -0
- package/dist/workflow/lib/compiler/concurrency.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/concurrency.js +104 -0
- package/dist/workflow/lib/compiler/concurrency.js.map +1 -0
- package/dist/workflow/lib/compiler/dag.d.ts +10 -0
- package/dist/workflow/lib/compiler/dag.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/dag.js +74 -0
- package/dist/workflow/lib/compiler/dag.js.map +1 -0
- package/dist/workflow/lib/compiler/index.d.ts +6 -0
- package/dist/workflow/lib/compiler/index.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/index.js +14 -0
- package/dist/workflow/lib/compiler/index.js.map +1 -0
- package/dist/workflow/lib/compiler/inputs.d.ts +4 -0
- package/dist/workflow/lib/compiler/inputs.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/inputs.js +108 -0
- package/dist/workflow/lib/compiler/inputs.js.map +1 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.d.ts +9 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.js +76 -0
- package/dist/workflow/lib/compiler/installation-resource-validator.js.map +1 -0
- package/dist/workflow/lib/compiler/manifest-source.d.ts +4 -0
- package/dist/workflow/lib/compiler/manifest-source.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/manifest-source.js +100 -0
- package/dist/workflow/lib/compiler/manifest-source.js.map +1 -0
- package/dist/workflow/lib/compiler/matrix.d.ts +4 -0
- package/dist/workflow/lib/compiler/matrix.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/matrix.js +76 -0
- package/dist/workflow/lib/compiler/matrix.js.map +1 -0
- package/dist/workflow/lib/compiler/mount-plan.d.ts +4 -0
- package/dist/workflow/lib/compiler/mount-plan.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/mount-plan.js +96 -0
- package/dist/workflow/lib/compiler/mount-plan.js.map +1 -0
- package/dist/workflow/lib/compiler/payload-reach-in.d.ts +14 -0
- package/dist/workflow/lib/compiler/payload-reach-in.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/payload-reach-in.js +273 -0
- package/dist/workflow/lib/compiler/payload-reach-in.js.map +1 -0
- package/dist/workflow/lib/compiler/permissions.d.ts +6 -0
- package/dist/workflow/lib/compiler/permissions.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/permissions.js +43 -0
- package/dist/workflow/lib/compiler/permissions.js.map +1 -0
- package/dist/workflow/lib/compiler/retry-timeout.d.ts +6 -0
- package/dist/workflow/lib/compiler/retry-timeout.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/retry-timeout.js +64 -0
- package/dist/workflow/lib/compiler/retry-timeout.js.map +1 -0
- package/dist/workflow/lib/compiler/review-step.d.ts +18 -0
- package/dist/workflow/lib/compiler/review-step.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/review-step.js +247 -0
- package/dist/workflow/lib/compiler/review-step.js.map +1 -0
- package/dist/workflow/lib/compiler/types.d.ts +42 -0
- package/dist/workflow/lib/compiler/types.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/types.js +3 -0
- package/dist/workflow/lib/compiler/types.js.map +1 -0
- package/dist/workflow/lib/compiler/variable-requirements.d.ts +5 -0
- package/dist/workflow/lib/compiler/variable-requirements.d.ts.map +1 -0
- package/dist/workflow/lib/compiler/variable-requirements.js +119 -0
- package/dist/workflow/lib/compiler/variable-requirements.js.map +1 -0
- package/dist/workflow/lib/deliverable-spec-keys.d.ts +3 -0
- package/dist/workflow/lib/deliverable-spec-keys.d.ts.map +1 -0
- package/dist/workflow/lib/deliverable-spec-keys.js +90 -0
- package/dist/workflow/lib/deliverable-spec-keys.js.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/index.d.ts +23 -0
- package/dist/workflow/lib/dispatch-inputs/index.d.ts.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/index.js +106 -0
- package/dist/workflow/lib/dispatch-inputs/index.js.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.d.ts +3 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.d.ts.map +1 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.js +43 -0
- package/dist/workflow/lib/dispatch-inputs/to-json-schema.js.map +1 -0
- package/dist/workflow/lib/duration.d.ts +2 -0
- package/dist/workflow/lib/duration.d.ts.map +1 -0
- package/dist/workflow/lib/duration.js +26 -0
- package/dist/workflow/lib/duration.js.map +1 -0
- package/dist/workflow/lib/errors.d.ts +9 -0
- package/dist/workflow/lib/errors.d.ts.map +1 -0
- package/dist/workflow/lib/errors.js +28 -0
- package/dist/workflow/lib/errors.js.map +1 -0
- package/dist/workflow/lib/expression/ast.d.ts +61 -0
- package/dist/workflow/lib/expression/ast.d.ts.map +1 -0
- package/dist/workflow/lib/expression/ast.js +34 -0
- package/dist/workflow/lib/expression/ast.js.map +1 -0
- package/dist/workflow/lib/expression/context.d.ts +63 -0
- package/dist/workflow/lib/expression/context.d.ts.map +1 -0
- package/dist/workflow/lib/expression/context.js +32 -0
- package/dist/workflow/lib/expression/context.js.map +1 -0
- package/dist/workflow/lib/expression/evaluator.d.ts +5 -0
- package/dist/workflow/lib/expression/evaluator.d.ts.map +1 -0
- package/dist/workflow/lib/expression/evaluator.js +291 -0
- package/dist/workflow/lib/expression/evaluator.js.map +1 -0
- package/dist/workflow/lib/expression/index.d.ts +9 -0
- package/dist/workflow/lib/expression/index.d.ts.map +1 -0
- package/dist/workflow/lib/expression/index.js +26 -0
- package/dist/workflow/lib/expression/index.js.map +1 -0
- package/dist/workflow/lib/expression/interpolation.d.ts +9 -0
- package/dist/workflow/lib/expression/interpolation.d.ts.map +1 -0
- package/dist/workflow/lib/expression/interpolation.js +51 -0
- package/dist/workflow/lib/expression/interpolation.js.map +1 -0
- package/dist/workflow/lib/expression/parser.d.ts +4 -0
- package/dist/workflow/lib/expression/parser.d.ts.map +1 -0
- package/dist/workflow/lib/expression/parser.js +203 -0
- package/dist/workflow/lib/expression/parser.js.map +1 -0
- package/dist/workflow/lib/expression/template.d.ts +18 -0
- package/dist/workflow/lib/expression/template.d.ts.map +1 -0
- package/dist/workflow/lib/expression/template.js +63 -0
- package/dist/workflow/lib/expression/template.js.map +1 -0
- package/dist/workflow/lib/expression/tokenizer.d.ts +3 -0
- package/dist/workflow/lib/expression/tokenizer.d.ts.map +1 -0
- package/dist/workflow/lib/expression/tokenizer.js +153 -0
- package/dist/workflow/lib/expression/tokenizer.js.map +1 -0
- package/dist/workflow/lib/expression/tokens.d.ts +25 -0
- package/dist/workflow/lib/expression/tokens.d.ts.map +1 -0
- package/dist/workflow/lib/expression/tokens.js +24 -0
- package/dist/workflow/lib/expression/tokens.js.map +1 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.d.ts +5 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.d.ts.map +1 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.js +138 -0
- package/dist/workflow/lib/expression/walk-artifact-refs.js.map +1 -0
- package/dist/workflow/lib/installation-resource-kind.d.ts +14 -0
- package/dist/workflow/lib/installation-resource-kind.d.ts.map +1 -0
- package/dist/workflow/lib/installation-resource-kind.js +59 -0
- package/dist/workflow/lib/installation-resource-kind.js.map +1 -0
- package/dist/workflow/lib/schemas-loader.d.ts +4 -0
- package/dist/workflow/lib/schemas-loader.d.ts.map +1 -0
- package/dist/workflow/lib/schemas-loader.js +36 -0
- package/dist/workflow/lib/schemas-loader.js.map +1 -0
- package/dist/workflow/lib/serializer.d.ts +3 -0
- package/dist/workflow/lib/serializer.d.ts.map +1 -0
- package/dist/workflow/lib/serializer.js +15 -0
- package/dist/workflow/lib/serializer.js.map +1 -0
- package/dist/workflow/lib/types.d.ts +179 -0
- package/dist/workflow/lib/types.d.ts.map +1 -0
- package/dist/workflow/lib/types.js +3 -0
- package/dist/workflow/lib/types.js.map +1 -0
- package/dist/workflow/lib/validate.d.ts +8 -0
- package/dist/workflow/lib/validate.d.ts.map +1 -0
- package/dist/workflow/lib/validate.js +119 -0
- package/dist/workflow/lib/validate.js.map +1 -0
- package/dist/workspace-manifest/index.d.ts +6 -0
- package/dist/workspace-manifest/index.d.ts.map +1 -0
- package/dist/workspace-manifest/index.js +22 -0
- package/dist/workspace-manifest/index.js.map +1 -0
- package/dist/workspace-manifest/lib/compile.d.ts +8 -0
- package/dist/workspace-manifest/lib/compile.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/compile.js +439 -0
- package/dist/workspace-manifest/lib/compile.js.map +1 -0
- package/dist/workspace-manifest/lib/interpolate.d.ts +12 -0
- package/dist/workspace-manifest/lib/interpolate.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/interpolate.js +81 -0
- package/dist/workspace-manifest/lib/interpolate.js.map +1 -0
- package/dist/workspace-manifest/lib/resolve-extends.d.ts +10 -0
- package/dist/workspace-manifest/lib/resolve-extends.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/resolve-extends.js +108 -0
- package/dist/workspace-manifest/lib/resolve-extends.js.map +1 -0
- package/dist/workspace-manifest/lib/schema.d.ts +710 -0
- package/dist/workspace-manifest/lib/schema.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/schema.js +355 -0
- package/dist/workspace-manifest/lib/schema.js.map +1 -0
- package/dist/workspace-manifest/lib/types.d.ts +153 -0
- package/dist/workspace-manifest/lib/types.d.ts.map +1 -0
- package/dist/workspace-manifest/lib/types.js +10 -0
- package/dist/workspace-manifest/lib/types.js.map +1 -0
- package/package.json +79 -0
- package/schema/action.schema.json +181 -0
- package/schema/reusable-workflow.schema.json +46 -0
- package/schema/workflow.schema.json +373 -0
- package/src/deliverable-spec/index.ts +19 -0
- package/src/deliverable-spec/lib/schema.ts +248 -0
- package/src/deliverable-spec/lib/types.ts +26 -0
- package/src/payload-codec/index.ts +40 -0
- package/src/payload-codec/lib/blob-store.ts +0 -0
- package/src/payload-codec/lib/codec-context.ts +38 -0
- package/src/payload-codec/lib/codec.ts +593 -0
- package/src/payload-codec/lib/enums.ts +58 -0
- package/src/payload-codec/lib/errors.ts +54 -0
- package/src/payload-codec/lib/http-blob-store.ts +257 -0
- package/src/payload-codec/lib/lru-cache.ts +81 -0
- package/src/workflow/index.ts +98 -0
- package/src/workflow/lib/action-input-validator.ts +160 -0
- package/src/workflow/lib/compiler/action-shape.ts +71 -0
- package/src/workflow/lib/compiler/canonical-json.ts +53 -0
- package/src/workflow/lib/compiler/compile.ts +1518 -0
- package/src/workflow/lib/compiler/concurrency.ts +223 -0
- package/src/workflow/lib/compiler/dag.ts +108 -0
- package/src/workflow/lib/compiler/index.ts +10 -0
- package/src/workflow/lib/compiler/inputs.ts +199 -0
- package/src/workflow/lib/compiler/installation-resource-validator.ts +114 -0
- package/src/workflow/lib/compiler/manifest-source.ts +176 -0
- package/src/workflow/lib/compiler/matrix.ts +135 -0
- package/src/workflow/lib/compiler/mount-plan.ts +202 -0
- package/src/workflow/lib/compiler/payload-reach-in.ts +497 -0
- package/src/workflow/lib/compiler/permissions.ts +64 -0
- package/src/workflow/lib/compiler/retry-timeout.ts +105 -0
- package/src/workflow/lib/compiler/review-step.ts +517 -0
- package/src/workflow/lib/compiler/types.ts +170 -0
- package/src/workflow/lib/compiler/variable-requirements.ts +208 -0
- package/src/workflow/lib/deliverable-spec-keys.ts +109 -0
- package/src/workflow/lib/dispatch-inputs/index.ts +160 -0
- package/src/workflow/lib/dispatch-inputs/to-json-schema.ts +60 -0
- package/src/workflow/lib/duration.ts +48 -0
- package/src/workflow/lib/errors.ts +37 -0
- package/src/workflow/lib/expression/ast.ts +108 -0
- package/src/workflow/lib/expression/context.ts +148 -0
- package/src/workflow/lib/expression/evaluator.ts +492 -0
- package/src/workflow/lib/expression/index.ts +28 -0
- package/src/workflow/lib/expression/interpolation.ts +84 -0
- package/src/workflow/lib/expression/parser.ts +264 -0
- package/src/workflow/lib/expression/template.ts +117 -0
- package/src/workflow/lib/expression/tokenizer.ts +200 -0
- package/src/workflow/lib/expression/tokens.ts +30 -0
- package/src/workflow/lib/expression/walk-artifact-refs.ts +232 -0
- package/src/workflow/lib/installation-resource-kind.ts +107 -0
- package/src/workflow/lib/schemas-loader.ts +64 -0
- package/src/workflow/lib/serializer.ts +30 -0
- package/src/workflow/lib/types.ts +361 -0
- package/src/workflow/lib/validate.ts +199 -0
- package/src/workspace-manifest/index.ts +27 -0
- package/src/workspace-manifest/lib/compile.ts +608 -0
- package/src/workspace-manifest/lib/interpolate.ts +140 -0
- package/src/workspace-manifest/lib/resolve-extends.ts +260 -0
- package/src/workspace-manifest/lib/schema.ts +612 -0
- package/src/workspace-manifest/lib/types.ts +392 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time helpers for `xema/review@*` steps.
|
|
3
|
+
*
|
|
4
|
+
* The review action's author surface is intentionally small:
|
|
5
|
+
*
|
|
6
|
+
* uses: xema/review@<semver>
|
|
7
|
+
* needs: [<producer>]
|
|
8
|
+
* with:
|
|
9
|
+
* subject: ${{ needs.<producer>.outputs.deliverable }} # or an array
|
|
10
|
+
* redraft: { step: <producer> } # optional
|
|
11
|
+
* reviewers: [...]
|
|
12
|
+
* policy: { kind: ... }
|
|
13
|
+
* iterationTimeoutSeconds: ...
|
|
14
|
+
* onIterationTimeout: ...
|
|
15
|
+
*
|
|
16
|
+
* The worker contract on `reviewWorkflow` is different: it expects
|
|
17
|
+
* `subjects: unknown[]` (always-array) and `redraft: { uses, with }`
|
|
18
|
+
* (embedded action invocation, not a step ref). This module bridges
|
|
19
|
+
* the two:
|
|
20
|
+
*
|
|
21
|
+
* 1. `validateReviewSteps` — throws on bad author shapes BEFORE
|
|
22
|
+
* anything else compiles. Catches missing/dangling step refs,
|
|
23
|
+
* shape mismatches, and the forbidden `redraft set without
|
|
24
|
+
* subject` combination.
|
|
25
|
+
*
|
|
26
|
+
* 2. `computeReviewNeedsExtensions` — returns a map of
|
|
27
|
+
* `reviewJobKey → string[]` of extra needs the compiler must add
|
|
28
|
+
* so the review step's expression context (built from `needs:` at
|
|
29
|
+
* dispatch) can resolve every `${{ needs.* }}` reference embedded
|
|
30
|
+
* in the redraft step's inherited `with:` block. Authors don't
|
|
31
|
+
* have to repeat the producer's transitive needs by hand.
|
|
32
|
+
*
|
|
33
|
+
* 3. `rewriteReviewStepWith` — produces the IR `with:` for a review
|
|
34
|
+
* step: `subject` normalized to `subjects: unknown[]`, and
|
|
35
|
+
* `redraft.step` resolved into the named producer's
|
|
36
|
+
* `{ uses, with }`.
|
|
37
|
+
*
|
|
38
|
+
* The action manifest's input schema (in built-in-action-manifests.ts)
|
|
39
|
+
* describes the AUTHOR shape. The IR shape produced here is the
|
|
40
|
+
* worker's runtime contract — they intentionally diverge.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { WorkflowErrorCode } from '@xemahq/kernel-contracts/workflow';
|
|
44
|
+
|
|
45
|
+
import { WorkflowDslError } from '../errors';
|
|
46
|
+
import {
|
|
47
|
+
compileExpression,
|
|
48
|
+
ExpressionNodeKind,
|
|
49
|
+
type ExpressionNode,
|
|
50
|
+
} from '../expression';
|
|
51
|
+
import { stripInterpolation } from '../expression/interpolation';
|
|
52
|
+
import type {
|
|
53
|
+
WorkflowDocument,
|
|
54
|
+
WorkflowJobDeclaration,
|
|
55
|
+
WorkflowStrategyDeclaration,
|
|
56
|
+
} from '../types';
|
|
57
|
+
|
|
58
|
+
/** Matches `xema/review@<semver>` (1, 2, or 3 dotted version segments). */
|
|
59
|
+
const REVIEW_ACTION_RE =
|
|
60
|
+
/^xema\/review@[0-9]+(\.[0-9]+(\.[0-9]+(-[A-Za-z0-9.-]+)?)?)?$/;
|
|
61
|
+
|
|
62
|
+
/** Matches `xema/agent@<semver>` — only valid `redraft.step` producer kind. */
|
|
63
|
+
const AGENT_ACTION_RE =
|
|
64
|
+
/^xema\/agent@[0-9]+(\.[0-9]+(\.[0-9]+(-[A-Za-z0-9.-]+)?)?)?$/;
|
|
65
|
+
|
|
66
|
+
export function isReviewActionRef(uses: string): boolean {
|
|
67
|
+
return REVIEW_ACTION_RE.test(uses);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface ReviewWithShape {
|
|
71
|
+
readonly subject: string | readonly string[] | undefined;
|
|
72
|
+
readonly redraft: { readonly step: string } | undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Per-job redraft-strategy descriptor carried on the IR `redraft` block
|
|
77
|
+
* when the producer step has a matrix strategy. Mirrors the worker
|
|
78
|
+
* consumer in `biomes/workflow-runtime/api/workflow-runtime-worker/src/workflows/review.workflow.ts`
|
|
79
|
+
* — kept JSON-serializable so the IR survives Temporal payload encoding.
|
|
80
|
+
*
|
|
81
|
+
* `static` entries are already cartesian-expanded at compile time;
|
|
82
|
+
* `dynamic.fromExpression` is the producer's expression body (already
|
|
83
|
+
* `${{ … }}`-stripped) which the parent dispatcher evaluates against
|
|
84
|
+
* the review step's expression context before spawning the child
|
|
85
|
+
* workflow. `keyBy` is informational only — per-CU keyed targeting is
|
|
86
|
+
* a Phase-2 feature; Phase 1 always re-runs every matrix entry.
|
|
87
|
+
*/
|
|
88
|
+
export type ReviewRedraftStrategyIR =
|
|
89
|
+
| {
|
|
90
|
+
readonly kind: 'static';
|
|
91
|
+
readonly entries: readonly Readonly<Record<string, unknown>>[];
|
|
92
|
+
readonly maxParallel: number;
|
|
93
|
+
}
|
|
94
|
+
| {
|
|
95
|
+
readonly kind: 'dynamic';
|
|
96
|
+
readonly fromExpression: string;
|
|
97
|
+
readonly bindingName: string;
|
|
98
|
+
readonly maxEntries: number;
|
|
99
|
+
readonly maxParallel: number;
|
|
100
|
+
readonly keyBy: string | null;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Lightweight typed projection of a review step's `with:` block.
|
|
105
|
+
* Throws DSL_SEMANTIC_INVALID on malformed shape; downstream consumers
|
|
106
|
+
* (rewriter, validators) trust the projection.
|
|
107
|
+
*/
|
|
108
|
+
function projectReviewWith(
|
|
109
|
+
jobKey: string,
|
|
110
|
+
rawWith: Readonly<Record<string, unknown>> | undefined,
|
|
111
|
+
): ReviewWithShape {
|
|
112
|
+
const w = rawWith ?? {};
|
|
113
|
+
const subjectRaw = (w as Record<string, unknown>)['subject'];
|
|
114
|
+
let subject: ReviewWithShape['subject'];
|
|
115
|
+
if (subjectRaw === undefined) {
|
|
116
|
+
subject = undefined;
|
|
117
|
+
} else if (typeof subjectRaw === 'string') {
|
|
118
|
+
if (subjectRaw.length === 0) {
|
|
119
|
+
throw new WorkflowDslError(
|
|
120
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
121
|
+
`Job '${jobKey}' with.subject must be a non-empty string or array of strings.`,
|
|
122
|
+
{ jobKey, fieldPath: 'with.subject' },
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
subject = subjectRaw;
|
|
126
|
+
} else if (Array.isArray(subjectRaw)) {
|
|
127
|
+
if (subjectRaw.length === 0) {
|
|
128
|
+
throw new WorkflowDslError(
|
|
129
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
130
|
+
`Job '${jobKey}' with.subject must be a non-empty array (got []).`,
|
|
131
|
+
{ jobKey, fieldPath: 'with.subject' },
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
for (let i = 0; i < subjectRaw.length; i++) {
|
|
135
|
+
const item = subjectRaw[i];
|
|
136
|
+
if (typeof item !== 'string' || item.length === 0) {
|
|
137
|
+
throw new WorkflowDslError(
|
|
138
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
139
|
+
`Job '${jobKey}' with.subject[${i}] must be a non-empty string.`,
|
|
140
|
+
{ jobKey, fieldPath: `with.subject[${i}]` },
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
subject = subjectRaw as readonly string[];
|
|
145
|
+
} else {
|
|
146
|
+
throw new WorkflowDslError(
|
|
147
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
148
|
+
`Job '${jobKey}' with.subject must be a string or an array of strings (got ${typeof subjectRaw}).`,
|
|
149
|
+
{ jobKey, fieldPath: 'with.subject' },
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const redraftRaw = (w as Record<string, unknown>)['redraft'];
|
|
154
|
+
let redraft: ReviewWithShape['redraft'];
|
|
155
|
+
if (redraftRaw === undefined) {
|
|
156
|
+
redraft = undefined;
|
|
157
|
+
} else if (
|
|
158
|
+
typeof redraftRaw === 'object' &&
|
|
159
|
+
redraftRaw !== null &&
|
|
160
|
+
!Array.isArray(redraftRaw)
|
|
161
|
+
) {
|
|
162
|
+
const stepRaw = (redraftRaw as Record<string, unknown>)['step'];
|
|
163
|
+
if (typeof stepRaw !== 'string' || stepRaw.length === 0) {
|
|
164
|
+
throw new WorkflowDslError(
|
|
165
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
166
|
+
`Job '${jobKey}' with.redraft.step must be a non-empty string naming a producer step.`,
|
|
167
|
+
{ jobKey, fieldPath: 'with.redraft.step' },
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
redraft = { step: stepRaw };
|
|
171
|
+
} else {
|
|
172
|
+
throw new WorkflowDslError(
|
|
173
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
174
|
+
`Job '${jobKey}' with.redraft must be an object of shape '{ step: <stepName> }'.`,
|
|
175
|
+
{ jobKey, fieldPath: 'with.redraft' },
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { subject, redraft };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Validate every review step in the workflow. Run before topo sort +
|
|
184
|
+
* expression validation so author errors surface with clear messages
|
|
185
|
+
* before downstream passes throw less-helpful errors.
|
|
186
|
+
*/
|
|
187
|
+
export function validateReviewSteps(workflow: WorkflowDocument): void {
|
|
188
|
+
for (const [jobKey, job] of Object.entries(workflow.jobs)) {
|
|
189
|
+
if (!isReviewActionRef(job.uses)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const shape = projectReviewWith(jobKey, job.with);
|
|
193
|
+
|
|
194
|
+
// `redraft` set without `subject` is forbidden — the producer
|
|
195
|
+
// must be authored as a separate step that publishes a deliverable
|
|
196
|
+
// as the iter-1 subject. This keeps the standalone draft step
|
|
197
|
+
// observable in the run UI and avoids the "ghost step that runs
|
|
198
|
+
// but is ignored" footgun.
|
|
199
|
+
if (shape.redraft !== undefined && shape.subject === undefined) {
|
|
200
|
+
throw new WorkflowDslError(
|
|
201
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
202
|
+
`Job '${jobKey}' has 'redraft' set but no 'subject'. Author the producer as a separate step and reference it via 'subject: \${{ needs.${shape.redraft.step}.outputs.deliverable }}'.`,
|
|
203
|
+
{ jobKey, fieldPath: 'with' },
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (shape.redraft !== undefined) {
|
|
208
|
+
const stepName = shape.redraft.step;
|
|
209
|
+
const producer = workflow.jobs[stepName];
|
|
210
|
+
if (!producer) {
|
|
211
|
+
throw new WorkflowDslError(
|
|
212
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
213
|
+
`Job '${jobKey}' with.redraft.step references unknown job '${stepName}'.`,
|
|
214
|
+
{ jobKey, fieldPath: 'with.redraft.step', referencedJob: stepName },
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (stepName === jobKey) {
|
|
218
|
+
throw new WorkflowDslError(
|
|
219
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
220
|
+
`Job '${jobKey}' with.redraft.step cannot reference itself.`,
|
|
221
|
+
{ jobKey, fieldPath: 'with.redraft.step' },
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (!AGENT_ACTION_RE.test(producer.uses)) {
|
|
225
|
+
throw new WorkflowDslError(
|
|
226
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
227
|
+
`Job '${jobKey}' with.redraft.step = '${stepName}' must reference a step that uses 'xema/agent@<semver>' (got '${producer.uses}').`,
|
|
228
|
+
{
|
|
229
|
+
jobKey,
|
|
230
|
+
fieldPath: 'with.redraft.step',
|
|
231
|
+
referencedJob: stepName,
|
|
232
|
+
referencedUses: producer.uses,
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
// Matrix-strategy producers ARE allowed here — on `reject`, the
|
|
237
|
+
// worker re-dispatches every matrix entry of the producer in
|
|
238
|
+
// parallel (one entry → one redraft agent dispatch). The DSL
|
|
239
|
+
// pre-resolves the matrix shape onto the IR `redraft.strategy`
|
|
240
|
+
// block so the parent dispatcher (root-run.workflow.ts) can
|
|
241
|
+
// evaluate dynamic `from:` expressions against the review step's
|
|
242
|
+
// expression context before spawning the child review workflow.
|
|
243
|
+
const declaredNeeds = job.needs ?? [];
|
|
244
|
+
if (!declaredNeeds.includes(stepName)) {
|
|
245
|
+
throw new WorkflowDslError(
|
|
246
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
247
|
+
`Job '${jobKey}' with.redraft.step = '${stepName}' must also be listed in 'needs:'. Add 'needs: [${[...declaredNeeds, stepName].map((s) => `'${s}'`).join(', ')}]'.`,
|
|
248
|
+
{
|
|
249
|
+
jobKey,
|
|
250
|
+
fieldPath: 'with.redraft.step',
|
|
251
|
+
referencedJob: stepName,
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Compute extra `needs:` entries each review step requires beyond what
|
|
261
|
+
* the author declared. Specifically: for any review step with
|
|
262
|
+
* `redraft.step: <producer>`, add `<producer>`'s OWN `needs:` list so
|
|
263
|
+
* the review step's expression context (built from its `needs:`) has
|
|
264
|
+
* every upstream value the embedded redraft `with:` may reference at
|
|
265
|
+
* dispatch time.
|
|
266
|
+
*
|
|
267
|
+
* Returns an empty map when no review steps have a redraft. The caller
|
|
268
|
+
* folds these into the topo-sort input alongside `matrixGather`.
|
|
269
|
+
*/
|
|
270
|
+
export function computeReviewNeedsExtensions(
|
|
271
|
+
workflow: WorkflowDocument,
|
|
272
|
+
): Readonly<Record<string, readonly string[]>> {
|
|
273
|
+
const out: Record<string, readonly string[]> = {};
|
|
274
|
+
for (const [jobKey, job] of Object.entries(workflow.jobs)) {
|
|
275
|
+
if (!isReviewActionRef(job.uses)) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const shape = projectReviewWith(jobKey, job.with);
|
|
279
|
+
if (shape.redraft === undefined) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const producer = workflow.jobs[shape.redraft.step];
|
|
283
|
+
if (!producer) {
|
|
284
|
+
// validateReviewSteps already throws for this case; defensive only.
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const declared = new Set<string>(producer.needs ?? []);
|
|
288
|
+
// For dynamic-matrix producers, the `from:` expression is evaluated
|
|
289
|
+
// by the parent dispatcher at review-dispatch time against the
|
|
290
|
+
// review step's expression context — every `needs.<X>` it
|
|
291
|
+
// references MUST therefore be in the review step's `needs:` too.
|
|
292
|
+
if (producer.strategy !== undefined && 'dynamic' in producer.strategy) {
|
|
293
|
+
const fromRefs = collectNeedsReferencesFromExpression(
|
|
294
|
+
producer.strategy.dynamic.from,
|
|
295
|
+
);
|
|
296
|
+
for (const ref of fromRefs) {
|
|
297
|
+
declared.add(ref);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (declared.size === 0) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
out[jobKey] = Object.freeze([...declared]) as readonly string[];
|
|
304
|
+
}
|
|
305
|
+
return Object.freeze(out);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Walk a `${{ … }}`-wrapped expression body and collect every
|
|
310
|
+
* `needs.<X>` root reference. Used by the review-step needs extension
|
|
311
|
+
* pass when the redraft producer is a dynamic matrix — its `from:`
|
|
312
|
+
* expression resolves at the parent dispatcher's review-dispatch time
|
|
313
|
+
* against the review step's expression context, so every referenced
|
|
314
|
+
* upstream step must be in the review step's `needs:` too. Returns a
|
|
315
|
+
* deduplicated sorted-by-insertion list of step keys.
|
|
316
|
+
*/
|
|
317
|
+
function collectNeedsReferencesFromExpression(
|
|
318
|
+
rawExpression: string,
|
|
319
|
+
): readonly string[] {
|
|
320
|
+
const body = stripInterpolation(rawExpression);
|
|
321
|
+
const ast = compileExpression(body);
|
|
322
|
+
const seen = new Set<string>();
|
|
323
|
+
walkForNeedsRefs(ast, seen);
|
|
324
|
+
return [...seen];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function walkForNeedsRefs(node: ExpressionNode, sink: Set<string>): void {
|
|
328
|
+
switch (node.kind) {
|
|
329
|
+
case ExpressionNodeKind.MEMBER: {
|
|
330
|
+
// Match `needs.<X>` (target is the `needs` identifier).
|
|
331
|
+
if (
|
|
332
|
+
node.target.kind === ExpressionNodeKind.IDENTIFIER &&
|
|
333
|
+
node.target.name === 'needs'
|
|
334
|
+
) {
|
|
335
|
+
sink.add(node.property);
|
|
336
|
+
}
|
|
337
|
+
walkForNeedsRefs(node.target, sink);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
case ExpressionNodeKind.INDEX:
|
|
341
|
+
walkForNeedsRefs(node.target, sink);
|
|
342
|
+
walkForNeedsRefs(node.index, sink);
|
|
343
|
+
return;
|
|
344
|
+
case ExpressionNodeKind.CALL:
|
|
345
|
+
for (const arg of node.args) {
|
|
346
|
+
walkForNeedsRefs(arg, sink);
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
case ExpressionNodeKind.UNARY_NOT:
|
|
350
|
+
walkForNeedsRefs(node.operand, sink);
|
|
351
|
+
return;
|
|
352
|
+
case ExpressionNodeKind.BINARY_EQ:
|
|
353
|
+
case ExpressionNodeKind.BINARY_NEQ:
|
|
354
|
+
case ExpressionNodeKind.BINARY_AND:
|
|
355
|
+
case ExpressionNodeKind.BINARY_OR:
|
|
356
|
+
walkForNeedsRefs(node.left, sink);
|
|
357
|
+
walkForNeedsRefs(node.right, sink);
|
|
358
|
+
return;
|
|
359
|
+
case ExpressionNodeKind.LITERAL:
|
|
360
|
+
case ExpressionNodeKind.IDENTIFIER:
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Rewrite a review step's `with:` block from the author shape to the
|
|
367
|
+
* worker contract:
|
|
368
|
+
*
|
|
369
|
+
* - Author `subject: <string>` → IR `subjects: [<string>]`
|
|
370
|
+
* - Author `subject: <string[]>` → IR `subjects: <string[]>`
|
|
371
|
+
* - Author `redraft: { step: X }` → IR `redraft: { uses, with }`
|
|
372
|
+
* looked up from the named producer step.
|
|
373
|
+
*
|
|
374
|
+
* Other fields pass through verbatim. Inputs that are not review steps
|
|
375
|
+
* fall back to `payload.with ?? {}` unchanged — callers may always
|
|
376
|
+
* route through this helper without an `isReviewActionRef` check.
|
|
377
|
+
*/
|
|
378
|
+
export function rewriteReviewStepWith(
|
|
379
|
+
jobKey: string,
|
|
380
|
+
payload: WorkflowJobDeclaration,
|
|
381
|
+
workflow: WorkflowDocument,
|
|
382
|
+
): Readonly<Record<string, unknown>> {
|
|
383
|
+
if (!isReviewActionRef(payload.uses)) {
|
|
384
|
+
return payload.with ?? {};
|
|
385
|
+
}
|
|
386
|
+
const shape = projectReviewWith(jobKey, payload.with);
|
|
387
|
+
const out: Record<string, unknown> = { ...(payload.with ?? {}) };
|
|
388
|
+
// Drop the author-only fields so the IR carries only worker-facing
|
|
389
|
+
// shapes.
|
|
390
|
+
delete out['subject'];
|
|
391
|
+
delete out['redraft'];
|
|
392
|
+
|
|
393
|
+
if (shape.subject !== undefined) {
|
|
394
|
+
out['subjects'] =
|
|
395
|
+
typeof shape.subject === 'string' ? [shape.subject] : [...shape.subject];
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (shape.redraft !== undefined) {
|
|
399
|
+
const producer = workflow.jobs[shape.redraft.step];
|
|
400
|
+
if (!producer) {
|
|
401
|
+
// validateReviewSteps already throws here; keep defensive.
|
|
402
|
+
throw new WorkflowDslError(
|
|
403
|
+
WorkflowErrorCode.DSL_SEMANTIC_INVALID,
|
|
404
|
+
`Job '${jobKey}' with.redraft.step references unknown job '${shape.redraft.step}'.`,
|
|
405
|
+
{ jobKey, fieldPath: 'with.redraft.step' },
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
// Preserve the original step name on the IR `redraft` block. The
|
|
409
|
+
// worker uses it as the `artifactScope` override when the redraft's
|
|
410
|
+
// agent activity emits its outputs, so the regenerated bytes
|
|
411
|
+
// version the SAME logical artifact as the original draft (e.g.
|
|
412
|
+
// v2 of `draft.response`) rather than landing under the review
|
|
413
|
+
// step's identity (which would create a parallel `review.response`
|
|
414
|
+
// artifact and starve the reviewer's iter-2 view of the new draft).
|
|
415
|
+
//
|
|
416
|
+
// For matrix producers, also carry the producer's matrix shape
|
|
417
|
+
// onto the IR so the parent dispatcher (root-run.workflow.ts) can
|
|
418
|
+
// expand entries at review-dispatch time and pass the resolved
|
|
419
|
+
// bindings into the review child workflow. On `reject`, the worker
|
|
420
|
+
// re-dispatches every matrix entry in parallel — one redraft agent
|
|
421
|
+
// dispatch per entry.
|
|
422
|
+
const ir: Record<string, unknown> = {
|
|
423
|
+
step: shape.redraft.step,
|
|
424
|
+
uses: producer.uses,
|
|
425
|
+
with: producer.with ?? {},
|
|
426
|
+
};
|
|
427
|
+
const strategy = compileRedraftStrategyIR(producer.strategy);
|
|
428
|
+
if (strategy !== null) {
|
|
429
|
+
ir['strategy'] = strategy;
|
|
430
|
+
}
|
|
431
|
+
if (producer.matrixGather && producer.matrixGather.length > 0) {
|
|
432
|
+
ir['matrixGather'] = [...producer.matrixGather];
|
|
433
|
+
}
|
|
434
|
+
out['redraft'] = ir;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return out;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Pre-resolve a producer step's authored matrix strategy into a
|
|
442
|
+
* JSON-serializable IR descriptor the worker carries through the
|
|
443
|
+
* review step's `with.redraft`. `null` for non-matrix producers — the
|
|
444
|
+
* worker takes the legacy single-dispatch path when `strategy` is
|
|
445
|
+
* absent on the IR `redraft` block. Static matrices are NOT
|
|
446
|
+
* cartesian-expanded here (that's the job of `compileStrategy` which
|
|
447
|
+
* runs against the producer's own job IR); we propagate the raw axes
|
|
448
|
+
* because the worker reuses the same compile-time expansion behavior
|
|
449
|
+
* via the parent dispatcher's per-job CompiledJob lookup. Keeping this
|
|
450
|
+
* descriptor small and shape-faithful avoids drift with
|
|
451
|
+
* `compileStrategy`'s output — the parent dispatcher reads the
|
|
452
|
+
* producer's own `CompiledJob.strategy` (Source of truth) and uses
|
|
453
|
+
* THIS descriptor only as a discriminator hint + entry-resolution
|
|
454
|
+
* fallback. Mirror of the worker contract `ReviewRedraftStrategyIR`.
|
|
455
|
+
*/
|
|
456
|
+
function compileRedraftStrategyIR(
|
|
457
|
+
strategy: WorkflowStrategyDeclaration | undefined,
|
|
458
|
+
): ReviewRedraftStrategyIR | null {
|
|
459
|
+
if (strategy === undefined) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
if ('matrix' in strategy) {
|
|
463
|
+
// Carry static axes; the worker side mirrors `compileStrategy`'s
|
|
464
|
+
// cartesian expansion. `maxParallel` defaults align with
|
|
465
|
+
// `matrix.ts:resolveMaxParallel` — when absent, the worker uses 8.
|
|
466
|
+
const entries = expandStaticMatrixAxes(strategy.matrix);
|
|
467
|
+
const maxParallel =
|
|
468
|
+
strategy.maxParallel !== undefined && strategy.maxParallel > 0
|
|
469
|
+
? Math.min(strategy.maxParallel, entries.length || 1)
|
|
470
|
+
: Math.min(8, Math.max(1, entries.length));
|
|
471
|
+
return {
|
|
472
|
+
kind: 'static',
|
|
473
|
+
entries,
|
|
474
|
+
maxParallel,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
const dyn = strategy.dynamic;
|
|
478
|
+
const body = stripInterpolation(dyn.from);
|
|
479
|
+
const maxEntries = dyn.maxEntries ?? 64;
|
|
480
|
+
const maxParallel =
|
|
481
|
+
strategy.maxParallel !== undefined && strategy.maxParallel > 0
|
|
482
|
+
? Math.min(strategy.maxParallel, maxEntries)
|
|
483
|
+
: Math.min(8, maxEntries);
|
|
484
|
+
return {
|
|
485
|
+
kind: 'dynamic',
|
|
486
|
+
fromExpression: body,
|
|
487
|
+
bindingName: dyn.as,
|
|
488
|
+
maxEntries,
|
|
489
|
+
maxParallel,
|
|
490
|
+
keyBy: dyn.keyBy ?? null,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Cartesian expansion of a static-matrix axes block. Mirrors
|
|
496
|
+
* `expandStaticMatrix` in `./matrix.ts` minus the cap-checking, which
|
|
497
|
+
* has already been enforced on the producer's own CompiledJob by the
|
|
498
|
+
* normal compile path. Lexicographic axis order keeps replay
|
|
499
|
+
* deterministic.
|
|
500
|
+
*/
|
|
501
|
+
function expandStaticMatrixAxes(
|
|
502
|
+
axes: Readonly<Record<string, readonly unknown[]>>,
|
|
503
|
+
): readonly Readonly<Record<string, unknown>>[] {
|
|
504
|
+
const axisKeys = Object.keys(axes).sort();
|
|
505
|
+
let entries: Record<string, unknown>[] = [{}];
|
|
506
|
+
for (const axis of axisKeys) {
|
|
507
|
+
const values = axes[axis]!;
|
|
508
|
+
const next: Record<string, unknown>[] = [];
|
|
509
|
+
for (const partial of entries) {
|
|
510
|
+
for (const value of values) {
|
|
511
|
+
next.push({ ...partial, [axis]: value });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
entries = next;
|
|
515
|
+
}
|
|
516
|
+
return entries.map(Object.freeze) as readonly Readonly<Record<string, unknown>>[];
|
|
517
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActionExecutionKind,
|
|
3
|
+
Briefcase,
|
|
4
|
+
TaskQueueName,
|
|
5
|
+
TriggerPayload,
|
|
6
|
+
WalletContents,
|
|
7
|
+
WorkflowRef,
|
|
8
|
+
} from '@xemahq/kernel-contracts/workflow';
|
|
9
|
+
import type { ActionManifest, WorkflowDocument } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Everything the compiler needs beyond the workflow document itself. The
|
|
13
|
+
* engine gathers these inputs before calling `compileWorkflow(...)`:
|
|
14
|
+
*
|
|
15
|
+
* - `workflow` — validated WorkflowDocument (pass through
|
|
16
|
+
* `validateWorkflowDocument` first).
|
|
17
|
+
* - `workflowRef` — slug/version/isSystem, resolved from the DB.
|
|
18
|
+
* - `trigger` — the payload that fired this run.
|
|
19
|
+
* - `resolvedRefs` — map of `uses:` strings to resolved refs, populated
|
|
20
|
+
* by looking up each job's `uses:` in the action-manifest registry
|
|
21
|
+
* (for `xema/...`) or the reusable-workflow registry (for
|
|
22
|
+
* `xema://workflow/...`).
|
|
23
|
+
* - `workflowDefinitionVersionSha256` — sha256 of the source YAML the
|
|
24
|
+
* compiler is consuming. Emitted into CompiledRun for audit.
|
|
25
|
+
*/
|
|
26
|
+
export interface CompileInput {
|
|
27
|
+
readonly workflow: WorkflowDocument;
|
|
28
|
+
readonly workflowRef: WorkflowRef;
|
|
29
|
+
readonly trigger: TriggerPayload;
|
|
30
|
+
readonly resolvedRefs: Readonly<Record<string, ResolvedRef>>;
|
|
31
|
+
/**
|
|
32
|
+
* Map of literal `agentSlug` values (used in `with.agentSlug` and
|
|
33
|
+
* `with.reviewers[].agentSlug`) to the agent metadata the compiler
|
|
34
|
+
* needs for validation. The engine pre-fetches this from
|
|
35
|
+
* llm-registry-api. The compiler validates that every literal slug
|
|
36
|
+
* appears here; expression-shaped slugs (e.g. `${{ inputs.agentSlug }}`)
|
|
37
|
+
* are skipped — they resolve at dispatch and the activity's
|
|
38
|
+
* `AGENT_NOT_REGISTERED` preflight catches them at runtime.
|
|
39
|
+
*
|
|
40
|
+
* Pass an empty map (or omit) to opt out of agent validation (e.g.
|
|
41
|
+
* for the `preview-raw` endpoint when the engine cannot reach the
|
|
42
|
+
* registry, or for tests that don't exercise the registry).
|
|
43
|
+
*/
|
|
44
|
+
readonly resolvedAgents?: Readonly<Record<string, ResolvedAgent>>;
|
|
45
|
+
/**
|
|
46
|
+
* Map of literal deliverable-spec refs (used in `with.deliverableSpecRef`
|
|
47
|
+
* and the `produces:` array) to the spec metadata the compiler needs
|
|
48
|
+
* for validation. The engine pre-fetches this from
|
|
49
|
+
* deliverable-specs-api. Same literal-vs-expression rule as
|
|
50
|
+
* `resolvedAgents`. Same opt-out semantics: empty / omitted = skip.
|
|
51
|
+
*/
|
|
52
|
+
readonly resolvedDeliverableSpecs?: Readonly<
|
|
53
|
+
Record<string, ResolvedDeliverableSpec>
|
|
54
|
+
>;
|
|
55
|
+
readonly workflowDefinitionVersionSha256: string;
|
|
56
|
+
/**
|
|
57
|
+
* Contents of every wallet referenced under `requires.wallets`, plus
|
|
58
|
+
* any additional wallets a dispatch caller asked for. The compiler
|
|
59
|
+
* unions the entries to derive `CompiledRun.requiredVariables` and to
|
|
60
|
+
* validate `${{ vars.X }}` / `${{ secrets.X }}` references. When
|
|
61
|
+
* omitted, wallet-derived validation is skipped — used by
|
|
62
|
+
* `preview-raw` during authoring where the engine can't reach the
|
|
63
|
+
* variable store.
|
|
64
|
+
*/
|
|
65
|
+
readonly resolvedWallets?: Readonly<Record<string, WalletContents>>;
|
|
66
|
+
/**
|
|
67
|
+
* Installation scope of this dispatch — present when the workflow is
|
|
68
|
+
* dispatched from a biome installation (webhook trigger gated on
|
|
69
|
+
* the installation, manual dispatch from the installation's UX, a
|
|
70
|
+
* scheduled trigger tied to the installation). Absent for system /
|
|
71
|
+
* org-wide dispatches that don't belong to any installation.
|
|
72
|
+
*
|
|
73
|
+
* The compiler's installation-resource validator uses this scope to
|
|
74
|
+
* resolve `x-installation-resource: { kind: ... }` references in
|
|
75
|
+
* action input schemas against the installation's bound resources
|
|
76
|
+
* (wallets / repos / channels / …). When `installationScope` is
|
|
77
|
+
* undefined, the validator skips installation-bound fields — system
|
|
78
|
+
* workflows can still reference wallet ids, but compile-time
|
|
79
|
+
* binding-set validation only happens for biome-installed runs.
|
|
80
|
+
*/
|
|
81
|
+
readonly installationScope?: InstallationCompileScope;
|
|
82
|
+
/**
|
|
83
|
+
* When true, the compiler substitutes type-appropriate sentinel values
|
|
84
|
+
* for missing required inputs instead of throwing. Intended only for
|
|
85
|
+
* the `preview-raw` endpoint (Monaco editor live feedback) where the
|
|
86
|
+
* caller deliberately omits real input values during YAML authoring.
|
|
87
|
+
* Never set this on real run compilation paths.
|
|
88
|
+
*/
|
|
89
|
+
readonly previewMode?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Run-scoped Briefcase the dispatch caller attached to this run.
|
|
92
|
+
* Synthesized server-side from the StartRun request body (uploads /
|
|
93
|
+
* references / vars / mcpTools) and threaded into the CompiledRun so
|
|
94
|
+
* the run's snapshot captures the dispatch payload alongside its
|
|
95
|
+
* inputs and trigger. Absent for runs without dispatch-attached
|
|
96
|
+
* context (compiler treats absent and `emptyBriefcase()` identically
|
|
97
|
+
* downstream).
|
|
98
|
+
*/
|
|
99
|
+
readonly briefcase?: Briefcase;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Installation context the engine pre-fetches at dispatch time. Each
|
|
104
|
+
* `boundResources` entry lists the resource ids the installation is
|
|
105
|
+
* bound to for a single `InstallationResourceKind` — the compiler
|
|
106
|
+
* checks every literal value in a `x-installation-resource` field
|
|
107
|
+
* against the matching kind's set and rejects unbound values.
|
|
108
|
+
*
|
|
109
|
+
* The engine populates this from biome-host-api right before calling
|
|
110
|
+
* the compiler. Passing an empty array for a kind means "no resources
|
|
111
|
+
* bound" — every literal value for that kind fails validation.
|
|
112
|
+
*/
|
|
113
|
+
export interface InstallationCompileScope {
|
|
114
|
+
readonly installationId: string;
|
|
115
|
+
readonly orgId: string;
|
|
116
|
+
readonly projectId: string;
|
|
117
|
+
readonly biomeId: string;
|
|
118
|
+
readonly boundResources: Readonly<Record<string, readonly string[]>>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Minimal agent metadata the compiler keeps for each literal `agentSlug`.
|
|
123
|
+
* The engine fetches the full row from llm-registry-api but the compiler
|
|
124
|
+
* only needs the existence proof and the snapshot hash (so a future
|
|
125
|
+
* `CompiledRun` can carry it for cache-invalidation matching). Extend
|
|
126
|
+
* with more fields only when the compiler actually consumes them.
|
|
127
|
+
*/
|
|
128
|
+
export interface ResolvedAgent {
|
|
129
|
+
readonly slug: string;
|
|
130
|
+
readonly snapshotHash: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Minimal deliverable-spec metadata the compiler keeps for each literal
|
|
135
|
+
* `deliverableSpecRef`. Same minimality rule as `ResolvedAgent`.
|
|
136
|
+
*
|
|
137
|
+
* Optional `topLevelKeys` enables compile-time field validation for
|
|
138
|
+
* expressions of shape
|
|
139
|
+
* `${{ job.outputs.deliverable.content.value.<field> }}` — when
|
|
140
|
+
* populated, the DSL asserts `<field>` is one of these. The engine
|
|
141
|
+
* extracts the keys from the spec's `zodSchemaSource` (Zod) or
|
|
142
|
+
* `content`'s `properties` (JSON Schema) at registry-fetch time. Specs
|
|
143
|
+
* whose kind has no known shape (e.g. CUSTOM, ENDPOINT_FETCH) leave it
|
|
144
|
+
* undefined and the validator skips them.
|
|
145
|
+
*/
|
|
146
|
+
export interface ResolvedDeliverableSpec {
|
|
147
|
+
readonly ref: string;
|
|
148
|
+
readonly slug: string;
|
|
149
|
+
readonly snapshotHash: string;
|
|
150
|
+
readonly topLevelKeys?: readonly string[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resolved reference for a single `uses:` value. Emitted by the engine's
|
|
155
|
+
* ref-resolution layer before compilation; the compiler trusts it blindly.
|
|
156
|
+
*
|
|
157
|
+
* For action refs, `actionManifest` is the full manifest (the compiler
|
|
158
|
+
* needs retry/timeout/allowedMounts). For reusable-workflow refs,
|
|
159
|
+
* `actionManifest` is null — reusable workflows have their own compilation
|
|
160
|
+
* pass, so this outer compilation only records the pointer.
|
|
161
|
+
*/
|
|
162
|
+
export interface ResolvedRef {
|
|
163
|
+
readonly id: string;
|
|
164
|
+
readonly version: string;
|
|
165
|
+
readonly manifestSha256: string;
|
|
166
|
+
readonly executionKind: ActionExecutionKind;
|
|
167
|
+
readonly taskQueue: TaskQueueName;
|
|
168
|
+
readonly isReusableWorkflow: boolean;
|
|
169
|
+
readonly actionManifest: ActionManifest | null;
|
|
170
|
+
}
|