Skip to content

Commit 3211f57

Browse files
GPT-5.2uvwt
authored andcommitted
fix(task): declare create-only required fields
1 parent e30ad78 commit 3211f57

2 files changed

Lines changed: 42 additions & 5 deletions

File tree

internal/app/runtime_validation_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,31 @@ func TestRuntimeCallEnforcesRequiredEnumBoundsAndOneOf(t *testing.T) {
6060
}
6161
}
6262

63+
func TestRuntimeCallEnforcesTaskCreateRequiredFields(t *testing.T) {
64+
runtime := newRuntimeValidationTestRuntime(t)
65+
valid := map[string]any{
66+
"action": "create",
67+
"title": "schema contract",
68+
"goal": "match runtime requirements",
69+
"completion_conditions": []any{"task is created"},
70+
}
71+
for _, field := range []string{"title", "goal", "completion_conditions"} {
72+
args := make(map[string]any, len(valid)-1)
73+
for key, value := range valid {
74+
if key != field {
75+
args[key] = value
76+
}
77+
}
78+
t.Run("missing_"+field, func(t *testing.T) {
79+
assertInvalidToolArguments(t, runtime, "task_manage", args)
80+
})
81+
}
82+
83+
if _, err := runtime.Call(context.Background(), "task_manage", valid); err != nil {
84+
t.Fatalf("schema-complete task create failed: %v", err)
85+
}
86+
}
87+
6388
func TestRuntimeCallRejectsNestedUnknownFields(t *testing.T) {
6489
runtime := newRuntimeValidationTestRuntime(t)
6590
assertInvalidToolArguments(t, runtime, "task_manage", map[string]any{

internal/tool/task/contract.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ func ManageInputSchema(cfg config.Config) map[string]any {
1313
props := map[string]any{
1414
"action": map[string]any{"type": "string", "description": "Task lifecycle action. Use checkpoint to update live step progress.", "enum": []string{"create", "list", "get", "checkpoint", "block", "resume", "final_review", "complete"}},
1515
"task_id": stringProp("Persistent task id for get, checkpoint, block, resume, final_review, or complete."),
16-
"title": stringProp("Short task title for create."),
17-
"goal": stringProp("Fixed task goal for create."),
18-
"completion_conditions": map[string]any{"type": "array", "minItems": 1, "items": map[string]any{"type": "string"}, "description": "Conditions that must be true before final_review can pass."},
16+
"title": stringProp("Short task title. Required for action=create."),
17+
"goal": stringProp("Fixed task goal. Required for action=create."),
18+
"completion_conditions": map[string]any{"type": "array", "minItems": 1, "items": map[string]any{"type": "string"}, "description": "Conditions that must be true before final_review can pass. Required for action=create."},
1919
"step_id": stringProp("Task step id for a single-step checkpoint."),
2020
"completed_step_ids": map[string]any{"type": "array", "minItems": 1, "maxItems": 12, "uniqueItems": true, "items": map[string]any{"type": "string"}, "description": "Task step ids to mark completed in one atomic batch checkpoint."},
2121
"current_step_id": stringProp("Single task step id to mark in_progress in a batch checkpoint."),
@@ -33,7 +33,7 @@ func ManageInputSchema(cfg config.Config) map[string]any {
3333
"type": "array", "maxItems": 12, "description": "Concrete task steps.",
3434
"items": map[string]any{"type": "object", "additionalProperties": false, "required": []string{"id", "title"}, "properties": map[string]any{"id": stringProp("Stable step id."), "title": stringProp("Human-readable step title.")}},
3535
}
36-
return toolcontract.InputObject(props, "action")
36+
return taskManageInputObject(props)
3737
}
3838

3939
props["project"] = stringProp("Optional project identifier used to hard-scope Evolution guidance and evidence candidates. Omit only for global tasks.")
@@ -55,7 +55,19 @@ func ManageInputSchema(cfg config.Config) map[string]any {
5555
},
5656
},
5757
}
58-
return toolcontract.InputObject(props, "action")
58+
return taskManageInputObject(props)
59+
}
60+
61+
func taskManageInputObject(props map[string]any) map[string]any {
62+
schema := toolcontract.InputObject(props, "action")
63+
schema["allOf"] = []any{map[string]any{
64+
"if": map[string]any{
65+
"properties": map[string]any{"action": map[string]any{"const": "create"}},
66+
"required": []string{"action"},
67+
},
68+
"then": map[string]any{"required": []string{"title", "goal", "completion_conditions"}},
69+
}}
70+
return schema
5971
}
6072

6173
func ManageOutputSchema(cfg config.Config) map[string]any {

0 commit comments

Comments
 (0)