feat: authorize workflow contract authoring

This commit is contained in:
lda
2026-08-14 17:43:14 +07:00 Verified
parent bc23a722c3
commit bd226a2fa7
20 changed files with 1506 additions and 14 deletions
@@ -157,6 +157,55 @@ const DraftWorkspaceSchema = Schema.Struct({
workspace_id: Schema.String,
});
const AuthoringPathOptionSchema = Schema.Struct({
path: Schema.String,
label: Schema.String,
origin: Schema.Literal(
"workflow_input",
"workflow_state",
"runtime_context",
"step_input",
"step_output",
"workflow_output",
),
schema: JsonObjectSchema,
required: Schema.Boolean,
availability: Schema.Literal("available", "conditional"),
uses: Schema.Array(
Schema.Literal(
"step_input",
"step_output_source",
"state_target",
"workflow_output",
),
),
description: Schema.optional(Schema.String),
reason: Schema.optional(Schema.String),
});
const AuthoringStepContractSchema = Schema.Struct({
step_id: Schema.String,
label: Schema.String,
description: Schema.optional(Schema.String),
input_targets: Schema.optional(Schema.Array(AuthoringPathOptionSchema)),
output_sources: Schema.optional(Schema.Array(AuthoringPathOptionSchema)),
outcomes: Schema.optional(Schema.Array(Schema.String)),
});
const AuthoringContractInventorySchema = Schema.Struct({
workspace_id: Schema.String,
revision: PositiveIntegerSchema,
selected_step_id: Schema.NullOr(Schema.String),
readable_sources: Schema.Array(AuthoringPathOptionSchema),
step_input_targets: Schema.Array(AuthoringPathOptionSchema),
step_output_sources: Schema.Array(AuthoringPathOptionSchema),
state_targets: Schema.Array(AuthoringPathOptionSchema),
workflow_output_targets: Schema.Array(AuthoringPathOptionSchema),
entry_steps: Schema.Array(AuthoringStepContractSchema),
workflow_outcomes: Schema.Array(Schema.String),
warnings: Schema.Array(Schema.String),
});
// Effect's empty Struct does not traverse excess keys; the impossible optional
// field keeps the authored empty payload strict under onExcessProperty:error.
const EmptyPayloadSchema = Schema.Struct({
@@ -478,6 +527,14 @@ export const authoredRpcSchemas = {
}),
success: DraftWorkspaceSchema,
},
"workflow.draft_workspaces.inspect_authoring_contract": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
revision: PositiveIntegerSchema,
selected_step_id: Schema.optional(Schema.NullOr(Schema.String)),
}),
success: Schema.Union(AuthoringContractInventorySchema, DraftWorkspaceSchema),
},
"workflow.draft_workspaces.create_empty": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
@@ -490,6 +547,25 @@ export const authoredRpcSchemas = {
}),
success: DraftWorkspaceSchema,
},
"workflow.draft_workspaces.set_contract": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
revision: PositiveIntegerSchema,
input_schema: Schema.optional(Schema.NullOr(JsonObjectSchema)),
state_schema: Schema.optional(Schema.NullOr(JsonObjectSchema)),
output_schema: Schema.optional(Schema.NullOr(JsonObjectSchema)),
outcomes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),
}),
success: DraftWorkspaceSchema,
},
"workflow.draft_workspaces.set_start": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
revision: PositiveIntegerSchema,
step_id: Schema.String.pipe(Schema.minLength(1)),
}),
success: DraftWorkspaceSchema,
},
"workflow.draft_workspaces.create_from_capability": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
@@ -574,6 +650,14 @@ export const authoredRpcSchemas = {
}),
success: DraftWorkspaceSchema,
},
"workflow.draft_workspaces.set_workflow_output_bindings": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
revision: PositiveIntegerSchema,
bindings: Schema.Array(Schema.Union(InputPathBindingSchema, InputValueBindingSchema)),
}),
success: DraftWorkspaceSchema,
},
"workflow.draft_workspaces.validate": {
payload: Schema.Struct({
workspace_id: Schema.String.pipe(Schema.minLength(1)),
@@ -13,14 +13,22 @@ import {
WorkflowDraftWorkspacesCreateFromCapabilityResultSchema,
WorkflowDraftWorkspacesGetPayloadSchema,
WorkflowDraftWorkspacesGetResultSchema,
WorkflowDraftWorkspacesInspectAuthoringContractPayloadSchema,
WorkflowDraftWorkspacesInspectAuthoringContractResultSchema,
WorkflowDraftWorkspacesListPayloadSchema,
WorkflowDraftWorkspacesListResultSchema,
WorkflowDraftWorkspacesSetContractPayloadSchema,
WorkflowDraftWorkspacesSetContractResultSchema,
WorkflowDraftWorkspacesSetRoutePayloadSchema,
WorkflowDraftWorkspacesSetRouteResultSchema,
WorkflowDraftWorkspacesSetStartPayloadSchema,
WorkflowDraftWorkspacesSetStartResultSchema,
WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
WorkflowDraftWorkspacesSetStepInputBindingsResultSchema,
WorkflowDraftWorkspacesSetStepOutputBindingsPayloadSchema,
WorkflowDraftWorkspacesSetStepOutputBindingsResultSchema,
WorkflowDraftWorkspacesSetWorkflowOutputBindingsPayloadSchema,
WorkflowDraftWorkspacesSetWorkflowOutputBindingsResultSchema,
WorkflowDraftWorkspacesUpdateCapabilityStepPayloadSchema,
WorkflowDraftWorkspacesUpdateCapabilityStepResultSchema,
WorkflowDraftWorkspacesValidatePayloadSchema,
@@ -203,6 +211,66 @@ const draftWorkspace = {
},
};
const authoringPathOption = {
path: "input.title",
label: "Title",
origin: "workflow_input",
schema: { type: "string" },
required: true,
availability: "available",
uses: ["step_input"],
};
const authoringContractInventory = {
workspace_id: "console.demo",
revision: 7,
selected_step_id: "render",
readable_sources: [authoringPathOption],
step_input_targets: [
{
...authoringPathOption,
path: "local.title",
origin: "step_input",
uses: ["step_input"],
},
],
step_output_sources: [
{
...authoringPathOption,
path: "local.markdown",
origin: "step_output",
uses: ["step_output_source"],
},
],
state_targets: [
{
...authoringPathOption,
path: "state.report",
origin: "workflow_state",
uses: ["state_target", "workflow_output"],
},
],
workflow_output_targets: [
{
...authoringPathOption,
path: "output.report",
origin: "workflow_output",
uses: ["workflow_output"],
},
],
entry_steps: [
{
step_id: "render",
label: "Render report",
input_targets: [],
output_sources: [],
outcomes: ["ok", "error"],
},
],
workflow_outcomes: ["ok", "error"],
warnings: [],
};
const createFromCapabilityResult = {
...draftWorkspace,
next_actions: {
@@ -460,6 +528,22 @@ const parityCases: ReadonlyArray<ParityCase> = [
draft: null,
},
},
{
method: "workflow.draft_workspaces.inspect_authoring_contract",
payload: WorkflowDraftWorkspacesInspectAuthoringContractPayloadSchema,
success: WorkflowDraftWorkspacesInspectAuthoringContractResultSchema,
validPayload: {
workspace_id: "console.demo",
revision: 7,
selected_step_id: "render",
},
invalidPayload: { workspace_id: "console.demo", revision: 0 },
validSuccess: authoringContractInventory,
invalidSuccess: {
...authoringContractInventory,
entry_steps: [{ step_id: "render" }],
},
},
{
method: "workflow.draft_workspaces.create_empty",
payload: WorkflowDraftWorkspacesCreateEmptyPayloadSchema,
@@ -590,6 +674,41 @@ const parityCases: ReadonlyArray<ParityCase> = [
summary: { ...draftWorkspace.summary, steps: [1] },
},
},
{
method: "workflow.draft_workspaces.set_contract",
payload: WorkflowDraftWorkspacesSetContractPayloadSchema,
success: WorkflowDraftWorkspacesSetContractResultSchema,
validPayload: {
workspace_id: "console.demo",
revision: 7,
input_schema: { type: "object" },
state_schema: { type: "object" },
output_schema: { type: "object" },
outcomes: ["ok", "error"],
},
invalidPayload: { workspace_id: "console.demo", revision: 0 },
validSuccess: draftWorkspace,
invalidSuccess: {
...draftWorkspace,
summary: { ...draftWorkspace.summary, steps: [1] },
},
},
{
method: "workflow.draft_workspaces.set_start",
payload: WorkflowDraftWorkspacesSetStartPayloadSchema,
success: WorkflowDraftWorkspacesSetStartResultSchema,
validPayload: {
workspace_id: "console.demo",
revision: 7,
step_id: "render",
},
invalidPayload: { workspace_id: "console.demo", revision: 7, step_id: "" },
validSuccess: draftWorkspace,
invalidSuccess: {
...draftWorkspace,
summary: { ...draftWorkspace.summary, steps: [1] },
},
},
{
method: "workflow.draft_workspaces.set_step_input_bindings",
payload: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
@@ -640,6 +759,29 @@ const parityCases: ReadonlyArray<ParityCase> = [
summary: { ...draftWorkspace.summary, steps: [1] },
},
},
{
method: "workflow.draft_workspaces.set_workflow_output_bindings",
payload: WorkflowDraftWorkspacesSetWorkflowOutputBindingsPayloadSchema,
success: WorkflowDraftWorkspacesSetWorkflowOutputBindingsResultSchema,
validPayload: {
workspace_id: "console.demo",
revision: 7,
bindings: [
{ path: "state.report", target: "report" },
{ target: "format", value: "markdown" },
],
},
invalidPayload: {
workspace_id: "console.demo",
revision: 7,
bindings: [{ expression: { kind: "literal", value: "bad" } }],
},
validSuccess: draftWorkspace,
invalidSuccess: {
...draftWorkspace,
summary: { ...draftWorkspace.summary, steps: [1] },
},
},
{
method: "workflow.draft_workspaces.validate",
payload: WorkflowDraftWorkspacesValidatePayloadSchema,
@@ -1188,13 +1330,17 @@ describe("authored RPC and manifest schema parity", () => {
"workflow.capabilities.call",
"workflow.draft_workspaces.list",
"workflow.draft_workspaces.get",
"workflow.draft_workspaces.inspect_authoring_contract",
"workflow.draft_workspaces.create_empty",
"workflow.draft_workspaces.create_from_capability",
"workflow.draft_workspaces.add_step_from_capability",
"workflow.draft_workspaces.update_capability_step",
"workflow.draft_workspaces.set_route",
"workflow.draft_workspaces.set_contract",
"workflow.draft_workspaces.set_start",
"workflow.draft_workspaces.set_step_input_bindings",
"workflow.draft_workspaces.set_step_output_bindings",
"workflow.draft_workspaces.set_workflow_output_bindings",
"workflow.draft_workspaces.validate",
"workflow.artifacts.list",
"workflow.artifacts.inspect",
@@ -1231,6 +1377,7 @@ describe("authored RPC and manifest schema parity", () => {
"workflow.draft_workspaces.update_capability_step:payload:oneOf@#/components/schemas/InputPathBinding.properties.path",
"workflow.draft_workspaces.set_step_input_bindings:payload:oneOf@#/components/schemas/InputPathBinding.properties.path",
"workflow.draft_workspaces.set_step_output_bindings:payload:oneOf@#/components/schemas/OutputBinding.properties.source",
"workflow.draft_workspaces.set_workflow_output_bindings:payload:oneOf@#/components/schemas/InputPathBinding.properties.path",
]);
});
});
@@ -27,10 +27,14 @@ describe("runtimeSchemasFor", () => {
"workflow.draft_workspaces.create_empty",
"workflow.draft_workspaces.create_from_capability",
"workflow.draft_workspaces.get",
"workflow.draft_workspaces.inspect_authoring_contract",
"workflow.draft_workspaces.list",
"workflow.draft_workspaces.set_contract",
"workflow.draft_workspaces.set_route",
"workflow.draft_workspaces.set_start",
"workflow.draft_workspaces.set_step_input_bindings",
"workflow.draft_workspaces.set_step_output_bindings",
"workflow.draft_workspaces.set_workflow_output_bindings",
"workflow.draft_workspaces.update_capability_step",
"workflow.draft_workspaces.validate",
"workflow.health",