From 40f6c5cdeb13a5bf8e52ed9fd8ba5fb5eb438595 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Wed, 28 Oct 2026 11:16:14 +0000 Subject: [PATCH 1/2] Revert "refactor: drop scheduleWorkflowName from the schema" This reverts commit 5b72b6f5c85d9e917ee83a74a01703020b74bfd9. --- constants.go | 1 + definitions.go | 7 +++++++ metadata_test.go | 12 ++++++++++++ schema.json | 7 +++++++ schema.yaml | 6 ++++++ schema_test.go | 8 ++++---- 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/constants.go b/constants.go index 5c46403..f3ddb9d 100644 --- a/constants.go +++ b/constants.go @@ -111,6 +111,7 @@ const ( propRetryPolicy = "retryPolicy" propTaskQueue = "taskQueue" propType = "type" + propScheduleWorkflowName = "scheduleWorkflowName" propScheduleWorkflowType = "scheduleWorkflowType" propScheduleID = "scheduleId" propScheduleInput = "scheduleInput" diff --git a/definitions.go b/definitions.go index f7ead7a..2186942 100644 --- a/definitions.go +++ b/definitions.go @@ -420,6 +420,13 @@ var documentMetadataDefinition = &jsonschema.Schema{ "Defaults to document.workflowType; required when every top-level task is a Do task.", MinLength: utils.Ptr(1), }, + propScheduleWorkflowName: { + Type: typeString, + Title: "ScheduleWorkflowName", + Description: "Deprecated: use scheduleWorkflowType.", + MinLength: utils.Ptr(1), + Deprecated: true, + }, propScheduleID: { Type: typeString, Title: "ScheduleID", diff --git a/metadata_test.go b/metadata_test.go index f9b6622..6405269 100644 --- a/metadata_test.go +++ b/metadata_test.go @@ -158,6 +158,7 @@ func TestDocumentMetadataDefinitionShape(t *testing.T) { for _, key := range []string{ propCanMaxHistory, + propScheduleWorkflowName, propScheduleWorkflowType, propScheduleID, propScheduleInput, @@ -167,6 +168,8 @@ func TestDocumentMetadataDefinitionShape(t *testing.T) { } assert.Equal(t, typeInteger, def.Properties[propCanMaxHistory].Type) + assert.Equal(t, typeString, def.Properties[propScheduleWorkflowName].Type) + assert.True(t, def.Properties[propScheduleWorkflowName].Deprecated, "scheduleWorkflowName is a deprecated alias") assert.Equal(t, typeString, def.Properties[propScheduleWorkflowType].Type) assert.False(t, def.Properties[propScheduleWorkflowType].Deprecated) assert.Equal(t, typeString, def.Properties[propScheduleID].Type) @@ -224,6 +227,10 @@ func TestDocumentMetadata_Validation(t *testing.T) { expectError bool }{ // ---- valid cases ---- + { + name: "scheduleWorkflowName string is accepted", + meta: map[string]any{"scheduleWorkflowName": testWorkflowType}, + }, { name: "scheduleWorkflowType string is accepted", meta: map[string]any{propScheduleWorkflowType: testWorkflowType}, @@ -262,6 +269,11 @@ func TestDocumentMetadata_Validation(t *testing.T) { }, // ---- invalid cases ---- + { + name: "scheduleWorkflowName as integer is rejected", + meta: map[string]any{"scheduleWorkflowName": 42}, + expectError: true, + }, { name: "scheduleWorkflowType as integer is rejected", meta: map[string]any{propScheduleWorkflowType: 42}, diff --git a/schema.json b/schema.json index e33add0..24a1879 100644 --- a/schema.json +++ b/schema.json @@ -517,6 +517,13 @@ "title": "ScheduleInput", "description": "Set the input." }, + "scheduleWorkflowName": { + "type": "string", + "title": "ScheduleWorkflowName", + "description": "Deprecated: use scheduleWorkflowType.", + "deprecated": true, + "minLength": 1 + }, "scheduleWorkflowType": { "type": "string", "title": "ScheduleWorkflowType", diff --git a/schema.yaml b/schema.yaml index ed93248..22414d6 100644 --- a/schema.yaml +++ b/schema.yaml @@ -305,6 +305,12 @@ $defs: description: Set the input. title: ScheduleInput type: array + scheduleWorkflowName: + deprecated: true + description: 'Deprecated: use scheduleWorkflowType.' + minLength: 1 + title: ScheduleWorkflowName + type: string scheduleWorkflowType: description: 'The workflow type the schedule starts: the document.workflowType or a top-level Do task. Defaults to document.workflowType; required when diff --git a/schema_test.go b/schema_test.go index 986249c..b21b7a4 100644 --- a/schema_test.go +++ b/schema_test.go @@ -107,10 +107,10 @@ func TestSchema_ScheduleNeedsNoWorkflowKey(t *testing.T) { resolved := resolvedTestSchema(t) for name, meta := range map[string]map[string]any{ - "no document.metadata": nil, - "metadata without a key": {propScheduleID: "my-schedule"}, - "the workflow type key": {propScheduleWorkflowType: testWorkflowType}, - "the old key, now unlisted": {"scheduleWorkflowName": testWorkflowType}, + "no document.metadata": nil, + "metadata without a key": {propScheduleID: "my-schedule"}, + "the workflow type key": {propScheduleWorkflowType: testWorkflowType}, + "the deprecated alias": {propScheduleWorkflowName: testWorkflowType}, } { t.Run(name, func(t *testing.T) { doc := minimalWorkflow() From 901e2739d7a998a51b2b8608b001e49193072e88 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Wed, 28 Oct 2026 11:16:14 +0000 Subject: [PATCH 2/2] Revert "feat: add scheduleWorkflowType and deprecate scheduleWorkflowName" This reverts commit 44f9b58cbc9e3363ba86a22f325d670840156640. Signed-off-by: Simon Emms --- constants.go | 1 - definitions.go | 10 +-------- metadata_test.go | 20 +---------------- schema.go | 16 +++++++++++++ schema.json | 35 +++++++++++++++++++++-------- schema.yaml | 25 +++++++++++++-------- schema_test.go | 58 ++++++++++++++++++++++++++++-------------------- 7 files changed, 94 insertions(+), 71 deletions(-) diff --git a/constants.go b/constants.go index f3ddb9d..92c1046 100644 --- a/constants.go +++ b/constants.go @@ -112,7 +112,6 @@ const ( propTaskQueue = "taskQueue" propType = "type" propScheduleWorkflowName = "scheduleWorkflowName" - propScheduleWorkflowType = "scheduleWorkflowType" propScheduleID = "scheduleId" propScheduleInput = "scheduleInput" propSeconds = "seconds" diff --git a/definitions.go b/definitions.go index 2186942..e4326ad 100644 --- a/definitions.go +++ b/definitions.go @@ -413,19 +413,11 @@ var documentMetadataDefinition = &jsonschema.Schema{ Title: "ContinueAsNewMaxHistoryLength", Description: "Allows you to test the Continue-As-New functionality by specifying the max history length before triggering.", }, - propScheduleWorkflowType: { - Type: typeString, - Title: "ScheduleWorkflowType", - Description: "The workflow type the schedule starts: the document.workflowType or a top-level Do task. " + - "Defaults to document.workflowType; required when every top-level task is a Do task.", - MinLength: utils.Ptr(1), - }, propScheduleWorkflowName: { Type: typeString, Title: "ScheduleWorkflowName", - Description: "Deprecated: use scheduleWorkflowType.", + Description: "Set the workflow name to trigger - this will either be the document.workflowType or the Do task", MinLength: utils.Ptr(1), - Deprecated: true, }, propScheduleID: { Type: typeString, diff --git a/metadata_test.go b/metadata_test.go index 6405269..6b177c4 100644 --- a/metadata_test.go +++ b/metadata_test.go @@ -159,7 +159,6 @@ func TestDocumentMetadataDefinitionShape(t *testing.T) { for _, key := range []string{ propCanMaxHistory, propScheduleWorkflowName, - propScheduleWorkflowType, propScheduleID, propScheduleInput, } { @@ -169,9 +168,6 @@ func TestDocumentMetadataDefinitionShape(t *testing.T) { assert.Equal(t, typeInteger, def.Properties[propCanMaxHistory].Type) assert.Equal(t, typeString, def.Properties[propScheduleWorkflowName].Type) - assert.True(t, def.Properties[propScheduleWorkflowName].Deprecated, "scheduleWorkflowName is a deprecated alias") - assert.Equal(t, typeString, def.Properties[propScheduleWorkflowType].Type) - assert.False(t, def.Properties[propScheduleWorkflowType].Deprecated) assert.Equal(t, typeString, def.Properties[propScheduleID].Type) assert.Equal(t, typeArray, def.Properties[propScheduleInput].Type) } @@ -229,11 +225,7 @@ func TestDocumentMetadata_Validation(t *testing.T) { // ---- valid cases ---- { name: "scheduleWorkflowName string is accepted", - meta: map[string]any{"scheduleWorkflowName": testWorkflowType}, - }, - { - name: "scheduleWorkflowType string is accepted", - meta: map[string]any{propScheduleWorkflowType: testWorkflowType}, + meta: map[string]any{"scheduleWorkflowName": "my-workflow"}, }, { name: "scheduleId string is accepted", @@ -274,16 +266,6 @@ func TestDocumentMetadata_Validation(t *testing.T) { meta: map[string]any{"scheduleWorkflowName": 42}, expectError: true, }, - { - name: "scheduleWorkflowType as integer is rejected", - meta: map[string]any{propScheduleWorkflowType: 42}, - expectError: true, - }, - { - name: "empty scheduleWorkflowType is rejected", - meta: map[string]any{propScheduleWorkflowType: ""}, - expectError: true, - }, { name: "canMaxHistoryLength as string is rejected", meta: map[string]any{propCanMaxHistory: "not-an-int"}, diff --git a/schema.go b/schema.go index e093ee4..f2bc917 100644 --- a/schema.go +++ b/schema.go @@ -36,6 +36,22 @@ func BuildSchema(version, format string) (*jsonschema.Schema, error) { Required: []string{propDo, propDocument}, Properties: schemaProperties, Defs: buildDefinitions(), + If: &jsonschema.Schema{ + Required: []string{"schedule"}, + }, + Then: &jsonschema.Schema{ + Required: []string{propDocument}, + Properties: map[string]*jsonschema.Schema{ + propDocument: { + Required: []string{propMetadata}, + Properties: map[string]*jsonschema.Schema{ + propMetadata: { + Required: []string{propScheduleWorkflowName}, + }, + }, + }, + }, + }, } // Validate the generated schema diff --git a/schema.json b/schema.json index 24a1879..8bc006c 100644 --- a/schema.json +++ b/schema.json @@ -520,14 +520,7 @@ "scheduleWorkflowName": { "type": "string", "title": "ScheduleWorkflowName", - "description": "Deprecated: use scheduleWorkflowType.", - "deprecated": true, - "minLength": 1 - }, - "scheduleWorkflowType": { - "type": "string", - "title": "ScheduleWorkflowType", - "description": "The workflow type the schedule starts: the document.workflowType or a top-level Do task. Defaults to document.workflowType; required when every top-level task is a Do task.", + "description": "Set the workflow name to trigger - this will either be the document.workflowType or the Do task", "minLength": 1 } }, @@ -1841,5 +1834,29 @@ "required": [ "do", "document" - ] + ], + "if": { + "required": [ + "schedule" + ] + }, + "then": { + "properties": { + "document": { + "properties": { + "metadata": { + "required": [ + "scheduleWorkflowName" + ] + } + }, + "required": [ + "metadata" + ] + } + }, + "required": [ + "document" + ] + } } diff --git a/schema.yaml b/schema.yaml index 22414d6..15944dc 100644 --- a/schema.yaml +++ b/schema.yaml @@ -306,18 +306,11 @@ $defs: title: ScheduleInput type: array scheduleWorkflowName: - deprecated: true - description: 'Deprecated: use scheduleWorkflowType.' + description: Set the workflow name to trigger - this will either be the document.workflowType + or the Do task minLength: 1 title: ScheduleWorkflowName type: string - scheduleWorkflowType: - description: 'The workflow type the schedule starts: the document.workflowType - or a top-level Do task. Defaults to document.workflowType; required when - every top-level task is a Do task.' - minLength: 1 - title: ScheduleWorkflowType - type: string title: DocumentMetadata type: object duration: @@ -1252,6 +1245,9 @@ $defs: $id: https://zigflow.dev/schema.yaml $schema: https://json-schema.org/draft/2020-12/schema description: JSON Schema for a Zigflow workflow definition. +if: + required: + - schedule properties: do: $ref: '#/$defs/taskList' @@ -1343,5 +1339,16 @@ properties: required: - do - document +then: + properties: + document: + properties: + metadata: + required: + - scheduleWorkflowName + required: + - metadata + required: + - document title: Zigflow type: object diff --git a/schema_test.go b/schema_test.go index b21b7a4..6d01839 100644 --- a/schema_test.go +++ b/schema_test.go @@ -23,10 +23,7 @@ import ( "github.com/stretchr/testify/require" ) -const ( - testCronExpr = "0 0 * * *" - testWorkflowType = "my-workflow" -) +const testCronExpr = "0 0 * * *" // minimalWorkflow returns a minimal structurally valid workflow document. // It is used as a baseline for validation tests so that each test only @@ -100,28 +97,41 @@ func TestSchema_DocumentFields(t *testing.T) { }) } -// TestSchema_ScheduleNeedsNoWorkflowKey verifies that a schedule validates -// without a workflow key: the runtime defaults it to document.workflowType, and -// only it can tell when that does not apply (every top-level task a Do task). -func TestSchema_ScheduleNeedsNoWorkflowKey(t *testing.T) { +// TestSchema_ScheduleRequiresScheduleWorkflowName verifies the conditional +// if/then rule: when top-level schedule is present, document.metadata must +// exist and contain scheduleWorkflowName. +func TestSchema_ScheduleRequiresScheduleWorkflowName(t *testing.T) { resolved := resolvedTestSchema(t) - for name, meta := range map[string]map[string]any{ - "no document.metadata": nil, - "metadata without a key": {propScheduleID: "my-schedule"}, - "the workflow type key": {propScheduleWorkflowType: testWorkflowType}, - "the deprecated alias": {propScheduleWorkflowName: testWorkflowType}, - } { - t.Run(name, func(t *testing.T) { - doc := minimalWorkflow() - doc["schedule"] = map[string]any{propCron: testCronExpr} - if meta != nil { - doc["document"].(map[string]any)["metadata"] = meta - } - - assert.NoError(t, resolved.Validate(doc), "a schedule must validate with %s", name) - }) - } + t.Run("schedule present without document.metadata is rejected", func(t *testing.T) { + doc := minimalWorkflow() + doc["schedule"] = map[string]any{propCron: testCronExpr} + + err := resolved.Validate(doc) + assert.Error(t, err, "schedule without document.metadata must fail validation") + }) + + t.Run("schedule present with document.metadata but missing scheduleWorkflowName is rejected", func(t *testing.T) { + doc := minimalWorkflow() + doc["schedule"] = map[string]any{propCron: testCronExpr} + doc["document"].(map[string]any)["metadata"] = map[string]any{ + propScheduleID: "my-schedule", + } + + err := resolved.Validate(doc) + assert.Error(t, err, "schedule with document.metadata but no scheduleWorkflowName must fail validation") + }) + + t.Run("schedule present with valid document.metadata.scheduleWorkflowName is accepted", func(t *testing.T) { + doc := minimalWorkflow() + doc["schedule"] = map[string]any{propCron: testCronExpr} + doc["document"].(map[string]any)["metadata"] = map[string]any{ + propScheduleWorkflowName: "my-workflow", + } + + err := resolved.Validate(doc) + assert.NoError(t, err, "schedule with valid scheduleWorkflowName must pass validation") + }) } // TestSchema_RejectsUnknownTopLevelProperties verifies that the root schema